package index import ( "sort" "sync" "time" ) // Entry is the API/JSON shape the server returns. // StoredAt is RFC3339/RFC3339Nano in UTC. type Entry struct { Hash string `json:"hash"` Bytes int64 `json:"bytes"` StoredAt string `json:"stored_at"` // RFC3339( Nano ) string Private bool `json:"private"` CreatorTZ string `json:"creator_tz,omitempty"` // IANA TZ like "America/New_York" } // internal record with real time.Time for sorting/comparison. type rec struct { Hash string Bytes int64 StoredAt time.Time Private bool CreatorTZ string } // Index is an in-memory index keyed by hash. type Index struct { mu sync.RWMutex hash map[string]rec } // New creates an empty Index. func New() *Index { return &Index{ hash: make(map[string]rec), } } // Put inserts or replaces an entry. // e.StoredAt may be RFC3339( Nano ); if empty/invalid we use time.Now().UTC(). func (ix *Index) Put(e Entry) error { ix.mu.Lock() defer ix.mu.Unlock() t := parseWhen(e.StoredAt) if t.IsZero() { t = time.Now().UTC() } ix.hash[e.Hash] = rec{ Hash: e.Hash, Bytes: e.Bytes, StoredAt: t, Private: e.Private, CreatorTZ: e.CreatorTZ, } return nil } // Delete removes an entry by hash (no error if absent). func (ix *Index) Delete(hash string) error { ix.mu.Lock() defer ix.mu.Unlock() delete(ix.hash, hash) return nil } // List returns entries sorted by StoredAt descending. func (ix *Index) List() ([]Entry, error) { ix.mu.RLock() defer ix.mu.RUnlock() tmp := make([]rec, 0, len(ix.hash)) for _, r := range ix.hash { tmp = append(tmp, r) } sort.Slice(tmp, func(i, j int) bool { return tmp[i].StoredAt.After(tmp[j].StoredAt) }) out := make([]Entry, len(tmp)) for i, r := range tmp { out[i] = Entry{ Hash: r.Hash, Bytes: r.Bytes, StoredAt: r.StoredAt.UTC().Format(time.RFC3339Nano), Private: r.Private, CreatorTZ: r.CreatorTZ, } } return out, nil } // parseWhen tries RFC3339Nano then RFC3339; returns zero time on failure. func parseWhen(s string) time.Time { if s == "" { return time.Time{} } if t, err := time.Parse(time.RFC3339Nano, s); err == nil { return t } if t, err := time.Parse(time.RFC3339, s); err == nil { return t } return time.Time{} }