diff --git a/path.go b/path.go index e5b5f41..331ce00 100644 --- a/path.go +++ b/path.go @@ -1,190 +1,32 @@ package nasin -import "io" import "os" -import "io/fs" -import "strings" import "path/filepath" -// FS is Tomo's implementation of fs.FS. It provides access to a specific part -// of the filesystem. -type FS struct { - path string +// ApplicationUserDataDir returns the directory path where an application can +// store its user data files. If the directory does not exist, it will be +// created. +func ApplicationUserDataDir (app ApplicationDescription) (string, error) { + return userMkdirAll(app.ID, userDataDir) } -// FileWriter is a writable version of fs.File. -type FileWriter interface { - fs.File - io.Writer +// ApplicationUserConfigDir returns the directory path where an application can +// store its user configuration files. +func ApplicationUserConfigDir (app ApplicationDescription) (string, error) { + return userMkdirAll(app.ID, userConfigDir) } -// ApplicationUserDataFS returns an FS that an application can use to store user -// data files. -func ApplicationUserDataFS (app ApplicationDescription) (*FS, error) { - dataDir, err := userDataDir() - if err != nil { return nil, err } - return appFs(dataDir, app) +// ApplicationUserCacheDir returns the directory path where an application can +// store its user cache files. +func ApplicationUserCacheDir (app ApplicationDescription) (string, error) { + return userMkdirAll(app.ID, userCacheDir) } -// ApplicationUserConfigFS returns an FS that an application can use to store -// user configuration files. -func ApplicationUserConfigFS (app ApplicationDescription) (*FS, error) { - configDir, err := userConfigDir() - if err != nil { return nil, err } - return appFs(configDir, app) -} - -// ApplicationUserCacheFS returns an FS that an application can use to store -// user cache files. -func ApplicationUserCacheFS (app ApplicationDescription) (*FS, error) { - cacheDir, err := userCacheDir() - if err != nil { return nil, err } - return appFs(cacheDir, app) -} - -func pathErr (op, path string, err error) error { - return &fs.PathError { - Op: op, - Path: path, - Err: err, - } -} - -func appFs (root string, app ApplicationDescription) (*FS, error) { - // remove slashes - appid := app.ID - appid = strings.ReplaceAll(appid, "/", "-") - appid = strings.ReplaceAll(appid, "\\", "-") - - path := filepath.Join(root, appid) - - // ensure the directory actually exists - err := os.MkdirAll(path, 700) - if err != nil { return nil, err } - - return &FS { path: path }, nil -} - -func (this FS) subPath (name string) (string, error) { - if !fs.ValidPath(name) { return "", fs.ErrInvalid } - if strings.Contains(name, "/") { return "", fs.ErrInvalid } - return filepath.Join(this.path, name), nil -} - -// Open opens the named file. -func (this FS) Open (name string) (fs.File, error) { - path, err := this.subPath(name) - if err != nil { - return nil, pathErr("open", name, err) - } - - return os.Open(path) -} - -// Create creates or truncates the named file. -func (this FS) Create (name string) (FileWriter, error) { - path, err := this.subPath(name) - if err != nil { - return nil, pathErr("create", name, err) - } - - return os.Create(path) -} - -// OpenFile is the generalized open call; most users will use Open or Create -// instead. -func (this FS) OpenFile ( - name string, - flag int, - perm os.FileMode, -) ( - FileWriter, - error, -) { - path, err := this.subPath(name) - if err != nil { - return nil, pathErr("open", name, err) - } - - return os.OpenFile(path, flag, perm) -} - -// ReadDir reads the named directory and returns a list of directory entries -// sorted by filename. -func (this FS) ReadDir (name string) ([]fs.DirEntry, error) { - path, err := this.subPath(name) - if err != nil { - return nil, pathErr("readdir", name, err) - } - - return os.ReadDir(path) -} - -// ReadFile reads the named file and returns its contents. -// A successful call returns a nil error, not io.EOF. -// (Because ReadFile reads the whole file, the expected EOF -// from the final Read is not treated as an error to be reported.) -// -// The caller is permitted to modify the returned byte slice. -func (this FS) ReadFile (name string) ([]byte, error) { - path, err := this.subPath(name) - if err != nil { - return nil, pathErr("readfile", name, err) - } - - return os.ReadFile(path) -} - -// WriteFile writes data to the named file, creating it if necessary. -func (this FS) WriteFile (name string, data []byte, perm os.FileMode) error { - path, err := this.subPath(name) - if err != nil { - return pathErr("writefile", name, err) - } - - return os.WriteFile(path, data, perm) -} - -// Stat returns a FileInfo describing the file. -func (this FS) Stat (name string) (fs.FileInfo, error) { - path, err := this.subPath(name) - if err != nil { - return nil, pathErr("stat", name, err) - } - - return os.Stat(path) -} - -// Remove removes the named file or (empty) directory. -func (this FS) Remove (name string) error { - path, err := this.subPath(name) - if err != nil { - return pathErr("remove", name, err) - } - - return os.Remove(path) -} - -// RemoveAll removes name and any children it contains. -func (this FS) RemoveAll (name string) error { - path, err := this.subPath(name) - if err != nil { - return pathErr("removeall", name, err) - } - - return os.RemoveAll(path) -} - -// Rename renames (moves) oldname to newname. -func (this FS) Rename (oldname, newname string) error { - oldpath, err := this.subPath(oldname) - if err != nil { - return pathErr("rename", oldname, err) - } - newpath, err := this.subPath(newname) - if err != nil { - return pathErr("rename", newname, err) - } - - return os.Rename(oldpath, newpath) +func userMkdirAll (sub string, getter func () (string, error)) (string, error) { + path, err := getter() + if err != nil { return "", err } + path = filepath.Join(path, sub) + err = os.MkdirAll(path, 0700) + if err != nil { return "", err } + return path, nil }