Artifact fe96a8aace77dc3379d12452dfea1b2c7737ef105fedf7c953b258e428bdeab0:
- File
simio/storage_mem.go
— part of check-in
[f5744359d3]
at
2026-04-19 16:30:54
on branch trunk
— Initial open source release
libfossil is a pure-Go implementation of the Fossil SCM, reading and writing the same .fossil SQLite repository format as upstream Fossil.
This is the first public release of code that previously lived in a private repository. The module path is github.com/danmestas/libfossil and the binary is named libfossil to avoid $PATH collisions with upstream fossil.
Included: - Repo lifecycle (create, clone), checkout/checkin, timeline, merge, diff, annotate, shun, tag, branch, bisect, stash, undo/redo - Sync-over-HTTP protocol (client and handler) - Two SQLite drivers: modernc (pure Go, default) and ncruces (wasm-capable) - Observer interfaces (SyncObserver, CheckoutObserver) with an optional OpenTelemetry implementation in observer/otel (separate submodule) - Deterministic simulation testing harness with BUGGIFY fault injection - Kong-based embeddable CLI under cli/ and a standalone binary under cmd/libfossil/ (user: [email protected], size: 6196) [annotate] [blame] [check-ins using]
package simio import ( "fmt" "io/fs" "os" "path/filepath" "sort" "strings" "time" ) // MemStorage is an in-memory Storage implementation for tests and browser fallback. type MemStorage struct { files map[string][]byte dirs map[string]bool mtimes map[string]time.Time } // NewMemStorage creates a new in-memory storage. func NewMemStorage() *MemStorage { return &MemStorage{ files: make(map[string][]byte), dirs: make(map[string]bool), mtimes: make(map[string]time.Time), } } // Stat returns file info for the given path. func (m *MemStorage) Stat(path string) (os.FileInfo, error) { if path == "" { panic("MemStorage.Stat: path must not be empty") } // Check files first if data, ok := m.files[path]; ok { return &memFileInfo{ name: filepath.Base(path), size: int64(len(data)), isDir: false, mtime: m.mtimes[path], }, nil } // Check directories if m.dirs[path] { return &memFileInfo{ name: filepath.Base(path), size: 0, isDir: true, }, nil } return nil, fmt.Errorf("stat %s: %w", path, os.ErrNotExist) } // Remove deletes the file or empty directory at the given path. func (m *MemStorage) Remove(path string) error { if path == "" { panic("MemStorage.Remove: path must not be empty") } if _, ok := m.files[path]; ok { delete(m.files, path) return nil } if m.dirs[path] { delete(m.dirs, path) return nil } return os.ErrNotExist } // MkdirAll creates the directory and all parent directories. func (m *MemStorage) MkdirAll(path string, perm os.FileMode) error { // Mark all parent paths as directories parts := strings.Split(path, string(filepath.Separator)) current := "" for _, part := range parts { if part == "" { continue } if current == "" { current = string(filepath.Separator) + part } else { current = filepath.Join(current, part) } m.dirs[current] = true } return nil } // ReadFile reads the file at the given path. Returns a copy of the data. func (m *MemStorage) ReadFile(path string) ([]byte, error) { if path == "" { panic("MemStorage.ReadFile: path must not be empty") } data, ok := m.files[path] if !ok { return nil, os.ErrNotExist } // Return a copy to prevent external modification result := make([]byte, len(data)) copy(result, data) return result, nil } // WriteFile writes data to the file at the given path. Stores a copy. func (m *MemStorage) WriteFile(path string, data []byte, perm os.FileMode) error { if path == "" { panic("MemStorage.WriteFile: path must not be empty") } if data == nil { panic("MemStorage.WriteFile: data must not be nil") } // Store a copy to prevent external modification stored := make([]byte, len(data)) copy(stored, data) m.files[path] = stored return nil } // ReadDir returns directory entries for the given path. func (m *MemStorage) ReadDir(path string) ([]fs.DirEntry, error) { if path == "" { panic("MemStorage.ReadDir: path must not be empty") } // Build prefix for scanning prefix := path if !strings.HasSuffix(prefix, string(filepath.Separator)) { prefix += string(filepath.Separator) } // Track unique direct children (files and subdirectories) children := make(map[string]bool) childIsDir := make(map[string]bool) // Scan files for filePath := range m.files { if strings.HasPrefix(filePath, prefix) { remainder := strings.TrimPrefix(filePath, prefix) if remainder == "" { continue // Skip exact match } // Extract first path component sepIdx := strings.Index(remainder, string(filepath.Separator)) var childName string if sepIdx == -1 { // Direct child file childName = remainder childIsDir[childName] = false } else { // File in subdirectory childName = remainder[:sepIdx] childIsDir[childName] = true } children[childName] = true } } // Scan dirs for empty subdirectories for dirPath := range m.dirs { if strings.HasPrefix(dirPath, prefix) && dirPath != path { remainder := strings.TrimPrefix(dirPath, prefix) if remainder == "" { continue } sepIdx := strings.Index(remainder, string(filepath.Separator)) var childName string if sepIdx == -1 { childName = remainder } else { childName = remainder[:sepIdx] } children[childName] = true childIsDir[childName] = true } } // Check if path exists (either as dir or has children) if len(children) == 0 && !m.dirs[path] { return nil, os.ErrNotExist } // Build sorted entry list entries := make([]fs.DirEntry, 0, len(children)) for name := range children { entries = append(entries, &memDirEntry{ name: name, isDir: childIsDir[name], }) } // Sort by name to match os.ReadDir behavior sort.Slice(entries, func(i, j int) bool { return entries[i].Name() < entries[j].Name() }) return entries, nil } // Chtimes sets the access and modification times of the named file. func (m *MemStorage) Chtimes(path string, atime, mtime time.Time) error { if path == "" { panic("MemStorage.Chtimes: path must not be empty") } if _, ok := m.files[path]; !ok { return fmt.Errorf("chtimes %s: %w", path, os.ErrNotExist) } m.mtimes[path] = mtime return nil } // memDirEntry implements fs.DirEntry for in-memory directory entries. type memDirEntry struct { name string isDir bool } func (e *memDirEntry) Name() string { return e.name } func (e *memDirEntry) IsDir() bool { return e.isDir } func (e *memDirEntry) Type() fs.FileMode { if e.isDir { return fs.ModeDir } return 0 } func (e *memDirEntry) Info() (fs.FileInfo, error) { return &memFileInfo{ name: e.name, size: 0, isDir: e.isDir, }, nil } // memFileInfo implements os.FileInfo for in-memory files. type memFileInfo struct { name string size int64 isDir bool mtime time.Time } func (f *memFileInfo) Name() string { return f.name } func (f *memFileInfo) Size() int64 { return f.size } func (f *memFileInfo) Mode() os.FileMode { if f.isDir { return os.ModeDir | 0755 } return 0644 } func (f *memFileInfo) ModTime() time.Time { return f.mtime } func (f *memFileInfo) IsDir() bool { return f.isDir } func (f *memFileInfo) Sys() any { return nil }