portal/api/s5/fs_dir.go

133 lines
2.5 KiB
Go

package s5
import (
"errors"
"io/fs"
"strings"
"git.lumeweb.com/LumeWeb/libs5-go/node"
"git.lumeweb.com/LumeWeb/libs5-go/encoding"
"git.lumeweb.com/LumeWeb/libs5-go/metadata"
)
var _ fs.FS = (*dirFs)(nil)
type dirFs struct {
root *encoding.CID
s5 *S5API
}
func (w *dirFs) Open(name string) (fs.File, error) {
file := w.s5.newFile(FileParams{
Hash: w.root.Hash.HashBytes(),
Type: w.root.Type,
})
manifest, err := file.Manifest()
if err != nil {
return nil, err
}
dir, ok := manifest.(*metadata.DirectoryMetadata)
if !ok {
return nil, errors.New("manifest is not a directory")
}
segments := strings.Split(name, "/")
if len(segments) == 1 {
return w.openDirectly(name, dir)
}
nextDirName := segments[0]
remainingPath := strings.Join(segments[1:], "/")
return w.openNestedDir(nextDirName, remainingPath, dir)
}
func (w *dirFs) openDirectly(name string, dir *metadata.DirectoryMetadata) (fs.File, error) {
file := dir.Files.Get(name)
subDir := dir.Directories.Get(name)
if file != nil {
return w.s5.newFile(FileParams{
Hash: file.File.CID().Hash.HashBytes(),
Type: file.File.CID().Type,
Name: file.Name,
}), nil
}
if subDir != nil {
cid, err := w.resolveDirCid(subDir)
if err != nil {
return nil, err
}
return w.s5.newFile(FileParams{
Hash: cid.Hash.HashBytes(),
Type: cid.Type,
Name: name,
}), nil
}
if name == "." {
return w.s5.newFile(FileParams{
Hash: w.root.Hash.HashBytes(),
Type: w.root.Type,
Name: name,
}), nil
}
return nil, fs.ErrNotExist
}
func (w dirFs) openNestedDir(name string, remainingPath string, dir *metadata.DirectoryMetadata) (fs.File, error) {
subDir := dir.Directories.Get(name)
if subDir == nil {
return nil, fs.ErrNotExist
}
cid, err := w.resolveDirCid(subDir)
if err != nil {
return nil, err
}
nestedFs := newDirFs(cid, w.s5)
return nestedFs.Open(remainingPath)
}
func (w *dirFs) resolveDirCid(dir *metadata.DirectoryReference) (*encoding.CID, error) {
return resolveDirCid(dir, w.s5.getNode())
}
func newDirFs(root *encoding.CID, s5 *S5API) *dirFs {
return &dirFs{
root: root,
s5: s5,
}
}
func resolveDirCid(dir *metadata.DirectoryReference, node *node.Node) (*encoding.CID, error) {
if len(dir.PublicKey) == 0 {
return nil, errors.New("missing public key")
}
entry, err := node.Services().Registry().Get(dir.PublicKey)
if err != nil {
return nil, err
}
cid, err := encoding.CIDFromRegistry(entry.Data())
if err != nil {
return nil, err
}
return cid, nil
}