150 lines
2.5 KiB
Go
150 lines
2.5 KiB
Go
package database
|
|
|
|
import (
|
|
"bufio"
|
|
"crypto/rand"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"errors"
|
|
"os"
|
|
"sort"
|
|
"time"
|
|
)
|
|
|
|
var (
|
|
ErrNotFound = errors.New("entry not found")
|
|
)
|
|
|
|
type Database struct {
|
|
path string
|
|
entries map[string]*Entry
|
|
}
|
|
|
|
func Load(path string) (*Database, error) {
|
|
db := &Database{
|
|
path: path,
|
|
entries: make(map[string]*Entry),
|
|
}
|
|
|
|
file, err := os.Open(path)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return db, nil
|
|
}
|
|
return nil, err
|
|
}
|
|
defer file.Close()
|
|
|
|
scanner := bufio.NewScanner(file)
|
|
// Set larger buffer for potentially long clipboard entries
|
|
buf := make([]byte, 0, 64*1024)
|
|
scanner.Buffer(buf, 1024*1024) // 1MB max line size
|
|
|
|
for scanner.Scan() {
|
|
line := scanner.Text()
|
|
if line == "" {
|
|
continue
|
|
}
|
|
|
|
var entry Entry
|
|
if err := json.Unmarshal([]byte(line), &entry); err != nil {
|
|
continue
|
|
}
|
|
|
|
db.entries[entry.ID] = &entry
|
|
}
|
|
|
|
if err := scanner.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return db, nil
|
|
}
|
|
|
|
func (db *Database) Save() error {
|
|
file, err := os.Create(db.path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer file.Close()
|
|
|
|
for _, entry := range db.entries {
|
|
data, err := json.Marshal(entry)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if _, err := file.Write(data); err != nil {
|
|
return err
|
|
}
|
|
if _, err := file.WriteString("\n"); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func generateID() string {
|
|
bytes := make([]byte, 2) // 2 bytes = 4 hex chars
|
|
rand.Read(bytes)
|
|
return hex.EncodeToString(bytes)
|
|
}
|
|
|
|
func (db *Database) Add(value string) (*Entry, error) {
|
|
entry := &Entry{
|
|
ID: generateID(),
|
|
Value: value,
|
|
DateTime: time.Now(),
|
|
}
|
|
|
|
db.entries[entry.ID] = entry
|
|
return entry, db.Save()
|
|
}
|
|
|
|
func (db *Database) Get(id string) (*Entry, error) {
|
|
entry, exists := db.entries[id]
|
|
if !exists {
|
|
return nil, ErrNotFound
|
|
}
|
|
return entry, nil
|
|
}
|
|
|
|
func (db *Database) Delete(id string) error {
|
|
if _, exists := db.entries[id]; !exists {
|
|
return ErrNotFound
|
|
}
|
|
|
|
delete(db.entries, id)
|
|
return db.Save()
|
|
}
|
|
|
|
func (db *Database) List(limit int) []*Entry {
|
|
entries := make([]*Entry, 0, len(db.entries))
|
|
for _, entry := range db.entries {
|
|
entries = append(entries, entry)
|
|
}
|
|
|
|
// Sort by datetime descending (most recent first)
|
|
sort.Slice(entries, func(i, j int) bool {
|
|
return entries[i].DateTime.After(entries[j].DateTime)
|
|
})
|
|
|
|
if limit > 0 && limit < len(entries) {
|
|
entries = entries[:limit]
|
|
}
|
|
|
|
return entries
|
|
}
|
|
|
|
func (db *Database) MostRecent() *Entry {
|
|
entries := db.List(1)
|
|
if len(entries) == 0 {
|
|
return nil
|
|
}
|
|
return entries[0]
|
|
}
|
|
|
|
func (db *Database) Count() int {
|
|
return len(db.entries)
|
|
}
|