2022-07-13 06:30:54 +00:00
package file
import (
"context"
2023-03-21 20:57:26 +00:00
"fmt"
2022-07-13 06:30:54 +00:00
"io/fs"
2023-03-21 20:57:26 +00:00
"path/filepath"
2022-07-13 06:30:54 +00:00
"strconv"
2023-04-19 03:06:53 +00:00
"strings"
2022-07-13 06:30:54 +00:00
"time"
)
// FolderID represents an ID of a folder.
type FolderID int32
// String converts the ID to a string.
func ( i FolderID ) String ( ) string {
return strconv . Itoa ( int ( i ) )
}
// Folder represents a folder in the file system.
type Folder struct {
ID FolderID ` json:"id" `
DirEntry
Path string ` json:"path" `
ParentFolderID * FolderID ` json:"parent_folder_id" `
CreatedAt time . Time ` json:"created_at" `
UpdatedAt time . Time ` json:"updated_at" `
}
func ( f * Folder ) Info ( fs FS ) ( fs . FileInfo , error ) {
return f . info ( fs , f . Path )
}
2023-03-28 22:28:11 +00:00
type FolderFinder interface {
Find ( ctx context . Context , id FolderID ) ( * Folder , error )
}
2023-03-21 20:57:26 +00:00
// FolderPathFinder finds Folders by their path.
type FolderPathFinder interface {
FindByPath ( ctx context . Context , path string ) ( * Folder , error )
}
2022-07-13 06:30:54 +00:00
// FolderGetter provides methods to find Folders.
type FolderGetter interface {
2023-03-28 22:28:11 +00:00
FolderFinder
2023-03-21 20:57:26 +00:00
FolderPathFinder
2022-07-13 06:30:54 +00:00
FindByZipFileID ( ctx context . Context , zipFileID ID ) ( [ ] * Folder , error )
FindAllInPaths ( ctx context . Context , p [ ] string , limit , offset int ) ( [ ] * Folder , error )
FindByParentFolderID ( ctx context . Context , parentFolderID FolderID ) ( [ ] * Folder , error )
}
type FolderCounter interface {
CountAllInPaths ( ctx context . Context , p [ ] string ) ( int , error )
}
// FolderCreator provides methods to create Folders.
type FolderCreator interface {
Create ( ctx context . Context , f * Folder ) error
}
2023-03-21 20:57:26 +00:00
type FolderFinderCreator interface {
FolderPathFinder
FolderCreator
}
2022-07-13 06:30:54 +00:00
// FolderUpdater provides methods to update Folders.
type FolderUpdater interface {
Update ( ctx context . Context , f * Folder ) error
}
type FolderDestroyer interface {
Destroy ( ctx context . Context , id FolderID ) error
}
2022-08-08 00:48:02 +00:00
type FolderGetterDestroyer interface {
FolderGetter
FolderDestroyer
}
2022-07-13 06:30:54 +00:00
// FolderStore provides methods to find, create and update Folders.
type FolderStore interface {
FolderGetter
FolderCounter
FolderCreator
FolderUpdater
FolderDestroyer
}
2023-03-21 20:57:26 +00:00
// GetOrCreateFolderHierarchy gets the folder for the given path, or creates a folder hierarchy for the given path if one if no existing folder is found.
// Does not create any folders in the file system
func GetOrCreateFolderHierarchy ( ctx context . Context , fc FolderFinderCreator , path string ) ( * Folder , error ) {
// get or create folder hierarchy
folder , err := fc . FindByPath ( ctx , path )
if err != nil {
return nil , err
}
if folder == nil {
parentPath := filepath . Dir ( path )
parent , err := GetOrCreateFolderHierarchy ( ctx , fc , parentPath )
if err != nil {
return nil , err
}
now := time . Now ( )
folder = & Folder {
Path : path ,
ParentFolderID : & parent . ID ,
DirEntry : DirEntry {
// leave mod time empty for now - it will be updated when the folder is scanned
} ,
CreatedAt : now ,
UpdatedAt : now ,
}
if err = fc . Create ( ctx , folder ) ; err != nil {
return nil , fmt . Errorf ( "creating folder %s: %w" , path , err )
}
}
return folder , nil
}
2023-04-19 03:06:53 +00:00
// TransferZipFolderHierarchy creates the folder hierarchy for zipFileID under newPath, and removes
// ZipFileID from folders under oldPath.
func TransferZipFolderHierarchy ( ctx context . Context , folderStore FolderStore , zipFileID ID , oldPath string , newPath string ) error {
zipFolders , err := folderStore . FindByZipFileID ( ctx , zipFileID )
if err != nil {
return err
}
for _ , oldFolder := range zipFolders {
oldZfPath := oldFolder . Path
// sanity check - ignore folders which aren't under oldPath
if ! strings . HasPrefix ( oldZfPath , oldPath ) {
continue
}
relZfPath , err := filepath . Rel ( oldPath , oldZfPath )
if err != nil {
return err
}
newZfPath := filepath . Join ( newPath , relZfPath )
newFolder , err := GetOrCreateFolderHierarchy ( ctx , folderStore , newZfPath )
if err != nil {
return err
}
// add ZipFileID to new folder
newFolder . ZipFileID = & zipFileID
if err = folderStore . Update ( ctx , newFolder ) ; err != nil {
return err
}
// remove ZipFileID from old folder
oldFolder . ZipFileID = nil
if err = folderStore . Update ( ctx , oldFolder ) ; err != nil {
return err
}
}
return nil
}