filesystem.go
package testmark
import (
"io/fs"
"path"
"strings"
"time"
)
// FS returns an fs.FS implementation that provides filesystem-like access
// to the testmark document structure.
func (d *Document) FS() fs.FS {
return &testmarkFS{
doc: d,
root: d.Root(),
}
}
// testmarkFS implements fs.FS using testmark document structure
type testmarkFS struct {
doc *Document
root *DirEntry
}
// Open implements fs.FS.Open
func (tfs *testmarkFS) Open(name string) (fs.File, error) {
if !fs.ValidPath(name) {
return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrInvalid}
}
entry := tfs.findEntry(name)
if entry == nil {
return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist}
}
return &testmarkFile{
entry: entry,
name: name,
}, nil
}
// findEntry locates a DirEntry by path
func (tfs *testmarkFS) findEntry(name string) *DirEntry {
if name == "." {
return tfs.root
}
parts := strings.Split(name, "/")
current := tfs.root
for _, part := range parts {
if part == "" || part == "." {
continue
}
child := current.Child(part)
if child == nil {
return nil
}
current = child
}
return current
}
// testmarkFile implements fs.File
type testmarkFile struct {
entry *DirEntry
name string
offset int64
}
// Stat implements fs.File.Stat
func (tf *testmarkFile) Stat() (fs.FileInfo, error) {
return &testmarkFileInfo{
entry: tf.entry,
name: path.Base(tf.name),
}, nil
}
// Read implements fs.File.Read
func (tf *testmarkFile) Read(b []byte) (int, error) {
if tf.entry.hunk == nil {
// Directory - cannot read
return 0, &fs.PathError{Op: "read", Path: tf.name, Err: fs.ErrInvalid}
}
body := tf.entry.hunk.Body()
if tf.offset >= int64(len(body)) {
return 0, fs.ErrClosed
}
n := copy(b, body[tf.offset:])
tf.offset += int64(n)
return n, nil
}
// Close implements fs.File.Close
func (tf *testmarkFile) Close() error {
return nil
}
// ReadDir implements fs.ReadDirFile.ReadDir
func (tf *testmarkFile) ReadDir(n int) ([]fs.DirEntry, error) {
if !tf.entry.IsDir() {
return nil, &fs.PathError{Op: "readdir", Path: tf.name, Err: fs.ErrInvalid}
}
var entries []fs.DirEntry
count := 0
for child := range tf.entry.Children() {
if n >= 0 && count >= n {
break
}
entries = append(entries, &testmarkDirEntry{entry: child})
count++
}
return entries, nil
}
// testmarkFileInfo implements fs.FileInfo
type testmarkFileInfo struct {
entry *DirEntry
name string
}
// Name implements fs.FileInfo.Name
func (tfi *testmarkFileInfo) Name() string {
return tfi.name
}
// Size implements fs.FileInfo.Size
func (tfi *testmarkFileInfo) Size() int64 {
if tfi.entry.hunk == nil {
return 0 // Directory size
}
return int64(len(tfi.entry.hunk.Body()))
}
// Mode implements fs.FileInfo.Mode
func (tfi *testmarkFileInfo) Mode() fs.FileMode {
if tfi.entry.IsDir() {
return fs.ModeDir | 0755
}
return 0644
}
// ModTime implements fs.FileInfo.ModTime
func (tfi *testmarkFileInfo) ModTime() time.Time {
return time.Time{} // No modification time available
}
// IsDir implements fs.FileInfo.IsDir
func (tfi *testmarkFileInfo) IsDir() bool {
return tfi.entry.IsDir()
}
// Sys implements fs.FileInfo.Sys
func (tfi *testmarkFileInfo) Sys() any {
return tfi.entry
}
// testmarkDirEntry implements fs.DirEntry
type testmarkDirEntry struct {
entry *DirEntry
}
// Name implements fs.DirEntry.Name
func (tde *testmarkDirEntry) Name() string {
return tde.entry.Name()
}
// IsDir implements fs.DirEntry.IsDir
func (tde *testmarkDirEntry) IsDir() bool {
return tde.entry.IsDir()
}
// Type implements fs.DirEntry.Type
func (tde *testmarkDirEntry) Type() fs.FileMode {
if tde.entry.IsDir() {
return fs.ModeDir
}
return 0
}
// Info implements fs.DirEntry.Info
func (tde *testmarkDirEntry) Info() (fs.FileInfo, error) {
return &testmarkFileInfo{
entry: tde.entry,
name: tde.entry.Name(),
}, nil
}
v2/filesystem.go · 3.8 KiB · b9084569f7