From 97ec0f1fc43fc197369c92775e1c4b094e5ae0a7 Mon Sep 17 00:00:00 2001 From: Ofer Chen Date: Wed, 27 Aug 2025 19:26:11 +0200 Subject: [PATCH] refactor: share write-only helper --- cmd/dump/client.go | 9 +++------ internal/common/writeonly.go | 9 +++++++++ internal/common/writeonly_test.go | 23 +++++++++++++++++++++++ transfer/delta.go | 7 ++----- 4 files changed, 37 insertions(+), 11 deletions(-) create mode 100644 internal/common/writeonly.go create mode 100644 internal/common/writeonly_test.go diff --git a/cmd/dump/client.go b/cmd/dump/client.go index 579a93f2..6da7f93d 100644 --- a/cmd/dump/client.go +++ b/cmd/dump/client.go @@ -16,6 +16,7 @@ import ( rootcmd "lvmsync_go/cmd/root" "lvmsync_go/common" "lvmsync_go/device" + internalcommon "lvmsync_go/internal/common" "lvmsync_go/internal/config" cpufeatures "lvmsync_go/internal/cpufeatures" digestpkg "lvmsync_go/internal/digest" @@ -197,10 +198,6 @@ var copyBufferPool = sync.Pool{New: func() any { return &buf }} -type writeOnlyReadWriter struct{ io.Writer } - -func (writeOnlyReadWriter) Read(p []byte) (int, error) { return 0, io.EOF } - type contextReader struct { ctx context.Context r io.Reader @@ -549,7 +546,7 @@ func (r *Runner) StreamToRemote(ctx context.Context, cfg *config.Config, remoteS return fmt.Errorf("compute digest: %w", err) } - rw := writeOnlyReadWriter{remoteStdin} + rw := internalcommon.WriteOnlyReadWriter{Writer: remoteStdin} cl := rsyncwire.NewClient(rsyncwire.NewStream(rw, maxFrame)) if err := cl.SendDigest(ctx, alg, sum); err != nil { remoteStdin.Close() @@ -582,7 +579,7 @@ func (r *Runner) streamRsyncDelta(ctx context.Context, cfg *config.Config, remot } defer common.CloseWithErr(orig, &err, "close origin") - rw := writeOnlyReadWriter{remoteStdin} + rw := internalcommon.WriteOnlyReadWriter{Writer: remoteStdin} cl := rsyncwire.NewClient(rsyncwire.NewStream(rw, maxFrame)) esc, err := privilege.New(ctx, logger) if err != nil { diff --git a/internal/common/writeonly.go b/internal/common/writeonly.go new file mode 100644 index 00000000..f8a6df05 --- /dev/null +++ b/internal/common/writeonly.go @@ -0,0 +1,9 @@ +package common + +import "io" + +// WriteOnlyReadWriter wraps an io.Writer to satisfy io.ReadWriter. +type WriteOnlyReadWriter struct{ io.Writer } + +// Read always returns io.EOF, making it safe to use where a read side is required. +func (WriteOnlyReadWriter) Read([]byte) (int, error) { return 0, io.EOF } diff --git a/internal/common/writeonly_test.go b/internal/common/writeonly_test.go new file mode 100644 index 00000000..046d1b23 --- /dev/null +++ b/internal/common/writeonly_test.go @@ -0,0 +1,23 @@ +package common + +import ( + "bytes" + "io" + "testing" +) + +func TestWriteOnlyReadWriter(t *testing.T) { + var buf bytes.Buffer + rw := WriteOnlyReadWriter{Writer: &buf} + if _, err := rw.Write([]byte("hello")); err != nil { + t.Fatalf("unexpected write error: %v", err) + } + b := make([]byte, 1) + n, err := rw.Read(b) + if n != 0 || err != io.EOF { + t.Fatalf("Read() = %d, %v; want 0, io.EOF", n, err) + } + if got := buf.String(); got != "hello" { + t.Fatalf("unexpected buffer content: %q", got) + } +} diff --git a/transfer/delta.go b/transfer/delta.go index e9ba1eb9..8d33d8cd 100644 --- a/transfer/delta.go +++ b/transfer/delta.go @@ -13,6 +13,7 @@ import ( "lvmsync_go/common" "lvmsync_go/dedup" "lvmsync_go/device" + internalcommon "lvmsync_go/internal/common" "lvmsync_go/internal/config" digestpkg "lvmsync_go/internal/digest" "lvmsync_go/internal/privilege" @@ -22,10 +23,6 @@ import ( const rsyncMaxFrame = 1 << 20 -type writeOnlyReadWriter struct{ io.Writer } - -func (w writeOnlyReadWriter) Read([]byte) (int, error) { return 0, io.EOF } - // streamRsyncDelta performs a byte-level delta pre-pass using rsyncwire. // It streams signatures and deltas followed by a digest frame. // When out implements io.Closer, it is closed on completion. @@ -57,7 +54,7 @@ func (t *Transfer) streamRsyncDelta(ctx context.Context, cfg *config.Config, sna rwOK bool ) if rw, rwOK = out.(io.ReadWriter); !rwOK { - rw = writeOnlyReadWriter{out} + rw = internalcommon.WriteOnlyReadWriter{Writer: out} } stream = rsyncwire.NewStream(rw, rsyncMaxFrame) cl := rsyncwire.NewClient(stream)