package history import ( "encoding/json" "errors" "fmt" "io/fs" "os" "slices" "sync" "time" "ddc/internal/files" ) const maxEntries = 500 type Entry struct { FileName string `json:"fileName"` SourceURL string `json:"sourceUrl"` Destination string `json:"destination"` Items []string `json:"items"` MovedAt time.Time `json:"movedAt"` } type Store struct { mutex sync.Mutex path string entries []Entry } func Open(path string) (*Store, error) { store := &Store{path: path, entries: []Entry{}} data, err := os.ReadFile(path) if errors.Is(err, fs.ErrNotExist) { return store, nil } if err != nil { return nil, err } if err := json.Unmarshal(data, &store.entries); err != nil { return nil, fmt.Errorf("history file %s: %w", path, err) } if store.entries == nil { store.entries = []Entry{} } return store, nil } func (store *Store) List() []Entry { store.mutex.Lock() defer store.mutex.Unlock() return slices.Clone(store.entries) } func (store *Store) Add(entry Entry) error { store.mutex.Lock() defer store.mutex.Unlock() next := append([]Entry{entry}, store.entries...) if len(next) > maxEntries { next = next[:maxEntries] } return store.save(next) } func (store *Store) Clear() error { store.mutex.Lock() defer store.mutex.Unlock() return store.save([]Entry{}) } func (store *Store) save(entries []Entry) error { data, err := json.MarshalIndent(entries, "", " ") if err != nil { return err } if err := files.WriteAtomically(store.path, data); err != nil { return err } store.entries = entries return nil }