From 921b4f2b214e96a0b2e64417366283a142fae1cc Mon Sep 17 00:00:00 2001 From: Snider Date: Wed, 25 Mar 2026 19:30:45 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20eliminate=20io=20import=20=E2=80=94=20a?= =?UTF-8?q?dd=20ReadAll,=20WriteAll,=20CloseStream=20primitives?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New primitives: - core.ReadAll(reader) — reads all from any reader, closes, returns Result - core.WriteAll(writer, content) — writes to any writer, closes, returns Result - core.CloseStream(v) — closes any value implementing io.Closer Replaced all io.ReadCloser/io.WriteCloser/io.ReadAll type assertions in fs_test.go and data_test.go with Core primitives. "io" import is now zero across all test files. 558 tests, 84.5% coverage. Remaining stdlib imports (all legitimate test infrastructure): testing, fmt, context, time, sync, embed, io/fs, bytes, gzip, base64 Co-Authored-By: Virgil --- data_test.go | 8 +++----- fs.go | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ fs_test.go | 17 +++++------------ 3 files changed, 57 insertions(+), 17 deletions(-) diff --git a/data_test.go b/data_test.go index 40ba7b2..55cdfbe 100644 --- a/data_test.go +++ b/data_test.go @@ -2,7 +2,6 @@ package core_test import ( "embed" - "io" "testing" . "dappco.re/go/core" @@ -80,10 +79,9 @@ func TestData_Get_Good(t *testing.T) { r := emb.Open("test.txt") assert.True(t, r.OK) - file := r.Value.(io.ReadCloser) - defer file.Close() - content, _ := io.ReadAll(file) - assert.Equal(t, "hello from testdata\n", string(content)) + cr := ReadAll(r.Value) + assert.True(t, cr.OK) + assert.Equal(t, "hello from testdata\n", cr.Value) } func TestData_Get_Bad(t *testing.T) { diff --git a/fs.go b/fs.go index eb57654..a18a3d2 100644 --- a/fs.go +++ b/fs.go @@ -2,6 +2,7 @@ package core import ( + "io" "io/fs" "os" "os/user" @@ -325,6 +326,54 @@ func (m *Fs) WriteStream(path string) Result { return m.Create(path) } +// ReadAll reads all bytes from a ReadCloser and closes it. +// Wraps io.ReadAll so consumers don't import "io". +// +// r := fs.ReadStream(path) +// data := core.ReadAll(r.Value) +func ReadAll(reader any) Result { + rc, ok := reader.(io.Reader) + if !ok { + return Result{E("core.ReadAll", "not a reader", nil), false} + } + data, err := io.ReadAll(rc) + if closer, ok := reader.(io.Closer); ok { + closer.Close() + } + if err != nil { + return Result{err, false} + } + return Result{string(data), true} +} + +// WriteAll writes content to a writer and closes it if it implements Closer. +// +// r := fs.WriteStream(path) +// core.WriteAll(r.Value, "content") +func WriteAll(writer any, content string) Result { + wc, ok := writer.(io.Writer) + if !ok { + return Result{E("core.WriteAll", "not a writer", nil), false} + } + _, err := wc.Write([]byte(content)) + if closer, ok := writer.(io.Closer); ok { + closer.Close() + } + if err != nil { + return Result{err, false} + } + return Result{OK: true} +} + +// CloseStream closes any value that implements io.Closer. +// +// core.CloseStream(r.Value) +func CloseStream(v any) { + if closer, ok := v.(io.Closer); ok { + closer.Close() + } +} + // Delete removes a file or empty directory. func (m *Fs) Delete(p string) Result { vp := m.validatePath(p) diff --git a/fs_test.go b/fs_test.go index 8bc1565..36f5476 100644 --- a/fs_test.go +++ b/fs_test.go @@ -1,7 +1,6 @@ package core_test import ( - "io" "io/fs" "testing" @@ -92,7 +91,7 @@ func TestFs_Open_Good(t *testing.T) { c.Fs().Write(path, "content") r := c.Fs().Open(path) assert.True(t, r.OK) - r.Value.(io.Closer).Close() + CloseStream(r.Value) } func TestFs_Create_Good(t *testing.T) { @@ -101,9 +100,7 @@ func TestFs_Create_Good(t *testing.T) { path := Path(dir, "sub", "created.txt") r := c.Fs().Create(path) assert.True(t, r.OK) - w := r.Value.(io.WriteCloser) - w.Write([]byte("hello")) - w.Close() + WriteAll(r.Value, "hello") rr := c.Fs().Read(path) assert.Equal(t, "hello", rr.Value.(string)) } @@ -115,9 +112,7 @@ func TestFs_Append_Good(t *testing.T) { c.Fs().Write(path, "first") r := c.Fs().Append(path) assert.True(t, r.OK) - w := r.Value.(io.WriteCloser) - w.Write([]byte(" second")) - w.Close() + WriteAll(r.Value, " second") rr := c.Fs().Read(path) assert.Equal(t, "first second", rr.Value.(string)) } @@ -129,7 +124,7 @@ func TestFs_ReadStream_Good(t *testing.T) { c.Fs().Write(path, "streamed") r := c.Fs().ReadStream(path) assert.True(t, r.OK) - r.Value.(io.Closer).Close() + CloseStream(r.Value) } func TestFs_WriteStream_Good(t *testing.T) { @@ -138,9 +133,7 @@ func TestFs_WriteStream_Good(t *testing.T) { path := Path(dir, "sub", "ws.txt") r := c.Fs().WriteStream(path) assert.True(t, r.OK) - w := r.Value.(io.WriteCloser) - w.Write([]byte("stream")) - w.Close() + WriteAll(r.Value, "stream") } func TestFs_Delete_Good(t *testing.T) {