213 lines
5.3 KiB
Go
213 lines
5.3 KiB
Go
package file
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"codeberg.org/mapleopentech/monorepo/native/desktop/maplefile/internal/domain/file"
|
|
"codeberg.org/mapleopentech/monorepo/native/desktop/maplefile/pkg/storage"
|
|
)
|
|
|
|
const (
|
|
fileKeyPrefix = "file:"
|
|
collectionFileIndex = "collection_file_index:"
|
|
statusFileIndex = "status_file_index:"
|
|
)
|
|
|
|
type repository struct {
|
|
storage storage.Storage
|
|
}
|
|
|
|
// ProvideRepository creates a new file repository for Wire
|
|
func ProvideRepository(storage storage.Storage) file.Repository {
|
|
return &repository{storage: storage}
|
|
}
|
|
|
|
func (r *repository) Create(f *file.File) error {
|
|
return r.save(f)
|
|
}
|
|
|
|
func (r *repository) Get(id string) (*file.File, error) {
|
|
key := fileKeyPrefix + id
|
|
data, err := r.storage.Get(key)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get file: %w", err)
|
|
}
|
|
if data == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
var f file.File
|
|
if err := json.Unmarshal(data, &f); err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal file: %w", err)
|
|
}
|
|
|
|
return &f, nil
|
|
}
|
|
|
|
func (r *repository) Update(f *file.File) error {
|
|
// Get existing file to clean up old indexes if collection changed
|
|
existing, err := r.Get(f.ID)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get existing file: %w", err)
|
|
}
|
|
if existing != nil {
|
|
// Clean up old collection index if collection changed
|
|
if existing.CollectionID != f.CollectionID {
|
|
oldIndexKey := collectionFileIndex + existing.CollectionID + ":" + existing.ID
|
|
_ = r.storage.Delete(oldIndexKey)
|
|
}
|
|
// Clean up old status index if status changed
|
|
if existing.SyncStatus != f.SyncStatus {
|
|
oldStatusKey := statusFileIndex + existing.SyncStatus.String() + ":" + existing.ID
|
|
_ = r.storage.Delete(oldStatusKey)
|
|
}
|
|
}
|
|
|
|
return r.save(f)
|
|
}
|
|
|
|
func (r *repository) Delete(id string) error {
|
|
// Get file first to remove indexes
|
|
f, err := r.Get(id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if f == nil {
|
|
return nil // Nothing to delete
|
|
}
|
|
|
|
// Delete collection index
|
|
collIndexKey := collectionFileIndex + f.CollectionID + ":" + id
|
|
if err := r.storage.Delete(collIndexKey); err != nil {
|
|
return fmt.Errorf("failed to delete collection index: %w", err)
|
|
}
|
|
|
|
// Delete status index
|
|
statusKey := statusFileIndex + f.SyncStatus.String() + ":" + id
|
|
if err := r.storage.Delete(statusKey); err != nil {
|
|
return fmt.Errorf("failed to delete status index: %w", err)
|
|
}
|
|
|
|
// Delete file
|
|
fileKey := fileKeyPrefix + id
|
|
if err := r.storage.Delete(fileKey); err != nil {
|
|
return fmt.Errorf("failed to delete file: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (r *repository) List() ([]*file.File, error) {
|
|
var files []*file.File
|
|
|
|
err := r.storage.Iterate(func(key, value []byte) error {
|
|
keyStr := string(key)
|
|
if strings.HasPrefix(keyStr, fileKeyPrefix) {
|
|
var f file.File
|
|
if err := json.Unmarshal(value, &f); err != nil {
|
|
return fmt.Errorf("failed to unmarshal file: %w", err)
|
|
}
|
|
files = append(files, &f)
|
|
}
|
|
return nil
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to list files: %w", err)
|
|
}
|
|
|
|
return files, nil
|
|
}
|
|
|
|
func (r *repository) ListByCollection(collectionID string) ([]*file.File, error) {
|
|
var files []*file.File
|
|
prefix := collectionFileIndex + collectionID + ":"
|
|
|
|
err := r.storage.Iterate(func(key, value []byte) error {
|
|
keyStr := string(key)
|
|
if strings.HasPrefix(keyStr, prefix) {
|
|
// Extract file ID from index key
|
|
fileID := strings.TrimPrefix(keyStr, prefix)
|
|
f, err := r.Get(fileID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if f != nil {
|
|
files = append(files, f)
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to list files by collection: %w", err)
|
|
}
|
|
|
|
return files, nil
|
|
}
|
|
|
|
func (r *repository) ListByStatus(status file.SyncStatus) ([]*file.File, error) {
|
|
var files []*file.File
|
|
prefix := statusFileIndex + status.String() + ":"
|
|
|
|
err := r.storage.Iterate(func(key, value []byte) error {
|
|
keyStr := string(key)
|
|
if strings.HasPrefix(keyStr, prefix) {
|
|
// Extract file ID from index key
|
|
fileID := strings.TrimPrefix(keyStr, prefix)
|
|
f, err := r.Get(fileID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if f != nil {
|
|
files = append(files, f)
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to list files by status: %w", err)
|
|
}
|
|
|
|
return files, nil
|
|
}
|
|
|
|
func (r *repository) Exists(id string) (bool, error) {
|
|
key := fileKeyPrefix + id
|
|
data, err := r.storage.Get(key)
|
|
if err != nil {
|
|
return false, fmt.Errorf("failed to check file existence: %w", err)
|
|
}
|
|
return data != nil, nil
|
|
}
|
|
|
|
// save persists the file and maintains indexes
|
|
func (r *repository) save(f *file.File) error {
|
|
data, err := json.Marshal(f)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to marshal file: %w", err)
|
|
}
|
|
|
|
// Save file by ID
|
|
fileKey := fileKeyPrefix + f.ID
|
|
if err := r.storage.Set(fileKey, data); err != nil {
|
|
return fmt.Errorf("failed to save file: %w", err)
|
|
}
|
|
|
|
// Create collection index (for ListByCollection)
|
|
collIndexKey := collectionFileIndex + f.CollectionID + ":" + f.ID
|
|
if err := r.storage.Set(collIndexKey, []byte(f.ID)); err != nil {
|
|
return fmt.Errorf("failed to create collection index: %w", err)
|
|
}
|
|
|
|
// Create status index (for ListByStatus)
|
|
statusKey := statusFileIndex + f.SyncStatus.String() + ":" + f.ID
|
|
if err := r.storage.Set(statusKey, []byte(f.ID)); err != nil {
|
|
return fmt.Errorf("failed to create status index: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|