summaryrefslogtreecommitdiff
path: root/download.go
diff options
context:
space:
mode:
authorpablu <me@pablu.de>2026-08-05 13:06:36 +0200
committerpablu <me@pablu.de>2026-08-05 16:11:59 +0200
commitaa9d403d09186b5b0cc9da3b322c1ba53c0f832a (patch)
tree02ce07ae0a15e01f3f03bb469ccc2cb9fede56cc /download.go
parent67916c5d1c7001bab5bf08b6fec3a7f14ea4dd64 (diff)
feat: create working client
Diffstat (limited to 'download.go')
-rw-r--r--download.go71
1 files changed, 71 insertions, 0 deletions
diff --git a/download.go b/download.go
new file mode 100644
index 0000000..19dd1b2
--- /dev/null
+++ b/download.go
@@ -0,0 +1,71 @@
+package zw
+
+import (
+ "context"
+ "io"
+ "io/fs"
+ "sync"
+ "sync/atomic"
+)
+
+type downloadReader struct {
+ recv func() ([]byte, error)
+ cancel context.CancelFunc
+
+ pending []byte
+ terminal error
+
+ cancelOnce sync.Once
+ closed atomic.Bool
+}
+
+func (r *downloadReader) Read(destination []byte) (int, error) {
+ if len(destination) == 0 {
+ return 0, nil
+ }
+
+ if r.closed.Load() {
+ return 0, fs.ErrClosed
+ }
+
+ if len(r.pending) > 0 {
+ n := copy(destination, r.pending)
+ r.pending = r.pending[n:]
+ return n, nil
+ }
+
+ if r.terminal != nil {
+ return 0, r.terminal
+ }
+
+ for {
+ chunk, err := r.recv()
+ if err != nil {
+ if r.closed.Load() {
+ return 0, fs.ErrClosed
+ }
+
+ if err == io.EOF {
+ r.terminal = io.EOF
+ } else {
+ r.terminal = convertRPCError(err)
+ }
+ r.cancelOnce.Do(r.cancel)
+ return 0, r.terminal
+ }
+
+ if len(chunk) == 0 {
+ continue
+ }
+
+ n := copy(destination, chunk)
+ r.pending = chunk[n:]
+ return n, nil
+ }
+}
+
+func (r *downloadReader) Close() error {
+ r.closed.Store(true)
+ r.cancelOnce.Do(r.cancel)
+ return nil
+}