package archive import ( "context" "io/fs" "strings" "time" "git.kmsign.ru/royalcat/tstor/src/vfs" ) var ArchiveFactories = map[string]vfs.FsFactory{ ".zip": func(ctx context.Context, sourcePath string, f vfs.File) (vfs.Filesystem, error) { stat, err := f.Info() if err != nil { return nil, err } return NewArchive(ctx, sourcePath, stat.Name(), f, stat.Size(), ZipLoader) }, ".rar": func(ctx context.Context, sourcePath string, f vfs.File) (vfs.Filesystem, error) { stat, err := f.Info() if err != nil { return nil, err } return NewArchive(ctx, sourcePath, stat.Name(), f, stat.Size(), RarLoader) }, ".7z": func(ctx context.Context, sourcePath string, f vfs.File) (vfs.Filesystem, error) { stat, err := f.Info() if err != nil { return nil, err } return NewArchive(ctx, sourcePath, stat.Name(), f, stat.Size(), SevenZipLoader) }, } type archiveLoader func(ctx context.Context, archivePath string, r vfs.File, size int64) (map[string]fileEntry, error) var _ vfs.Filesystem = &ArchiveFS{} type fileEntry struct { fs.FileInfo open func(ctx context.Context) (vfs.File, error) } type ArchiveFS struct { name string size int64 files map[string]fileEntry } // Rename implements Filesystem. func (a *ArchiveFS) Rename(ctx context.Context, oldpath string, newpath string) error { return vfs.ErrNotImplemented } // ModTime implements Filesystem. func (a *ArchiveFS) ModTime() time.Time { return time.Time{} } // Mode implements Filesystem. func (a *ArchiveFS) Mode() fs.FileMode { return fs.ModeDir } // Size implements Filesystem. func (a *ArchiveFS) Size() int64 { return int64(a.size) } // Sys implements Filesystem. func (a *ArchiveFS) Sys() any { return nil } // FsName implements Filesystem. func (a *ArchiveFS) FsName() string { return "archivefs" } func NewArchive(ctx context.Context, archivePath, name string, f vfs.File, size int64, loader archiveLoader) (*ArchiveFS, error) { archiveFiles, err := loader(ctx, archivePath, f, size) if err != nil { return nil, err } // TODO make optional singleDir := true for k := range archiveFiles { if !strings.HasPrefix(k, "/"+name+"/") { singleDir = false break } } files := make(map[string]fileEntry, len(archiveFiles)) for k, v := range archiveFiles { // TODO make optional if strings.Contains(k, "/__MACOSX/") { continue } if singleDir { k, _ = strings.CutPrefix(k, "/"+name) } files[k] = v } // FIXME configurable files["/.forcegallery"] = fileEntry{ FileInfo: vfs.NewFileInfo("/.forcegallery", 0, time.Time{}), open: func(ctx context.Context) (vfs.File, error) { return vfs.NewMemoryFile(".forcegallery", []byte{}), nil }, } return &ArchiveFS{ name: name, size: size, files: files, }, nil }