diff --git a/datanode/medium.go b/datanode/medium.go index 177c6ac..2cd39fe 100644 --- a/datanode/medium.go +++ b/datanode/medium.go @@ -38,6 +38,8 @@ type Medium struct { lock sync.RWMutex } +// Example: medium := datanode.New() +// Example: _ = medium.Write("jobs/run.log", "started") func New() *Medium { return &Medium{ dataNode: borgdatanode.New(), diff --git a/io.go b/io.go index 20fc2fa..3c3634f 100644 --- a/io.go +++ b/io.go @@ -15,31 +15,43 @@ import ( // Example: backup, _ := io.NewSandboxed("/srv/backup") // Example: _ = io.Copy(medium, "data/report.json", backup, "daily/report.json") type Medium interface { + // Example: content, _ := medium.Read("config/app.yaml") Read(path string) (string, error) + // Example: _ = medium.Write("config/app.yaml", "port: 8080") Write(path, content string) error // Example: _ = medium.WriteMode("keys/private.key", key, 0600) WriteMode(path, content string, mode fs.FileMode) error + // Example: _ = medium.EnsureDir("config/app") EnsureDir(path string) error + // Example: isFile := medium.IsFile("config/app.yaml") IsFile(path string) bool + // Example: _ = medium.Delete("config/app.yaml") Delete(path string) error + // Example: _ = medium.DeleteAll("logs/archive") DeleteAll(path string) error + // Example: _ = medium.Rename("drafts/todo.txt", "archive/todo.txt") Rename(oldPath, newPath string) error + // Example: entries, _ := medium.List("config") List(path string) ([]fs.DirEntry, error) + // Example: info, _ := medium.Stat("config/app.yaml") Stat(path string) (fs.FileInfo, error) + // Example: file, _ := medium.Open("config/app.yaml") Open(path string) (fs.File, error) + // Example: writer, _ := medium.Create("logs/app.log") Create(path string) (goio.WriteCloser, error) + // Example: writer, _ := medium.Append("logs/app.log") Append(path string) (goio.WriteCloser, error) // Example: reader, _ := medium.ReadStream("logs/app.log") diff --git a/local/medium.go b/local/medium.go index 7c9c104..39e6cc4 100644 --- a/local/medium.go +++ b/local/medium.go @@ -238,6 +238,7 @@ func (medium *Medium) WriteMode(path, content string, mode fs.FileMode) error { return resultError("local.WriteMode", core.Concat("write failed: ", path), unrestrictedFileSystem.WriteMode(resolvedPath, content, mode)) } +// Example: _ = medium.EnsureDir("config/app") func (medium *Medium) EnsureDir(path string) error { resolvedPath, err := medium.validatePath(path) if err != nil { @@ -246,6 +247,7 @@ func (medium *Medium) EnsureDir(path string) error { return resultError("local.EnsureDir", core.Concat("ensure dir failed: ", path), unrestrictedFileSystem.EnsureDir(resolvedPath)) } +// Example: isDirectory := medium.IsDir("config") func (medium *Medium) IsDir(path string) bool { if path == "" { return false @@ -257,6 +259,7 @@ func (medium *Medium) IsDir(path string) bool { return unrestrictedFileSystem.IsDir(resolvedPath) } +// Example: isFile := medium.IsFile("config/app.yaml") func (medium *Medium) IsFile(path string) bool { if path == "" { return false @@ -268,6 +271,7 @@ func (medium *Medium) IsFile(path string) bool { return unrestrictedFileSystem.IsFile(resolvedPath) } +// Example: exists := medium.Exists("config/app.yaml") func (medium *Medium) Exists(path string) bool { resolvedPath, err := medium.validatePath(path) if err != nil { @@ -276,6 +280,7 @@ func (medium *Medium) Exists(path string) bool { return unrestrictedFileSystem.Exists(resolvedPath) } +// Example: entries, _ := medium.List("config") func (medium *Medium) List(path string) ([]fs.DirEntry, error) { resolvedPath, err := medium.validatePath(path) if err != nil { @@ -284,6 +289,7 @@ func (medium *Medium) List(path string) ([]fs.DirEntry, error) { return resultDirEntries("local.List", core.Concat("list failed: ", path), unrestrictedFileSystem.List(resolvedPath)) } +// Example: info, _ := medium.Stat("config/app.yaml") func (medium *Medium) Stat(path string) (fs.FileInfo, error) { resolvedPath, err := medium.validatePath(path) if err != nil { @@ -292,6 +298,7 @@ func (medium *Medium) Stat(path string) (fs.FileInfo, error) { return resultFileInfo("local.Stat", core.Concat("stat failed: ", path), unrestrictedFileSystem.Stat(resolvedPath)) } +// Example: file, _ := medium.Open("config/app.yaml") func (medium *Medium) Open(path string) (fs.File, error) { resolvedPath, err := medium.validatePath(path) if err != nil { @@ -300,6 +307,7 @@ func (medium *Medium) Open(path string) (fs.File, error) { return resultFile("local.Open", core.Concat("open failed: ", path), unrestrictedFileSystem.Open(resolvedPath)) } +// Example: writer, _ := medium.Create("logs/app.log") func (medium *Medium) Create(path string) (goio.WriteCloser, error) { resolvedPath, err := medium.validatePath(path) if err != nil { @@ -308,6 +316,7 @@ func (medium *Medium) Create(path string) (goio.WriteCloser, error) { return resultWriteCloser("local.Create", core.Concat("create failed: ", path), unrestrictedFileSystem.Create(resolvedPath)) } +// Example: writer, _ := medium.Append("logs/app.log") func (medium *Medium) Append(path string) (goio.WriteCloser, error) { resolvedPath, err := medium.validatePath(path) if err != nil { @@ -326,6 +335,7 @@ func (medium *Medium) WriteStream(path string) (goio.WriteCloser, error) { return medium.Create(path) } +// Example: _ = medium.Delete("config/app.yaml") func (medium *Medium) Delete(path string) error { resolvedPath, err := medium.validatePath(path) if err != nil { @@ -337,6 +347,7 @@ func (medium *Medium) Delete(path string) error { return resultError("local.Delete", core.Concat("delete failed: ", path), unrestrictedFileSystem.Delete(resolvedPath)) } +// Example: _ = medium.DeleteAll("logs/archive") func (medium *Medium) DeleteAll(path string) error { resolvedPath, err := medium.validatePath(path) if err != nil { @@ -348,6 +359,7 @@ func (medium *Medium) DeleteAll(path string) error { return resultError("local.DeleteAll", core.Concat("delete all failed: ", path), unrestrictedFileSystem.DeleteAll(resolvedPath)) } +// Example: _ = medium.Rename("drafts/todo.txt", "archive/todo.txt") func (medium *Medium) Rename(oldPath, newPath string) error { oldResolvedPath, err := medium.validatePath(oldPath) if err != nil { diff --git a/node/node.go b/node/node.go index 3ada8e7..deb8349 100644 --- a/node/node.go +++ b/node/node.go @@ -232,6 +232,7 @@ func (node *Node) CopyTo(target coreio.Medium, sourcePath, destPath string) erro return nil } +// Example: file, _ := nodeTree.Open("config/app.yaml") func (node *Node) Open(name string) (fs.File, error) { name = core.TrimPrefix(name, "/") if dataFile, ok := node.files[name]; ok { @@ -249,6 +250,7 @@ func (node *Node) Open(name string) (fs.File, error) { return nil, core.E("node.Open", core.Concat("path not found: ", name), fs.ErrNotExist) } +// Example: info, _ := nodeTree.Stat("config/app.yaml") func (node *Node) Stat(name string) (fs.FileInfo, error) { name = core.TrimPrefix(name, "/") if dataFile, ok := node.files[name]; ok { @@ -266,6 +268,7 @@ func (node *Node) Stat(name string) (fs.FileInfo, error) { return nil, core.E("node.Stat", core.Concat("path not found: ", name), fs.ErrNotExist) } +// Example: entries, _ := nodeTree.ReadDir("config") func (node *Node) ReadDir(name string) ([]fs.DirEntry, error) { name = core.TrimPrefix(name, "/") if name == "." { @@ -314,6 +317,7 @@ func (node *Node) ReadDir(name string) ([]fs.DirEntry, error) { return entries, nil } +// Example: content, _ := nodeTree.Read("config/app.yaml") func (node *Node) Read(filePath string) (string, error) { filePath = core.TrimPrefix(filePath, "/") file, ok := node.files[filePath] @@ -323,11 +327,13 @@ func (node *Node) Read(filePath string) (string, error) { return string(file.content), nil } +// Example: _ = nodeTree.Write("config/app.yaml", "port: 8080") func (node *Node) Write(filePath, content string) error { node.AddData(filePath, []byte(content)) return nil } +// Example: _ = nodeTree.WriteMode("keys/private.key", key, 0600) func (node *Node) WriteMode(filePath, content string, mode fs.FileMode) error { return node.Write(filePath, content) } @@ -337,17 +343,20 @@ func (node *Node) EnsureDir(directoryPath string) error { return nil } +// Example: exists := nodeTree.Exists("config/app.yaml") func (node *Node) Exists(filePath string) bool { _, err := node.Stat(filePath) return err == nil } +// Example: isFile := nodeTree.IsFile("config/app.yaml") func (node *Node) IsFile(filePath string) bool { filePath = core.TrimPrefix(filePath, "/") _, ok := node.files[filePath] return ok } +// Example: isDirectory := nodeTree.IsDir("config") func (node *Node) IsDir(filePath string) bool { info, err := node.Stat(filePath) if err != nil { @@ -356,6 +365,7 @@ func (node *Node) IsDir(filePath string) bool { return info.IsDir() } +// Example: _ = nodeTree.Delete("config/app.yaml") func (node *Node) Delete(filePath string) error { filePath = core.TrimPrefix(filePath, "/") if _, ok := node.files[filePath]; ok { @@ -365,6 +375,7 @@ func (node *Node) Delete(filePath string) error { return core.E("node.Delete", core.Concat("path not found: ", filePath), fs.ErrNotExist) } +// Example: _ = nodeTree.DeleteAll("logs/archive") func (node *Node) DeleteAll(filePath string) error { filePath = core.TrimPrefix(filePath, "/") @@ -388,6 +399,7 @@ func (node *Node) DeleteAll(filePath string) error { return nil } +// Example: _ = nodeTree.Rename("drafts/todo.txt", "archive/todo.txt") func (node *Node) Rename(oldPath, newPath string) error { oldPath = core.TrimPrefix(oldPath, "/") newPath = core.TrimPrefix(newPath, "/") @@ -403,6 +415,7 @@ func (node *Node) Rename(oldPath, newPath string) error { return nil } +// Example: entries, _ := nodeTree.List("config") func (node *Node) List(filePath string) ([]fs.DirEntry, error) { filePath = core.TrimPrefix(filePath, "/") if filePath == "" || filePath == "." { @@ -411,11 +424,13 @@ func (node *Node) List(filePath string) ([]fs.DirEntry, error) { return node.ReadDir(filePath) } +// Example: writer, _ := nodeTree.Create("logs/app.log") func (node *Node) Create(filePath string) (goio.WriteCloser, error) { filePath = core.TrimPrefix(filePath, "/") return &nodeWriter{node: node, path: filePath}, nil } +// Example: writer, _ := nodeTree.Append("logs/app.log") func (node *Node) Append(filePath string) (goio.WriteCloser, error) { filePath = core.TrimPrefix(filePath, "/") var existing []byte diff --git a/node/node_test.go b/node/node_test.go index 279f8aa..2e783e6 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -15,16 +15,16 @@ import ( ) func TestNode_New_Good(t *testing.T) { - n := New() - require.NotNil(t, n, "New() must not return nil") - assert.NotNil(t, n.files, "New() must initialise the files map") + nodeTree := New() + require.NotNil(t, nodeTree, "New() must not return nil") + assert.NotNil(t, nodeTree.files, "New() must initialise the files map") } func TestNode_AddData_Good(t *testing.T) { - n := New() - n.AddData("foo.txt", []byte("foo")) + nodeTree := New() + nodeTree.AddData("foo.txt", []byte("foo")) - file, ok := n.files["foo.txt"] + file, ok := nodeTree.files["foo.txt"] require.True(t, ok, "file foo.txt should be present") assert.Equal(t, []byte("foo"), file.content) @@ -34,38 +34,38 @@ func TestNode_AddData_Good(t *testing.T) { } func TestNode_AddData_Bad(t *testing.T) { - n := New() + nodeTree := New() - n.AddData("", []byte("data")) - assert.Empty(t, n.files, "empty name must not be stored") + nodeTree.AddData("", []byte("data")) + assert.Empty(t, nodeTree.files, "empty name must not be stored") - n.AddData("dir/", nil) - assert.Empty(t, n.files, "directory entry must not be stored") + nodeTree.AddData("dir/", nil) + assert.Empty(t, nodeTree.files, "directory entry must not be stored") } func TestNode_AddData_EdgeCases_Good(t *testing.T) { t.Run("Overwrite", func(t *testing.T) { - n := New() - n.AddData("foo.txt", []byte("foo")) - n.AddData("foo.txt", []byte("bar")) + nodeTree := New() + nodeTree.AddData("foo.txt", []byte("foo")) + nodeTree.AddData("foo.txt", []byte("bar")) - file := n.files["foo.txt"] + file := nodeTree.files["foo.txt"] assert.Equal(t, []byte("bar"), file.content, "second AddData should overwrite") }) t.Run("LeadingSlash", func(t *testing.T) { - n := New() - n.AddData("/hello.txt", []byte("hi")) - _, ok := n.files["hello.txt"] + nodeTree := New() + nodeTree.AddData("/hello.txt", []byte("hi")) + _, ok := nodeTree.files["hello.txt"] assert.True(t, ok, "leading slash should be trimmed") }) } func TestNode_Open_Good(t *testing.T) { - n := New() - n.AddData("foo.txt", []byte("foo")) + nodeTree := New() + nodeTree.AddData("foo.txt", []byte("foo")) - file, err := n.Open("foo.txt") + file, err := nodeTree.Open("foo.txt") require.NoError(t, err) defer file.Close() @@ -76,17 +76,17 @@ func TestNode_Open_Good(t *testing.T) { } func TestNode_Open_Bad(t *testing.T) { - n := New() - _, err := n.Open("nonexistent.txt") + nodeTree := New() + _, err := nodeTree.Open("nonexistent.txt") require.Error(t, err) assert.ErrorIs(t, err, fs.ErrNotExist) } func TestNode_Open_Directory_Good(t *testing.T) { - n := New() - n.AddData("bar/baz.txt", []byte("baz")) + nodeTree := New() + nodeTree.AddData("bar/baz.txt", []byte("baz")) - file, err := n.Open("bar") + file, err := nodeTree.Open("bar") require.NoError(t, err) defer file.Close() @@ -99,88 +99,88 @@ func TestNode_Open_Directory_Good(t *testing.T) { } func TestNode_Stat_Good(t *testing.T) { - n := New() - n.AddData("foo.txt", []byte("foo")) - n.AddData("bar/baz.txt", []byte("baz")) + nodeTree := New() + nodeTree.AddData("foo.txt", []byte("foo")) + nodeTree.AddData("bar/baz.txt", []byte("baz")) - info, err := n.Stat("bar/baz.txt") + info, err := nodeTree.Stat("bar/baz.txt") require.NoError(t, err) assert.Equal(t, "baz.txt", info.Name()) assert.Equal(t, int64(3), info.Size()) assert.False(t, info.IsDir()) - dirInfo, err := n.Stat("bar") + dirInfo, err := nodeTree.Stat("bar") require.NoError(t, err) assert.True(t, dirInfo.IsDir()) assert.Equal(t, "bar", dirInfo.Name()) } func TestNode_Stat_Bad(t *testing.T) { - n := New() - _, err := n.Stat("nonexistent") + nodeTree := New() + _, err := nodeTree.Stat("nonexistent") require.Error(t, err) assert.ErrorIs(t, err, fs.ErrNotExist) } func TestNode_Stat_RootDirectory_Good(t *testing.T) { - n := New() - n.AddData("foo.txt", []byte("foo")) + nodeTree := New() + nodeTree.AddData("foo.txt", []byte("foo")) - info, err := n.Stat(".") + info, err := nodeTree.Stat(".") require.NoError(t, err) assert.True(t, info.IsDir()) assert.Equal(t, ".", info.Name()) } func TestNode_ReadFile_Good(t *testing.T) { - n := New() - n.AddData("hello.txt", []byte("hello world")) + nodeTree := New() + nodeTree.AddData("hello.txt", []byte("hello world")) - data, err := n.ReadFile("hello.txt") + data, err := nodeTree.ReadFile("hello.txt") require.NoError(t, err) assert.Equal(t, []byte("hello world"), data) } func TestNode_ReadFile_Bad(t *testing.T) { - n := New() - _, err := n.ReadFile("missing.txt") + nodeTree := New() + _, err := nodeTree.ReadFile("missing.txt") require.Error(t, err) assert.ErrorIs(t, err, fs.ErrNotExist) } func TestNode_ReadFile_ReturnsCopy_Good(t *testing.T) { - n := New() - n.AddData("data.bin", []byte("original")) + nodeTree := New() + nodeTree.AddData("data.bin", []byte("original")) - data, err := n.ReadFile("data.bin") + data, err := nodeTree.ReadFile("data.bin") require.NoError(t, err) data[0] = 'X' - data2, err := n.ReadFile("data.bin") + data2, err := nodeTree.ReadFile("data.bin") require.NoError(t, err) assert.Equal(t, []byte("original"), data2, "ReadFile must return an independent copy") } func TestNode_ReadDir_Good(t *testing.T) { - n := New() - n.AddData("foo.txt", []byte("foo")) - n.AddData("bar/baz.txt", []byte("baz")) - n.AddData("bar/qux.txt", []byte("qux")) + nodeTree := New() + nodeTree.AddData("foo.txt", []byte("foo")) + nodeTree.AddData("bar/baz.txt", []byte("baz")) + nodeTree.AddData("bar/qux.txt", []byte("qux")) - entries, err := n.ReadDir(".") + entries, err := nodeTree.ReadDir(".") require.NoError(t, err) assert.Equal(t, []string{"bar", "foo.txt"}, sortedNames(entries)) - barEntries, err := n.ReadDir("bar") + barEntries, err := nodeTree.ReadDir("bar") require.NoError(t, err) assert.Equal(t, []string{"baz.txt", "qux.txt"}, sortedNames(barEntries)) } func TestNode_ReadDir_Bad(t *testing.T) { - n := New() - n.AddData("foo.txt", []byte("foo")) + nodeTree := New() + nodeTree.AddData("foo.txt", []byte("foo")) - _, err := n.ReadDir("foo.txt") + _, err := nodeTree.ReadDir("foo.txt") require.Error(t, err) var pathErr *fs.PathError require.True(t, core.As(err, &pathErr)) @@ -188,45 +188,45 @@ func TestNode_ReadDir_Bad(t *testing.T) { } func TestNode_ReadDir_IgnoresEmptyEntry_Good(t *testing.T) { - n := New() - n.AddData("bar/baz.txt", []byte("baz")) - n.AddData("empty_dir/", nil) + nodeTree := New() + nodeTree.AddData("bar/baz.txt", []byte("baz")) + nodeTree.AddData("empty_dir/", nil) - entries, err := n.ReadDir(".") + entries, err := nodeTree.ReadDir(".") require.NoError(t, err) assert.Equal(t, []string{"bar"}, sortedNames(entries)) } func TestNode_Exists_Good(t *testing.T) { - n := New() - n.AddData("foo.txt", []byte("foo")) - n.AddData("bar/baz.txt", []byte("baz")) + nodeTree := New() + nodeTree.AddData("foo.txt", []byte("foo")) + nodeTree.AddData("bar/baz.txt", []byte("baz")) - assert.True(t, n.Exists("foo.txt")) - assert.True(t, n.Exists("bar")) + assert.True(t, nodeTree.Exists("foo.txt")) + assert.True(t, nodeTree.Exists("bar")) } func TestNode_Exists_Bad(t *testing.T) { - n := New() - assert.False(t, n.Exists("nonexistent")) + nodeTree := New() + assert.False(t, nodeTree.Exists("nonexistent")) } func TestNode_Exists_RootAndEmptyPath_Good(t *testing.T) { - n := New() - n.AddData("dummy.txt", []byte("dummy")) + nodeTree := New() + nodeTree.AddData("dummy.txt", []byte("dummy")) - assert.True(t, n.Exists("."), "root '.' must exist") - assert.True(t, n.Exists(""), "empty path (root) must exist") + assert.True(t, nodeTree.Exists("."), "root '.' must exist") + assert.True(t, nodeTree.Exists(""), "empty path (root) must exist") } func TestNode_Walk_Default_Good(t *testing.T) { - n := New() - n.AddData("foo.txt", []byte("foo")) - n.AddData("bar/baz.txt", []byte("baz")) - n.AddData("bar/qux.txt", []byte("qux")) + nodeTree := New() + nodeTree.AddData("foo.txt", []byte("foo")) + nodeTree.AddData("bar/baz.txt", []byte("baz")) + nodeTree.AddData("bar/qux.txt", []byte("qux")) var paths []string - err := n.Walk(".", func(p string, d fs.DirEntry, err error) error { + err := nodeTree.Walk(".", func(p string, d fs.DirEntry, err error) error { paths = append(paths, p) return nil }, WalkOptions{}) @@ -237,10 +237,10 @@ func TestNode_Walk_Default_Good(t *testing.T) { } func TestNode_Walk_Default_Bad(t *testing.T) { - n := New() + nodeTree := New() var called bool - err := n.Walk("nonexistent", func(p string, d fs.DirEntry, err error) error { + err := nodeTree.Walk("nonexistent", func(p string, d fs.DirEntry, err error) error { called = true assert.Error(t, err) assert.ErrorIs(t, err, fs.ErrNotExist) @@ -251,13 +251,13 @@ func TestNode_Walk_Default_Bad(t *testing.T) { } func TestNode_Walk_CallbackError_Good(t *testing.T) { - n := New() - n.AddData("a/b.txt", []byte("b")) - n.AddData("a/c.txt", []byte("c")) + nodeTree := New() + nodeTree.AddData("a/b.txt", []byte("b")) + nodeTree.AddData("a/c.txt", []byte("c")) walkErr := core.NewError("stop walking") var paths []string - err := n.Walk(".", func(p string, d fs.DirEntry, err error) error { + err := nodeTree.Walk(".", func(p string, d fs.DirEntry, err error) error { if p == "a/b.txt" { return walkErr } @@ -269,15 +269,15 @@ func TestNode_Walk_CallbackError_Good(t *testing.T) { } func TestNode_Walk_Good(t *testing.T) { - n := New() - n.AddData("root.txt", []byte("root")) - n.AddData("a/a1.txt", []byte("a1")) - n.AddData("a/b/b1.txt", []byte("b1")) - n.AddData("c/c1.txt", []byte("c1")) + nodeTree := New() + nodeTree.AddData("root.txt", []byte("root")) + nodeTree.AddData("a/a1.txt", []byte("a1")) + nodeTree.AddData("a/b/b1.txt", []byte("b1")) + nodeTree.AddData("c/c1.txt", []byte("c1")) t.Run("MaxDepth", func(t *testing.T) { var paths []string - err := n.Walk(".", func(p string, d fs.DirEntry, err error) error { + err := nodeTree.Walk(".", func(p string, d fs.DirEntry, err error) error { paths = append(paths, p) return nil }, WalkOptions{MaxDepth: 1}) @@ -289,7 +289,7 @@ func TestNode_Walk_Good(t *testing.T) { t.Run("Filter", func(t *testing.T) { var paths []string - err := n.Walk(".", func(p string, d fs.DirEntry, err error) error { + err := nodeTree.Walk(".", func(p string, d fs.DirEntry, err error) error { paths = append(paths, p) return nil }, WalkOptions{Filter: func(p string, d fs.DirEntry) bool { @@ -303,7 +303,7 @@ func TestNode_Walk_Good(t *testing.T) { t.Run("SkipErrors", func(t *testing.T) { var called bool - err := n.Walk("nonexistent", func(p string, d fs.DirEntry, err error) error { + err := nodeTree.Walk("nonexistent", func(p string, d fs.DirEntry, err error) error { called = true return err }, WalkOptions{SkipErrors: true}) @@ -314,11 +314,11 @@ func TestNode_Walk_Good(t *testing.T) { } func TestNode_CopyFile_Good(t *testing.T) { - n := New() - n.AddData("foo.txt", []byte("foo")) + nodeTree := New() + nodeTree.AddData("foo.txt", []byte("foo")) tmpfile := core.Path(t.TempDir(), "test.txt") - err := n.CopyFile("foo.txt", tmpfile, 0644) + err := nodeTree.CopyFile("foo.txt", tmpfile, 0644) require.NoError(t, err) content, err := coreio.Local.Read(tmpfile) @@ -327,40 +327,40 @@ func TestNode_CopyFile_Good(t *testing.T) { } func TestNode_CopyFile_Bad(t *testing.T) { - n := New() + nodeTree := New() tmpfile := core.Path(t.TempDir(), "test.txt") - err := n.CopyFile("nonexistent.txt", tmpfile, 0644) + err := nodeTree.CopyFile("nonexistent.txt", tmpfile, 0644) assert.Error(t, err) - n.AddData("foo.txt", []byte("foo")) - err = n.CopyFile("foo.txt", "/nonexistent_dir/test.txt", 0644) + nodeTree.AddData("foo.txt", []byte("foo")) + err = nodeTree.CopyFile("foo.txt", "/nonexistent_dir/test.txt", 0644) assert.Error(t, err) } func TestNode_CopyFile_DirectorySource_Bad(t *testing.T) { - n := New() - n.AddData("bar/baz.txt", []byte("baz")) + nodeTree := New() + nodeTree.AddData("bar/baz.txt", []byte("baz")) tmpfile := core.Path(t.TempDir(), "test.txt") - err := n.CopyFile("bar", tmpfile, 0644) + err := nodeTree.CopyFile("bar", tmpfile, 0644) assert.Error(t, err) } func TestNode_CopyTo_Good(t *testing.T) { - n := New() - n.AddData("config/app.yaml", []byte("port: 8080")) - n.AddData("config/env/app.env", []byte("MODE=test")) + nodeTree := New() + nodeTree.AddData("config/app.yaml", []byte("port: 8080")) + nodeTree.AddData("config/env/app.env", []byte("MODE=test")) fileTarget := coreio.NewMemoryMedium() - err := n.CopyTo(fileTarget, "config/app.yaml", "backup/app.yaml") + err := nodeTree.CopyTo(fileTarget, "config/app.yaml", "backup/app.yaml") require.NoError(t, err) content, err := fileTarget.Read("backup/app.yaml") require.NoError(t, err) assert.Equal(t, "port: 8080", content) dirTarget := coreio.NewMemoryMedium() - err = n.CopyTo(dirTarget, "config", "backup/config") + err = nodeTree.CopyTo(dirTarget, "config", "backup/config") require.NoError(t, err) content, err = dirTarget.Read("backup/config/app.yaml") require.NoError(t, err) @@ -371,35 +371,35 @@ func TestNode_CopyTo_Good(t *testing.T) { } func TestNode_CopyTo_Bad(t *testing.T) { - n := New() - err := n.CopyTo(coreio.NewMemoryMedium(), "missing", "backup/missing") + nodeTree := New() + err := nodeTree.CopyTo(coreio.NewMemoryMedium(), "missing", "backup/missing") assert.Error(t, err) } func TestNode_MediumFacade_Good(t *testing.T) { - n := New() + nodeTree := New() - require.NoError(t, n.Write("docs/readme.txt", "hello")) - require.NoError(t, n.WriteMode("docs/mode.txt", "mode", 0600)) - require.NoError(t, n.Write("docs/guide.txt", "guide")) - require.NoError(t, n.EnsureDir("ignored")) + require.NoError(t, nodeTree.Write("docs/readme.txt", "hello")) + require.NoError(t, nodeTree.WriteMode("docs/mode.txt", "mode", 0600)) + require.NoError(t, nodeTree.Write("docs/guide.txt", "guide")) + require.NoError(t, nodeTree.EnsureDir("ignored")) - value, err := n.Read("docs/readme.txt") + value, err := nodeTree.Read("docs/readme.txt") require.NoError(t, err) assert.Equal(t, "hello", value) - value, err = n.Read("docs/guide.txt") + value, err = nodeTree.Read("docs/guide.txt") require.NoError(t, err) assert.Equal(t, "guide", value) - assert.True(t, n.IsFile("docs/readme.txt")) - assert.True(t, n.IsDir("docs")) + assert.True(t, nodeTree.IsFile("docs/readme.txt")) + assert.True(t, nodeTree.IsDir("docs")) - entries, err := n.List("docs") + entries, err := nodeTree.List("docs") require.NoError(t, err) assert.Equal(t, []string{"guide.txt", "mode.txt", "readme.txt"}, sortedNames(entries)) - file, err := n.Open("docs/readme.txt") + file, err := nodeTree.Open("docs/readme.txt") require.NoError(t, err) info, err := file.Stat() require.NoError(t, err) @@ -409,7 +409,7 @@ func TestNode_MediumFacade_Good(t *testing.T) { assert.Nil(t, info.Sys()) require.NoError(t, file.Close()) - dir, err := n.Open("docs") + dir, err := nodeTree.Open("docs") require.NoError(t, err) dirInfo, err := dir.Stat() require.NoError(t, err) @@ -419,47 +419,47 @@ func TestNode_MediumFacade_Good(t *testing.T) { assert.Nil(t, dirInfo.Sys()) require.NoError(t, dir.Close()) - createWriter, err := n.Create("docs/generated.txt") + createWriter, err := nodeTree.Create("docs/generated.txt") require.NoError(t, err) _, err = createWriter.Write([]byte("generated")) require.NoError(t, err) require.NoError(t, createWriter.Close()) - appendWriter, err := n.Append("docs/generated.txt") + appendWriter, err := nodeTree.Append("docs/generated.txt") require.NoError(t, err) _, err = appendWriter.Write([]byte(" content")) require.NoError(t, err) require.NoError(t, appendWriter.Close()) - streamReader, err := n.ReadStream("docs/generated.txt") + streamReader, err := nodeTree.ReadStream("docs/generated.txt") require.NoError(t, err) streamData, err := io.ReadAll(streamReader) require.NoError(t, err) assert.Equal(t, "generated content", string(streamData)) require.NoError(t, streamReader.Close()) - writeStream, err := n.WriteStream("docs/stream.txt") + writeStream, err := nodeTree.WriteStream("docs/stream.txt") require.NoError(t, err) _, err = writeStream.Write([]byte("stream")) require.NoError(t, err) require.NoError(t, writeStream.Close()) - require.NoError(t, n.Rename("docs/stream.txt", "docs/stream-renamed.txt")) - assert.True(t, n.Exists("docs/stream-renamed.txt")) + require.NoError(t, nodeTree.Rename("docs/stream.txt", "docs/stream-renamed.txt")) + assert.True(t, nodeTree.Exists("docs/stream-renamed.txt")) - require.NoError(t, n.Delete("docs/stream-renamed.txt")) - assert.False(t, n.Exists("docs/stream-renamed.txt")) + require.NoError(t, nodeTree.Delete("docs/stream-renamed.txt")) + assert.False(t, nodeTree.Exists("docs/stream-renamed.txt")) - require.NoError(t, n.DeleteAll("docs")) - assert.False(t, n.Exists("docs")) + require.NoError(t, nodeTree.DeleteAll("docs")) + assert.False(t, nodeTree.Exists("docs")) } func TestNode_ToTar_Good(t *testing.T) { - n := New() - n.AddData("foo.txt", []byte("foo")) - n.AddData("bar/baz.txt", []byte("baz")) + nodeTree := New() + nodeTree.AddData("foo.txt", []byte("foo")) + nodeTree.AddData("bar/baz.txt", []byte("baz")) - tarball, err := n.ToTar() + tarball, err := nodeTree.ToTar() require.NoError(t, err) require.NotEmpty(t, tarball) @@ -500,11 +500,11 @@ func TestNode_FromTar_Good(t *testing.T) { } require.NoError(t, tw.Close()) - n, err := FromTar(buf.Bytes()) + nodeTree, err := FromTar(buf.Bytes()) require.NoError(t, err) - assert.True(t, n.Exists("foo.txt"), "foo.txt should exist") - assert.True(t, n.Exists("bar/baz.txt"), "bar/baz.txt should exist") + assert.True(t, nodeTree.Exists("foo.txt"), "foo.txt should exist") + assert.True(t, nodeTree.Exists("bar/baz.txt"), "bar/baz.txt should exist") } func TestNode_FromTar_Bad(t *testing.T) { @@ -514,41 +514,41 @@ func TestNode_FromTar_Bad(t *testing.T) { } func TestNode_TarRoundTrip_Good(t *testing.T) { - n1 := New() - n1.AddData("a.txt", []byte("alpha")) - n1.AddData("b/c.txt", []byte("charlie")) + nodeTree1 := New() + nodeTree1.AddData("a.txt", []byte("alpha")) + nodeTree1.AddData("b/c.txt", []byte("charlie")) - tarball, err := n1.ToTar() + tarball, err := nodeTree1.ToTar() require.NoError(t, err) - n2, err := FromTar(tarball) + nodeTree2, err := FromTar(tarball) require.NoError(t, err) - data, err := n2.ReadFile("a.txt") + data, err := nodeTree2.ReadFile("a.txt") require.NoError(t, err) assert.Equal(t, []byte("alpha"), data) - data, err = n2.ReadFile("b/c.txt") + data, err = nodeTree2.ReadFile("b/c.txt") require.NoError(t, err) assert.Equal(t, []byte("charlie"), data) } func TestNode_FSInterface_Good(t *testing.T) { - n := New() - n.AddData("hello.txt", []byte("world")) + nodeTree := New() + nodeTree.AddData("hello.txt", []byte("world")) - var fsys fs.FS = n + var fsys fs.FS = nodeTree file, err := fsys.Open("hello.txt") require.NoError(t, err) defer file.Close() - var statFS fs.StatFS = n + var statFS fs.StatFS = nodeTree info, err := statFS.Stat("hello.txt") require.NoError(t, err) assert.Equal(t, "hello.txt", info.Name()) assert.Equal(t, int64(5), info.Size()) - var readFS fs.ReadFileFS = n + var readFS fs.ReadFileFS = nodeTree data, err := readFS.ReadFile("hello.txt") require.NoError(t, err) assert.Equal(t, []byte("world"), data) diff --git a/s3/s3.go b/s3/s3.go index 58ea246..5baf152 100644 --- a/s3/s3.go +++ b/s3/s3.go @@ -119,6 +119,7 @@ func (medium *Medium) objectKey(filePath string) string { return medium.prefix + clean } +// Example: content, _ := medium.Read("reports/daily.txt") func (medium *Medium) Read(filePath string) (string, error) { key := medium.objectKey(filePath) if key == "" { @@ -141,6 +142,7 @@ func (medium *Medium) Read(filePath string) (string, error) { return string(data), nil } +// Example: _ = medium.Write("reports/daily.txt", "done") func (medium *Medium) Write(filePath, content string) error { key := medium.objectKey(filePath) if key == "" { @@ -184,6 +186,7 @@ func (medium *Medium) IsFile(filePath string) bool { return err == nil } +// Example: _ = medium.Delete("reports/daily.txt") func (medium *Medium) Delete(filePath string) error { key := medium.objectKey(filePath) if key == "" { @@ -470,6 +473,7 @@ func (medium *Medium) Append(filePath string) (goio.WriteCloser, error) { }, nil } +// Example: reader, _ := medium.ReadStream("reports/daily.txt") func (medium *Medium) ReadStream(filePath string) (goio.ReadCloser, error) { key := medium.objectKey(filePath) if key == "" { @@ -486,6 +490,7 @@ func (medium *Medium) ReadStream(filePath string) (goio.ReadCloser, error) { return out.Body, nil } +// Example: writer, _ := medium.WriteStream("reports/daily.txt") func (medium *Medium) WriteStream(filePath string) (goio.WriteCloser, error) { return medium.Create(filePath) } diff --git a/sqlite/sqlite.go b/sqlite/sqlite.go index e852f9b..b3d4475 100644 --- a/sqlite/sqlite.go +++ b/sqlite/sqlite.go @@ -88,6 +88,7 @@ func normaliseEntryPath(filePath string) string { return core.TrimPrefix(clean, "/") } +// Example: content, _ := medium.Read("config/app.yaml") func (medium *Medium) Read(filePath string) (string, error) { key := normaliseEntryPath(filePath) if key == "" { @@ -111,6 +112,7 @@ func (medium *Medium) Read(filePath string) (string, error) { return string(content), nil } +// Example: _ = medium.Write("config/app.yaml", "port: 8080") func (medium *Medium) Write(filePath, content string) error { return medium.WriteMode(filePath, content, 0644) } @@ -151,6 +153,7 @@ func (medium *Medium) EnsureDir(filePath string) error { return nil } +// Example: isFile := medium.IsFile("config/app.yaml") func (medium *Medium) IsFile(filePath string) bool { key := normaliseEntryPath(filePath) if key == "" { @@ -401,6 +404,7 @@ func (medium *Medium) List(filePath string) ([]fs.DirEntry, error) { return entries, nil } +// Example: info, _ := medium.Stat("config/app.yaml") func (medium *Medium) Stat(filePath string) (fs.FileInfo, error) { key := normaliseEntryPath(filePath) if key == "" { @@ -431,6 +435,7 @@ func (medium *Medium) Stat(filePath string) (fs.FileInfo, error) { }, nil } +// Example: file, _ := medium.Open("config/app.yaml") func (medium *Medium) Open(filePath string) (fs.File, error) { key := normaliseEntryPath(filePath) if key == "" { @@ -462,6 +467,7 @@ func (medium *Medium) Open(filePath string) (fs.File, error) { }, nil } +// Example: writer, _ := medium.Create("logs/app.log") func (medium *Medium) Create(filePath string) (goio.WriteCloser, error) { key := normaliseEntryPath(filePath) if key == "" { @@ -473,6 +479,7 @@ func (medium *Medium) Create(filePath string) (goio.WriteCloser, error) { }, nil } +// Example: writer, _ := medium.Append("logs/app.log") func (medium *Medium) Append(filePath string) (goio.WriteCloser, error) { key := normaliseEntryPath(filePath) if key == "" { @@ -494,6 +501,7 @@ func (medium *Medium) Append(filePath string) (goio.WriteCloser, error) { }, nil } +// Example: reader, _ := medium.ReadStream("logs/app.log") func (medium *Medium) ReadStream(filePath string) (goio.ReadCloser, error) { key := normaliseEntryPath(filePath) if key == "" { @@ -518,10 +526,12 @@ func (medium *Medium) ReadStream(filePath string) (goio.ReadCloser, error) { return goio.NopCloser(bytes.NewReader(content)), nil } +// Example: writer, _ := medium.WriteStream("logs/app.log") func (medium *Medium) WriteStream(filePath string) (goio.WriteCloser, error) { return medium.Create(filePath) } +// Example: exists := medium.Exists("config/app.yaml") func (medium *Medium) Exists(filePath string) bool { key := normaliseEntryPath(filePath) if key == "" { @@ -538,6 +548,7 @@ func (medium *Medium) Exists(filePath string) bool { return count > 0 } +// Example: isDirectory := medium.IsDir("config") func (medium *Medium) IsDir(filePath string) bool { key := normaliseEntryPath(filePath) if key == "" { diff --git a/store/medium_test.go b/store/medium_test.go index c45a89e..7ea7bec 100644 --- a/store/medium_test.go +++ b/store/medium_test.go @@ -11,10 +11,10 @@ import ( func newTestKeyValueMedium(t *testing.T) *Medium { t.Helper() - m, err := NewMedium(Options{Path: ":memory:"}) + medium, err := NewMedium(Options{Path: ":memory:"}) require.NoError(t, err) - t.Cleanup(func() { m.Close() }) - return m + t.Cleanup(func() { medium.Close() }) + return medium } func TestKeyValueMedium_ReadWrite_Good(t *testing.T) { diff --git a/store/store.go b/store/store.go index 65b36a2..f59d818 100644 --- a/store/store.go +++ b/store/store.go @@ -18,6 +18,7 @@ type Store struct { database *sql.DB } +// Example: keyValueStore, _ := store.New(store.Options{Path: ":memory:"}) type Options struct { Path string } diff --git a/store/store_test.go b/store/store_test.go index 74df399..07e49a8 100644 --- a/store/store_test.go +++ b/store/store_test.go @@ -10,17 +10,17 @@ import ( func newTestStore(t *testing.T) *Store { t.Helper() - s, err := New(Options{Path: ":memory:"}) + keyValueStore, err := New(Options{Path: ":memory:"}) require.NoError(t, err) t.Cleanup(func() { - require.NoError(t, s.Close()) + require.NoError(t, keyValueStore.Close()) }) - return s + return keyValueStore } func TestStore_New_Options_Good(t *testing.T) { - s := newTestStore(t) - assert.NotNil(t, s) + keyValueStore := newTestStore(t) + assert.NotNil(t, keyValueStore) } func TestStore_New_Options_Bad(t *testing.T) { @@ -29,86 +29,86 @@ func TestStore_New_Options_Bad(t *testing.T) { } func TestStore_SetGet_Good(t *testing.T) { - s := newTestStore(t) + keyValueStore := newTestStore(t) - err := s.Set("config", "theme", "dark") + err := keyValueStore.Set("config", "theme", "dark") require.NoError(t, err) - val, err := s.Get("config", "theme") + val, err := keyValueStore.Get("config", "theme") require.NoError(t, err) assert.Equal(t, "dark", val) } func TestStore_Get_NotFound_Bad(t *testing.T) { - s := newTestStore(t) + keyValueStore := newTestStore(t) - _, err := s.Get("config", "missing") + _, err := keyValueStore.Get("config", "missing") assert.ErrorIs(t, err, NotFoundError) } func TestStore_Delete_Good(t *testing.T) { - s := newTestStore(t) + keyValueStore := newTestStore(t) - _ = s.Set("config", "key", "val") - err := s.Delete("config", "key") + _ = keyValueStore.Set("config", "key", "val") + err := keyValueStore.Delete("config", "key") require.NoError(t, err) - _, err = s.Get("config", "key") + _, err = keyValueStore.Get("config", "key") assert.ErrorIs(t, err, NotFoundError) } func TestStore_Count_Good(t *testing.T) { - s := newTestStore(t) + keyValueStore := newTestStore(t) - _ = s.Set("group", "a", "1") - _ = s.Set("group", "b", "2") - _ = s.Set("other", "c", "3") + _ = keyValueStore.Set("group", "a", "1") + _ = keyValueStore.Set("group", "b", "2") + _ = keyValueStore.Set("other", "c", "3") - n, err := s.Count("group") + count, err := keyValueStore.Count("group") require.NoError(t, err) - assert.Equal(t, 2, n) + assert.Equal(t, 2, count) } func TestStore_DeleteGroup_Good(t *testing.T) { - s := newTestStore(t) + keyValueStore := newTestStore(t) - _ = s.Set("group", "a", "1") - _ = s.Set("group", "b", "2") - err := s.DeleteGroup("group") + _ = keyValueStore.Set("group", "a", "1") + _ = keyValueStore.Set("group", "b", "2") + err := keyValueStore.DeleteGroup("group") require.NoError(t, err) - n, _ := s.Count("group") - assert.Equal(t, 0, n) + count, _ := keyValueStore.Count("group") + assert.Equal(t, 0, count) } func TestStore_GetAll_Good(t *testing.T) { - s := newTestStore(t) + keyValueStore := newTestStore(t) - _ = s.Set("group", "a", "1") - _ = s.Set("group", "b", "2") - _ = s.Set("other", "c", "3") + _ = keyValueStore.Set("group", "a", "1") + _ = keyValueStore.Set("group", "b", "2") + _ = keyValueStore.Set("other", "c", "3") - all, err := s.GetAll("group") + all, err := keyValueStore.GetAll("group") require.NoError(t, err) assert.Equal(t, map[string]string{"a": "1", "b": "2"}, all) } func TestStore_GetAll_Empty_Good(t *testing.T) { - s := newTestStore(t) + keyValueStore := newTestStore(t) - all, err := s.GetAll("empty") + all, err := keyValueStore.GetAll("empty") require.NoError(t, err) assert.Empty(t, all) } func TestStore_Render_Good(t *testing.T) { - s := newTestStore(t) + keyValueStore := newTestStore(t) - _ = s.Set("user", "pool", "pool.lthn.io:3333") - _ = s.Set("user", "wallet", "iz...") + _ = keyValueStore.Set("user", "pool", "pool.lthn.io:3333") + _ = keyValueStore.Set("user", "wallet", "iz...") tmpl := `{"pool":"{{ .pool }}","wallet":"{{ .wallet }}"}` - out, err := s.Render(tmpl, "user") + out, err := keyValueStore.Render(tmpl, "user") require.NoError(t, err) assert.Contains(t, out, "pool.lthn.io:3333") assert.Contains(t, out, "iz...")