From 2bcd120d8666da572396d0916869c3ab293e1c0b Mon Sep 17 00:00:00 2001 From: Team Humaki Date: Thu, 17 Sep 2026 05:23:18 -0700 Subject: [PATCH] data_source: snapshot readers so Append can reload --- data_source.go | 21 +++++++++++++++++++-- file_test.go | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/data_source.go b/data_source.go index 6e2572b..b45acfa 100644 --- a/data_source.go +++ b/data_source.go @@ -59,6 +59,21 @@ func (s *sourceReadCloser) ReadCloser() (io.ReadCloser, error) { return s.reader, nil } +func snapshotReader(r io.Reader, c io.Closer) (dataSource, error) { + data, err := io.ReadAll(r) + var cerr error + if c != nil { + cerr = c.Close() + } + if err != nil { + return nil, err + } + if cerr != nil { + return nil, cerr + } + return &sourceData{data}, nil +} + func parseDataSource(source interface{}) (dataSource, error) { switch s := source.(type) { case string: @@ -66,9 +81,11 @@ func parseDataSource(source interface{}) (dataSource, error) { case []byte: return &sourceData{s}, nil case io.ReadCloser: - return &sourceReadCloser{s}, nil + // Snapshot so Reload/Append can re-read after the original closer is + // consumed (io.Reader is not rewindable; Close made a second pass fail). + return snapshotReader(s, s) case io.Reader: - return &sourceReadCloser{io.NopCloser(s)}, nil + return snapshotReader(s, nil) default: return nil, fmt.Errorf("error parsing data source: unknown type %q", s) } diff --git a/file_test.go b/file_test.go index 306c0c8..50ef4e2 100644 --- a/file_test.go +++ b/file_test.go @@ -16,9 +16,11 @@ package ini import ( "bytes" + "io" "os" "runtime" "sort" + "strings" "testing" "github.com/stretchr/testify/assert" @@ -399,6 +401,41 @@ NAME = Unknwon`))) }) } +type closeOnceReader struct { + r *strings.Reader + closed bool +} + +func (c *closeOnceReader) Read(p []byte) (int, error) { + if c.closed { + return 0, os.ErrClosed + } + return c.r.Read(p) +} + +func (c *closeOnceReader) Close() error { + c.closed = true + return nil +} + +func TestFile_AppendReadersTwice(t *testing.T) { + f := Empty() + require.NotNil(t, f) + + r1 := &closeOnceReader{r: strings.NewReader("[one]\na = 1\n")} + require.NoError(t, f.Append(r1)) + assert.Equal(t, "1", f.Section("one").Key("a").String()) + + r2 := io.NopCloser(strings.NewReader("[two]\nb = 2\n")) + require.NoError(t, f.Append(r2)) + assert.Equal(t, "1", f.Section("one").Key("a").String()) + assert.Equal(t, "2", f.Section("two").Key("b").String()) + + require.NoError(t, f.Reload()) + assert.Equal(t, "1", f.Section("one").Key("a").String()) + assert.Equal(t, "2", f.Section("two").Key("b").String()) +} + func TestFile_WriteTo(t *testing.T) { if runtime.GOOS == "windows" { t.Skip("Skipping testing on Windows")