diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..f71254f --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "external/go"] + path = external/go + url = https://github.com/dappcore/go.git + branch = dev diff --git a/external/go b/external/go new file mode 160000 index 0000000..d661b70 --- /dev/null +++ b/external/go @@ -0,0 +1 @@ +Subproject commit d661b703e16183b3cbab101de189f688888a1174 diff --git a/go.work b/go.work new file mode 100644 index 0000000..ac2afa6 --- /dev/null +++ b/go.work @@ -0,0 +1,10 @@ +go 1.26.2 + +// Workspace mode for development: pulls local Core modules from external/. +// +// CI should run with GOWORK=off so go/go.mod tags drive reproducible builds. + +use ( + ./go + ./external/go +) diff --git a/actions.go b/go/actions.go similarity index 98% rename from actions.go rename to go/actions.go index a96058c..20791e5 100644 --- a/actions.go +++ b/go/actions.go @@ -209,7 +209,10 @@ func copyAction(_ context.Context, opts core.Options) core.Result { // localMediumFromOptions constructs a sandboxed local Medium using the // "root" option. -func localMediumFromOptions(opts core.Options) (Medium, error) { +func localMediumFromOptions(opts core.Options) ( + Medium, + error, +) { root := opts.String("root") if root == "" { return nil, core.E("io.localMediumFromOptions", "root is required", fs.ErrInvalid) @@ -217,7 +220,10 @@ func localMediumFromOptions(opts core.Options) (Medium, error) { return NewSandboxed(root) } -func mediumFromOptions(opts core.Options, operation string) (Medium, error) { +func mediumFromOptions(opts core.Options, operation string) ( + Medium, + error, +) { medium, ok := opts.Get("medium").Value.(Medium) if !ok { return nil, core.E(operation, "medium is required", fs.ErrInvalid) diff --git a/actions_example_test.go b/go/actions_example_test.go similarity index 100% rename from actions_example_test.go rename to go/actions_example_test.go diff --git a/actions_test.go b/go/actions_test.go similarity index 100% rename from actions_test.go rename to go/actions_test.go diff --git a/bench_test.go b/go/bench_test.go similarity index 100% rename from bench_test.go rename to go/bench_test.go diff --git a/cube/actions.go b/go/cube/actions.go similarity index 98% rename from cube/actions.go rename to go/cube/actions.go index a970ccc..4f8fa17 100644 --- a/cube/actions.go +++ b/go/cube/actions.go @@ -157,7 +157,10 @@ func unpackAction(_ context.Context, opts core.Options) core.Result { return core.Ok(nil) } -func keyFromOptions(opts core.Options, operation string) ([]byte, error) { +func keyFromOptions(opts core.Options, operation string) ( + []byte, + error, +) { key, ok := opts.Get("key").Value.([]byte) if !ok { return nil, core.E(operation, errKeyType, fs.ErrInvalid) diff --git a/cube/actions_example_test.go b/go/cube/actions_example_test.go similarity index 100% rename from cube/actions_example_test.go rename to go/cube/actions_example_test.go diff --git a/cube/actions_test.go b/go/cube/actions_test.go similarity index 100% rename from cube/actions_test.go rename to go/cube/actions_test.go diff --git a/cube/cube.go b/go/cube/cube.go similarity index 86% rename from cube/cube.go rename to go/cube/cube.go index 9d47a33..ee1392e 100644 --- a/cube/cube.go +++ b/go/cube/cube.go @@ -48,7 +48,10 @@ type Options struct { // Example: medium, _ := cube.New(cube.Options{Inner: io.NewMemoryMedium(), Key: key}) // Example: _ = medium.Write("secret.txt", "classified") // Example: plaintext, _ := medium.Read("secret.txt") -func New(options Options) (*Medium, error) { +func New(options Options) ( + *Medium, + error, +) { if options.Inner == nil { return nil, core.E(opCubeNew, "inner medium is required", fs.ErrInvalid) } @@ -68,7 +71,10 @@ func (medium *Medium) Inner() coreio.Medium { } // Example: content, _ := medium.Read("secret.txt") -func (medium *Medium) Read(path string) (string, error) { +func (medium *Medium) Read(path string) ( + string, + error, +) { ciphertext, err := medium.inner.Read(path) if err != nil { return "", err @@ -81,12 +87,14 @@ func (medium *Medium) Read(path string) (string, error) { } // Example: _ = medium.Write("secret.txt", "classified") -func (medium *Medium) Write(path, content string) error { +func (medium *Medium) Write(path, content string) error { // legacy error contract + return medium.WriteMode(path, content, 0644) } // Example: _ = medium.WriteMode("keys/private.key", key, 0600) -func (medium *Medium) WriteMode(path, content string, mode fs.FileMode) error { +func (medium *Medium) WriteMode(path, content string, mode fs.FileMode) error { // legacy error contract + ciphertext, err := sigil.Transmute([]byte(content), []sigil.Sigil{medium.sigil}) if err != nil { return core.E("cube.WriteMode", core.Concat("failed to encrypt: ", path), err) @@ -95,7 +103,8 @@ func (medium *Medium) WriteMode(path, content string, mode fs.FileMode) error { } // Example: _ = medium.EnsureDir("data") -func (medium *Medium) EnsureDir(path string) error { +func (medium *Medium) EnsureDir(path string) error { // legacy error contract + return medium.inner.EnsureDir(path) } @@ -105,32 +114,44 @@ func (medium *Medium) IsFile(path string) bool { } // Example: _ = medium.Delete("secret.txt") -func (medium *Medium) Delete(path string) error { +func (medium *Medium) Delete(path string) error { // legacy error contract + return medium.inner.Delete(path) } // Example: _ = medium.DeleteAll("archive") -func (medium *Medium) DeleteAll(path string) error { +func (medium *Medium) DeleteAll(path string) error { // legacy error contract + return medium.inner.DeleteAll(path) } // Example: _ = medium.Rename("draft.txt", "final.txt") -func (medium *Medium) Rename(oldPath, newPath string) error { +func (medium *Medium) Rename(oldPath, newPath string) error { // legacy error contract + return medium.inner.Rename(oldPath, newPath) } // Example: entries, _ := medium.List("data") -func (medium *Medium) List(path string) ([]fs.DirEntry, error) { +func (medium *Medium) List(path string) ( + []fs.DirEntry, + error, +) { return medium.inner.List(path) } // Example: info, _ := medium.Stat("secret.txt") -func (medium *Medium) Stat(path string) (fs.FileInfo, error) { +func (medium *Medium) Stat(path string) ( + fs.FileInfo, + error, +) { return medium.inner.Stat(path) } // Example: file, _ := medium.Open("secret.txt") -func (medium *Medium) Open(path string) (fs.File, error) { +func (medium *Medium) Open(path string) ( + fs.File, + error, +) { // Read via cube semantics (decrypt) then wrap in an in-memory fs.File. ciphertext, err := medium.inner.Read(path) if err != nil { @@ -153,12 +174,18 @@ func (medium *Medium) Open(path string) (fs.File, error) { } // Example: writer, _ := medium.Create("secret.txt") -func (medium *Medium) Create(path string) (goio.WriteCloser, error) { +func (medium *Medium) Create(path string) ( + goio.WriteCloser, + error, +) { return &cubeWriteCloser{medium: medium, path: path, mode: 0644}, nil } // Example: writer, _ := medium.Append("log.txt") -func (medium *Medium) Append(path string) (goio.WriteCloser, error) { +func (medium *Medium) Append(path string) ( + goio.WriteCloser, + error, +) { var existing []byte if medium.inner.Exists(path) { plain, err := medium.Read(path) @@ -171,7 +198,10 @@ func (medium *Medium) Append(path string) (goio.WriteCloser, error) { } // Example: reader, _ := medium.ReadStream("secret.txt") -func (medium *Medium) ReadStream(path string) (goio.ReadCloser, error) { +func (medium *Medium) ReadStream(path string) ( + goio.ReadCloser, + error, +) { file, err := medium.Open(path) if err != nil { return nil, err @@ -180,7 +210,10 @@ func (medium *Medium) ReadStream(path string) (goio.ReadCloser, error) { } // Example: writer, _ := medium.WriteStream("secret.txt") -func (medium *Medium) WriteStream(path string) (goio.WriteCloser, error) { +func (medium *Medium) WriteStream(path string) ( + goio.WriteCloser, + error, +) { return medium.Create(path) } @@ -203,11 +236,17 @@ type cubeFile struct { modTime time.Time } -func (file *cubeFile) Stat() (fs.FileInfo, error) { +func (file *cubeFile) Stat() ( + fs.FileInfo, + error, +) { return coreio.NewFileInfo(file.name, int64(len(file.content)), file.mode, file.modTime, false), nil } -func (file *cubeFile) Read(buffer []byte) (int, error) { +func (file *cubeFile) Read(buffer []byte) ( + int, + error, +) { if file.offset >= int64(len(file.content)) { return 0, goio.EOF } @@ -216,7 +255,8 @@ func (file *cubeFile) Read(buffer []byte) (int, error) { return readCount, nil } -func (file *cubeFile) Close() error { +func (file *cubeFile) Close() error { // legacy error contract + return nil } @@ -225,7 +265,10 @@ type cubeArchiveBuffer struct { data []byte } -func (buffer *cubeArchiveBuffer) Write(data []byte) (int, error) { +func (buffer *cubeArchiveBuffer) Write(data []byte) ( + int, + error, +) { buffer.data = append(buffer.data, data...) return len(data), nil } @@ -238,12 +281,16 @@ type cubeWriteCloser struct { mode fs.FileMode } -func (writer *cubeWriteCloser) Write(data []byte) (int, error) { +func (writer *cubeWriteCloser) Write(data []byte) ( + int, + error, +) { writer.data = append(writer.data, data...) return len(data), nil } -func (writer *cubeWriteCloser) Close() error { +func (writer *cubeWriteCloser) Close() error { // legacy error contract + mode := writer.mode if mode == 0 { mode = 0644 @@ -255,7 +302,8 @@ func (writer *cubeWriteCloser) Close() error { // // Pack walks the source Medium, packs every file into a tar archive, encrypts // the archive, and writes the ciphertext to outputPath on the local filesystem. -func Pack(outputPath string, source coreio.Medium, key []byte) error { +func Pack(outputPath string, source coreio.Medium, key []byte) error { // legacy error contract + if source == nil { return core.E(opCubePack, "source medium is required", fs.ErrInvalid) } @@ -286,7 +334,8 @@ func Pack(outputPath string, source coreio.Medium, key []byte) error { // // Unpack reads the encrypted archive from cubePath, decrypts it, unpacks the // tar contents, and writes every entry to the destination Medium. -func Unpack(cubePath string, destination coreio.Medium, key []byte) error { +func Unpack(cubePath string, destination coreio.Medium, key []byte) error { // legacy error contract + if destination == nil { return core.E(opCubeUnpack, "destination medium is required", fs.ErrInvalid) } @@ -320,7 +369,10 @@ func Unpack(cubePath string, destination coreio.Medium, key []byte) error { // Open reads the encrypted archive at cubePath, decrypts it, and returns a // Medium backed by an in-memory node.Node. Reads and writes do not flow back // to the .cube file — use Pack again to persist updates. -func Open(cubePath string, key []byte) (coreio.Medium, error) { +func Open(cubePath string, key []byte) ( + coreio.Medium, + error, +) { if cubePath == "" { return nil, core.E(opCubeOpen, "cube path is required", fs.ErrInvalid) } @@ -353,14 +405,19 @@ func Open(cubePath string, key []byte) (coreio.Medium, error) { return medium, nil } -func validateCubeKey(operation string, key []byte) error { +func validateCubeKey(operation string, key []byte) error { // legacy error contract + if _, err := sigil.NewChaChaPolySigil(key, nil); err != nil { return core.E(operation, errCreateCipher, err) } return nil } -func sandboxedLocalForPath(operation, filePath string) (coreio.Medium, string, error) { +func sandboxedLocalForPath(operation, filePath string) ( + coreio.Medium, + string, + error, +) { if filePath == "" { return nil, "", core.E(operation, "path is required", fs.ErrInvalid) } @@ -398,7 +455,10 @@ func archiveChildPath(parent, name string) string { return core.PathJoin(parent, name) } -func archiveMediumToBorgDataNode(source coreio.Medium) (*borgdatanode.DataNode, error) { +func archiveMediumToBorgDataNode(source coreio.Medium) ( + *borgdatanode.DataNode, + error, +) { dataNode := borgdatanode.New() if err := addMediumPathToBorgDataNode(source, "", dataNode); err != nil { return nil, err @@ -406,7 +466,8 @@ func archiveMediumToBorgDataNode(source coreio.Medium) (*borgdatanode.DataNode, return dataNode, nil } -func addMediumPathToBorgDataNode(source coreio.Medium, directoryPath string, dataNode *borgdatanode.DataNode) error { +func addMediumPathToBorgDataNode(source coreio.Medium, directoryPath string, dataNode *borgdatanode.DataNode) error { // legacy error contract + entries, err := source.List(directoryPath) if err != nil { return core.E(opCubeArchive, core.Concat("failed to list: ", directoryPath), err) @@ -428,14 +489,16 @@ func addMediumPathToBorgDataNode(source coreio.Medium, directoryPath string, dat return nil } -func writeBorgDataNodeToMedium(dataNode *borgdatanode.DataNode, destination coreio.Medium) error { +func writeBorgDataNodeToMedium(dataNode *borgdatanode.DataNode, destination coreio.Medium) error { // legacy error contract + if dataNode == nil { return core.E(opCubeExtract, "Borg DataNode is required", fs.ErrInvalid) } return writeBorgDataNodeDir(dataNode, "", destination) } -func writeBorgDataNodeDir(dataNode *borgdatanode.DataNode, directoryPath string, destination coreio.Medium) error { +func writeBorgDataNodeDir(dataNode *borgdatanode.DataNode, directoryPath string, destination coreio.Medium) error { // legacy error contract + entries, err := dataNode.ReadDir(directoryPath) if err != nil { return core.E(opCubeExtract, core.Concat("failed to list Borg DataNode: ", directoryPath), err) @@ -455,7 +518,8 @@ func writeBorgDataNodeDir(dataNode *borgdatanode.DataNode, directoryPath string, return nil } -func writeBorgDataNodeFile(dataNode *borgdatanode.DataNode, filePath string, destination coreio.Medium) error { +func writeBorgDataNodeFile(dataNode *borgdatanode.DataNode, filePath string, destination coreio.Medium) error { // legacy error contract + name, ok, err := validatedTarEntryName(filePath) if err != nil || !ok { return err @@ -481,7 +545,11 @@ func closeBorgDataNodeFile(file fs.File, filePath string) { } } -func validatedTarEntryName(rawName string) (string, bool, error) { +func validatedTarEntryName(rawName string) ( + string, + bool, + error, +) { if rawName == "" { return "", false, nil } diff --git a/cube/cube_example_test.go b/go/cube/cube_example_test.go similarity index 100% rename from cube/cube_example_test.go rename to go/cube/cube_example_test.go diff --git a/cube/cube_test.go b/go/cube/cube_test.go similarity index 100% rename from cube/cube_test.go rename to go/cube/cube_test.go diff --git a/datanode/medium.go b/go/datanode/medium.go similarity index 89% rename from datanode/medium.go rename to go/datanode/medium.go index d8dfb2e..3754e3f 100644 --- a/datanode/medium.go +++ b/go/datanode/medium.go @@ -64,7 +64,10 @@ func New() *Medium { // Example: sourceMedium := datanode.New() // Example: snapshot, _ := sourceMedium.Snapshot() // Example: restored, _ := datanode.FromTar(snapshot) -func FromTar(data []byte) (*Medium, error) { +func FromTar(data []byte) ( + *Medium, + error, +) { dataNode, err := borgdatanode.FromTar(data) if err != nil { return nil, core.E("datanode.FromTar", "failed to restore", err) @@ -78,7 +81,10 @@ func FromTar(data []byte) (*Medium, error) { } // Example: snapshot, _ := medium.Snapshot() -func (medium *Medium) Snapshot() ([]byte, error) { +func (medium *Medium) Snapshot() ( + []byte, + error, +) { medium.lock.RLock() defer medium.lock.RUnlock() data, err := medium.dataNode.ToTar() @@ -89,7 +95,8 @@ func (medium *Medium) Snapshot() ([]byte, error) { } // Example: _ = medium.Restore(snapshot) -func (medium *Medium) Restore(data []byte) error { +func (medium *Medium) Restore(data []byte) error { // legacy error contract + dataNode, err := borgdatanode.FromTar(data) if err != nil { return core.E("datanode.Restore", "tar failed", err) @@ -136,7 +143,10 @@ func closeDataNodeFile(file fs.File, filePath string) { } } -func (medium *Medium) Read(filePath string) (string, error) { +func (medium *Medium) Read(filePath string) ( + string, + error, +) { medium.lock.RLock() defer medium.lock.RUnlock() @@ -162,7 +172,8 @@ func (medium *Medium) Read(filePath string) (string, error) { return string(data), nil } -func (medium *Medium) Write(filePath, content string) error { +func (medium *Medium) Write(filePath, content string) error { // legacy error contract + medium.lock.Lock() defer medium.lock.Unlock() @@ -176,11 +187,13 @@ func (medium *Medium) Write(filePath, content string) error { return nil } -func (medium *Medium) WriteMode(filePath, content string, mode fs.FileMode) error { +func (medium *Medium) WriteMode(filePath, content string, mode fs.FileMode) error { // legacy error contract + return medium.Write(filePath, content) } -func (medium *Medium) EnsureDir(filePath string) error { +func (medium *Medium) EnsureDir(filePath string) error { // legacy error contract + medium.lock.Lock() defer medium.lock.Unlock() @@ -215,7 +228,8 @@ func (medium *Medium) isFileLocked(filePath string) bool { return err == nil && !info.IsDir() } -func (medium *Medium) Delete(filePath string) error { +func (medium *Medium) Delete(filePath string) error { // legacy error contract + medium.lock.Lock() defer medium.lock.Unlock() @@ -239,14 +253,16 @@ func (medium *Medium) Delete(filePath string) error { return nil } -func (medium *Medium) deleteDirectoryFallbackLocked(filePath string) error { +func (medium *Medium) deleteDirectoryFallbackLocked(filePath string) error { // legacy error contract + if medium.directorySet[filePath] { return medium.deleteDirectoryLocked(filePath) } return core.E(opDatanodeDelete, core.Concat(msgDatanodeNotFound, filePath), fs.ErrNotExist) } -func (medium *Medium) deleteDirectoryLocked(filePath string) error { +func (medium *Medium) deleteDirectoryLocked(filePath string) error { // legacy error contract + hasChildren, err := medium.hasPrefixLocked(filePath + "/") if err != nil { return core.E(opDatanodeDelete, core.Concat("failed to inspect directory: ", filePath), err) @@ -258,7 +274,8 @@ func (medium *Medium) deleteDirectoryLocked(filePath string) error { return nil } -func (medium *Medium) DeleteAll(filePath string) error { +func (medium *Medium) DeleteAll(filePath string) error { // legacy error contract + medium.lock.Lock() defer medium.lock.Unlock() @@ -294,7 +311,10 @@ func (medium *Medium) DeleteAll(filePath string) error { return nil } -func (medium *Medium) deleteFileIfPresentLocked(filePath string) (bool, error) { +func (medium *Medium) deleteFileIfPresentLocked(filePath string) ( + bool, + error, +) { info, err := medium.dataNode.Stat(filePath) if err != nil || info.IsDir() { return false, nil @@ -305,7 +325,10 @@ func (medium *Medium) deleteFileIfPresentLocked(filePath string) (bool, error) { return true, nil } -func (medium *Medium) deleteEntriesMatchingLocked(entries []string, filePath, prefix string) (bool, error) { +func (medium *Medium) deleteEntriesMatchingLocked(entries []string, filePath, prefix string) ( + bool, + error, +) { found := false for _, name := range entries { if name != filePath && !core.HasPrefix(name, prefix) { @@ -330,7 +353,8 @@ func (medium *Medium) deleteDirectoriesMatchingLocked(filePath, prefix string) b return found } -func (medium *Medium) Rename(oldPath, newPath string) error { +func (medium *Medium) Rename(oldPath, newPath string) error { // legacy error contract + medium.lock.Lock() defer medium.lock.Unlock() @@ -349,7 +373,8 @@ func (medium *Medium) Rename(oldPath, newPath string) error { return medium.renameDirectoryLocked(oldPath, newPath) } -func (medium *Medium) renameFileLocked(oldPath, newPath string) error { +func (medium *Medium) renameFileLocked(oldPath, newPath string) error { // legacy error contract + data, err := medium.readFileLocked(oldPath) if err != nil { return core.E(opDatanodeRename, core.Concat("failed to read source file: ", oldPath), err) @@ -362,7 +387,8 @@ func (medium *Medium) renameFileLocked(oldPath, newPath string) error { return nil } -func (medium *Medium) renameDirectoryLocked(oldPath, newPath string) error { +func (medium *Medium) renameDirectoryLocked(oldPath, newPath string) error { // legacy error contract + oldPrefix := oldPath + "/" newPrefix := newPath + "/" @@ -399,7 +425,10 @@ func (medium *Medium) renameDirectoryLocked(oldPath, newPath string) error { return nil } -func (medium *Medium) List(filePath string) ([]fs.DirEntry, error) { +func (medium *Medium) List(filePath string) ( + []fs.DirEntry, + error, +) { medium.lock.RLock() defer medium.lock.RUnlock() @@ -444,7 +473,10 @@ func (medium *Medium) List(filePath string) ([]fs.DirEntry, error) { return entries, nil } -func (medium *Medium) Stat(filePath string) (fs.FileInfo, error) { +func (medium *Medium) Stat(filePath string) ( + fs.FileInfo, + error, +) { medium.lock.RLock() defer medium.lock.RUnlock() @@ -464,7 +496,10 @@ func (medium *Medium) Stat(filePath string) (fs.FileInfo, error) { return nil, core.E("datanode.Stat", core.Concat(msgDatanodeNotFound, filePath), fs.ErrNotExist) } -func (medium *Medium) Open(filePath string) (fs.File, error) { +func (medium *Medium) Open(filePath string) ( + fs.File, + error, +) { medium.lock.RLock() defer medium.lock.RUnlock() @@ -472,7 +507,10 @@ func (medium *Medium) Open(filePath string) (fs.File, error) { return medium.dataNode.Open(filePath) } -func (medium *Medium) Create(filePath string) (goio.WriteCloser, error) { +func (medium *Medium) Create(filePath string) ( + goio.WriteCloser, + error, +) { filePath = normaliseEntryPath(filePath) if filePath == "" { return nil, core.E("datanode.Create", msgDatanodeEmptyPath, fs.ErrInvalid) @@ -480,7 +518,10 @@ func (medium *Medium) Create(filePath string) (goio.WriteCloser, error) { return &writeCloser{medium: medium, path: filePath}, nil } -func (medium *Medium) Append(filePath string) (goio.WriteCloser, error) { +func (medium *Medium) Append(filePath string) ( + goio.WriteCloser, + error, +) { filePath = normaliseEntryPath(filePath) if filePath == "" { return nil, core.E("datanode.Append", msgDatanodeEmptyPath, fs.ErrInvalid) @@ -501,7 +542,10 @@ func (medium *Medium) Append(filePath string) (goio.WriteCloser, error) { return &writeCloser{medium: medium, path: filePath, buffer: existing}, nil } -func (medium *Medium) ReadStream(filePath string) (goio.ReadCloser, error) { +func (medium *Medium) ReadStream(filePath string) ( + goio.ReadCloser, + error, +) { medium.lock.RLock() defer medium.lock.RUnlock() @@ -513,7 +557,10 @@ func (medium *Medium) ReadStream(filePath string) (goio.ReadCloser, error) { return file.(goio.ReadCloser), nil } -func (medium *Medium) WriteStream(filePath string) (goio.WriteCloser, error) { +func (medium *Medium) WriteStream(filePath string) ( + goio.WriteCloser, + error, +) { return medium.Create(filePath) } @@ -547,7 +594,10 @@ func (medium *Medium) IsDir(filePath string) bool { return medium.directorySet[filePath] } -func (medium *Medium) hasPrefixLocked(prefix string) (bool, error) { +func (medium *Medium) hasPrefixLocked(prefix string) ( + bool, + error, +) { entries, err := medium.collectAllLocked() if err != nil { return false, err @@ -565,7 +615,10 @@ func (medium *Medium) hasPrefixLocked(prefix string) (bool, error) { return false, nil } -func (medium *Medium) collectAllLocked() ([]string, error) { +func (medium *Medium) collectAllLocked() ( + []string, + error, +) { var names []string err := dataNodeWalkDir(medium.dataNode, ".", func(filePath string, entry fs.DirEntry, err error) error { if err != nil { @@ -579,7 +632,10 @@ func (medium *Medium) collectAllLocked() ([]string, error) { return names, err } -func (medium *Medium) readFileLocked(filePath string) ([]byte, error) { +func (medium *Medium) readFileLocked(filePath string) ( + []byte, + error, +) { file, err := dataNodeOpen(medium.dataNode, filePath) if err != nil { return nil, err @@ -599,7 +655,8 @@ func (medium *Medium) readFileLocked(filePath string) ([]byte, error) { // This is O(n) per call, leading to O(n²) behaviour when deleting many files in a loop. // TODO(perf): use a DataNode deletion API if borgdatanode ever exposes one, or batch deletions // by collecting targets before rebuilding once. -func (medium *Medium) removeFileLocked(target string) error { +func (medium *Medium) removeFileLocked(target string) error { // legacy error contract + entries, err := medium.collectAllLocked() if err != nil { return err @@ -625,12 +682,16 @@ type writeCloser struct { buffer []byte } -func (writer *writeCloser) Write(data []byte) (int, error) { +func (writer *writeCloser) Write(data []byte) ( + int, + error, +) { writer.buffer = append(writer.buffer, data...) return len(data), nil } -func (writer *writeCloser) Close() error { +func (writer *writeCloser) Close() error { // legacy error contract + writer.medium.lock.Lock() defer writer.medium.lock.Unlock() @@ -649,7 +710,10 @@ func (entry *dirEntry) IsDir() bool { return true } func (entry *dirEntry) Type() fs.FileMode { return fs.ModeDir } -func (entry *dirEntry) Info() (fs.FileInfo, error) { +func (entry *dirEntry) Info() ( + fs.FileInfo, + error, +) { return &fileInfo{name: entry.name, isDir: true, mode: fs.ModeDir | 0755}, nil } diff --git a/datanode/medium_example_test.go b/go/datanode/medium_example_test.go similarity index 100% rename from datanode/medium_example_test.go rename to go/datanode/medium_example_test.go diff --git a/datanode/medium_test.go b/go/datanode/medium_test.go similarity index 100% rename from datanode/medium_test.go rename to go/datanode/medium_test.go diff --git a/doc.go b/go/doc.go similarity index 100% rename from doc.go rename to go/doc.go diff --git a/go.mod b/go/go.mod similarity index 100% rename from go.mod rename to go/go.mod diff --git a/go.sum b/go/go.sum similarity index 100% rename from go.sum rename to go/go.sum diff --git a/internal/fsutil/direntry.go b/go/internal/fsutil/direntry.go similarity index 100% rename from internal/fsutil/direntry.go rename to go/internal/fsutil/direntry.go diff --git a/internal/fsutil/direntry_example_test.go b/go/internal/fsutil/direntry_example_test.go similarity index 100% rename from internal/fsutil/direntry_example_test.go rename to go/internal/fsutil/direntry_example_test.go diff --git a/internal/fsutil/direntry_test.go b/go/internal/fsutil/direntry_test.go similarity index 100% rename from internal/fsutil/direntry_test.go rename to go/internal/fsutil/direntry_test.go diff --git a/io.go b/go/io.go similarity index 91% rename from io.go rename to go/io.go index c3249ed..875bb8d 100644 --- a/io.go +++ b/go/io.go @@ -148,32 +148,46 @@ func init() { // Example: medium, _ := io.NewSandboxed("/srv/app") // Example: _ = medium.Write("config/app.yaml", "port: 8080") -func NewSandboxed(root string) (Medium, error) { +func NewSandboxed(root string) ( + Medium, + error, +) { return local.New(root) } // Example: content, _ := io.Read(medium, "config/app.yaml") -func Read(medium Medium, path string) (string, error) { +func Read(medium Medium, path string) ( + string, + error, +) { return medium.Read(path) } // Example: _ = io.Write(medium, "config/app.yaml", "port: 8080") -func Write(medium Medium, path, content string) error { +func Write(medium Medium, path, content string) error { // legacy error contract + return medium.Write(path, content) } // Example: reader, _ := io.ReadStream(medium, "logs/app.log") -func ReadStream(medium Medium, path string) (goio.ReadCloser, error) { +func ReadStream(medium Medium, path string) ( + goio.ReadCloser, + error, +) { return medium.ReadStream(path) } // Example: writer, _ := io.WriteStream(medium, "logs/app.log") -func WriteStream(medium Medium, path string) (goio.WriteCloser, error) { +func WriteStream(medium Medium, path string) ( + goio.WriteCloser, + error, +) { return medium.WriteStream(path) } // Example: _ = io.EnsureDir(medium, "config") -func EnsureDir(medium Medium, path string) error { +func EnsureDir(medium Medium, path string) error { // legacy error contract + return medium.EnsureDir(path) } @@ -183,7 +197,8 @@ func IsFile(medium Medium, path string) bool { } // Example: _ = io.Copy(sourceMedium, "input.txt", destinationMedium, "backup/input.txt") -func Copy(sourceMedium Medium, sourcePath string, destinationMedium Medium, destinationPath string) error { +func Copy(sourceMedium Medium, sourcePath string, destinationMedium Medium, destinationPath string) error { // legacy error contract + content, err := sourceMedium.Read(sourcePath) if err != nil { return core.E("io.Copy", core.Concat("read failed: ", sourcePath), err) @@ -261,7 +276,10 @@ func (medium *MemoryMedium) directoryExists(path string) bool { } // Example: value, _ := io.NewMemoryMedium().Read("notes.txt") -func (medium *MemoryMedium) Read(path string) (string, error) { +func (medium *MemoryMedium) Read(path string) ( + string, + error, +) { content, ok := medium.fileContents[path] if !ok { return "", core.E("io.MemoryMedium.Read", core.Concat("file not found: ", path), fs.ErrNotExist) @@ -270,12 +288,14 @@ func (medium *MemoryMedium) Read(path string) (string, error) { } // Example: _ = io.NewMemoryMedium().Write("notes.txt", "hello") -func (medium *MemoryMedium) Write(path, content string) error { +func (medium *MemoryMedium) Write(path, content string) error { // legacy error contract + return medium.WriteMode(path, content, 0644) } // Example: _ = io.NewMemoryMedium().WriteMode("keys/private.key", "secret", 0600) -func (medium *MemoryMedium) WriteMode(filePath, content string, mode fs.FileMode) error { +func (medium *MemoryMedium) WriteMode(filePath, content string, mode fs.FileMode) error { // legacy error contract + // Verify no ancestor directory component is stored as a file. ancestor := core.PathDir(filePath) for ancestor != "." && ancestor != "" { @@ -299,7 +319,8 @@ func (medium *MemoryMedium) WriteMode(filePath, content string, mode fs.FileMode } // Example: _ = io.NewMemoryMedium().EnsureDir("config/app") -func (medium *MemoryMedium) EnsureDir(path string) error { +func (medium *MemoryMedium) EnsureDir(path string) error { // legacy error contract + if _, ok := medium.fileContents[path]; ok { return core.E("io.MemoryMedium.EnsureDir", core.Concat("path is already a file: ", path), fs.ErrExist) } @@ -315,7 +336,8 @@ func (medium *MemoryMedium) IsFile(path string) bool { } // Example: _ = io.NewMemoryMedium().Delete("old.txt") -func (medium *MemoryMedium) Delete(path string) error { +func (medium *MemoryMedium) Delete(path string) error { // legacy error contract + if _, ok := medium.fileContents[path]; ok { delete(medium.fileContents, path) delete(medium.fileModes, path) @@ -356,7 +378,8 @@ func (medium *MemoryMedium) directoryHasChildren(path string) bool { } // Example: _ = io.NewMemoryMedium().DeleteAll("logs") -func (medium *MemoryMedium) DeleteAll(path string) error { +func (medium *MemoryMedium) DeleteAll(path string) error { // legacy error contract + found := false if _, ok := medium.fileContents[path]; ok { delete(medium.fileContents, path) @@ -394,7 +417,8 @@ func (medium *MemoryMedium) DeleteAll(path string) error { } // Example: _ = io.NewMemoryMedium().Rename("drafts/todo.txt", "archive/todo.txt") -func (medium *MemoryMedium) Rename(oldPath, newPath string) error { +func (medium *MemoryMedium) Rename(oldPath, newPath string) error { // legacy error contract + if medium.renameFile(oldPath, newPath) { return nil } @@ -468,7 +492,10 @@ func (medium *MemoryMedium) renameDirectoriesWithPrefix(oldPrefix, newPrefix str } // Example: file, _ := io.NewMemoryMedium().Open("notes.txt") -func (medium *MemoryMedium) Open(path string) (fs.File, error) { +func (medium *MemoryMedium) Open(path string) ( + fs.File, + error, +) { content, ok := medium.fileContents[path] if !ok { return nil, core.E("io.MemoryMedium.Open", core.Concat("file not found: ", path), fs.ErrNotExist) @@ -482,7 +509,10 @@ func (medium *MemoryMedium) Open(path string) (fs.File, error) { } // Example: writer, _ := io.NewMemoryMedium().Create("notes.txt") -func (medium *MemoryMedium) Create(path string) (goio.WriteCloser, error) { +func (medium *MemoryMedium) Create(path string) ( + goio.WriteCloser, + error, +) { return &MemoryWriteCloser{ medium: medium, path: path, @@ -491,7 +521,10 @@ func (medium *MemoryMedium) Create(path string) (goio.WriteCloser, error) { } // Example: writer, _ := io.NewMemoryMedium().Append("notes.txt") -func (medium *MemoryMedium) Append(path string) (goio.WriteCloser, error) { +func (medium *MemoryMedium) Append(path string) ( + goio.WriteCloser, + error, +) { content := medium.fileContents[path] return &MemoryWriteCloser{ medium: medium, @@ -502,12 +535,18 @@ func (medium *MemoryMedium) Append(path string) (goio.WriteCloser, error) { } // Example: reader, _ := io.NewMemoryMedium().ReadStream("notes.txt") -func (medium *MemoryMedium) ReadStream(path string) (goio.ReadCloser, error) { +func (medium *MemoryMedium) ReadStream(path string) ( + goio.ReadCloser, + error, +) { return medium.Open(path) } // Example: writer, _ := io.NewMemoryMedium().WriteStream("notes.txt") -func (medium *MemoryMedium) WriteStream(path string) (goio.WriteCloser, error) { +func (medium *MemoryMedium) WriteStream(path string) ( + goio.WriteCloser, + error, +) { return medium.Create(path) } @@ -523,11 +562,17 @@ type MemoryFile struct { var _ fs.File = (*MemoryFile)(nil) var _ goio.ReadCloser = (*MemoryFile)(nil) -func (file *MemoryFile) Stat() (fs.FileInfo, error) { +func (file *MemoryFile) Stat() ( + fs.FileInfo, + error, +) { return NewFileInfo(file.name, int64(len(file.content)), file.mode, file.modTime, false), nil } -func (file *MemoryFile) Read(buffer []byte) (int, error) { +func (file *MemoryFile) Read(buffer []byte) ( + int, + error, +) { if file.offset >= int64(len(file.content)) { return 0, goio.EOF } @@ -536,7 +581,8 @@ func (file *MemoryFile) Read(buffer []byte) (int, error) { return readCount, nil } -func (file *MemoryFile) Close() error { +func (file *MemoryFile) Close() error { // legacy error contract + return nil } @@ -550,12 +596,16 @@ type MemoryWriteCloser struct { var _ goio.WriteCloser = (*MemoryWriteCloser)(nil) -func (writeCloser *MemoryWriteCloser) Write(data []byte) (int, error) { +func (writeCloser *MemoryWriteCloser) Write(data []byte) ( + int, + error, +) { writeCloser.data = append(writeCloser.data, data...) return len(data), nil } -func (writeCloser *MemoryWriteCloser) Close() error { +func (writeCloser *MemoryWriteCloser) Close() error { // legacy error contract + if _, ok := writeCloser.medium.directories[writeCloser.path]; ok { return core.E("io.MemoryWriteCloser.Close", core.Concat("path is a directory: ", writeCloser.path), fs.ErrExist) } @@ -581,7 +631,10 @@ func (medium *MemoryMedium) modificationTimeForPath(path string) time.Time { } // Example: entries, _ := io.NewMemoryMedium().List("config") -func (medium *MemoryMedium) List(path string) ([]fs.DirEntry, error) { +func (medium *MemoryMedium) List(path string) ( + []fs.DirEntry, + error, +) { if _, ok := medium.directories[path]; !ok { if !medium.hasChildrenAtPath(path) && path != "" { return nil, core.E("io.MemoryMedium.List", core.Concat("directory not found: ", path), fs.ErrNotExist) @@ -678,7 +731,10 @@ func memoryDirEntry(name string) fs.DirEntry { } // Example: info, _ := io.NewMemoryMedium().Stat("notes.txt") -func (medium *MemoryMedium) Stat(path string) (fs.FileInfo, error) { +func (medium *MemoryMedium) Stat(path string) ( + fs.FileInfo, + error, +) { if content, ok := medium.fileContents[path]; ok { modTime, ok := medium.modificationTimes[path] if !ok { diff --git a/io_example_test.go b/go/io_example_test.go similarity index 100% rename from io_example_test.go rename to go/io_example_test.go diff --git a/io_test.go b/go/io_test.go similarity index 100% rename from io_test.go rename to go/io_test.go diff --git a/local/medium.go b/go/local/medium.go similarity index 89% rename from local/medium.go rename to go/local/medium.go index a6bf896..6f0ca6e 100644 --- a/local/medium.go +++ b/go/local/medium.go @@ -31,7 +31,10 @@ const ( // Example: medium, _ := local.New("/srv/app") // Example: _ = medium.Write("config/app.yaml", "port: 8080") -func New(root string) (*Medium, error) { +func New(root string) ( + *Medium, + error, +) { absoluteRoot := absolutePath(root) if resolvedRoot, err := resolveSymlinksPath(absoluteRoot); err == nil { absoluteRoot = resolvedRoot @@ -94,11 +97,17 @@ func splitPathParts(path string) []string { return parts } -func resolveSymlinksPath(path string) (string, error) { +func resolveSymlinksPath(path string) ( + string, + error, +) { return resolveSymlinksRecursive(absolutePath(path), map[string]struct{}{}) } -func resolveSymlinksRecursive(path string, seen map[string]struct{}) (string, error) { +func resolveSymlinksRecursive(path string, seen map[string]struct{}) ( + string, + error, +) { path = core.Path(path) if path == dirSeparator() { return path, nil @@ -130,7 +139,10 @@ func resolveSymlinksRecursive(path string, seen map[string]struct{}) (string, er return current, nil } -func resolveSymlinkTarget(current, next string, seen map[string]struct{}) (string, error) { +func resolveSymlinkTarget(current, next string, seen map[string]struct{}) ( + string, + error, +) { target, err := readlink(next) if err != nil { return "", err @@ -218,7 +230,10 @@ func (medium *Medium) sandboxedPath(path string) string { // directory component with O_NOFOLLOW (openat-style) so that the resolved fd is used for the // next step rather than re-resolving from a path string. Until then, symlink-based escape is // only possible on systems where an attacker can swap filesystem objects between syscalls. -func (medium *Medium) validatePath(path string) (string, error) { +func (medium *Medium) validatePath(path string) ( + string, + error, +) { if medium.filesystemRoot == dirSeparator() { return medium.sandboxedPath(path), nil } @@ -247,7 +262,10 @@ func (medium *Medium) validatePath(path string) (string, error) { return current, nil } -func (medium *Medium) Read(path string) (string, error) { +func (medium *Medium) Read(path string) ( + string, + error, +) { resolvedPath, err := medium.validatePath(path) if err != nil { return "", err @@ -255,11 +273,13 @@ func (medium *Medium) Read(path string) (string, error) { return resultString("local.Read", core.Concat("read failed: ", path), unrestrictedFileSystem.Read(resolvedPath)) } -func (medium *Medium) Write(path, content string) error { +func (medium *Medium) Write(path, content string) error { // legacy error contract + return medium.WriteMode(path, content, 0644) } -func (medium *Medium) WriteMode(path, content string, mode fs.FileMode) error { +func (medium *Medium) WriteMode(path, content string, mode fs.FileMode) error { // legacy error contract + resolvedPath, err := medium.validatePath(path) if err != nil { return err @@ -268,7 +288,8 @@ func (medium *Medium) WriteMode(path, content string, mode fs.FileMode) error { } // Example: _ = medium.EnsureDir("config/app") -func (medium *Medium) EnsureDir(path string) error { +func (medium *Medium) EnsureDir(path string) error { // legacy error contract + resolvedPath, err := medium.validatePath(path) if err != nil { return err @@ -310,7 +331,10 @@ func (medium *Medium) Exists(path string) bool { } // Example: entries, _ := medium.List("config") -func (medium *Medium) List(path string) ([]fs.DirEntry, error) { +func (medium *Medium) List(path string) ( + []fs.DirEntry, + error, +) { resolvedPath, err := medium.validatePath(path) if err != nil { return nil, err @@ -328,7 +352,10 @@ func (medium *Medium) List(path string) ([]fs.DirEntry, error) { } // Example: info, _ := medium.Stat("config/app.yaml") -func (medium *Medium) Stat(path string) (fs.FileInfo, error) { +func (medium *Medium) Stat(path string) ( + fs.FileInfo, + error, +) { resolvedPath, err := medium.validatePath(path) if err != nil { return nil, err @@ -337,7 +364,10 @@ func (medium *Medium) Stat(path string) (fs.FileInfo, error) { } // Example: file, _ := medium.Open("config/app.yaml") -func (medium *Medium) Open(path string) (fs.File, error) { +func (medium *Medium) Open(path string) ( + fs.File, + error, +) { resolvedPath, err := medium.validatePath(path) if err != nil { return nil, err @@ -346,7 +376,10 @@ func (medium *Medium) Open(path string) (fs.File, error) { } // Example: writer, _ := medium.Create("logs/app.log") -func (medium *Medium) Create(path string) (goio.WriteCloser, error) { +func (medium *Medium) Create(path string) ( + goio.WriteCloser, + error, +) { resolvedPath, err := medium.validatePath(path) if err != nil { return nil, err @@ -355,7 +388,10 @@ func (medium *Medium) Create(path string) (goio.WriteCloser, error) { } // Example: writer, _ := medium.Append("logs/app.log") -func (medium *Medium) Append(path string) (goio.WriteCloser, error) { +func (medium *Medium) Append(path string) ( + goio.WriteCloser, + error, +) { resolvedPath, err := medium.validatePath(path) if err != nil { return nil, err @@ -364,17 +400,24 @@ func (medium *Medium) Append(path string) (goio.WriteCloser, error) { } // Example: reader, _ := medium.ReadStream("logs/app.log") -func (medium *Medium) ReadStream(path string) (goio.ReadCloser, error) { +func (medium *Medium) ReadStream(path string) ( + goio.ReadCloser, + error, +) { return medium.Open(path) } // Example: writer, _ := medium.WriteStream("logs/app.log") -func (medium *Medium) WriteStream(path string) (goio.WriteCloser, error) { +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 { +func (medium *Medium) Delete(path string) error { // legacy error contract + resolvedPath, err := medium.validatePath(path) if err != nil { return err @@ -389,7 +432,8 @@ func (medium *Medium) Delete(path string) error { } // Example: _ = medium.DeleteAll("logs/archive") -func (medium *Medium) DeleteAll(path string) error { +func (medium *Medium) DeleteAll(path string) error { // legacy error contract + resolvedPath, err := medium.validatePath(path) if err != nil { return err @@ -404,7 +448,8 @@ func (medium *Medium) DeleteAll(path string) error { } // Example: _ = medium.Rename("drafts/todo.txt", "archive/todo.txt") -func (medium *Medium) Rename(oldPath, newPath string) error { +func (medium *Medium) Rename(oldPath, newPath string) error { // legacy error contract + oldResolvedPath, err := medium.validatePath(oldPath) if err != nil { return err @@ -416,7 +461,10 @@ func (medium *Medium) Rename(oldPath, newPath string) error { return resultError("local.Rename", core.Concat("rename failed: ", oldPath), unrestrictedFileSystem.Rename(oldResolvedPath, newResolvedPath)) } -func lstat(path string) (*syscall.Stat_t, error) { +func lstat(path string) ( + *syscall.Stat_t, + error, +) { info := &syscall.Stat_t{} if err := syscall.Lstat(path, info); err != nil { return nil, err @@ -431,7 +479,10 @@ func isSymlink(mode uint32) bool { return mode&syscall.S_IFMT == syscall.S_IFLNK } -func readlink(path string) (string, error) { +func readlink(path string) ( + string, + error, +) { size := 256 for { linkBuffer := make([]byte, size) @@ -446,7 +497,8 @@ func readlink(path string) (string, error) { } } -func resultError(operation, message string, result core.Result) error { +func resultError(operation, message string, result core.Result) error { // legacy error contract + if result.OK { return nil } @@ -456,7 +508,10 @@ func resultError(operation, message string, result core.Result) error { return core.E(operation, message, nil) } -func resultString(operation, message string, result core.Result) (string, error) { +func resultString(operation, message string, result core.Result) ( + string, + error, +) { if !result.OK { return "", resultError(operation, message, result) } @@ -467,7 +522,10 @@ func resultString(operation, message string, result core.Result) (string, error) return value, nil } -func resultDirEntries(operation, message string, result core.Result) ([]fs.DirEntry, error) { +func resultDirEntries(operation, message string, result core.Result) ( + []fs.DirEntry, + error, +) { if !result.OK { return nil, resultError(operation, message, result) } @@ -478,7 +536,10 @@ func resultDirEntries(operation, message string, result core.Result) ([]fs.DirEn return entries, nil } -func resultFileInfo(operation, message string, result core.Result) (fs.FileInfo, error) { +func resultFileInfo(operation, message string, result core.Result) ( + fs.FileInfo, + error, +) { if !result.OK { return nil, resultError(operation, message, result) } @@ -489,7 +550,10 @@ func resultFileInfo(operation, message string, result core.Result) (fs.FileInfo, return fileInfo, nil } -func resultFile(operation, message string, result core.Result) (fs.File, error) { +func resultFile(operation, message string, result core.Result) ( + fs.File, + error, +) { if !result.OK { return nil, resultError(operation, message, result) } @@ -500,7 +564,10 @@ func resultFile(operation, message string, result core.Result) (fs.File, error) return file, nil } -func resultWriteCloser(operation, message string, result core.Result) (goio.WriteCloser, error) { +func resultWriteCloser(operation, message string, result core.Result) ( + goio.WriteCloser, + error, +) { if !result.OK { return nil, resultError(operation, message, result) } diff --git a/local/medium_example_test.go b/go/local/medium_example_test.go similarity index 100% rename from local/medium_example_test.go rename to go/local/medium_example_test.go diff --git a/local/medium_test.go b/go/local/medium_test.go similarity index 100% rename from local/medium_test.go rename to go/local/medium_test.go diff --git a/medium_test.go b/go/medium_test.go similarity index 100% rename from medium_test.go rename to go/medium_test.go diff --git a/mock.go b/go/mock.go similarity index 85% rename from mock.go rename to go/mock.go index 6c68a1a..4c38c0e 100644 --- a/mock.go +++ b/go/mock.go @@ -51,7 +51,10 @@ func NewMockMedium() *MockMedium { var _ Medium = (*MockMedium)(nil) -func (m *MockMedium) Read(path string) (string, error) { +func (m *MockMedium) Read(path string) ( + string, + error, +) { m.mu.RLock() defer m.mu.RUnlock() content, ok := m.Files[path] @@ -61,11 +64,13 @@ func (m *MockMedium) Read(path string) (string, error) { return content, nil } -func (m *MockMedium) Write(path, content string) error { +func (m *MockMedium) Write(path, content string) error { // legacy error contract + return m.WriteMode(path, content, 0644) } -func (m *MockMedium) WriteMode(path, content string, mode fs.FileMode) error { +func (m *MockMedium) WriteMode(path, content string, mode fs.FileMode) error { // legacy error contract + m.mu.Lock() defer m.mu.Unlock() m.Files[path] = content @@ -73,7 +78,8 @@ func (m *MockMedium) WriteMode(path, content string, mode fs.FileMode) error { return nil } -func (m *MockMedium) EnsureDir(path string) error { +func (m *MockMedium) EnsureDir(path string) error { // legacy error contract + m.mu.Lock() defer m.mu.Unlock() m.dirs[path] = true @@ -87,7 +93,8 @@ func (m *MockMedium) IsFile(path string) bool { return ok } -func (m *MockMedium) Delete(path string) error { +func (m *MockMedium) Delete(path string) error { // legacy error contract + m.mu.Lock() defer m.mu.Unlock() if _, ok := m.Files[path]; ok { @@ -102,7 +109,8 @@ func (m *MockMedium) Delete(path string) error { return fs.ErrNotExist } -func (m *MockMedium) DeleteAll(path string) error { +func (m *MockMedium) DeleteAll(path string) error { // legacy error contract + m.mu.Lock() defer m.mu.Unlock() found := false @@ -125,7 +133,8 @@ func (m *MockMedium) DeleteAll(path string) error { return nil } -func (m *MockMedium) Rename(oldPath, newPath string) error { +func (m *MockMedium) Rename(oldPath, newPath string) error { // legacy error contract + m.mu.Lock() defer m.mu.Unlock() f, ok := m.Files[oldPath] @@ -141,7 +150,10 @@ func (m *MockMedium) Rename(oldPath, newPath string) error { return nil } -func (m *MockMedium) List(path string) ([]fs.DirEntry, error) { +func (m *MockMedium) List(path string) ( + []fs.DirEntry, + error, +) { m.mu.RLock() defer m.mu.RUnlock() prefix := mockListPrefix(path) @@ -217,7 +229,10 @@ func appendMockDirectoryEntry(entries []fs.DirEntry, seen map[string]bool, name return append(entries, NewDirEntry(name, true, 0755, NewFileInfo(name, 0, 0755, time.Now(), true))) } -func (m *MockMedium) Stat(path string) (fs.FileInfo, error) { +func (m *MockMedium) Stat(path string) ( + fs.FileInfo, + error, +) { m.mu.RLock() defer m.mu.RUnlock() if content, ok := m.Files[path]; ok { @@ -230,7 +245,10 @@ func (m *MockMedium) Stat(path string) (fs.FileInfo, error) { return nil, fs.ErrNotExist } -func (m *MockMedium) Open(path string) (fs.File, error) { +func (m *MockMedium) Open(path string) ( + fs.File, + error, +) { m.mu.RLock() defer m.mu.RUnlock() content, ok := m.Files[path] @@ -241,18 +259,27 @@ func (m *MockMedium) Open(path string) (fs.File, error) { return &MockFile{reader: core.NewReader(content), info: NewFileInfo(core.PathBase(path), int64(len(content)), mt.mode, mt.modTime, false)}, nil } -func (m *MockMedium) Create(path string) (goio.WriteCloser, error) { +func (m *MockMedium) Create(path string) ( + goio.WriteCloser, + error, +) { return &MockWriteCloser{medium: m, path: path}, nil } -func (m *MockMedium) Append(path string) (goio.WriteCloser, error) { +func (m *MockMedium) Append(path string) ( + goio.WriteCloser, + error, +) { m.mu.RLock() existing := m.Files[path] m.mu.RUnlock() return &MockWriteCloser{medium: m, path: path, data: []byte(existing)}, nil } -func (m *MockMedium) ReadStream(path string) (goio.ReadCloser, error) { +func (m *MockMedium) ReadStream(path string) ( + goio.ReadCloser, + error, +) { m.mu.RLock() defer m.mu.RUnlock() f, ok := m.Files[path] @@ -262,7 +289,10 @@ func (m *MockMedium) ReadStream(path string) (goio.ReadCloser, error) { return goio.NopCloser(core.NewReader(f)), nil } -func (m *MockMedium) WriteStream(path string) (goio.WriteCloser, error) { +func (m *MockMedium) WriteStream(path string) ( + goio.WriteCloser, + error, +) { return m.Create(path) } @@ -308,11 +338,15 @@ type MockWriteCloser struct { data []byte } -func (w *MockWriteCloser) Write(p []byte) (int, error) { +func (w *MockWriteCloser) Write(p []byte) ( + int, + error, +) { w.data = append(w.data, p...) return len(p), nil } -func (w *MockWriteCloser) Close() error { +func (w *MockWriteCloser) Close() error { // legacy error contract + return w.medium.Write(w.path, string(w.data)) } diff --git a/mock_example_test.go b/go/mock_example_test.go similarity index 100% rename from mock_example_test.go rename to go/mock_example_test.go diff --git a/mock_test.go b/go/mock_test.go similarity index 100% rename from mock_test.go rename to go/mock_test.go diff --git a/node/node.go b/go/node/node.go similarity index 89% rename from node/node.go rename to go/node/node.go index b820670..40d11aa 100644 --- a/node/node.go +++ b/go/node/node.go @@ -45,7 +45,10 @@ type nodeArchiveBuffer struct { data []byte } -func (buffer *nodeArchiveBuffer) Write(data []byte) (int, error) { +func (buffer *nodeArchiveBuffer) Write(data []byte) ( + int, + error, +) { buffer.data = append(buffer.data, data...) return len(data), nil } @@ -67,7 +70,10 @@ func (node *Node) AddData(name string, content []byte) { } // Example: snapshot, _ := nodeTree.ToTar() -func (node *Node) ToTar() ([]byte, error) { +func (node *Node) ToTar() ( + []byte, + error, +) { buffer := &nodeArchiveBuffer{} tarWriter := tar.NewWriter(buffer) @@ -94,7 +100,10 @@ func (node *Node) ToTar() ([]byte, error) { } // Example: restored, _ := node.FromTar(snapshot) -func FromTar(data []byte) (*Node, error) { +func FromTar(data []byte) ( + *Node, + error, +) { restoredNode := New() if err := restoredNode.LoadTar(data); err != nil { return nil, err @@ -103,7 +112,8 @@ func FromTar(data []byte) (*Node, error) { } // Example: _ = nodeTree.LoadTar(snapshot) -func (node *Node) LoadTar(data []byte) error { +func (node *Node) LoadTar(data []byte) error { // legacy error contract + newFiles := make(map[string]*dataFile) tarReader := tar.NewReader(core.NewReader(string(data))) @@ -132,7 +142,10 @@ func (node *Node) LoadTar(data []byte) error { return nil } -func dataFileFromTarEntry(tarReader *tar.Reader, header *tar.Header) (*dataFile, error) { +func dataFileFromTarEntry(tarReader *tar.Reader, header *tar.Header) ( + *dataFile, + error, +) { contentResult := core.ReadAll(tarReader) if !contentResult.OK { if err, ok := contentResult.Value.(error); ok { @@ -159,7 +172,8 @@ type WalkOptions struct { } // Example: _ = nodeTree.Walk(".", func(_ string, _ fs.DirEntry, _ error) error { return nil }, node.WalkOptions{MaxDepth: 1, SkipErrors: true}) -func (node *Node) Walk(root string, walkFunc fs.WalkDirFunc, options WalkOptions) error { +func (node *Node) Walk(root string, walkFunc fs.WalkDirFunc, options WalkOptions) error { // legacy error contract + if options.SkipErrors { if _, err := node.Stat(root); err != nil { return nil @@ -181,7 +195,10 @@ func (node *Node) Walk(root string, walkFunc fs.WalkDirFunc, options WalkOptions }) } -func walkFilterDecision(entryPath string, entry fs.DirEntry, err error, filter func(string, fs.DirEntry) bool) (bool, error) { +func walkFilterDecision(entryPath string, entry fs.DirEntry, err error, filter func(string, fs.DirEntry) bool) ( + bool, + error, +) { if filter == nil || err != nil || filter(entryPath, entry) { return false, nil } @@ -211,7 +228,10 @@ func pathDepth(path string) int { } // Example: content, _ := nodeTree.ReadFile("config/app.yaml") -func (node *Node) ReadFile(name string) ([]byte, error) { +func (node *Node) ReadFile(name string) ( + []byte, + error, +) { name = core.TrimPrefix(name, "/") file, ok := node.files[name] if !ok { @@ -225,7 +245,8 @@ func (node *Node) ReadFile(name string) ([]byte, error) { // ExportFile writes a node file to the local filesystem. It operates on coreio.Local directly // and is intentionally local-only — use CopyTo for Medium-agnostic transfers. // Example: _ = nodeTree.ExportFile("config/app.yaml", "backup/app.yaml", 0644) -func (node *Node) ExportFile(sourcePath, destinationPath string, permissions fs.FileMode) error { +func (node *Node) ExportFile(sourcePath, destinationPath string, permissions fs.FileMode) error { // legacy error contract + sourcePath = core.TrimPrefix(sourcePath, "/") file, ok := node.files[sourcePath] if !ok { @@ -247,7 +268,8 @@ func (node *Node) ExportFile(sourcePath, destinationPath string, permissions fs. } // Example: _ = nodeTree.CopyTo(io.NewMemoryMedium(), "config", "backup/config") -func (node *Node) CopyTo(target coreio.Medium, sourcePath, destinationPath string) error { +func (node *Node) CopyTo(target coreio.Medium, sourcePath, destinationPath string) error { // legacy error contract + sourcePath = core.TrimPrefix(sourcePath, "/") if sourcePath == "." { sourcePath = "" @@ -287,7 +309,10 @@ func (node *Node) CopyTo(target coreio.Medium, sourcePath, destinationPath strin } // Example: file, _ := nodeTree.Open("config/app.yaml") -func (node *Node) Open(name string) (fs.File, error) { +func (node *Node) Open(name string) ( + fs.File, + error, +) { name = core.TrimPrefix(name, "/") if name == "." { name = "" @@ -308,7 +333,10 @@ func (node *Node) Open(name string) (fs.File, error) { } // Example: info, _ := nodeTree.Stat("config/app.yaml") -func (node *Node) Stat(name string) (fs.FileInfo, error) { +func (node *Node) Stat(name string) ( + fs.FileInfo, + error, +) { name = core.TrimPrefix(name, "/") if name == "." { name = "" @@ -329,7 +357,10 @@ func (node *Node) Stat(name string) (fs.FileInfo, error) { } // Example: entries, _ := nodeTree.ReadDir("config") -func (node *Node) ReadDir(name string) ([]fs.DirEntry, error) { +func (node *Node) ReadDir(name string) ( + []fs.DirEntry, + error, +) { name = core.TrimPrefix(name, "/") if name == "." { name = "" @@ -380,7 +411,10 @@ func (node *Node) ReadDir(name string) ([]fs.DirEntry, error) { } // Example: content, _ := nodeTree.Read("config/app.yaml") -func (node *Node) Read(filePath string) (string, error) { +func (node *Node) Read(filePath string) ( + string, + error, +) { filePath = core.TrimPrefix(filePath, "/") file, ok := node.files[filePath] if !ok { @@ -390,7 +424,8 @@ func (node *Node) Read(filePath string) (string, error) { } // Example: _ = nodeTree.Write("config/app.yaml", "port: 8080") -func (node *Node) Write(filePath, content string) error { +func (node *Node) Write(filePath, content string) error { // legacy error contract + filePath = core.TrimPrefix(filePath, "/") if filePath == "" || filePath == "." { return core.E("node.Write", "empty path", fs.ErrInvalid) @@ -401,12 +436,14 @@ func (node *Node) Write(filePath, content string) error { // Example: _ = nodeTree.WriteMode("keys/private.key", key, 0600) // Note: mode is intentionally ignored — in-memory nodes have no filesystem permission model. -func (node *Node) WriteMode(filePath, content string, mode fs.FileMode) error { +func (node *Node) WriteMode(filePath, content string, mode fs.FileMode) error { // legacy error contract + return node.Write(filePath, content) } // Example: _ = nodeTree.EnsureDir("config") -func (node *Node) EnsureDir(directoryPath string) error { +func (node *Node) EnsureDir(directoryPath string) error { // legacy error contract + return nil } @@ -433,7 +470,8 @@ func (node *Node) IsDir(filePath string) bool { } // Example: _ = nodeTree.Delete("config/app.yaml") -func (node *Node) Delete(filePath string) error { +func (node *Node) Delete(filePath string) error { // legacy error contract + filePath = core.TrimPrefix(filePath, "/") if _, ok := node.files[filePath]; ok { delete(node.files, filePath) @@ -443,7 +481,8 @@ func (node *Node) Delete(filePath string) error { } // Example: _ = nodeTree.DeleteAll("logs/archive") -func (node *Node) DeleteAll(filePath string) error { +func (node *Node) DeleteAll(filePath string) error { // legacy error contract + filePath = core.TrimPrefix(filePath, "/") found := false @@ -468,7 +507,8 @@ func (node *Node) DeleteAll(filePath string) error { // Example: _ = nodeTree.Rename("drafts/todo.txt", "archive/todo.txt") // Example: _ = nodeTree.Rename("drafts", "archive") -func (node *Node) Rename(oldPath, newPath string) error { +func (node *Node) Rename(oldPath, newPath string) error { // legacy error contract + oldPath = core.TrimPrefix(oldPath, "/") newPath = core.TrimPrefix(newPath, "/") @@ -507,7 +547,10 @@ func (node *Node) Rename(oldPath, newPath string) error { } // Example: entries, _ := nodeTree.List("config") -func (node *Node) List(filePath string) ([]fs.DirEntry, error) { +func (node *Node) List(filePath string) ( + []fs.DirEntry, + error, +) { filePath = core.TrimPrefix(filePath, "/") if filePath == "" || filePath == "." { return node.ReadDir(".") @@ -516,13 +559,19 @@ func (node *Node) List(filePath string) ([]fs.DirEntry, error) { } // Example: writer, _ := nodeTree.Create("logs/app.log") -func (node *Node) Create(filePath string) (goio.WriteCloser, error) { +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) { +func (node *Node) Append(filePath string) ( + goio.WriteCloser, + error, +) { filePath = core.TrimPrefix(filePath, "/") var existing []byte if file, ok := node.files[filePath]; ok { @@ -532,7 +581,10 @@ func (node *Node) Append(filePath string) (goio.WriteCloser, error) { return &nodeWriter{node: node, path: filePath, buffer: existing}, nil } -func (node *Node) ReadStream(filePath string) (goio.ReadCloser, error) { +func (node *Node) ReadStream(filePath string) ( + goio.ReadCloser, + error, +) { file, err := node.Open(filePath) if err != nil { return nil, err @@ -540,7 +592,10 @@ func (node *Node) ReadStream(filePath string) (goio.ReadCloser, error) { return file, nil } -func (node *Node) WriteStream(filePath string) (goio.WriteCloser, error) { +func (node *Node) WriteStream(filePath string) ( + goio.WriteCloser, + error, +) { return node.Create(filePath) } @@ -550,12 +605,16 @@ type nodeWriter struct { buffer []byte } -func (writer *nodeWriter) Write(data []byte) (int, error) { +func (writer *nodeWriter) Write(data []byte) ( + int, + error, +) { writer.buffer = append(writer.buffer, data...) return len(data), nil } -func (writer *nodeWriter) Close() error { +func (writer *nodeWriter) Close() error { // legacy error contract + if writer.path == "" || writer.path == "." { return core.E("node.nodeWriter.Close", "empty path", fs.ErrInvalid) } @@ -600,7 +659,10 @@ type dataFileReader struct { func (reader *dataFileReader) Stat() (fs.FileInfo, error) { return reader.file.Stat() } -func (reader *dataFileReader) Read(buffer []byte) (int, error) { +func (reader *dataFileReader) Read(buffer []byte) ( + int, + error, +) { if reader.offset >= int64(len(reader.file.content)) { return 0, goio.EOF } @@ -633,11 +695,17 @@ type dirFile struct { modTime time.Time } -func (directory *dirFile) Stat() (fs.FileInfo, error) { +func (directory *dirFile) Stat() ( + fs.FileInfo, + error, +) { return &dirInfo{name: core.PathBase(directory.path), modTime: directory.modTime}, nil } -func (directory *dirFile) Read([]byte) (int, error) { +func (directory *dirFile) Read([]byte) ( + int, + error, +) { return 0, core.E("node.dirFile.Read", core.Concat("cannot read directory: ", directory.path), &fs.PathError{Op: "read", Path: directory.path, Err: fs.ErrInvalid}) } diff --git a/node/node_example_test.go b/go/node/node_example_test.go similarity index 100% rename from node/node_example_test.go rename to go/node/node_example_test.go diff --git a/node/node_test.go b/go/node/node_test.go similarity index 100% rename from node/node_test.go rename to go/node/node_test.go diff --git a/pkg/api/handlers.go b/go/pkg/api/handlers.go similarity index 94% rename from pkg/api/handlers.go rename to go/pkg/api/handlers.go index aca3e69..fea7296 100644 --- a/pkg/api/handlers.go +++ b/go/pkg/api/handlers.go @@ -347,7 +347,10 @@ var mediumOperationHandlers = map[string]mediumOperationHandler{ "isdir": isDirMediumOperation, } -func dispatchMediumOperation(ctx context.Context, medium coreio.Medium, op string, req mediumRequest) (mediumResponse, error) { +func dispatchMediumOperation(ctx context.Context, medium coreio.Medium, op string, req mediumRequest) ( + mediumResponse, + error, +) { handler, ok := mediumOperationHandlers[core.Lower(op)] if !ok { return mediumResponse{}, core.Errorf("%w: %s", errUnsupportedMediumOperation, op) @@ -355,7 +358,10 @@ func dispatchMediumOperation(ctx context.Context, medium coreio.Medium, op strin return handler(ctx, medium, req) } -func readMediumOperation(_ context.Context, medium coreio.Medium, req mediumRequest) (mediumResponse, error) { +func readMediumOperation(_ context.Context, medium coreio.Medium, req mediumRequest) ( + mediumResponse, + error, +) { content, err := medium.Read(req.Path) if err != nil { return mediumResponse{}, err @@ -363,14 +369,20 @@ func readMediumOperation(_ context.Context, medium coreio.Medium, req mediumRequ return mediumResponse{OK: true, Content: content}, nil } -func writeMediumOperation(_ context.Context, medium coreio.Medium, req mediumRequest) (mediumResponse, error) { +func writeMediumOperation(_ context.Context, medium coreio.Medium, req mediumRequest) ( + mediumResponse, + error, +) { if err := medium.Write(req.Path, req.Content); err != nil { return mediumResponse{}, err } return mediumResponse{OK: true}, nil } -func writeModeMediumOperation(_ context.Context, medium coreio.Medium, req mediumRequest) (mediumResponse, error) { +func writeModeMediumOperation(_ context.Context, medium coreio.Medium, req mediumRequest) ( + mediumResponse, + error, +) { mode, err := fileModeValue(req.Mode, 0644) if err != nil { return mediumResponse{}, err @@ -381,19 +393,28 @@ func writeModeMediumOperation(_ context.Context, medium coreio.Medium, req mediu return mediumResponse{OK: true}, nil } -func ensureDirMediumOperation(_ context.Context, medium coreio.Medium, req mediumRequest) (mediumResponse, error) { +func ensureDirMediumOperation(_ context.Context, medium coreio.Medium, req mediumRequest) ( + mediumResponse, + error, +) { if err := medium.EnsureDir(req.Path); err != nil { return mediumResponse{}, err } return mediumResponse{OK: true}, nil } -func isFileMediumOperation(_ context.Context, medium coreio.Medium, req mediumRequest) (mediumResponse, error) { +func isFileMediumOperation(_ context.Context, medium coreio.Medium, req mediumRequest) ( + mediumResponse, + error, +) { ok := medium.IsFile(req.Path) return mediumResponse{OK: true, IsFile: &ok}, nil } -func deleteMediumOperation(ctx context.Context, medium coreio.Medium, req mediumRequest) (mediumResponse, error) { +func deleteMediumOperation(ctx context.Context, medium coreio.Medium, req mediumRequest) ( + mediumResponse, + error, +) { if req.Recursive { return deleteAllMediumOperation(ctx, medium, req) } @@ -403,21 +424,30 @@ func deleteMediumOperation(ctx context.Context, medium coreio.Medium, req medium return mediumResponse{OK: true}, nil } -func deleteAllMediumOperation(_ context.Context, medium coreio.Medium, req mediumRequest) (mediumResponse, error) { +func deleteAllMediumOperation(_ context.Context, medium coreio.Medium, req mediumRequest) ( + mediumResponse, + error, +) { if err := medium.DeleteAll(req.Path); err != nil { return mediumResponse{}, err } return mediumResponse{OK: true}, nil } -func renameMediumOperation(_ context.Context, medium coreio.Medium, req mediumRequest) (mediumResponse, error) { +func renameMediumOperation(_ context.Context, medium coreio.Medium, req mediumRequest) ( + mediumResponse, + error, +) { if err := medium.Rename(req.OldPath, req.NewPath); err != nil { return mediumResponse{}, err } return mediumResponse{OK: true}, nil } -func listMediumOperation(_ context.Context, medium coreio.Medium, req mediumRequest) (mediumResponse, error) { +func listMediumOperation(_ context.Context, medium coreio.Medium, req mediumRequest) ( + mediumResponse, + error, +) { entries, err := medium.List(req.Path) if err != nil { return mediumResponse{}, err @@ -425,7 +455,10 @@ func listMediumOperation(_ context.Context, medium coreio.Medium, req mediumRequ return mediumResponse{OK: true, Entries: dirEntryDTOs(entries)}, nil } -func statMediumOperation(_ context.Context, medium coreio.Medium, req mediumRequest) (mediumResponse, error) { +func statMediumOperation(_ context.Context, medium coreio.Medium, req mediumRequest) ( + mediumResponse, + error, +) { info, err := medium.Stat(req.Path) if err != nil { return mediumResponse{}, err @@ -433,7 +466,10 @@ func statMediumOperation(_ context.Context, medium coreio.Medium, req mediumRequ return mediumResponse{OK: true, Info: fileInfoDTOFromInfo(info)}, nil } -func openMediumOperation(_ context.Context, medium coreio.Medium, req mediumRequest) (mediumResponse, error) { +func openMediumOperation(_ context.Context, medium coreio.Medium, req mediumRequest) ( + mediumResponse, + error, +) { file, err := medium.Open(req.Path) if err != nil { return mediumResponse{}, err @@ -442,7 +478,10 @@ func openMediumOperation(_ context.Context, medium coreio.Medium, req mediumRequ return readAllContent(file) } -func createMediumOperation(_ context.Context, medium coreio.Medium, req mediumRequest) (mediumResponse, error) { +func createMediumOperation(_ context.Context, medium coreio.Medium, req mediumRequest) ( + mediumResponse, + error, +) { writer, err := medium.Create(req.Path) if err != nil { return mediumResponse{}, err @@ -453,7 +492,10 @@ func createMediumOperation(_ context.Context, medium coreio.Medium, req mediumRe return mediumResponse{OK: true}, nil } -func appendMediumOperation(_ context.Context, medium coreio.Medium, req mediumRequest) (mediumResponse, error) { +func appendMediumOperation(_ context.Context, medium coreio.Medium, req mediumRequest) ( + mediumResponse, + error, +) { writer, err := medium.Append(req.Path) if err != nil { return mediumResponse{}, err @@ -464,7 +506,10 @@ func appendMediumOperation(_ context.Context, medium coreio.Medium, req mediumRe return mediumResponse{OK: true}, nil } -func readStreamMediumOperation(_ context.Context, medium coreio.Medium, req mediumRequest) (mediumResponse, error) { +func readStreamMediumOperation(_ context.Context, medium coreio.Medium, req mediumRequest) ( + mediumResponse, + error, +) { reader, err := medium.ReadStream(req.Path) if err != nil { return mediumResponse{}, err @@ -473,7 +518,10 @@ func readStreamMediumOperation(_ context.Context, medium coreio.Medium, req medi return readAllContent(reader) } -func writeStreamMediumOperation(_ context.Context, medium coreio.Medium, req mediumRequest) (mediumResponse, error) { +func writeStreamMediumOperation(_ context.Context, medium coreio.Medium, req mediumRequest) ( + mediumResponse, + error, +) { writer, err := medium.WriteStream(req.Path) if err != nil { return mediumResponse{}, err @@ -484,17 +532,26 @@ func writeStreamMediumOperation(_ context.Context, medium coreio.Medium, req med return mediumResponse{OK: true}, nil } -func existsMediumOperation(_ context.Context, medium coreio.Medium, req mediumRequest) (mediumResponse, error) { +func existsMediumOperation(_ context.Context, medium coreio.Medium, req mediumRequest) ( + mediumResponse, + error, +) { ok := medium.Exists(req.Path) return mediumResponse{OK: true, Exists: &ok}, nil } -func isDirMediumOperation(_ context.Context, medium coreio.Medium, req mediumRequest) (mediumResponse, error) { +func isDirMediumOperation(_ context.Context, medium coreio.Medium, req mediumRequest) ( + mediumResponse, + error, +) { ok := medium.IsDir(req.Path) return mediumResponse{OK: true, IsDir: &ok}, nil } -func readAllContent(reader goio.Reader) (mediumResponse, error) { +func readAllContent(reader goio.Reader) ( + mediumResponse, + error, +) { content, err := goio.ReadAll(reader) if err != nil { return mediumResponse{}, err @@ -612,7 +669,10 @@ func normalizedValue(value any) any { } } -func fileModeValue(value any, fallback fs.FileMode) (fs.FileMode, error) { +func fileModeValue(value any, fallback fs.FileMode) ( + fs.FileMode, + error, +) { if value == nil { return fallback, nil } @@ -666,7 +726,8 @@ func fileInfoDTOFromInfo(info fs.FileInfo) *fileInfoDTO { } } -func writeAndClose(writer goio.WriteCloser, content string) error { +func writeAndClose(writer goio.WriteCloser, content string) error { // legacy error contract + if _, err := goio.WriteString(writer, content); err != nil { if closeErr := writer.Close(); closeErr != nil { return core.ErrorJoin(err, closeErr) diff --git a/pkg/api/handlers_test.go b/go/pkg/api/handlers_test.go similarity index 100% rename from pkg/api/handlers_test.go rename to go/pkg/api/handlers_test.go diff --git a/pkg/api/provider.go b/go/pkg/api/provider.go similarity index 100% rename from pkg/api/provider.go rename to go/pkg/api/provider.go diff --git a/pkg/api/provider_example_test.go b/go/pkg/api/provider_example_test.go similarity index 100% rename from pkg/api/provider_example_test.go rename to go/pkg/api/provider_example_test.go diff --git a/pkg/api/provider_test.go b/go/pkg/api/provider_test.go similarity index 100% rename from pkg/api/provider_test.go rename to go/pkg/api/provider_test.go diff --git a/pkg/medium/github/github.go b/go/pkg/medium/github/github.go similarity index 86% rename from pkg/medium/github/github.go rename to go/pkg/medium/github/github.go index b4422b6..383b973 100644 --- a/pkg/medium/github/github.go +++ b/go/pkg/medium/github/github.go @@ -56,7 +56,10 @@ type Options struct { } // New creates a GitHub Medium. -func New(options Options) (*Medium, error) { +func New(options Options) ( + *Medium, + error, +) { owner := core.Trim(options.Owner) if owner == "" { return nil, core.E(opNew, "owner is required", fs.ErrInvalid) @@ -124,7 +127,11 @@ func tokenFromEnvironment(tokenFile string) string { return core.Trim(data) } -func tokenFileMedium(tokenFile string) (coreio.Medium, string, error) { +func tokenFileMedium(tokenFile string) ( + coreio.Medium, + string, + error, +) { if core.PathIsAbs(tokenFile) { root := core.PathDir(tokenFile) relativePath := core.PathBase(tokenFile) @@ -164,7 +171,8 @@ func oauthClient(client *http.Client, token string) *http.Client { return &clone } -func setClientBaseURL(client *gh.Client, baseURL string) error { +func setClientBaseURL(client *gh.Client, baseURL string) error { // legacy error contract + parsed, err := url.Parse(baseURL) if err != nil { return err @@ -187,7 +195,10 @@ func cleanRelative(filePath string) string { return core.TrimPrefix(clean, "/") } -func requiredPath(operation, filePath string) (string, error) { +func requiredPath(operation, filePath string) ( + string, + error, +) { clean := cleanRelative(filePath) if clean == "" { return "", core.E(operation, "path is required", fs.ErrInvalid) @@ -202,7 +213,11 @@ func (medium *Medium) contentOptions() *gh.RepositoryContentGetOptions { return &gh.RepositoryContentGetOptions{Ref: medium.ref} } -func (medium *Medium) getContents(operation, filePath string) (*gh.RepositoryContent, []*gh.RepositoryContent, error) { +func (medium *Medium) getContents(operation, filePath string) ( + *gh.RepositoryContent, + []*gh.RepositoryContent, + error, +) { fileContent, directoryContent, _, err := medium.client.Repositories.GetContents( context.Background(), medium.owner, @@ -216,7 +231,8 @@ func (medium *Medium) getContents(operation, filePath string) (*gh.RepositoryCon return fileContent, directoryContent, nil } -func wrapGitHubError(operation, filePath string, err error) error { +func wrapGitHubError(operation, filePath string, err error) error { // legacy error contract + if err == nil { return nil } @@ -238,7 +254,8 @@ func wrapGitHubError(operation, filePath string, err error) error { return core.E(operation, core.Concat("GitHub contents request failed: ", filePath), err) } -func readOnly(operation string) error { +func readOnly(operation string) error { // legacy error contract + return core.E(operation, "GitHub medium is read-only", ErrReadOnly) } @@ -260,7 +277,10 @@ func dirInfoForPath(filePath string) coreio.FileInfo { } // Read reads a repository file into a string. -func (medium *Medium) Read(filePath string) (string, error) { +func (medium *Medium) Read(filePath string) ( + string, + error, +) { clean, err := requiredPath(opRead, filePath) if err != nil { return "", err @@ -283,17 +303,20 @@ func (medium *Medium) Read(filePath string) (string, error) { } // Write returns ErrReadOnly because GitHub Medium is read-only. -func (medium *Medium) Write(filePath, content string) error { +func (medium *Medium) Write(filePath, content string) error { // legacy error contract + return readOnly("github.Write") } // WriteMode returns ErrReadOnly because GitHub Medium is read-only. -func (medium *Medium) WriteMode(filePath, content string, mode fs.FileMode) error { +func (medium *Medium) WriteMode(filePath, content string, mode fs.FileMode) error { // legacy error contract + return readOnly("github.WriteMode") } // EnsureDir returns ErrReadOnly because GitHub Medium is read-only. -func (medium *Medium) EnsureDir(filePath string) error { +func (medium *Medium) EnsureDir(filePath string) error { // legacy error contract + return readOnly("github.EnsureDir") } @@ -308,22 +331,28 @@ func (medium *Medium) IsFile(filePath string) bool { } // Delete returns ErrReadOnly because GitHub Medium is read-only. -func (medium *Medium) Delete(filePath string) error { +func (medium *Medium) Delete(filePath string) error { // legacy error contract + return readOnly("github.Delete") } // DeleteAll returns ErrReadOnly because GitHub Medium is read-only. -func (medium *Medium) DeleteAll(filePath string) error { +func (medium *Medium) DeleteAll(filePath string) error { // legacy error contract + return readOnly("github.DeleteAll") } // Rename returns ErrReadOnly because GitHub Medium is read-only. -func (medium *Medium) Rename(oldPath, newPath string) error { +func (medium *Medium) Rename(oldPath, newPath string) error { // legacy error contract + return readOnly("github.Rename") } // List returns a recursive listing under a repository directory. -func (medium *Medium) List(filePath string) ([]fs.DirEntry, error) { +func (medium *Medium) List(filePath string) ( + []fs.DirEntry, + error, +) { clean := cleanRelative(filePath) entries, err := medium.listRecursive(clean) if err != nil { @@ -335,7 +364,10 @@ func (medium *Medium) List(filePath string) ([]fs.DirEntry, error) { return entries, nil } -func (medium *Medium) listRecursive(filePath string) ([]fs.DirEntry, error) { +func (medium *Medium) listRecursive(filePath string) ( + []fs.DirEntry, + error, +) { fileContent, directoryContent, err := medium.getContents(opList, filePath) if err != nil { return nil, err @@ -370,7 +402,10 @@ func (medium *Medium) listRecursive(filePath string) ([]fs.DirEntry, error) { } // Stat returns metadata for a repository path. -func (medium *Medium) Stat(filePath string) (fs.FileInfo, error) { +func (medium *Medium) Stat(filePath string) ( + fs.FileInfo, + error, +) { clean, err := requiredPath(opStat, filePath) if err != nil { return nil, err @@ -389,7 +424,10 @@ func (medium *Medium) Stat(filePath string) (fs.FileInfo, error) { } // Open opens a repository file for reading. -func (medium *Medium) Open(filePath string) (fs.File, error) { +func (medium *Medium) Open(filePath string) ( + fs.File, + error, +) { content, err := medium.Read(filePath) if err != nil { return nil, err @@ -407,17 +445,26 @@ func (medium *Medium) Open(filePath string) (fs.File, error) { } // Create returns ErrReadOnly because GitHub Medium is read-only. -func (medium *Medium) Create(filePath string) (goio.WriteCloser, error) { +func (medium *Medium) Create(filePath string) ( + goio.WriteCloser, + error, +) { return nil, readOnly("github.Create") } // Append returns ErrReadOnly because GitHub Medium is read-only. -func (medium *Medium) Append(filePath string) (goio.WriteCloser, error) { +func (medium *Medium) Append(filePath string) ( + goio.WriteCloser, + error, +) { return nil, readOnly("github.Append") } // ReadStream opens a repository file as an io.ReadCloser. -func (medium *Medium) ReadStream(filePath string) (goio.ReadCloser, error) { +func (medium *Medium) ReadStream(filePath string) ( + goio.ReadCloser, + error, +) { content, err := medium.Read(filePath) if err != nil { return nil, err @@ -426,7 +473,10 @@ func (medium *Medium) ReadStream(filePath string) (goio.ReadCloser, error) { } // WriteStream returns ErrReadOnly because GitHub Medium is read-only. -func (medium *Medium) WriteStream(filePath string) (goio.WriteCloser, error) { +func (medium *Medium) WriteStream(filePath string) ( + goio.WriteCloser, + error, +) { return nil, readOnly("github.WriteStream") } @@ -453,14 +503,20 @@ func (medium *Medium) IsDir(filePath string) bool { // Clone returns all file contents under filePath, keyed by repository path. // Default full-repo clones use Borg's Git cloner and DataNode substrate; custom // GitHub API URLs and explicit refs keep the REST contents path for compatibility. -func (medium *Medium) Clone(filePath string) (map[string]string, error) { +func (medium *Medium) Clone(filePath string) ( + map[string]string, + error, +) { if medium.baseURL == "" && medium.ref == "" { return medium.cloneWithBorg(filePath) } return medium.cloneWithContentsAPI(filePath) } -func (medium *Medium) cloneWithBorg(filePath string) (map[string]string, error) { +func (medium *Medium) cloneWithBorg(filePath string) ( + map[string]string, + error, +) { dataNode, err := borgvcs.NewGitCloner().CloneGitRepository(medium.borgCloneURL(), nil) if err != nil { return nil, core.E(opClone, "Borg clone failed", err) @@ -476,7 +532,10 @@ func (medium *Medium) borgCloneURL() string { return core.Sprintf("https://github.com/%s/%s.git", medium.owner, medium.repo) } -func collectBorgDataNodeContents(dataNode *borgdatanode.DataNode, clean string) (map[string]string, error) { +func collectBorgDataNodeContents(dataNode *borgdatanode.DataNode, clean string) ( + map[string]string, + error, +) { if dataNode == nil { return nil, fs.ErrInvalid } @@ -501,7 +560,8 @@ func collectBorgDataNodeContents(dataNode *borgdatanode.DataNode, clean string) return contents, nil } -func collectBorgDataNodeDir(dataNode *borgdatanode.DataNode, dirPath string, contents map[string]string) error { +func collectBorgDataNodeDir(dataNode *borgdatanode.DataNode, dirPath string, contents map[string]string) error { // legacy error contract + entries, err := dataNode.ReadDir(dirPath) if err != nil { return err @@ -526,7 +586,10 @@ func collectBorgDataNodeDir(dataNode *borgdatanode.DataNode, dirPath string, con return nil } -func readBorgDataNodeFile(dataNode *borgdatanode.DataNode, filePath string) (string, error) { +func readBorgDataNodeFile(dataNode *borgdatanode.DataNode, filePath string) ( + string, + error, +) { file, err := dataNode.Open(filePath) if err != nil { return "", err @@ -545,7 +608,10 @@ func closeGitHubDataNodeFile(file fs.File, filePath string) { } } -func (medium *Medium) cloneWithContentsAPI(filePath string) (map[string]string, error) { +func (medium *Medium) cloneWithContentsAPI(filePath string) ( + map[string]string, + error, +) { clean := cleanRelative(filePath) if clean != "" { info, err := medium.Stat(clean) @@ -590,11 +656,17 @@ type githubFile struct { var _ fs.File = (*githubFile)(nil) -func (file *githubFile) Stat() (fs.FileInfo, error) { +func (file *githubFile) Stat() ( + fs.FileInfo, + error, +) { return coreio.NewFileInfo(file.name, int64(len(file.content)), file.mode, file.modTime, false), nil } -func (file *githubFile) Read(data []byte) (int, error) { +func (file *githubFile) Read(data []byte) ( + int, + error, +) { if file.closed { return 0, fs.ErrClosed } @@ -607,7 +679,8 @@ func (file *githubFile) Read(data []byte) (int, error) { return n, err } -func (file *githubFile) Close() error { +func (file *githubFile) Close() error { // legacy error contract + file.closed = true return nil } diff --git a/pkg/medium/github/github_example_test.go b/go/pkg/medium/github/github_example_test.go similarity index 100% rename from pkg/medium/github/github_example_test.go rename to go/pkg/medium/github/github_example_test.go diff --git a/pkg/medium/github/github_test.go b/go/pkg/medium/github/github_test.go similarity index 100% rename from pkg/medium/github/github_test.go rename to go/pkg/medium/github/github_test.go diff --git a/pkg/medium/github/register.go b/go/pkg/medium/github/register.go similarity index 97% rename from pkg/medium/github/register.go rename to go/pkg/medium/github/register.go index c6dc8a9..36258fe 100644 --- a/pkg/medium/github/register.go +++ b/go/pkg/medium/github/register.go @@ -46,7 +46,10 @@ func RegisterActions(c *core.Core) { c.Action(ActionClone, cloneAction) } -func mediumFromOptions(opts core.Options) (*Medium, error) { +func mediumFromOptions(opts core.Options) ( + *Medium, + error, +) { if medium, ok := opts.Get("medium").Value.(*Medium); ok { return medium, nil } diff --git a/pkg/medium/github/register_example_test.go b/go/pkg/medium/github/register_example_test.go similarity index 100% rename from pkg/medium/github/register_example_test.go rename to go/pkg/medium/github/register_example_test.go diff --git a/pkg/medium/github/register_test.go b/go/pkg/medium/github/register_test.go similarity index 100% rename from pkg/medium/github/register_test.go rename to go/pkg/medium/github/register_test.go diff --git a/pkg/medium/pwa/pwa.go b/go/pkg/medium/pwa/pwa.go similarity index 82% rename from pkg/medium/pwa/pwa.go rename to go/pkg/medium/pwa/pwa.go index d8405e8..5a263c5 100644 --- a/pkg/medium/pwa/pwa.go +++ b/go/pkg/medium/pwa/pwa.go @@ -41,7 +41,10 @@ type Options struct { } // New creates a PWA Medium that lazily downloads the target into a Borg DataNode. -func New(options Options) (*Medium, error) { +func New(options Options) ( + *Medium, + error, +) { return &Medium{url: options.URL}, nil } @@ -53,7 +56,10 @@ func cleanRelative(filePath string) string { return core.TrimPrefix(clean, "/") } -func requiredPath(operation, filePath string) (string, error) { +func requiredPath(operation, filePath string) ( + string, + error, +) { clean := cleanRelative(filePath) if clean == "" { return "", core.E(operation, "path is required", fs.ErrInvalid) @@ -61,7 +67,10 @@ func requiredPath(operation, filePath string) (string, error) { return clean, nil } -func (medium *Medium) ensureDataNode() (*borgdatanode.DataNode, error) { +func (medium *Medium) ensureDataNode() ( + *borgdatanode.DataNode, + error, +) { medium.mu.RLock() dataNode := medium.dataNode medium.mu.RUnlock() @@ -93,12 +102,16 @@ func (medium *Medium) ensureDataNode() (*borgdatanode.DataNode, error) { return dataNode, nil } -func readOnly(operation string) error { +func readOnly(operation string) error { // legacy error contract + return core.E(operation, "PWA medium is read-only", errReadOnly) } // Read reads a downloaded PWA asset from Borg's DataNode. -func (medium *Medium) Read(filePath string) (string, error) { +func (medium *Medium) Read(filePath string) ( + string, + error, +) { clean, err := requiredPath(opRead, filePath) if err != nil { return "", err @@ -127,17 +140,20 @@ func (medium *Medium) Read(filePath string) (string, error) { } // Write returns an error because downloaded PWA DataNodes are read-only. -func (medium *Medium) Write(filePath, content string) error { +func (medium *Medium) Write(filePath, content string) error { // legacy error contract + return readOnly("pwa.Write") } // WriteMode returns an error because downloaded PWA DataNodes are read-only. -func (medium *Medium) WriteMode(filePath, content string, mode fs.FileMode) error { +func (medium *Medium) WriteMode(filePath, content string, mode fs.FileMode) error { // legacy error contract + return readOnly("pwa.WriteMode") } // EnsureDir returns an error because downloaded PWA DataNodes are read-only. -func (medium *Medium) EnsureDir(filePath string) error { +func (medium *Medium) EnsureDir(filePath string) error { // legacy error contract + return readOnly("pwa.EnsureDir") } @@ -152,22 +168,28 @@ func (medium *Medium) IsFile(filePath string) bool { } // Delete returns an error because downloaded PWA DataNodes are read-only. -func (medium *Medium) Delete(filePath string) error { +func (medium *Medium) Delete(filePath string) error { // legacy error contract + return readOnly("pwa.Delete") } // DeleteAll returns an error because downloaded PWA DataNodes are read-only. -func (medium *Medium) DeleteAll(filePath string) error { +func (medium *Medium) DeleteAll(filePath string) error { // legacy error contract + return readOnly("pwa.DeleteAll") } // Rename returns an error because downloaded PWA DataNodes are read-only. -func (medium *Medium) Rename(oldPath, newPath string) error { +func (medium *Medium) Rename(oldPath, newPath string) error { // legacy error contract + return readOnly("pwa.Rename") } // List lists downloaded PWA assets from Borg's DataNode. -func (medium *Medium) List(filePath string) ([]fs.DirEntry, error) { +func (medium *Medium) List(filePath string) ( + []fs.DirEntry, + error, +) { dataNode, err := medium.ensureDataNode() if err != nil { return nil, err @@ -181,7 +203,10 @@ func (medium *Medium) List(filePath string) ([]fs.DirEntry, error) { } // Stat returns metadata for a downloaded PWA asset. -func (medium *Medium) Stat(filePath string) (fs.FileInfo, error) { +func (medium *Medium) Stat(filePath string) ( + fs.FileInfo, + error, +) { dataNode, err := medium.ensureDataNode() if err != nil { return nil, err @@ -195,7 +220,10 @@ func (medium *Medium) Stat(filePath string) (fs.FileInfo, error) { } // Open opens a downloaded PWA asset from Borg's DataNode. -func (medium *Medium) Open(filePath string) (fs.File, error) { +func (medium *Medium) Open(filePath string) ( + fs.File, + error, +) { clean, err := requiredPath(opOpen, filePath) if err != nil { return nil, err @@ -227,17 +255,26 @@ func closePWAFile(file fs.File, filePath string) { } // Create returns an error because downloaded PWA DataNodes are read-only. -func (medium *Medium) Create(filePath string) (goio.WriteCloser, error) { +func (medium *Medium) Create(filePath string) ( + goio.WriteCloser, + error, +) { return nil, readOnly("pwa.Create") } // Append returns an error because downloaded PWA DataNodes are read-only. -func (medium *Medium) Append(filePath string) (goio.WriteCloser, error) { +func (medium *Medium) Append(filePath string) ( + goio.WriteCloser, + error, +) { return nil, readOnly("pwa.Append") } // ReadStream opens a downloaded PWA asset as a stream. -func (medium *Medium) ReadStream(filePath string) (goio.ReadCloser, error) { +func (medium *Medium) ReadStream(filePath string) ( + goio.ReadCloser, + error, +) { file, err := medium.Open(filePath) if err != nil { return nil, err @@ -246,7 +283,10 @@ func (medium *Medium) ReadStream(filePath string) (goio.ReadCloser, error) { } // WriteStream returns an error because downloaded PWA DataNodes are read-only. -func (medium *Medium) WriteStream(filePath string) (goio.WriteCloser, error) { +func (medium *Medium) WriteStream(filePath string) ( + goio.WriteCloser, + error, +) { return nil, readOnly("pwa.WriteStream") } diff --git a/pkg/medium/pwa/pwa_example_test.go b/go/pkg/medium/pwa/pwa_example_test.go similarity index 100% rename from pkg/medium/pwa/pwa_example_test.go rename to go/pkg/medium/pwa/pwa_example_test.go diff --git a/pkg/medium/pwa/pwa_test.go b/go/pkg/medium/pwa/pwa_test.go similarity index 100% rename from pkg/medium/pwa/pwa_test.go rename to go/pkg/medium/pwa/pwa_test.go diff --git a/pkg/medium/pwa/register.go b/go/pkg/medium/pwa/register.go similarity index 97% rename from pkg/medium/pwa/register.go rename to go/pkg/medium/pwa/register.go index 885655a..f5a1856 100644 --- a/pkg/medium/pwa/register.go +++ b/go/pkg/medium/pwa/register.go @@ -48,7 +48,10 @@ func RegisterActions(c *core.Core) { c.Action(ActionWrite, writeAction) } -func mediumFromOptions(opts core.Options) (*Medium, error) { +func mediumFromOptions(opts core.Options) ( + *Medium, + error, +) { if medium, ok := opts.Get("medium").Value.(*Medium); ok { return medium, nil } diff --git a/pkg/medium/pwa/register_example_test.go b/go/pkg/medium/pwa/register_example_test.go similarity index 100% rename from pkg/medium/pwa/register_example_test.go rename to go/pkg/medium/pwa/register_example_test.go diff --git a/pkg/medium/pwa/register_test.go b/go/pkg/medium/pwa/register_test.go similarity index 100% rename from pkg/medium/pwa/register_test.go rename to go/pkg/medium/pwa/register_test.go diff --git a/pkg/medium/sftp/register.go b/go/pkg/medium/sftp/register.go similarity index 100% rename from pkg/medium/sftp/register.go rename to go/pkg/medium/sftp/register.go diff --git a/pkg/medium/sftp/register_example_test.go b/go/pkg/medium/sftp/register_example_test.go similarity index 100% rename from pkg/medium/sftp/register_example_test.go rename to go/pkg/medium/sftp/register_example_test.go diff --git a/pkg/medium/sftp/register_test.go b/go/pkg/medium/sftp/register_test.go similarity index 100% rename from pkg/medium/sftp/register_test.go rename to go/pkg/medium/sftp/register_test.go diff --git a/pkg/medium/sftp/sftp.go b/go/pkg/medium/sftp/sftp.go similarity index 88% rename from pkg/medium/sftp/sftp.go rename to go/pkg/medium/sftp/sftp.go index 0b0ed49..f8b4b2e 100644 --- a/pkg/medium/sftp/sftp.go +++ b/go/pkg/medium/sftp/sftp.go @@ -54,7 +54,10 @@ type Options struct { // New creates an SFTP Medium. Tests and callers that already manage transport // state can inject Client directly; otherwise New dials Address using SSH. -func New(options Options) (*Medium, error) { +func New(options Options) ( + *Medium, + error, +) { root := normaliseRoot(options.Root) if options.Client != nil { return &Medium{client: options.Client, root: root}, nil @@ -96,7 +99,10 @@ func New(options Options) (*Medium, error) { }, nil } -func sshConfig(options Options) (*ssh.ClientConfig, error) { +func sshConfig(options Options) ( + *ssh.ClientConfig, + error, +) { if options.Config != nil { return options.Config, nil } @@ -156,7 +162,10 @@ func (medium *Medium) remotePath(filePath string) string { return core.PathJoin(medium.root, relative) } -func (medium *Medium) requiredRemotePath(operation, filePath string) (string, error) { +func (medium *Medium) requiredRemotePath(operation, filePath string) ( + string, + error, +) { if cleanRelative(filePath) == "" { return "", core.E(operation, "path is required", fs.ErrInvalid) } @@ -169,7 +178,8 @@ func closeSFTPCloser(closer goio.Closer, operation, target string) { } } -func (medium *Medium) ensureParent(remotePath string) error { +func (medium *Medium) ensureParent(remotePath string) error { // legacy error contract + parent := core.PathDir(remotePath) if parent == "." || parent == "/" { return nil @@ -178,7 +188,8 @@ func (medium *Medium) ensureParent(remotePath string) error { } // Close closes clients created by New. Injected clients remain caller-owned. -func (medium *Medium) Close() error { +func (medium *Medium) Close() error { // legacy error contract + var err error if medium.ownsClient && medium.client != nil { err = medium.client.Close() @@ -192,7 +203,10 @@ func (medium *Medium) Close() error { } // Read reads a remote file into a string. -func (medium *Medium) Read(filePath string) (string, error) { +func (medium *Medium) Read(filePath string) ( + string, + error, +) { remotePath, err := medium.requiredRemotePath(opRead, filePath) if err != nil { return "", err @@ -211,13 +225,15 @@ func (medium *Medium) Read(filePath string) (string, error) { } // Write writes a remote file using the default file mode. -func (medium *Medium) Write(filePath, content string) error { +func (medium *Medium) Write(filePath, content string) error { // legacy error contract + return medium.WriteMode(filePath, content, 0644) } // WriteMode writes a remote file and applies POSIX permissions when supported // by the SFTP server. -func (medium *Medium) WriteMode(filePath, content string, mode fs.FileMode) error { +func (medium *Medium) WriteMode(filePath, content string, mode fs.FileMode) error { // legacy error contract + remotePath, err := medium.requiredRemotePath(opWriteMode, filePath) if err != nil { return err @@ -246,7 +262,8 @@ func (medium *Medium) WriteMode(filePath, content string, mode fs.FileMode) erro } // EnsureDir creates a remote directory and any missing parents. -func (medium *Medium) EnsureDir(filePath string) error { +func (medium *Medium) EnsureDir(filePath string) error { // legacy error contract + remotePath := medium.remotePath(filePath) if remotePath == medium.root { return nil @@ -267,7 +284,8 @@ func (medium *Medium) IsFile(filePath string) bool { } // Delete removes a remote file or empty directory. -func (medium *Medium) Delete(filePath string) error { +func (medium *Medium) Delete(filePath string) error { // legacy error contract + remotePath, err := medium.requiredRemotePath("sftp.Delete", filePath) if err != nil { return err @@ -279,7 +297,8 @@ func (medium *Medium) Delete(filePath string) error { } // DeleteAll removes a remote file or directory tree. -func (medium *Medium) DeleteAll(filePath string) error { +func (medium *Medium) DeleteAll(filePath string) error { // legacy error contract + remotePath, err := medium.requiredRemotePath("sftp.DeleteAll", filePath) if err != nil { return err @@ -291,7 +310,8 @@ func (medium *Medium) DeleteAll(filePath string) error { } // Rename renames a remote path. -func (medium *Medium) Rename(oldPath, newPath string) error { +func (medium *Medium) Rename(oldPath, newPath string) error { // legacy error contract + oldRemotePath, err := medium.requiredRemotePath(opRename, oldPath) if err != nil { return err @@ -310,7 +330,10 @@ func (medium *Medium) Rename(oldPath, newPath string) error { } // List returns the immediate children under a remote directory. -func (medium *Medium) List(filePath string) ([]fs.DirEntry, error) { +func (medium *Medium) List(filePath string) ( + []fs.DirEntry, + error, +) { remotePath := medium.remotePath(filePath) infos, err := medium.client.ReadDir(remotePath) if err != nil { @@ -328,7 +351,10 @@ func (medium *Medium) List(filePath string) ([]fs.DirEntry, error) { } // Stat returns metadata for a remote path. -func (medium *Medium) Stat(filePath string) (fs.FileInfo, error) { +func (medium *Medium) Stat(filePath string) ( + fs.FileInfo, + error, +) { remotePath, err := medium.requiredRemotePath("sftp.Stat", filePath) if err != nil { return nil, err @@ -341,7 +367,10 @@ func (medium *Medium) Stat(filePath string) (fs.FileInfo, error) { } // Open opens a remote file for reading. -func (medium *Medium) Open(filePath string) (fs.File, error) { +func (medium *Medium) Open(filePath string) ( + fs.File, + error, +) { remotePath, err := medium.requiredRemotePath(opOpen, filePath) if err != nil { return nil, err @@ -354,7 +383,10 @@ func (medium *Medium) Open(filePath string) (fs.File, error) { } // Create opens a remote file for replacement. -func (medium *Medium) Create(filePath string) (goio.WriteCloser, error) { +func (medium *Medium) Create(filePath string) ( + goio.WriteCloser, + error, +) { remotePath, err := medium.requiredRemotePath(opCreate, filePath) if err != nil { return nil, err @@ -370,7 +402,10 @@ func (medium *Medium) Create(filePath string) (goio.WriteCloser, error) { } // Append opens a remote file for appending, creating it when missing. -func (medium *Medium) Append(filePath string) (goio.WriteCloser, error) { +func (medium *Medium) Append(filePath string) ( + goio.WriteCloser, + error, +) { remotePath, err := medium.requiredRemotePath(opAppend, filePath) if err != nil { return nil, err @@ -386,7 +421,10 @@ func (medium *Medium) Append(filePath string) (goio.WriteCloser, error) { } // ReadStream opens a remote file as an io.ReadCloser. -func (medium *Medium) ReadStream(filePath string) (goio.ReadCloser, error) { +func (medium *Medium) ReadStream(filePath string) ( + goio.ReadCloser, + error, +) { file, err := medium.Open(filePath) if err != nil { return nil, err @@ -395,7 +433,10 @@ func (medium *Medium) ReadStream(filePath string) (goio.ReadCloser, error) { } // WriteStream opens a remote file as an io.WriteCloser. -func (medium *Medium) WriteStream(filePath string) (goio.WriteCloser, error) { +func (medium *Medium) WriteStream(filePath string) ( + goio.WriteCloser, + error, +) { return medium.Create(filePath) } diff --git a/pkg/medium/sftp/sftp_example_test.go b/go/pkg/medium/sftp/sftp_example_test.go similarity index 100% rename from pkg/medium/sftp/sftp_example_test.go rename to go/pkg/medium/sftp/sftp_example_test.go diff --git a/pkg/medium/sftp/sftp_test.go b/go/pkg/medium/sftp/sftp_test.go similarity index 100% rename from pkg/medium/sftp/sftp_test.go rename to go/pkg/medium/sftp/sftp_test.go diff --git a/pkg/medium/webdav/register.go b/go/pkg/medium/webdav/register.go similarity index 100% rename from pkg/medium/webdav/register.go rename to go/pkg/medium/webdav/register.go diff --git a/pkg/medium/webdav/register_example_test.go b/go/pkg/medium/webdav/register_example_test.go similarity index 100% rename from pkg/medium/webdav/register_example_test.go rename to go/pkg/medium/webdav/register_example_test.go diff --git a/pkg/medium/webdav/register_test.go b/go/pkg/medium/webdav/register_test.go similarity index 100% rename from pkg/medium/webdav/register_test.go rename to go/pkg/medium/webdav/register_test.go diff --git a/pkg/medium/webdav/webdav.go b/go/pkg/medium/webdav/webdav.go similarity index 89% rename from pkg/medium/webdav/webdav.go rename to go/pkg/medium/webdav/webdav.go index 9f85b88..d448f61 100644 --- a/pkg/medium/webdav/webdav.go +++ b/go/pkg/medium/webdav/webdav.go @@ -61,7 +61,10 @@ type Options struct { } // New creates a WebDAV Medium. -func New(options Options) (*Medium, error) { +func New(options Options) ( + *Medium, + error, +) { if options.BaseURL == "" { return nil, core.E(opNew, "base URL is required", fs.ErrInvalid) } @@ -117,14 +120,20 @@ func (medium *Medium) resourceURL(filePath string) string { return u.String() } -func (medium *Medium) requiredResourceURL(operation, filePath string) (string, error) { +func (medium *Medium) requiredResourceURL(operation, filePath string) ( + string, + error, +) { if cleanRelative(filePath) == "" { return "", core.E(operation, "path is required", fs.ErrInvalid) } return medium.resourceURL(filePath), nil } -func (medium *Medium) newRequest(method, filePath string, body goio.Reader) (*http.Request, error) { +func (medium *Medium) newRequest(method, filePath string, body goio.Reader) ( + *http.Request, + error, +) { request, err := http.NewRequest(method, medium.resourceURL(filePath), body) if err != nil { return nil, err @@ -140,7 +149,10 @@ func (medium *Medium) newRequest(method, filePath string, body goio.Reader) (*ht return request, nil } -func (medium *Medium) do(method, filePath string, body goio.Reader) (*http.Response, error) { +func (medium *Medium) do(method, filePath string, body goio.Reader) ( + *http.Response, + error, +) { request, err := medium.newRequest(method, filePath, body) if err != nil { return nil, err @@ -148,7 +160,8 @@ func (medium *Medium) do(method, filePath string, body goio.Reader) (*http.Respo return medium.client.Do(request) } -func statusError(operation, resource string, statusCode int) error { +func statusError(operation, resource string, statusCode int) error { // legacy error contract + switch statusCode { case http.StatusNotFound: return core.E(operation, core.Concat("not found: ", resource), fs.ErrNotExist) @@ -176,7 +189,8 @@ func closeWebDAVBody(closer goio.Closer) { } } -func (medium *Medium) putBytes(filePath string, data []byte) error { +func (medium *Medium) putBytes(filePath string, data []byte) error { // legacy error contract + resource, err := medium.requiredResourceURL(opWriteMode, filePath) if err != nil { return err @@ -196,7 +210,8 @@ func (medium *Medium) putBytes(filePath string, data []byte) error { return nil } -func (medium *Medium) ensureParent(filePath string) error { +func (medium *Medium) ensureParent(filePath string) error { // legacy error contract + relative := cleanRelative(filePath) parent := core.PathDir(relative) if parent == "." || parent == "" { @@ -206,7 +221,10 @@ func (medium *Medium) ensureParent(filePath string) error { } // Read reads a WebDAV resource into a string. -func (medium *Medium) Read(filePath string) (string, error) { +func (medium *Medium) Read(filePath string) ( + string, + error, +) { resource, err := medium.requiredResourceURL(opRead, filePath) if err != nil { return "", err @@ -227,18 +245,21 @@ func (medium *Medium) Read(filePath string) (string, error) { } // Write writes a WebDAV resource using the default file mode. -func (medium *Medium) Write(filePath, content string) error { +func (medium *Medium) Write(filePath, content string) error { // legacy error contract + return medium.WriteMode(filePath, content, 0644) } // WriteMode writes a WebDAV resource. The mode is intentionally ignored // because WebDAV has no portable POSIX permission model. -func (medium *Medium) WriteMode(filePath, content string, mode fs.FileMode) error { +func (medium *Medium) WriteMode(filePath, content string, mode fs.FileMode) error { // legacy error contract + return medium.putBytes(filePath, []byte(content)) } // EnsureDir creates a WebDAV collection and any missing parent collections. -func (medium *Medium) EnsureDir(filePath string) error { +func (medium *Medium) EnsureDir(filePath string) error { // legacy error contract + relative := cleanRelative(filePath) if relative == "" { return nil @@ -258,7 +279,8 @@ func (medium *Medium) EnsureDir(filePath string) error { return nil } -func (medium *Medium) mkcol(filePath string) error { +func (medium *Medium) mkcol(filePath string) error { // legacy error contract + resource := medium.resourceURL(filePath) response, err := medium.do("MKCOL", filePath, nil) if err != nil { @@ -288,7 +310,8 @@ func (medium *Medium) IsFile(filePath string) bool { } // Delete removes a file or empty collection. -func (medium *Medium) Delete(filePath string) error { +func (medium *Medium) Delete(filePath string) error { // legacy error contract + resource, err := medium.requiredResourceURL(opDelete, filePath) if err != nil { return err @@ -319,7 +342,8 @@ func (medium *Medium) Delete(filePath string) error { } // DeleteAll removes a file or collection tree. -func (medium *Medium) DeleteAll(filePath string) error { +func (medium *Medium) DeleteAll(filePath string) error { // legacy error contract + resource, err := medium.requiredResourceURL(opDeleteAll, filePath) if err != nil { return err @@ -336,7 +360,8 @@ func (medium *Medium) DeleteAll(filePath string) error { } // Rename moves a WebDAV resource to a new path. -func (medium *Medium) Rename(oldPath, newPath string) error { +func (medium *Medium) Rename(oldPath, newPath string) error { // legacy error contract + source, err := medium.requiredResourceURL(opRename, oldPath) if err != nil { return err @@ -368,7 +393,10 @@ func (medium *Medium) Rename(oldPath, newPath string) error { } // List returns the immediate children under a WebDAV collection. -func (medium *Medium) List(filePath string) ([]fs.DirEntry, error) { +func (medium *Medium) List(filePath string) ( + []fs.DirEntry, + error, +) { responses, requestPath, err := medium.propfind(filePath, "1") if err != nil { return nil, err @@ -390,7 +418,10 @@ func (medium *Medium) List(filePath string) ([]fs.DirEntry, error) { } // Stat returns metadata for a WebDAV resource. -func (medium *Medium) Stat(filePath string) (fs.FileInfo, error) { +func (medium *Medium) Stat(filePath string) ( + fs.FileInfo, + error, +) { resource, err := medium.requiredResourceURL("webdav.Stat", filePath) if err != nil { return nil, err @@ -405,7 +436,11 @@ func (medium *Medium) Stat(filePath string) (fs.FileInfo, error) { return responses[0].fileInfo(requestPath), nil } -func (medium *Medium) propfind(filePath, depth string) ([]davResponse, string, error) { +func (medium *Medium) propfind(filePath, depth string) ( + []davResponse, + string, + error, +) { resource := medium.resourceURL(filePath) request, err := medium.newRequest("PROPFIND", filePath, core.NewReader(propfindBody)) if err != nil { @@ -431,7 +466,10 @@ func (medium *Medium) propfind(filePath, depth string) ([]davResponse, string, e } // Open opens a WebDAV resource as an fs.File. -func (medium *Medium) Open(filePath string) (fs.File, error) { +func (medium *Medium) Open(filePath string) ( + fs.File, + error, +) { content, err := medium.Read(filePath) if err != nil { return nil, err @@ -452,7 +490,10 @@ func (medium *Medium) Open(filePath string) (fs.File, error) { } // Create opens a buffered WebDAV writer that replaces the resource on close. -func (medium *Medium) Create(filePath string) (goio.WriteCloser, error) { +func (medium *Medium) Create(filePath string) ( + goio.WriteCloser, + error, +) { if _, err := medium.requiredResourceURL("webdav.Create", filePath); err != nil { return nil, err } @@ -461,7 +502,10 @@ func (medium *Medium) Create(filePath string) (goio.WriteCloser, error) { // Append opens a buffered WebDAV writer that appends locally then replaces the // resource on close. -func (medium *Medium) Append(filePath string) (goio.WriteCloser, error) { +func (medium *Medium) Append(filePath string) ( + goio.WriteCloser, + error, +) { if _, err := medium.requiredResourceURL("webdav.Append", filePath); err != nil { return nil, err } @@ -478,7 +522,10 @@ func (medium *Medium) Append(filePath string) (goio.WriteCloser, error) { } // ReadStream opens a WebDAV resource as an io.ReadCloser. -func (medium *Medium) ReadStream(filePath string) (goio.ReadCloser, error) { +func (medium *Medium) ReadStream(filePath string) ( + goio.ReadCloser, + error, +) { resource, err := medium.requiredResourceURL(opReadStream, filePath) if err != nil { return nil, err @@ -495,7 +542,10 @@ func (medium *Medium) ReadStream(filePath string) (goio.ReadCloser, error) { } // WriteStream opens a buffered WebDAV writer that replaces the resource on close. -func (medium *Medium) WriteStream(filePath string) (goio.WriteCloser, error) { +func (medium *Medium) WriteStream(filePath string) ( + goio.WriteCloser, + error, +) { return medium.Create(filePath) } @@ -608,11 +658,17 @@ type webdavFile struct { modTime time.Time } -func (file *webdavFile) Stat() (fs.FileInfo, error) { +func (file *webdavFile) Stat() ( + fs.FileInfo, + error, +) { return coreio.NewFileInfo(file.name, int64(len(file.content)), file.mode, file.modTime, false), nil } -func (file *webdavFile) Read(buffer []byte) (int, error) { +func (file *webdavFile) Read(buffer []byte) ( + int, + error, +) { if file.offset >= int64(len(file.content)) { return 0, goio.EOF } @@ -621,7 +677,8 @@ func (file *webdavFile) Read(buffer []byte) (int, error) { return bytesRead, nil } -func (file *webdavFile) Close() error { +func (file *webdavFile) Close() error { // legacy error contract + return nil } @@ -632,11 +689,15 @@ type webdavWriteCloser struct { mode fs.FileMode } -func (writer *webdavWriteCloser) Write(data []byte) (int, error) { +func (writer *webdavWriteCloser) Write(data []byte) ( + int, + error, +) { writer.data = append(writer.data, data...) return len(data), nil } -func (writer *webdavWriteCloser) Close() error { +func (writer *webdavWriteCloser) Close() error { // legacy error contract + return writer.medium.WriteMode(writer.path, string(writer.data), writer.mode) } diff --git a/pkg/medium/webdav/webdav_example_test.go b/go/pkg/medium/webdav/webdav_example_test.go similarity index 100% rename from pkg/medium/webdav/webdav_example_test.go rename to go/pkg/medium/webdav/webdav_example_test.go diff --git a/pkg/medium/webdav/webdav_test.go b/go/pkg/medium/webdav/webdav_test.go similarity index 100% rename from pkg/medium/webdav/webdav_test.go rename to go/pkg/medium/webdav/webdav_test.go diff --git a/s3/actions.go b/go/s3/actions.go similarity index 100% rename from s3/actions.go rename to go/s3/actions.go diff --git a/s3/actions_example_test.go b/go/s3/actions_example_test.go similarity index 100% rename from s3/actions_example_test.go rename to go/s3/actions_example_test.go diff --git a/s3/actions_test.go b/go/s3/actions_test.go similarity index 100% rename from s3/actions_test.go rename to go/s3/actions_test.go diff --git a/s3/s3.go b/go/s3/s3.go similarity index 91% rename from s3/s3.go rename to go/s3/s3.go index 1ea6a14..36e25aa 100644 --- a/s3/s3.go +++ b/go/s3/s3.go @@ -57,7 +57,8 @@ type Options struct { Prefix string } -func deleteObjectsError(prefix string, errs []types.Error) error { +func deleteObjectsError(prefix string, errs []types.Error) error { // legacy error contract + if len(errs) == 0 { return nil } @@ -80,7 +81,10 @@ func deleteObjectsError(prefix string, errs []types.Error) error { return core.E(opS3DeleteAll, core.Concat("partial delete failed under ", prefix, ": ", core.Join("; ", details...)), nil) } -func readAllString(reader goio.ReadCloser) (string, error) { +func readAllString(reader goio.ReadCloser) ( + string, + error, +) { defer closeS3Reader(reader) result := core.ReadAll(reader) @@ -120,7 +124,10 @@ func normalisePrefix(prefix string) string { // Example: medium, _ := s3.New(s3.Options{Bucket: "backups", Client: client, Prefix: "daily/"}) // Example: _ = medium.Write("reports/daily.txt", "done") -func New(options Options) (*Medium, error) { +func New(options Options) ( + *Medium, + error, +) { if options.Bucket == "" { return nil, core.E("s3.New", "bucket name is required", fs.ErrInvalid) } @@ -152,7 +159,10 @@ func (medium *Medium) objectKey(filePath string) string { } // Example: content, _ := medium.Read("reports/daily.txt") -func (medium *Medium) Read(filePath string) (string, error) { +func (medium *Medium) Read(filePath string) ( + string, + error, +) { key := medium.objectKey(filePath) if key == "" { return "", core.E(opS3Read, msgS3PathRequired, fs.ErrInvalid) @@ -173,7 +183,8 @@ func (medium *Medium) Read(filePath string) (string, error) { } // Example: _ = medium.Write("reports/daily.txt", "done") -func (medium *Medium) Write(filePath, content string) error { +func (medium *Medium) Write(filePath, content string) error { // legacy error contract + key := medium.objectKey(filePath) if key == "" { return core.E("s3.Write", msgS3PathRequired, fs.ErrInvalid) @@ -193,12 +204,14 @@ func (medium *Medium) Write(filePath, content string) error { // Example: _ = medium.WriteMode("keys/private.key", key, 0600) // Note: mode is intentionally ignored — S3 has no POSIX permission model. // Use S3 bucket policies and IAM for access control. -func (medium *Medium) WriteMode(filePath, content string, mode fs.FileMode) error { +func (medium *Medium) WriteMode(filePath, content string, mode fs.FileMode) error { // legacy error contract + return medium.Write(filePath, content) } // Example: _ = medium.EnsureDir("reports/2026") -func (medium *Medium) EnsureDir(directoryPath string) error { +func (medium *Medium) EnsureDir(directoryPath string) error { // legacy error contract + return nil } @@ -219,7 +232,8 @@ func (medium *Medium) IsFile(filePath string) bool { } // Example: _ = medium.Delete("reports/daily.txt") -func (medium *Medium) Delete(filePath string) error { +func (medium *Medium) Delete(filePath string) error { // legacy error contract + key := medium.objectKey(filePath) if key == "" { return core.E("s3.Delete", msgS3PathRequired, fs.ErrInvalid) @@ -236,7 +250,8 @@ func (medium *Medium) Delete(filePath string) error { } // Example: _ = medium.DeleteAll("reports/2026") -func (medium *Medium) DeleteAll(filePath string) error { +func (medium *Medium) DeleteAll(filePath string) error { // legacy error contract + key := medium.objectKey(filePath) if key == "" { return core.E(opS3DeleteAll, msgS3PathRequired, fs.ErrInvalid) @@ -270,7 +285,11 @@ func (medium *Medium) DeleteAll(filePath string) error { return nil } -func (medium *Medium) deleteObjectBatch(prefix string, continuationToken *string) (*string, bool, error) { +func (medium *Medium) deleteObjectBatch(prefix string, continuationToken *string) ( + *string, + bool, + error, +) { listOutput, err := medium.client.ListObjectsV2(context.Background(), &awss3.ListObjectsV2Input{ Bucket: aws.String(medium.bucket), Prefix: aws.String(prefix), @@ -303,7 +322,8 @@ func (medium *Medium) deleteObjectBatch(prefix string, continuationToken *string } // Example: _ = medium.Rename("drafts/todo.txt", "archive/todo.txt") -func (medium *Medium) Rename(oldPath, newPath string) error { +func (medium *Medium) Rename(oldPath, newPath string) error { // legacy error contract + oldKey := medium.objectKey(oldPath) newKey := medium.objectKey(newPath) if oldKey == "" || newKey == "" { @@ -333,7 +353,10 @@ func (medium *Medium) Rename(oldPath, newPath string) error { } // Example: entries, _ := medium.List("reports") -func (medium *Medium) List(filePath string) ([]fs.DirEntry, error) { +func (medium *Medium) List(filePath string) ( + []fs.DirEntry, + error, +) { prefix := medium.objectKey(filePath) if prefix != "" && !core.HasSuffix(prefix, "/") { prefix += "/" @@ -419,7 +442,10 @@ func s3ObjectEntry(name string, object types.Object) fs.DirEntry { } // Example: info, _ := medium.Stat("reports/daily.txt") -func (medium *Medium) Stat(filePath string) (fs.FileInfo, error) { +func (medium *Medium) Stat(filePath string) ( + fs.FileInfo, + error, +) { key := medium.objectKey(filePath) if key == "" { return nil, core.E("s3.Stat", msgS3PathRequired, fs.ErrInvalid) @@ -451,7 +477,10 @@ func (medium *Medium) Stat(filePath string) (fs.FileInfo, error) { }, nil } -func (medium *Medium) Open(filePath string) (fs.File, error) { +func (medium *Medium) Open(filePath string) ( + fs.File, + error, +) { key := medium.objectKey(filePath) if key == "" { return nil, core.E(opS3Open, msgS3PathRequired, fs.ErrInvalid) @@ -488,7 +517,10 @@ func (medium *Medium) Open(filePath string) (fs.File, error) { } // Example: writer, _ := medium.Create("reports/daily.txt") -func (medium *Medium) Create(filePath string) (goio.WriteCloser, error) { +func (medium *Medium) Create(filePath string) ( + goio.WriteCloser, + error, +) { key := medium.objectKey(filePath) if key == "" { return nil, core.E("s3.Create", msgS3PathRequired, fs.ErrInvalid) @@ -500,7 +532,10 @@ func (medium *Medium) Create(filePath string) (goio.WriteCloser, error) { } // Example: writer, _ := medium.Append("reports/daily.txt") -func (medium *Medium) Append(filePath string) (goio.WriteCloser, error) { +func (medium *Medium) Append(filePath string) ( + goio.WriteCloser, + error, +) { key := medium.objectKey(filePath) if key == "" { return nil, core.E("s3.Append", msgS3PathRequired, fs.ErrInvalid) @@ -527,7 +562,10 @@ func (medium *Medium) Append(filePath string) (goio.WriteCloser, error) { } // Example: reader, _ := medium.ReadStream("reports/daily.txt") -func (medium *Medium) ReadStream(filePath string) (goio.ReadCloser, error) { +func (medium *Medium) ReadStream(filePath string) ( + goio.ReadCloser, + error, +) { key := medium.objectKey(filePath) if key == "" { return nil, core.E("s3.ReadStream", msgS3PathRequired, fs.ErrInvalid) @@ -544,7 +582,10 @@ func (medium *Medium) ReadStream(filePath string) (goio.ReadCloser, error) { } // Example: writer, _ := medium.WriteStream("reports/daily.txt") -func (medium *Medium) WriteStream(filePath string) (goio.WriteCloser, error) { +func (medium *Medium) WriteStream(filePath string) ( + goio.WriteCloser, + error, +) { return medium.Create(filePath) } @@ -644,7 +685,10 @@ type s3File struct { modTime time.Time } -func (file *s3File) Stat() (fs.FileInfo, error) { +func (file *s3File) Stat() ( + fs.FileInfo, + error, +) { return &fileInfo{ name: file.name, size: int64(len(file.content)), @@ -653,7 +697,10 @@ func (file *s3File) Stat() (fs.FileInfo, error) { }, nil } -func (file *s3File) Read(buffer []byte) (int, error) { +func (file *s3File) Read(buffer []byte) ( + int, + error, +) { if file.offset >= int64(len(file.content)) { return 0, goio.EOF } @@ -662,7 +709,8 @@ func (file *s3File) Read(buffer []byte) (int, error) { return bytesRead, nil } -func (file *s3File) Close() error { +func (file *s3File) Close() error { // legacy error contract + return nil } @@ -672,12 +720,16 @@ type s3WriteCloser struct { data []byte } -func (writer *s3WriteCloser) Write(data []byte) (int, error) { +func (writer *s3WriteCloser) Write(data []byte) ( + int, + error, +) { writer.data = append(writer.data, data...) return len(data), nil } -func (writer *s3WriteCloser) Close() error { +func (writer *s3WriteCloser) Close() error { // legacy error contract + _, err := writer.medium.client.PutObject(context.Background(), &awss3.PutObjectInput{ Bucket: aws.String(writer.medium.bucket), Key: aws.String(writer.key), diff --git a/s3/s3_example_test.go b/go/s3/s3_example_test.go similarity index 100% rename from s3/s3_example_test.go rename to go/s3/s3_example_test.go diff --git a/s3/s3_test.go b/go/s3/s3_test.go similarity index 100% rename from s3/s3_test.go rename to go/s3/s3_test.go diff --git a/sigil/crypto_sigil.go b/go/sigil/crypto_sigil.go similarity index 96% rename from sigil/crypto_sigil.go rename to go/sigil/crypto_sigil.go index 24a9ff3..f77c27c 100644 --- a/sigil/crypto_sigil.go +++ b/go/sigil/crypto_sigil.go @@ -222,7 +222,10 @@ func (s *ChaChaPolySigil) SetObfuscator(obfuscator PreObfuscator) { // WARNING: when using a custom PreObfuscator, nonce uniqueness remains the // caller's responsibility. The PreObfuscator must treat the supplied entropy as // a per-message nonce and must not introduce deterministic nonce reuse. -func NewChaChaPolySigil(key []byte, nonce any) (*ChaChaPolySigil, error) { +func NewChaChaPolySigil(key []byte, nonce any) ( + *ChaChaPolySigil, + error, +) { if len(key) != 32 { return nil, InvalidKeyError } @@ -254,7 +257,10 @@ func NewChaChaPolySigil(key []byte, nonce any) (*ChaChaPolySigil, error) { return sigil, nil } -func (s *ChaChaPolySigil) In(data []byte) ([]byte, error) { +func (s *ChaChaPolySigil) In(data []byte) ( + []byte, + error, +) { if s.key == nil { return nil, NoKeyConfiguredError } @@ -286,7 +292,10 @@ func (s *ChaChaPolySigil) In(data []byte) ([]byte, error) { return ciphertext, nil } -func (s *ChaChaPolySigil) Out(data []byte) ([]byte, error) { +func (s *ChaChaPolySigil) Out(data []byte) ( + []byte, + error, +) { if s.key == nil { return nil, NoKeyConfiguredError } @@ -326,7 +335,10 @@ func (s *ChaChaPolySigil) Out(data []byte) ([]byte, error) { return plaintext, nil } -func (s *ChaChaPolySigil) newAEAD() (cipher.AEAD, error) { +func (s *ChaChaPolySigil) newAEAD() ( + cipher.AEAD, + error, +) { switch s.activeNonceSize() { case chacha20poly1305.NonceSize: return chacha20poly1305.New(s.key) @@ -351,7 +363,10 @@ func cloneBytes(data []byte) []byte { } // Example: nonce, _ := sigil.NonceFromCiphertext(ciphertext) -func NonceFromCiphertext(ciphertext []byte) ([]byte, error) { +func NonceFromCiphertext(ciphertext []byte) ( + []byte, + error, +) { nonceSize := chacha20poly1305.NonceSizeX if len(ciphertext) < nonceSize { return nil, CiphertextTooShortError diff --git a/sigil/crypto_sigil_example_test.go b/go/sigil/crypto_sigil_example_test.go similarity index 100% rename from sigil/crypto_sigil_example_test.go rename to go/sigil/crypto_sigil_example_test.go diff --git a/sigil/crypto_sigil_test.go b/go/sigil/crypto_sigil_test.go similarity index 100% rename from sigil/crypto_sigil_test.go rename to go/sigil/crypto_sigil_test.go diff --git a/sigil/sigil.go b/go/sigil/sigil.go similarity index 89% rename from sigil/sigil.go rename to go/sigil/sigil.go index 4f3df88..6725882 100644 --- a/sigil/sigil.go +++ b/go/sigil/sigil.go @@ -16,7 +16,10 @@ type Sigil interface { } // Example: encoded, _ := sigil.Transmute([]byte("payload"), []sigil.Sigil{hexSigil, gzipSigil}) -func Transmute(data []byte, sigils []Sigil) ([]byte, error) { +func Transmute(data []byte, sigils []Sigil) ( + []byte, + error, +) { var err error for _, sigilValue := range sigils { data, err = sigilValue.In(data) @@ -28,7 +31,10 @@ func Transmute(data []byte, sigils []Sigil) ([]byte, error) { } // Example: decoded, _ := sigil.Untransmute(encoded, []sigil.Sigil{hexSigil, gzipSigil}) -func Untransmute(data []byte, sigils []Sigil) ([]byte, error) { +func Untransmute(data []byte, sigils []Sigil) ( + []byte, + error, +) { var err error for i := len(sigils) - 1; i >= 0; i-- { data, err = sigils[i].Out(data) diff --git a/sigil/sigil_example_test.go b/go/sigil/sigil_example_test.go similarity index 100% rename from sigil/sigil_example_test.go rename to go/sigil/sigil_example_test.go diff --git a/sigil/sigil_test.go b/go/sigil/sigil_test.go similarity index 100% rename from sigil/sigil_test.go rename to go/sigil/sigil_test.go diff --git a/sigil/sigils.go b/go/sigil/sigils.go similarity index 92% rename from sigil/sigils.go rename to go/sigil/sigils.go index 5a80d43..f0114a8 100644 --- a/sigil/sigils.go +++ b/go/sigil/sigils.go @@ -27,7 +27,10 @@ const ( // Example: reverseSigil, _ := sigil.NewSigil("reverse") type ReverseSigil struct{} -func (sigil *ReverseSigil) In(data []byte) ([]byte, error) { +func (sigil *ReverseSigil) In(data []byte) ( + []byte, + error, +) { if data == nil { return nil, nil } @@ -38,14 +41,20 @@ func (sigil *ReverseSigil) In(data []byte) ([]byte, error) { return reversed, nil } -func (sigil *ReverseSigil) Out(data []byte) ([]byte, error) { +func (sigil *ReverseSigil) Out(data []byte) ( + []byte, + error, +) { return sigil.In(data) } // Example: hexSigil, _ := sigil.NewSigil("hex") type HexSigil struct{} -func (sigil *HexSigil) In(data []byte) ([]byte, error) { +func (sigil *HexSigil) In(data []byte) ( + []byte, + error, +) { if data == nil { return nil, nil } @@ -54,7 +63,10 @@ func (sigil *HexSigil) In(data []byte) ([]byte, error) { return encodedBytes, nil } -func (sigil *HexSigil) Out(data []byte) ([]byte, error) { +func (sigil *HexSigil) Out(data []byte) ( + []byte, + error, +) { if data == nil { return nil, nil } @@ -66,7 +78,10 @@ func (sigil *HexSigil) Out(data []byte) ([]byte, error) { // Example: base64Sigil, _ := sigil.NewSigil("base64") type Base64Sigil struct{} -func (sigil *Base64Sigil) In(data []byte) ([]byte, error) { +func (sigil *Base64Sigil) In(data []byte) ( + []byte, + error, +) { if data == nil { return nil, nil } @@ -75,7 +90,10 @@ func (sigil *Base64Sigil) In(data []byte) ([]byte, error) { return encodedBytes, nil } -func (sigil *Base64Sigil) Out(data []byte) ([]byte, error) { +func (sigil *Base64Sigil) Out(data []byte) ( + []byte, + error, +) { if data == nil { return nil, nil } @@ -104,7 +122,10 @@ type sigilBuffer struct { data []byte } -func (buffer *sigilBuffer) Write(data []byte) (int, error) { +func (buffer *sigilBuffer) Write(data []byte) ( + int, + error, +) { buffer.data = append(buffer.data, data...) return len(data), nil } @@ -113,7 +134,10 @@ func (buffer *sigilBuffer) Bytes() []byte { return buffer.data } -func (sigil *GzipSigil) In(data []byte) ([]byte, error) { +func (sigil *GzipSigil) In(data []byte) ( + []byte, + error, +) { if data == nil { return nil, nil } @@ -137,7 +161,10 @@ func (sigil *GzipSigil) In(data []byte) ([]byte, error) { return buffer.Bytes(), nil } -func (sigil *GzipSigil) Out(data []byte) ([]byte, error) { +func (sigil *GzipSigil) Out(data []byte) ( + []byte, + error, +) { if data == nil { return nil, nil } @@ -165,7 +192,10 @@ func closeGzipReader(reader interface{ Close() error }) { // Example: jsonSigil := &sigil.JSONSigil{Indent: true} type JSONSigil struct{ Indent bool } -func (sigil *JSONSigil) In(data []byte) ([]byte, error) { +func (sigil *JSONSigil) In(data []byte) ( + []byte, + error, +) { if data == nil { return nil, nil } @@ -186,7 +216,10 @@ func (sigil *JSONSigil) In(data []byte) ([]byte, error) { return []byte(compact), nil } -func (sigil *JSONSigil) Out(data []byte) ([]byte, error) { +func (sigil *JSONSigil) Out(data []byte) ( + []byte, + error, +) { return data, nil } @@ -201,7 +234,10 @@ func NewHashSigil(hashAlgorithm crypto.Hash) *HashSigil { return &HashSigil{Hash: hashAlgorithm} } -func (sigil *HashSigil) In(data []byte) ([]byte, error) { +func (sigil *HashSigil) In(data []byte) ( + []byte, + error, +) { var hasher sigilHash var err error switch sigil.Hash { @@ -254,7 +290,10 @@ func (sigil *HashSigil) In(data []byte) ([]byte, error) { return hasher.Sum(nil), nil } -func (sigil *HashSigil) Out(data []byte) ([]byte, error) { +func (sigil *HashSigil) Out(data []byte) ( + []byte, + error, +) { return data, nil } @@ -265,7 +304,10 @@ func (sigil *HashSigil) Out(data []byte) ([]byte, error) { // Example: hexSigil, _ := sigil.NewSigil("hex") // Example: gzipSigil, _ := sigil.NewSigil("gzip") // Example: transformed, _ := sigil.Transmute([]byte("payload"), []sigil.Sigil{hexSigil, gzipSigil}) -func NewSigil(sigilName string) (Sigil, error) { +func NewSigil(sigilName string) ( + Sigil, + error, +) { if sigilName == "chacha20poly1305" { return nil, core.E("sigil.NewSigil", "chacha20poly1305 scheme requires key material; use NewChaChaPolySigil", fs.ErrInvalid) } diff --git a/sigil/sigils_example_test.go b/go/sigil/sigils_example_test.go similarity index 100% rename from sigil/sigils_example_test.go rename to go/sigil/sigils_example_test.go diff --git a/sigil/sigils_test.go b/go/sigil/sigils_test.go similarity index 100% rename from sigil/sigils_test.go rename to go/sigil/sigils_test.go diff --git a/sqlite/sqlite.go b/go/sqlite/sqlite.go similarity index 91% rename from sqlite/sqlite.go rename to go/sqlite/sqlite.go index 2db5a4a..cbc689a 100644 --- a/sqlite/sqlite.go +++ b/go/sqlite/sqlite.go @@ -90,7 +90,10 @@ func isValidTableName(name string) bool { // Example: medium, _ := sqlite.New(sqlite.Options{Path: ":memory:", Table: "files"}) // Example: _ = medium.Write("config/app.yaml", "port: 8080") -func New(options Options) (*Medium, error) { +func New(options Options) ( + *Medium, + error, +) { if options.Path == "" { return nil, core.E(opSQLiteNew, "database path is required", fs.ErrInvalid) } @@ -129,7 +132,8 @@ func New(options Options) (*Medium, error) { } // Example: _ = medium.Close() -func (medium *Medium) Close() error { +func (medium *Medium) Close() error { // legacy error contract + if medium.database != nil { return medium.database.Close() } @@ -145,7 +149,10 @@ func normaliseEntryPath(filePath string) string { } // Example: content, _ := medium.Read("config/app.yaml") -func (medium *Medium) Read(filePath string) (string, error) { +func (medium *Medium) Read(filePath string) ( + string, + error, +) { key := normaliseEntryPath(filePath) if key == "" { return "", core.E(opSQLiteRead, msgSQLitePathRequired, fs.ErrInvalid) @@ -169,12 +176,14 @@ func (medium *Medium) Read(filePath string) (string, error) { } // Example: _ = medium.Write("config/app.yaml", "port: 8080") -func (medium *Medium) Write(filePath, content string) error { +func (medium *Medium) Write(filePath, content string) error { // legacy error contract + return medium.WriteMode(filePath, content, 0644) } // Example: _ = medium.WriteMode("keys/private.key", key, 0600) -func (medium *Medium) WriteMode(filePath, content string, mode fs.FileMode) error { +func (medium *Medium) WriteMode(filePath, content string, mode fs.FileMode) error { // legacy error contract + key := normaliseEntryPath(filePath) if key == "" { return core.E("sqlite.WriteMode", msgSQLitePathRequired, fs.ErrInvalid) @@ -192,7 +201,8 @@ func (medium *Medium) WriteMode(filePath, content string, mode fs.FileMode) erro } // Example: _ = medium.EnsureDir("config") -func (medium *Medium) EnsureDir(filePath string) error { +func (medium *Medium) EnsureDir(filePath string) error { // legacy error contract + key := normaliseEntryPath(filePath) if key == "" { return nil @@ -227,7 +237,8 @@ func (medium *Medium) IsFile(filePath string) bool { } // Example: _ = medium.Delete("config/app.yaml") -func (medium *Medium) Delete(filePath string) error { +func (medium *Medium) Delete(filePath string) error { // legacy error contract + key := normaliseEntryPath(filePath) if key == "" { return core.E(opSQLiteDelete, msgSQLitePathRequired, fs.ErrInvalid) @@ -270,7 +281,8 @@ func (medium *Medium) Delete(filePath string) error { } // Example: _ = medium.DeleteAll("config") -func (medium *Medium) DeleteAll(filePath string) error { +func (medium *Medium) DeleteAll(filePath string) error { // legacy error contract + key := normaliseEntryPath(filePath) if key == "" { return core.E(opSQLiteDeleteAll, msgSQLitePathRequired, fs.ErrInvalid) @@ -301,7 +313,8 @@ type sqliteEntryRow struct { } // Example: _ = medium.Rename("drafts/todo.txt", "archive/todo.txt") -func (medium *Medium) Rename(oldPath, newPath string) error { +func (medium *Medium) Rename(oldPath, newPath string) error { // legacy error contract + oldKey := normaliseEntryPath(oldPath) newKey := normaliseEntryPath(newPath) if oldKey == "" || newKey == "" { @@ -349,7 +362,10 @@ func (medium *Medium) Rename(oldPath, newPath string) error { return nil } -func (medium *Medium) renameSource(tx *sql.Tx, oldKey string) (sqliteEntryRow, error) { +func (medium *Medium) renameSource(tx *sql.Tx, oldKey string) ( + sqliteEntryRow, + error, +) { var source sqliteEntryRow source.path = oldKey err := tx.QueryRow( @@ -364,7 +380,8 @@ func (medium *Medium) renameSource(tx *sql.Tx, oldKey string) (sqliteEntryRow, e return source, nil } -func (medium *Medium) upsertEntry(tx *sql.Tx, entry sqliteEntryRow) error { +func (medium *Medium) upsertEntry(tx *sql.Tx, entry sqliteEntryRow) error { // legacy error contract + _, err := tx.Exec( `INSERT INTO `+medium.table+` (path, content, mode, is_dir, mtime) VALUES (?, ?, ?, ?, ?) ON CONFLICT(path) DO UPDATE SET content = excluded.content, mode = excluded.mode, is_dir = excluded.is_dir, mtime = excluded.mtime`, @@ -373,7 +390,8 @@ func (medium *Medium) upsertEntry(tx *sql.Tx, entry sqliteEntryRow) error { return err } -func (medium *Medium) renameChildren(tx *sql.Tx, oldKey, newKey string) error { +func (medium *Medium) renameChildren(tx *sql.Tx, oldKey, newKey string) error { // legacy error contract + oldPrefix := oldKey + "/" children, err := medium.childrenWithPrefix(tx, oldPrefix) if err != nil { @@ -393,7 +411,10 @@ func (medium *Medium) renameChildren(tx *sql.Tx, oldKey, newKey string) error { return nil } -func (medium *Medium) childrenWithPrefix(tx *sql.Tx, oldPrefix string) ([]sqliteEntryRow, error) { +func (medium *Medium) childrenWithPrefix(tx *sql.Tx, oldPrefix string) ( + []sqliteEntryRow, + error, +) { childRows, err := tx.Query( `SELECT path, content, mode, is_dir, mtime FROM `+medium.table+` WHERE path LIKE ?`, oldPrefix+"%", @@ -418,7 +439,10 @@ func (medium *Medium) childrenWithPrefix(tx *sql.Tx, oldPrefix string) ([]sqlite } // Example: entries, _ := medium.List("config") -func (medium *Medium) List(filePath string) ([]fs.DirEntry, error) { +func (medium *Medium) List(filePath string) ( + []fs.DirEntry, + error, +) { prefix := normaliseEntryPath(filePath) if prefix != "" { prefix += "/" @@ -509,7 +533,10 @@ func appendSQLiteDirEntry(name string, seen map[string]bool, entries *[]fs.DirEn } // Example: info, _ := medium.Stat("config/app.yaml") -func (medium *Medium) Stat(filePath string) (fs.FileInfo, error) { +func (medium *Medium) Stat(filePath string) ( + fs.FileInfo, + error, +) { key := normaliseEntryPath(filePath) if key == "" { return nil, core.E(opSQLiteStat, msgSQLitePathRequired, fs.ErrInvalid) @@ -540,7 +567,10 @@ func (medium *Medium) Stat(filePath string) (fs.FileInfo, error) { } // Example: file, _ := medium.Open("config/app.yaml") -func (medium *Medium) Open(filePath string) (fs.File, error) { +func (medium *Medium) Open(filePath string) ( + fs.File, + error, +) { key := normaliseEntryPath(filePath) if key == "" { return nil, core.E(opSQLiteOpen, msgSQLitePathRequired, fs.ErrInvalid) @@ -572,7 +602,10 @@ func (medium *Medium) Open(filePath string) (fs.File, error) { } // Example: writer, _ := medium.Create("logs/app.log") -func (medium *Medium) Create(filePath string) (goio.WriteCloser, error) { +func (medium *Medium) Create(filePath string) ( + goio.WriteCloser, + error, +) { key := normaliseEntryPath(filePath) if key == "" { return nil, core.E("sqlite.Create", msgSQLitePathRequired, fs.ErrInvalid) @@ -584,7 +617,10 @@ func (medium *Medium) Create(filePath string) (goio.WriteCloser, error) { } // Example: writer, _ := medium.Append("logs/app.log") -func (medium *Medium) Append(filePath string) (goio.WriteCloser, error) { +func (medium *Medium) Append(filePath string) ( + goio.WriteCloser, + error, +) { key := normaliseEntryPath(filePath) if key == "" { return nil, core.E("sqlite.Append", msgSQLitePathRequired, fs.ErrInvalid) @@ -606,7 +642,10 @@ func (medium *Medium) Append(filePath string) (goio.WriteCloser, error) { } // Example: reader, _ := medium.ReadStream("logs/app.log") -func (medium *Medium) ReadStream(filePath string) (goio.ReadCloser, error) { +func (medium *Medium) ReadStream(filePath string) ( + goio.ReadCloser, + error, +) { key := normaliseEntryPath(filePath) if key == "" { return nil, core.E(opSQLiteReadStream, msgSQLitePathRequired, fs.ErrInvalid) @@ -634,7 +673,10 @@ func (medium *Medium) ReadStream(filePath string) (goio.ReadCloser, error) { } // Example: writer, _ := medium.WriteStream("logs/app.log") -func (medium *Medium) WriteStream(filePath string) (goio.WriteCloser, error) { +func (medium *Medium) WriteStream(filePath string) ( + goio.WriteCloser, + error, +) { return medium.Create(filePath) } @@ -715,7 +757,10 @@ type sqliteFile struct { modTime time.Time } -func (file *sqliteFile) Stat() (fs.FileInfo, error) { +func (file *sqliteFile) Stat() ( + fs.FileInfo, + error, +) { return &fileInfo{ name: file.name, size: int64(len(file.content)), @@ -724,7 +769,10 @@ func (file *sqliteFile) Stat() (fs.FileInfo, error) { }, nil } -func (file *sqliteFile) Read(buffer []byte) (int, error) { +func (file *sqliteFile) Read(buffer []byte) ( + int, + error, +) { if file.offset >= int64(len(file.content)) { return 0, goio.EOF } @@ -733,7 +781,8 @@ func (file *sqliteFile) Read(buffer []byte) (int, error) { return bytesRead, nil } -func (file *sqliteFile) Close() error { +func (file *sqliteFile) Close() error { // legacy error contract + return nil } @@ -744,12 +793,16 @@ type sqliteWriteCloser struct { mode fs.FileMode } -func (writer *sqliteWriteCloser) Write(data []byte) (int, error) { +func (writer *sqliteWriteCloser) Write(data []byte) ( + int, + error, +) { writer.data = append(writer.data, data...) return len(data), nil } -func (writer *sqliteWriteCloser) Close() error { +func (writer *sqliteWriteCloser) Close() error { // legacy error contract + mode := writer.mode if mode == 0 { mode = 0644 diff --git a/sqlite/sqlite_example_test.go b/go/sqlite/sqlite_example_test.go similarity index 100% rename from sqlite/sqlite_example_test.go rename to go/sqlite/sqlite_example_test.go diff --git a/sqlite/sqlite_test.go b/go/sqlite/sqlite_test.go similarity index 100% rename from sqlite/sqlite_test.go rename to go/sqlite/sqlite_test.go diff --git a/store/doc.go b/go/store/doc.go similarity index 100% rename from store/doc.go rename to go/store/doc.go diff --git a/store/medium.go b/go/store/medium.go similarity index 85% rename from store/medium.go rename to go/store/medium.go index bad4724..a64a6ee 100644 --- a/store/medium.go +++ b/go/store/medium.go @@ -32,7 +32,10 @@ var _ fs.FS = (*Medium)(nil) // Example: medium, _ := store.NewMedium(store.Options{Path: "config.db"}) // Example: _ = medium.Write("app/theme", "midnight") -func NewMedium(options Options) (*Medium, error) { +func NewMedium(options Options) ( + *Medium, + error, +) { keyValueStore, err := New(options) if err != nil { return nil, err @@ -51,7 +54,8 @@ func (medium *Medium) KeyValueStore() *KeyValueStore { } // Example: _ = medium.Close() -func (medium *Medium) Close() error { +func (medium *Medium) Close() error { // legacy error contract + return medium.keyValueStore.Close() } @@ -68,7 +72,10 @@ func splitGroupKeyPath(entryPath string) (group, key string) { return parts[0], parts[1] } -func (medium *Medium) Read(entryPath string) (string, error) { +func (medium *Medium) Read(entryPath string) ( + string, + error, +) { group, key := splitGroupKeyPath(entryPath) if key == "" { return "", core.E("store.Read", msgStoreGroupKeyRequired, fs.ErrInvalid) @@ -76,7 +83,8 @@ func (medium *Medium) Read(entryPath string) (string, error) { return medium.keyValueStore.Get(group, key) } -func (medium *Medium) Write(entryPath, content string) error { +func (medium *Medium) Write(entryPath, content string) error { // legacy error contract + group, key := splitGroupKeyPath(entryPath) if key == "" { return core.E("store.Write", msgStoreGroupKeyRequired, fs.ErrInvalid) @@ -87,12 +95,14 @@ func (medium *Medium) Write(entryPath, content string) error { // Example: _ = medium.WriteMode("app/theme", "midnight", 0600) // Note: mode is not persisted — the SQLite store has no entry_mode column. // Use Write when mode is irrelevant; WriteMode satisfies the Medium interface only. -func (medium *Medium) WriteMode(entryPath, content string, mode fs.FileMode) error { +func (medium *Medium) WriteMode(entryPath, content string, mode fs.FileMode) error { // legacy error contract + return medium.Write(entryPath, content) } // Example: _ = medium.EnsureDir("app") -func (medium *Medium) EnsureDir(entryPath string) error { +func (medium *Medium) EnsureDir(entryPath string) error { // legacy error contract + return nil } @@ -105,7 +115,8 @@ func (medium *Medium) IsFile(entryPath string) bool { return err == nil } -func (medium *Medium) Delete(entryPath string) error { +func (medium *Medium) Delete(entryPath string) error { // legacy error contract + group, key := splitGroupKeyPath(entryPath) if group == "" { return core.E("store.Delete", msgStorePathRequired, fs.ErrInvalid) @@ -123,7 +134,8 @@ func (medium *Medium) Delete(entryPath string) error { return medium.keyValueStore.Delete(group, key) } -func (medium *Medium) DeleteAll(entryPath string) error { +func (medium *Medium) DeleteAll(entryPath string) error { // legacy error contract + group, key := splitGroupKeyPath(entryPath) if group == "" { return core.E("store.DeleteAll", msgStorePathRequired, fs.ErrInvalid) @@ -134,7 +146,8 @@ func (medium *Medium) DeleteAll(entryPath string) error { return medium.keyValueStore.Delete(group, key) } -func (medium *Medium) Rename(oldPath, newPath string) error { +func (medium *Medium) Rename(oldPath, newPath string) error { // legacy error contract + oldGroup, oldKey := splitGroupKeyPath(oldPath) newGroup, newKey := splitGroupKeyPath(newPath) if oldKey == "" || newKey == "" { @@ -154,7 +167,10 @@ func (medium *Medium) Rename(oldPath, newPath string) error { } // Example: entries, _ := medium.List("app") -func (medium *Medium) List(entryPath string) ([]fs.DirEntry, error) { +func (medium *Medium) List(entryPath string) ( + []fs.DirEntry, + error, +) { group, key := splitGroupKeyPath(entryPath) if group == "" { @@ -191,7 +207,10 @@ func (medium *Medium) List(entryPath string) ([]fs.DirEntry, error) { } // Example: info, _ := medium.Stat("app/theme") -func (medium *Medium) Stat(entryPath string) (fs.FileInfo, error) { +func (medium *Medium) Stat(entryPath string) ( + fs.FileInfo, + error, +) { group, key := splitGroupKeyPath(entryPath) if group == "" { return nil, core.E("store.Stat", msgStorePathRequired, fs.ErrInvalid) @@ -213,7 +232,10 @@ func (medium *Medium) Stat(entryPath string) (fs.FileInfo, error) { return &keyValueFileInfo{name: key, size: int64(len(value))}, nil } -func (medium *Medium) Open(entryPath string) (fs.File, error) { +func (medium *Medium) Open(entryPath string) ( + fs.File, + error, +) { group, key := splitGroupKeyPath(entryPath) if key == "" { return nil, core.E("store.Open", msgStoreGroupKeyRequired, fs.ErrInvalid) @@ -225,7 +247,10 @@ func (medium *Medium) Open(entryPath string) (fs.File, error) { return &keyValueFile{name: key, content: []byte(value)}, nil } -func (medium *Medium) Create(entryPath string) (goio.WriteCloser, error) { +func (medium *Medium) Create(entryPath string) ( + goio.WriteCloser, + error, +) { group, key := splitGroupKeyPath(entryPath) if key == "" { return nil, core.E("store.Create", msgStoreGroupKeyRequired, fs.ErrInvalid) @@ -233,7 +258,10 @@ func (medium *Medium) Create(entryPath string) (goio.WriteCloser, error) { return &keyValueWriteCloser{keyValueStore: medium.keyValueStore, group: group, key: key}, nil } -func (medium *Medium) Append(entryPath string) (goio.WriteCloser, error) { +func (medium *Medium) Append(entryPath string) ( + goio.WriteCloser, + error, +) { group, key := splitGroupKeyPath(entryPath) if key == "" { return nil, core.E("store.Append", msgStoreGroupKeyRequired, fs.ErrInvalid) @@ -245,7 +273,10 @@ func (medium *Medium) Append(entryPath string) (goio.WriteCloser, error) { return &keyValueWriteCloser{keyValueStore: medium.keyValueStore, group: group, key: key, data: []byte(existingValue)}, nil } -func (medium *Medium) ReadStream(entryPath string) (goio.ReadCloser, error) { +func (medium *Medium) ReadStream(entryPath string) ( + goio.ReadCloser, + error, +) { group, key := splitGroupKeyPath(entryPath) if key == "" { return nil, core.E("store.ReadStream", msgStoreGroupKeyRequired, fs.ErrInvalid) @@ -257,7 +288,10 @@ func (medium *Medium) ReadStream(entryPath string) (goio.ReadCloser, error) { return goio.NopCloser(core.NewReader(value)), nil } -func (medium *Medium) WriteStream(entryPath string) (goio.WriteCloser, error) { +func (medium *Medium) WriteStream(entryPath string) ( + goio.WriteCloser, + error, +) { return medium.Create(entryPath) } @@ -323,7 +357,10 @@ func (entry *keyValueDirEntry) Type() fs.FileMode { return 0 } -func (entry *keyValueDirEntry) Info() (fs.FileInfo, error) { +func (entry *keyValueDirEntry) Info() ( + fs.FileInfo, + error, +) { return &keyValueFileInfo{name: entry.name, size: entry.size, isDir: entry.isDir}, nil } @@ -333,11 +370,17 @@ type keyValueFile struct { offset int64 } -func (file *keyValueFile) Stat() (fs.FileInfo, error) { +func (file *keyValueFile) Stat() ( + fs.FileInfo, + error, +) { return &keyValueFileInfo{name: file.name, size: int64(len(file.content))}, nil } -func (file *keyValueFile) Read(buffer []byte) (int, error) { +func (file *keyValueFile) Read(buffer []byte) ( + int, + error, +) { if file.offset >= int64(len(file.content)) { return 0, goio.EOF } @@ -355,11 +398,15 @@ type keyValueWriteCloser struct { data []byte } -func (writer *keyValueWriteCloser) Write(data []byte) (int, error) { +func (writer *keyValueWriteCloser) Write(data []byte) ( + int, + error, +) { writer.data = append(writer.data, data...) return len(data), nil } -func (writer *keyValueWriteCloser) Close() error { +func (writer *keyValueWriteCloser) Close() error { // legacy error contract + return writer.keyValueStore.Set(writer.group, writer.key, string(writer.data)) } diff --git a/store/medium_example_test.go b/go/store/medium_example_test.go similarity index 100% rename from store/medium_example_test.go rename to go/store/medium_example_test.go diff --git a/store/medium_test.go b/go/store/medium_test.go similarity index 100% rename from store/medium_test.go rename to go/store/medium_test.go diff --git a/store/store.go b/go/store/store.go similarity index 91% rename from store/store.go rename to go/store/store.go index 27daaa8..c5ca412 100644 --- a/store/store.go +++ b/go/store/store.go @@ -46,7 +46,10 @@ type Options struct { // Example: keyValueStore, _ := store.New(store.Options{Path: ":memory:"}) // Example: _ = keyValueStore.Set("app", "theme", "midnight") -func New(options Options) (*KeyValueStore, error) { +func New(options Options) ( + *KeyValueStore, + error, +) { if options.Path == "" { return nil, core.E(opStoreNew, "database path is required", fs.ErrInvalid) } @@ -72,12 +75,16 @@ func New(options Options) (*KeyValueStore, error) { } // Example: _ = keyValueStore.Close() -func (keyValueStore *KeyValueStore) Close() error { +func (keyValueStore *KeyValueStore) Close() error { // legacy error contract + return keyValueStore.database.Close() } // Example: theme, _ := keyValueStore.Get("app", "theme") -func (keyValueStore *KeyValueStore) Get(group, key string) (string, error) { +func (keyValueStore *KeyValueStore) Get(group, key string) ( + string, + error, +) { var value string err := keyValueStore.database.QueryRow("SELECT entry_value FROM entries WHERE group_name = ? AND entry_key = ?", group, key).Scan(&value) if err == sql.ErrNoRows { @@ -90,7 +97,8 @@ func (keyValueStore *KeyValueStore) Get(group, key string) (string, error) { } // Example: _ = keyValueStore.Set("app", "theme", "midnight") -func (keyValueStore *KeyValueStore) Set(group, key, value string) error { +func (keyValueStore *KeyValueStore) Set(group, key, value string) error { // legacy error contract + _, err := keyValueStore.database.Exec( `INSERT INTO entries (group_name, entry_key, entry_value) VALUES (?, ?, ?) ON CONFLICT(group_name, entry_key) DO UPDATE SET entry_value = excluded.entry_value`, @@ -103,7 +111,8 @@ func (keyValueStore *KeyValueStore) Set(group, key, value string) error { } // Example: _ = keyValueStore.Delete("app", "theme") -func (keyValueStore *KeyValueStore) Delete(group, key string) error { +func (keyValueStore *KeyValueStore) Delete(group, key string) error { // legacy error contract + _, err := keyValueStore.database.Exec("DELETE FROM entries WHERE group_name = ? AND entry_key = ?", group, key) if err != nil { return core.E("store.Delete", "exec", err) @@ -112,7 +121,10 @@ func (keyValueStore *KeyValueStore) Delete(group, key string) error { } // Example: count, _ := keyValueStore.Count("app") -func (keyValueStore *KeyValueStore) Count(group string) (int, error) { +func (keyValueStore *KeyValueStore) Count(group string) ( + int, + error, +) { var count int err := keyValueStore.database.QueryRow("SELECT COUNT(*) FROM entries WHERE group_name = ?", group).Scan(&count) if err != nil { @@ -122,7 +134,8 @@ func (keyValueStore *KeyValueStore) Count(group string) (int, error) { } // Example: _ = keyValueStore.DeleteGroup("app") -func (keyValueStore *KeyValueStore) DeleteGroup(group string) error { +func (keyValueStore *KeyValueStore) DeleteGroup(group string) error { // legacy error contract + _, err := keyValueStore.database.Exec("DELETE FROM entries WHERE group_name = ?", group) if err != nil { return core.E("store.DeleteGroup", "exec", err) @@ -131,7 +144,10 @@ func (keyValueStore *KeyValueStore) DeleteGroup(group string) error { } // Example: groups, _ := keyValueStore.ListGroups() -func (keyValueStore *KeyValueStore) ListGroups() ([]string, error) { +func (keyValueStore *KeyValueStore) ListGroups() ( + []string, + error, +) { rows, err := keyValueStore.database.Query("SELECT DISTINCT group_name FROM entries ORDER BY group_name") if err != nil { return nil, core.E(opStoreListGroups, "query groups", err) @@ -153,7 +169,10 @@ func (keyValueStore *KeyValueStore) ListGroups() ([]string, error) { } // Example: values, _ := keyValueStore.GetAll("app") -func (keyValueStore *KeyValueStore) GetAll(group string) (map[string]string, error) { +func (keyValueStore *KeyValueStore) GetAll(group string) ( + map[string]string, + error, +) { rows, err := keyValueStore.database.Query("SELECT entry_key, entry_value FROM entries WHERE group_name = ?", group) if err != nil { return nil, core.E(opStoreGetAll, "query", err) @@ -177,7 +196,10 @@ func (keyValueStore *KeyValueStore) GetAll(group string) (map[string]string, err // Example: keyValueStore, _ := store.New(store.Options{Path: ":memory:"}) // Example: _ = keyValueStore.Set("user", "name", "alice") // Example: renderedText, _ := keyValueStore.Render("hello {{ .name }}", "user") -func (keyValueStore *KeyValueStore) Render(templateText, group string) (string, error) { +func (keyValueStore *KeyValueStore) Render(templateText, group string) ( + string, + error, +) { rows, err := keyValueStore.database.Query("SELECT entry_key, entry_value FROM entries WHERE group_name = ?", group) if err != nil { return "", core.E(opStoreRender, "query", err) diff --git a/store/store_example_test.go b/go/store/store_example_test.go similarity index 100% rename from store/store_example_test.go rename to go/store/store_example_test.go diff --git a/store/store_test.go b/go/store/store_test.go similarity index 100% rename from store/store_test.go rename to go/store/store_test.go diff --git a/workspace/command.go b/go/workspace/command.go similarity index 100% rename from workspace/command.go rename to go/workspace/command.go diff --git a/workspace/command_example_test.go b/go/workspace/command_example_test.go similarity index 100% rename from workspace/command_example_test.go rename to go/workspace/command_example_test.go diff --git a/workspace/command_test.go b/go/workspace/command_test.go similarity index 100% rename from workspace/command_test.go rename to go/workspace/command_test.go diff --git a/workspace/doc.go b/go/workspace/doc.go similarity index 100% rename from workspace/doc.go rename to go/workspace/doc.go diff --git a/workspace/service.go b/go/workspace/service.go similarity index 96% rename from workspace/service.go rename to go/workspace/service.go index a8274da..6638f6a 100644 --- a/workspace/service.go +++ b/go/workspace/service.go @@ -54,7 +54,10 @@ type workspaceSHA256Hash struct { data []byte } -func (hash *workspaceSHA256Hash) Write(data []byte) (int, error) { +func (hash *workspaceSHA256Hash) Write(data []byte) ( + int, + error, +) { hash.data = append(hash.data, data...) return len(data), nil } @@ -107,7 +110,10 @@ var _ EncryptedWorkspace = (*Service)(nil) // Example: Medium: io.NewMemoryMedium(), // Example: }) // Example: workspaceID, _ := service.CreateWorkspace("alice", "pass123") -func New(options Options) (*Service, error) { +func New(options Options) ( + *Service, + error, +) { rootPath := options.RootPath if rootPath == "" { home := resolveWorkspaceHomeDirectory() @@ -147,7 +153,10 @@ func New(options Options) (*Service, error) { } // Example: workspaceID, _ := service.CreateWorkspace("alice", "pass123") -func (service *Service) CreateWorkspace(identifier, passphrase string) (string, error) { +func (service *Service) CreateWorkspace(identifier, passphrase string) ( + string, + error, +) { service.stateLock.Lock() defer service.stateLock.Unlock() @@ -184,7 +193,8 @@ func (service *Service) CreateWorkspace(identifier, passphrase string) (string, } // Example: _ = service.SwitchWorkspace(workspaceID) -func (service *Service) SwitchWorkspace(workspaceID string) error { +func (service *Service) SwitchWorkspace(workspaceID string) error { // legacy error contract + service.stateLock.Lock() defer service.stateLock.Unlock() @@ -200,7 +210,10 @@ func (service *Service) SwitchWorkspace(workspaceID string) error { return nil } -func (service *Service) resolveActiveWorkspaceFilePath(operation, workspaceFilePath string) (string, error) { +func (service *Service) resolveActiveWorkspaceFilePath(operation, workspaceFilePath string) ( + string, + error, +) { if service.activeWorkspaceID == "" { return "", core.E(operation, "no active workspace", fs.ErrNotExist) } @@ -216,7 +229,10 @@ func (service *Service) resolveActiveWorkspaceFilePath(operation, workspaceFileP } // Example: cipherSigil, _ := service.workspaceCipherSigil("workspace.ReadWorkspaceFile") -func (service *Service) workspaceCipherSigil(operation string) (*sigil.ChaChaPolySigil, error) { +func (service *Service) workspaceCipherSigil(operation string) ( + *sigil.ChaChaPolySigil, + error, +) { if service.activeWorkspaceID == "" { return nil, core.E(operation, "no active workspace", fs.ErrNotExist) } @@ -240,7 +256,10 @@ func (service *Service) workspaceCipherSigil(operation string) (*sigil.ChaChaPol } // Example: content, _ := service.ReadWorkspaceFile("notes/todo.txt") -func (service *Service) ReadWorkspaceFile(workspaceFilePath string) (string, error) { +func (service *Service) ReadWorkspaceFile(workspaceFilePath string) ( + string, + error, +) { service.stateLock.RLock() defer service.stateLock.RUnlock() @@ -264,7 +283,8 @@ func (service *Service) ReadWorkspaceFile(workspaceFilePath string) (string, err } // Example: _ = service.WriteWorkspaceFile("notes/todo.txt", "ship it") -func (service *Service) WriteWorkspaceFile(workspaceFilePath, content string) error { +func (service *Service) WriteWorkspaceFile(workspaceFilePath, content string) error { // legacy error contract + service.stateLock.Lock() defer service.stateLock.Unlock() @@ -328,7 +348,10 @@ func resolveWorkspaceHomeDirectory() string { return core.Env("DIR_HOME") } -func joinPathWithinRoot(root string, parts ...string) (string, error) { +func joinPathWithinRoot(root string, parts ...string) ( + string, + error, +) { candidate := core.Path(append([]string{root}, parts...)...) separator := core.Env("CORE_PATH_SEPARATOR") if separator == "" { @@ -343,7 +366,10 @@ func joinPathWithinRoot(root string, parts ...string) (string, error) { return "", fs.ErrPermission } -func (service *Service) resolveWorkspaceDirectory(operation, workspaceID string) (string, error) { +func (service *Service) resolveWorkspaceDirectory(operation, workspaceID string) ( + string, + error, +) { if workspaceID == "" { return "", core.E(operation, "workspace id is required", fs.ErrInvalid) } diff --git a/workspace/service_example_test.go b/go/workspace/service_example_test.go similarity index 100% rename from workspace/service_example_test.go rename to go/workspace/service_example_test.go diff --git a/workspace/service_test.go b/go/workspace/service_test.go similarity index 100% rename from workspace/service_test.go rename to go/workspace/service_test.go diff --git a/workspace/workspace.go b/go/workspace/workspace.go similarity index 87% rename from workspace/workspace.go rename to go/workspace/workspace.go index cca063f..2c49843 100644 --- a/workspace/workspace.go +++ b/go/workspace/workspace.go @@ -26,7 +26,10 @@ type Workspace struct { } // NewWorkspace creates a workspace service backed by medium under baseSubpath. -func NewWorkspace(medium coreio.Medium, baseSubpath string) (*Workspace, error) { +func NewWorkspace(medium coreio.Medium, baseSubpath string) ( + *Workspace, + error, +) { if medium == nil { return nil, core.E(opNewWorkspace, "storage medium is required", fs.ErrInvalid) } @@ -46,7 +49,10 @@ func NewWorkspace(medium coreio.Medium, baseSubpath string) (*Workspace, error) } // CreateWorkspace creates a named workspace directory and returns a medium scoped to it. -func (workspace *Workspace) CreateWorkspace(name string) (coreio.Medium, error) { +func (workspace *Workspace) CreateWorkspace(name string) ( + coreio.Medium, + error, +) { if workspace == nil { return nil, core.E(opCreateWorkspace, errWorkspaceServiceNotConfigured, fs.ErrInvalid) } @@ -70,7 +76,8 @@ func (workspace *Workspace) CreateWorkspace(name string) (coreio.Medium, error) } // SwitchWorkspace records the named workspace as the current workspace. -func (workspace *Workspace) SwitchWorkspace(name string) error { +func (workspace *Workspace) SwitchWorkspace(name string) error { // legacy error contract + if workspace == nil { return core.E(opSwitchWorkspace, errWorkspaceServiceNotConfigured, fs.ErrInvalid) } @@ -99,7 +106,10 @@ func (workspace *Workspace) CurrentWorkspace() string { } // ReadWorkspaceFile reads a file from the named workspace. -func (workspace *Workspace) ReadWorkspaceFile(name, filePath string) (string, error) { +func (workspace *Workspace) ReadWorkspaceFile(name, filePath string) ( + string, + error, +) { if workspace == nil { return "", core.E("workspace.ReadWorkspaceFile", errWorkspaceServiceNotConfigured, fs.ErrInvalid) } @@ -114,7 +124,8 @@ func (workspace *Workspace) ReadWorkspaceFile(name, filePath string) (string, er } // WriteWorkspaceFile writes a file into the named workspace. -func (workspace *Workspace) WriteWorkspaceFile(name, filePath, content string) error { +func (workspace *Workspace) WriteWorkspaceFile(name, filePath, content string) error { // legacy error contract + if workspace == nil { return core.E("workspace.WriteWorkspaceFile", errWorkspaceServiceNotConfigured, fs.ErrInvalid) } @@ -129,7 +140,10 @@ func (workspace *Workspace) WriteWorkspaceFile(name, filePath, content string) e } // ListWorkspaceFiles lists entries under a workspace-relative directory. -func (workspace *Workspace) ListWorkspaceFiles(name, directoryPath string) ([]fs.DirEntry, error) { +func (workspace *Workspace) ListWorkspaceFiles(name, directoryPath string) ( + []fs.DirEntry, + error, +) { if workspace == nil { return nil, core.E("workspace.ListWorkspaceFiles", errWorkspaceServiceNotConfigured, fs.ErrInvalid) } @@ -179,7 +193,10 @@ func (workspace *Workspace) HandleWorkspaceCommand(command WorkspaceCommand) cor } } -func (workspace *Workspace) workspaceFilePath(operation, name, filePath string, allowEmptyPath bool) (string, error) { +func (workspace *Workspace) workspaceFilePath(operation, name, filePath string, allowEmptyPath bool) ( + string, + error, +) { _, workspacePath, err := workspace.workspaceNameAndPath(operation, name) if err != nil { return "", err @@ -194,12 +211,19 @@ func (workspace *Workspace) workspaceFilePath(operation, name, filePath string, return joinMediumSubpaths(workspacePath, cleanFilePath), nil } -func (workspace *Workspace) workspacePath(operation, name string) (string, error) { +func (workspace *Workspace) workspacePath(operation, name string) ( + string, + error, +) { _, workspacePath, err := workspace.workspaceNameAndPath(operation, name) return workspacePath, err } -func (workspace *Workspace) workspaceNameAndPath(operation, name string) (string, string, error) { +func (workspace *Workspace) workspaceNameAndPath(operation, name string) ( + string, + string, + error, +) { if workspace == nil || workspace.medium == nil { return "", "", core.E(operation, errWorkspaceServiceNotConfigured, fs.ErrInvalid) } @@ -217,7 +241,10 @@ func (workspace *Workspace) scopedMedium(root string) coreio.Medium { } } -func cleanWorkspaceName(operation, name string) (string, error) { +func cleanWorkspaceName(operation, name string) ( + string, + error, +) { name = core.Trim(name) if name == "" || name == "." || name == ".." { return "", core.E(operation, "workspace name is required", fs.ErrInvalid) @@ -228,7 +255,10 @@ func cleanWorkspaceName(operation, name string) (string, error) { return name, nil } -func cleanMediumSubpath(operation, subpath string, allowEmpty bool) (string, error) { +func cleanMediumSubpath(operation, subpath string, allowEmpty bool) ( + string, + error, +) { subpath = core.Trim(core.Replace(subpath, "\\", "/")) if subpath == "" || subpath == "." { if allowEmpty { @@ -275,7 +305,10 @@ type scopedMedium struct { var _ coreio.Medium = (*scopedMedium)(nil) var _ fs.FS = (*scopedMedium)(nil) -func (medium *scopedMedium) scopedPath(operation, entryPath string, allowRoot bool) (string, error) { +func (medium *scopedMedium) scopedPath(operation, entryPath string, allowRoot bool) ( + string, + error, +) { cleanPath, err := cleanMediumSubpath(operation, entryPath, allowRoot) if err != nil { return "", err @@ -283,7 +316,10 @@ func (medium *scopedMedium) scopedPath(operation, entryPath string, allowRoot bo return joinMediumSubpaths(medium.root, cleanPath), nil } -func (medium *scopedMedium) Read(entryPath string) (string, error) { +func (medium *scopedMedium) Read(entryPath string) ( + string, + error, +) { scopedPath, err := medium.scopedPath("workspace.scopedMedium.Read", entryPath, false) if err != nil { return "", err @@ -291,11 +327,13 @@ func (medium *scopedMedium) Read(entryPath string) (string, error) { return medium.medium.Read(scopedPath) } -func (medium *scopedMedium) Write(entryPath, content string) error { +func (medium *scopedMedium) Write(entryPath, content string) error { // legacy error contract + return medium.WriteMode(entryPath, content, 0644) } -func (medium *scopedMedium) WriteMode(entryPath, content string, mode fs.FileMode) error { +func (medium *scopedMedium) WriteMode(entryPath, content string, mode fs.FileMode) error { // legacy error contract + scopedPath, err := medium.scopedPath("workspace.scopedMedium.WriteMode", entryPath, false) if err != nil { return err @@ -303,7 +341,8 @@ func (medium *scopedMedium) WriteMode(entryPath, content string, mode fs.FileMod return medium.medium.WriteMode(scopedPath, content, mode) } -func (medium *scopedMedium) EnsureDir(entryPath string) error { +func (medium *scopedMedium) EnsureDir(entryPath string) error { // legacy error contract + scopedPath, err := medium.scopedPath("workspace.scopedMedium.EnsureDir", entryPath, true) if err != nil { return err @@ -319,7 +358,8 @@ func (medium *scopedMedium) IsFile(entryPath string) bool { return medium.medium.IsFile(scopedPath) } -func (medium *scopedMedium) Delete(entryPath string) error { +func (medium *scopedMedium) Delete(entryPath string) error { // legacy error contract + scopedPath, err := medium.scopedPath("workspace.scopedMedium.Delete", entryPath, false) if err != nil { return err @@ -327,7 +367,8 @@ func (medium *scopedMedium) Delete(entryPath string) error { return medium.medium.Delete(scopedPath) } -func (medium *scopedMedium) DeleteAll(entryPath string) error { +func (medium *scopedMedium) DeleteAll(entryPath string) error { // legacy error contract + scopedPath, err := medium.scopedPath("workspace.scopedMedium.DeleteAll", entryPath, false) if err != nil { return err @@ -335,7 +376,8 @@ func (medium *scopedMedium) DeleteAll(entryPath string) error { return medium.medium.DeleteAll(scopedPath) } -func (medium *scopedMedium) Rename(oldPath, newPath string) error { +func (medium *scopedMedium) Rename(oldPath, newPath string) error { // legacy error contract + scopedOldPath, err := medium.scopedPath("workspace.scopedMedium.Rename", oldPath, false) if err != nil { return err @@ -347,7 +389,10 @@ func (medium *scopedMedium) Rename(oldPath, newPath string) error { return medium.medium.Rename(scopedOldPath, scopedNewPath) } -func (medium *scopedMedium) List(entryPath string) ([]fs.DirEntry, error) { +func (medium *scopedMedium) List(entryPath string) ( + []fs.DirEntry, + error, +) { scopedPath, err := medium.scopedPath("workspace.scopedMedium.List", entryPath, true) if err != nil { return nil, err @@ -355,7 +400,10 @@ func (medium *scopedMedium) List(entryPath string) ([]fs.DirEntry, error) { return medium.medium.List(scopedPath) } -func (medium *scopedMedium) Stat(entryPath string) (fs.FileInfo, error) { +func (medium *scopedMedium) Stat(entryPath string) ( + fs.FileInfo, + error, +) { scopedPath, err := medium.scopedPath("workspace.scopedMedium.Stat", entryPath, true) if err != nil { return nil, err @@ -363,7 +411,10 @@ func (medium *scopedMedium) Stat(entryPath string) (fs.FileInfo, error) { return medium.medium.Stat(scopedPath) } -func (medium *scopedMedium) Open(entryPath string) (fs.File, error) { +func (medium *scopedMedium) Open(entryPath string) ( + fs.File, + error, +) { scopedPath, err := medium.scopedPath("workspace.scopedMedium.Open", entryPath, false) if err != nil { return nil, err @@ -371,7 +422,10 @@ func (medium *scopedMedium) Open(entryPath string) (fs.File, error) { return medium.medium.Open(scopedPath) } -func (medium *scopedMedium) Create(entryPath string) (goio.WriteCloser, error) { +func (medium *scopedMedium) Create(entryPath string) ( + goio.WriteCloser, + error, +) { scopedPath, err := medium.scopedPath("workspace.scopedMedium.Create", entryPath, false) if err != nil { return nil, err @@ -379,7 +433,10 @@ func (medium *scopedMedium) Create(entryPath string) (goio.WriteCloser, error) { return medium.medium.Create(scopedPath) } -func (medium *scopedMedium) Append(entryPath string) (goio.WriteCloser, error) { +func (medium *scopedMedium) Append(entryPath string) ( + goio.WriteCloser, + error, +) { scopedPath, err := medium.scopedPath("workspace.scopedMedium.Append", entryPath, false) if err != nil { return nil, err @@ -387,7 +444,10 @@ func (medium *scopedMedium) Append(entryPath string) (goio.WriteCloser, error) { return medium.medium.Append(scopedPath) } -func (medium *scopedMedium) ReadStream(entryPath string) (goio.ReadCloser, error) { +func (medium *scopedMedium) ReadStream(entryPath string) ( + goio.ReadCloser, + error, +) { scopedPath, err := medium.scopedPath("workspace.scopedMedium.ReadStream", entryPath, false) if err != nil { return nil, err @@ -395,7 +455,10 @@ func (medium *scopedMedium) ReadStream(entryPath string) (goio.ReadCloser, error return medium.medium.ReadStream(scopedPath) } -func (medium *scopedMedium) WriteStream(entryPath string) (goio.WriteCloser, error) { +func (medium *scopedMedium) WriteStream(entryPath string) ( + goio.WriteCloser, + error, +) { scopedPath, err := medium.scopedPath("workspace.scopedMedium.WriteStream", entryPath, false) if err != nil { return nil, err diff --git a/workspace/workspace_example_test.go b/go/workspace/workspace_example_test.go similarity index 100% rename from workspace/workspace_example_test.go rename to go/workspace/workspace_example_test.go diff --git a/workspace/workspace_test.go b/go/workspace/workspace_test.go similarity index 100% rename from workspace/workspace_test.go rename to go/workspace/workspace_test.go