refactor(ax): make public docs example-driven
Some checks failed
CI / auto-fix (push) Failing after 0s
CI / test (push) Failing after 2s
CI / auto-merge (push) Failing after 0s

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-03-30 21:23:35 +00:00
parent 41dd111072
commit 16d968b551
11 changed files with 93 additions and 88 deletions

View file

@ -36,7 +36,10 @@ var (
}
)
// Medium is an in-memory storage backend backed by a Borg DataNode.
// Example: medium := datanode.New()
// _ = medium.Write("jobs/run.log", "started")
// snapshot, _ := medium.Snapshot()
//
// All paths are relative (no leading slash). Thread-safe via RWMutex.
type Medium struct {
dataNode *borgdatanode.DataNode
@ -44,10 +47,8 @@ type Medium struct {
mu sync.RWMutex
}
// New creates an in-memory Medium that snapshots to tar.
//
// medium := datanode.New()
// _ = medium.Write("jobs/run.log", "started")
// Example: medium := datanode.New()
// _ = medium.Write("jobs/run.log", "started")
func New() *Medium {
return &Medium{
dataNode: borgdatanode.New(),

33
io.go
View file

@ -10,16 +10,18 @@ import (
"dappco.re/go/core/io/local"
)
// Medium defines the standard interface for a storage backend.
// This allows for different implementations (e.g., local disk, S3, SFTP)
// to be used interchangeably.
// Medium is the storage boundary used across CoreGO.
//
// medium, _ := io.NewSandboxed("/srv/app")
// _ = medium.Write("config/app.yaml", "port: 8080")
// backup, _ := io.NewSandboxed("/srv/backup")
// _ = io.Copy(medium, "data/report.json", backup, "daily/report.json")
type Medium interface {
Read(path string) (string, error)
Write(path, content string) error
// WriteMode saves content with explicit file permissions.
// Use 0600 for sensitive files (keys, secrets, encrypted output).
// Example: _ = medium.WriteMode("keys/private.key", key, 0600)
WriteMode(path, content string, mode fs.FileMode) error
EnsureDir(path string) error
@ -46,18 +48,16 @@ type Medium interface {
Append(path string) (goio.WriteCloser, error)
// ReadStream returns a reader for the file content.
// Use this for large files to avoid loading the entire content into memory.
// Example: reader, _ := medium.ReadStream("logs/app.log")
ReadStream(path string) (goio.ReadCloser, error)
// WriteStream returns a writer for the file content.
// Use this for large files to avoid loading the entire content into memory.
// Example: writer, _ := medium.WriteStream("logs/app.log")
WriteStream(path string) (goio.WriteCloser, error)
// Exists checks if a path exists (file or directory).
// Example: ok := medium.Exists("config/app.yaml")
Exists(path string) bool
// IsDir checks if a path exists and is a directory.
// Example: ok := medium.IsDir("config")
IsDir(path string) bool
}
@ -98,9 +98,9 @@ func (de DirEntry) Type() fs.FileMode { return de.mode.Type() }
func (de DirEntry) Info() (fs.FileInfo, error) { return de.info, nil }
// Local is a pre-initialised medium for the local filesystem.
// It uses "/" as root, providing unsandboxed access to the filesystem.
// For sandboxed access, use NewSandboxed with a specific root path.
// Local is the unsandboxed filesystem medium rooted at "/".
//
// io.Local.Read("/etc/hostname")
var Local Medium
var _ Medium = (*local.Medium)(nil)
@ -171,7 +171,10 @@ func Copy(source Medium, sourcePath string, destination Medium, destinationPath
// --- MockMedium ---
// MockMedium is an in-memory implementation of Medium for testing.
// MockMedium is an in-memory Medium for tests.
//
// medium := io.NewMockMedium()
// _ = medium.Write("config/app.yaml", "port: 8080")
type MockMedium struct {
Files map[string]string
Dirs map[string]bool

View file

@ -13,16 +13,15 @@ import (
core "dappco.re/go/core"
)
// Medium is a local filesystem storage backend.
// Medium is the local filesystem backend returned by New.
type Medium struct {
filesystemRoot string
}
var unrestrictedFileSystem = (&core.Fs{}).NewUnrestricted()
// New creates a filesystem rooted at root.
//
// Pass "/" for full filesystem access, or a project path to sandbox.
// local.New("/") exposes the full filesystem.
// local.New("/srv/app") confines access to a project root.
//
// medium, _ := local.New("/srv/app")
// _ = medium.Write("config/app.yaml", "port: 8080")

View file

@ -23,7 +23,11 @@ import (
coreio "dappco.re/go/core/io"
)
// Node is an in-memory filesystem that satisfies coreio.Medium and fs.FS.
// Example: nodeTree := node.New()
// nodeTree.AddData("config/app.yaml", []byte("port: 8080"))
// snapshot, _ := nodeTree.ToTar()
// restored, _ := node.FromTar(snapshot)
//
// Directories are implicit: they exist whenever a file path contains a "/".
type Node struct {
files map[string]*dataFile
@ -33,10 +37,8 @@ type Node struct {
var _ coreio.Medium = (*Node)(nil)
var _ fs.ReadFileFS = (*Node)(nil)
// Use New when you need an in-memory filesystem that can be snapshotted.
//
// nodeTree := New()
// nodeTree.AddData("config/app.yaml", []byte("port: 8080"))
// Example: nodeTree := node.New()
// nodeTree.AddData("config/app.yaml", []byte("port: 8080"))
func New() *Node {
return &Node{files: make(map[string]*dataFile)}
}
@ -135,7 +137,7 @@ func (n *Node) WalkNode(root string, fn fs.WalkDirFunc) error {
return fs.WalkDir(n, root, fn)
}
// WalkOptions configures WalkWithOptions.
// Example: options := node.WalkOptions{MaxDepth: 1, SkipErrors: true}
type WalkOptions struct {
// MaxDepth limits how many directory levels to descend. 0 means unlimited.
MaxDepth int
@ -380,7 +382,7 @@ func (n *Node) FileSet(filePath, content string) error {
return n.Write(filePath, content)
}
// EnsureDir is a no-op because directories are implicit in Node.
// Example: _ = nodeTree.EnsureDir("config")
func (n *Node) EnsureDir(_ string) error {
return nil
}

View file

@ -33,7 +33,10 @@ type Client interface {
CopyObject(ctx context.Context, params *awss3.CopyObjectInput, optFns ...func(*awss3.Options)) (*awss3.CopyObjectOutput, error)
}
// Medium is the concrete io.Medium returned by New.
// Medium is the S3-backed io.Medium returned by New.
//
// medium, _ := s3.New(s3.Options{Bucket: "backups", Client: client, Prefix: "daily/"})
// _ = medium.Write("reports/daily.txt", "done")
type Medium struct {
client Client
bucket string
@ -42,7 +45,7 @@ type Medium struct {
var _ coreio.Medium = (*Medium)(nil)
// Options configures New.
// Example: medium, _ := s3.New(s3.Options{Bucket: "backups", Client: client, Prefix: "daily/"})
type Options struct {
// Bucket is the target S3 bucket name.
Bucket string
@ -90,10 +93,8 @@ func normalisePrefix(prefix string) string {
return clean
}
// New opens an S3-backed medium for one bucket and optional prefix.
//
// medium, _ := s3.New(s3.Options{Bucket: "backups", Client: client, Prefix: "daily/"})
// _ = medium.Write("reports/daily.txt", "done")
// Example: medium, _ := s3.New(s3.Options{Bucket: "backups", Client: client, Prefix: "daily/"})
// _ = medium.Write("reports/daily.txt", "done")
func New(options Options) (*Medium, error) {
if options.Bucket == "" {
return nil, core.E("s3.New", "bucket name is required", nil)
@ -167,17 +168,17 @@ func (m *Medium) Write(filePath, content string) error {
return nil
}
// WriteMode ignores the requested mode because S3 objects do not store POSIX permissions.
// Example: _ = medium.WriteMode("keys/private.key", key, 0600)
func (m *Medium) WriteMode(filePath, content string, _ fs.FileMode) error {
return m.Write(filePath, content)
}
// EnsureDir is a no-op for S3 (S3 has no real directories).
// Example: _ = medium.EnsureDir("reports/2026")
func (m *Medium) EnsureDir(_ string) error {
return nil
}
// IsFile checks if a path exists and is a regular file (not a "directory" prefix).
// Example: ok := medium.IsFile("reports/daily.txt")
func (m *Medium) IsFile(filePath string) bool {
key := m.objectKey(filePath)
if key == "" {
@ -218,7 +219,7 @@ func (m *Medium) Delete(filePath string) error {
return nil
}
// DeleteAll removes all objects under the given prefix.
// Example: _ = medium.DeleteAll("reports/2026")
func (m *Medium) DeleteAll(filePath string) error {
key := m.objectKey(filePath)
if key == "" {
@ -283,7 +284,7 @@ func (m *Medium) DeleteAll(filePath string) error {
return nil
}
// Rename moves an object by copying then deleting the original.
// Example: _ = medium.Rename("drafts/todo.txt", "archive/todo.txt")
func (m *Medium) Rename(oldPath, newPath string) error {
oldKey := m.objectKey(oldPath)
newKey := m.objectKey(newPath)
@ -313,7 +314,7 @@ func (m *Medium) Rename(oldPath, newPath string) error {
return nil
}
// List returns directory entries for the given path using ListObjectsV2 with delimiter.
// Example: entries, _ := medium.List("reports")
func (m *Medium) List(filePath string) ([]fs.DirEntry, error) {
prefix := m.objectKey(filePath)
if prefix != "" && !core.HasSuffix(prefix, "/") {
@ -386,7 +387,7 @@ func (m *Medium) List(filePath string) ([]fs.DirEntry, error) {
return entries, nil
}
// Stat returns file information for the given path using HeadObject.
// Example: info, _ := medium.Stat("reports/daily.txt")
func (m *Medium) Stat(filePath string) (fs.FileInfo, error) {
key := m.objectKey(filePath)
if key == "" {
@ -456,8 +457,7 @@ func (m *Medium) Open(filePath string) (fs.File, error) {
}, nil
}
// Create creates or truncates the named file. Returns a writer that
// uploads the content on Close.
// Example: writer, _ := medium.Create("reports/daily.txt")
func (m *Medium) Create(filePath string) (goio.WriteCloser, error) {
key := m.objectKey(filePath)
if key == "" {
@ -469,8 +469,7 @@ func (m *Medium) Create(filePath string) (goio.WriteCloser, error) {
}, nil
}
// Append opens the named file for appending. It downloads the existing
// content (if any) and re-uploads the combined content on Close.
// Example: writer, _ := medium.Append("reports/daily.txt")
func (m *Medium) Append(filePath string) (goio.WriteCloser, error) {
key := m.objectKey(filePath)
if key == "" {
@ -514,7 +513,7 @@ func (m *Medium) WriteStream(filePath string) (goio.WriteCloser, error) {
return m.Create(filePath)
}
// Exists checks if a path exists (file or directory prefix).
// Example: ok := medium.Exists("reports/daily.txt")
func (m *Medium) Exists(filePath string) bool {
key := m.objectKey(filePath)
if key == "" {
@ -546,7 +545,7 @@ func (m *Medium) Exists(filePath string) bool {
return len(listOut.Contents) > 0 || len(listOut.CommonPrefixes) > 0
}
// IsDir checks if a path exists and is a directory (has objects under it as a prefix).
// Example: ok := medium.IsDir("reports")
func (m *Medium) IsDir(filePath string) bool {
key := m.objectKey(filePath)
if key == "" {

View file

@ -295,7 +295,7 @@ func TestS3_ReadWrite_Prefix_Good(t *testing.T) {
func TestS3_EnsureDir_Good(t *testing.T) {
m, _ := newTestMedium(t)
// EnsureDir is a no-op for S3
// Example: err := m.EnsureDir("any/path")
err := m.EnsureDir("any/path")
assert.NoError(t, err)
}

View file

@ -207,8 +207,8 @@ func (s *ShuffleMaskObfuscator) deriveMask(entropy []byte, length int) []byte {
return mask
}
// ChaChaPolySigil is returned by NewChaChaPolySigil and
// NewChaChaPolySigilWithObfuscator.
// Example: cipherSigil, _ := sigil.NewChaChaPolySigil(key)
// Example: cipherSigil, _ := sigil.NewChaChaPolySigilWithObfuscator(key, &sigil.ShuffleMaskObfuscator{})
type ChaChaPolySigil struct {
Key []byte
Obfuscator PreObfuscator

View file

@ -18,7 +18,10 @@ import (
_ "modernc.org/sqlite" // Pure Go SQLite driver
)
// Medium is a SQLite-backed storage backend implementing the io.Medium interface.
// Medium stores filesystem-shaped content in SQLite.
//
// medium, _ := sqlite.New(sqlite.Options{Path: ":memory:"})
// _ = medium.Write("config/app.yaml", "port: 8080")
type Medium struct {
database *sql.DB
table string
@ -26,7 +29,7 @@ type Medium struct {
var _ coreio.Medium = (*Medium)(nil)
// Options configures a SQLite-backed Medium.
// Example: medium, _ := sqlite.New(sqlite.Options{Path: ":memory:", Table: "files"})
type Options struct {
// Path is the SQLite database path. Use ":memory:" for tests.
Path string
@ -41,10 +44,8 @@ func normaliseTableName(table string) string {
return table
}
// New opens a SQLite-backed Medium at the provided database path.
//
// medium, _ := sqlite.New(sqlite.Options{Path: ":memory:", Table: "files"})
// _ = medium.Write("config/app.yaml", "port: 8080")
// Example: medium, _ := sqlite.New(sqlite.Options{Path: ":memory:", Table: "files"})
// _ = medium.Write("config/app.yaml", "port: 8080")
func New(options Options) (*Medium, error) {
if options.Path == "" {
return nil, core.E("sqlite.New", "database path is required", nil)
@ -125,7 +126,7 @@ func (m *Medium) Write(filePath, content string) error {
return m.WriteMode(filePath, content, 0644)
}
// WriteMode saves the given content with explicit permissions.
// Example: _ = medium.WriteMode("keys/private.key", key, 0600)
func (m *Medium) WriteMode(filePath, content string, mode fs.FileMode) error {
key := normaliseEntryPath(filePath)
if key == "" {
@ -143,7 +144,7 @@ func (m *Medium) WriteMode(filePath, content string, mode fs.FileMode) error {
return nil
}
// EnsureDir makes sure a directory exists, creating it if necessary.
// Example: _ = medium.EnsureDir("config")
func (m *Medium) EnsureDir(filePath string) error {
key := normaliseEntryPath(filePath)
if key == "" {
@ -186,7 +187,7 @@ func (m *Medium) FileSet(filePath, content string) error {
return m.Write(filePath, content)
}
// Delete removes a file or empty directory.
// Example: _ = medium.Delete("config/app.yaml")
func (m *Medium) Delete(filePath string) error {
key := normaliseEntryPath(filePath)
if key == "" {
@ -231,7 +232,7 @@ func (m *Medium) Delete(filePath string) error {
return nil
}
// DeleteAll removes a file or directory and all its contents recursively.
// Example: _ = medium.DeleteAll("config")
func (m *Medium) DeleteAll(filePath string) error {
key := normaliseEntryPath(filePath)
if key == "" {
@ -255,7 +256,7 @@ func (m *Medium) DeleteAll(filePath string) error {
return nil
}
// Rename moves a file or directory from oldPath to newPath.
// Example: _ = medium.Rename("drafts/todo.txt", "archive/todo.txt")
func (m *Medium) Rename(oldPath, newPath string) error {
oldKey := normaliseEntryPath(oldPath)
newKey := normaliseEntryPath(newPath)
@ -353,7 +354,7 @@ func (m *Medium) Rename(oldPath, newPath string) error {
return tx.Commit()
}
// List returns the directory entries for the given path.
// Example: entries, _ := medium.List("config")
func (m *Medium) List(filePath string) ([]fs.DirEntry, error) {
prefix := normaliseEntryPath(filePath)
if prefix != "" {

View file

@ -10,7 +10,10 @@ import (
coreio "dappco.re/go/core/io"
)
// Medium wraps a Store to satisfy the io.Medium interface.
// Example: medium, _ := store.NewMedium(store.Options{Path: "config.db"})
// _ = medium.Write("app/theme", "midnight")
// entries, _ := medium.List("app")
//
// Paths are mapped as group/key - the first segment is the group,
// the rest is the key. List("") returns groups as directories,
// List("group") returns keys as files.
@ -20,10 +23,8 @@ type Medium struct {
var _ coreio.Medium = (*Medium)(nil)
// NewMedium exposes a Store as an io.Medium.
//
// medium, _ := store.NewMedium(store.Options{Path: "config.db"})
// _ = medium.Write("app/theme", "midnight")
// Example: medium, _ := store.NewMedium(store.Options{Path: "config.db"})
// _ = medium.Write("app/theme", "midnight")
func NewMedium(options Options) (*Medium, error) {
store, err := New(options)
if err != nil {
@ -77,12 +78,12 @@ func (m *Medium) Write(entryPath, content string) error {
return m.store.Set(group, key, content)
}
// WriteMode ignores the requested mode because key-value entries do not store POSIX permissions.
// Example: _ = medium.WriteMode("app/theme", "midnight", 0600)
func (m *Medium) WriteMode(entryPath, content string, _ fs.FileMode) error {
return m.Write(entryPath, content)
}
// EnsureDir is a no-op — groups are created implicitly on Set.
// Example: _ = medium.EnsureDir("app")
func (m *Medium) EnsureDir(_ string) error {
return nil
}
@ -149,8 +150,7 @@ func (m *Medium) Rename(oldPath, newPath string) error {
return m.store.Delete(oldGroup, oldKey)
}
// List returns directory entries. Empty path returns groups.
// A group path returns keys in that group.
// Example: entries, _ := medium.List("app")
func (m *Medium) List(entryPath string) ([]fs.DirEntry, error) {
group, key := splitGroupKeyPath(entryPath)
@ -187,7 +187,7 @@ func (m *Medium) List(entryPath string) ([]fs.DirEntry, error) {
return entries, nil
}
// Stat returns file info for a group (dir) or key (file).
// Example: info, _ := medium.Stat("app/theme")
func (m *Medium) Stat(entryPath string) (fs.FileInfo, error) {
group, key := splitGroupKeyPath(entryPath)
if group == "" {

View file

@ -13,21 +13,21 @@ import (
// NotFoundError is returned when a key does not exist in the store.
var NotFoundError = errors.New("key not found")
// Store is returned by New for grouped key/value access.
// Store is the grouped key/value database returned by New.
//
// keyValueStore, _ := store.New(store.Options{Path: ":memory:"})
type Store struct {
database *sql.DB
}
// Options configures New.
// Example: keyValueStore, _ := store.New(store.Options{Path: ":memory:"})
type Options struct {
// Path is the SQLite database path. Use ":memory:" for tests.
Path string
}
// New opens a SQLite-backed key/value store.
//
// keyValueStore, _ := store.New(store.Options{Path: ":memory:"})
// _ = keyValueStore.Set("app", "theme", "midnight")
// Example: keyValueStore, _ := store.New(store.Options{Path: ":memory:"})
// _ = keyValueStore.Set("app", "theme", "midnight")
func New(options Options) (*Store, error) {
if options.Path == "" {
return nil, core.E("store.New", "database path is required", fs.ErrInvalid)

View file

@ -11,7 +11,9 @@ import (
"dappco.re/go/core/io"
)
// Workspace is the interface returned by New.
// Workspace is the workspace service interface returned by New.
//
// service, _ := workspace.New(workspace.Options{Core: core.New(), Crypt: cryptProvider})
type Workspace interface {
CreateWorkspace(identifier, password string) (string, error)
SwitchWorkspace(workspaceID string) error
@ -24,7 +26,7 @@ type CryptProvider interface {
CreateKeyPair(name, passphrase string) (string, error)
}
// Options configures New.
// Example: service, _ := workspace.New(workspace.Options{Core: core.New(), Crypt: cryptProvider})
type Options struct {
// Core is the Core runtime used by the service.
Core *core.Core
@ -32,7 +34,7 @@ type Options struct {
Crypt CryptProvider
}
// Service is the concrete Workspace implementation.
// Service is the Workspace implementation returned by New.
type Service struct {
core *core.Core
crypt CryptProvider
@ -44,10 +46,8 @@ type Service struct {
var _ Workspace = (*Service)(nil)
// New creates an encrypted workspace service.
//
// service, _ := workspace.New(workspace.Options{Core: core.New(), Crypt: cryptProvider})
// workspaceID, _ := service.CreateWorkspace("alice", "pass123")
// Example: service, _ := workspace.New(workspace.Options{Core: core.New(), Crypt: cryptProvider})
// workspaceID, _ := service.CreateWorkspace("alice", "pass123")
func New(options Options) (*Service, error) {
home := resolveWorkspaceHomeDirectory()
if home == "" {