init commit
This commit is contained in:
149
internal/database/database.go
Normal file
149
internal/database/database.go
Normal file
@@ -0,0 +1,149 @@
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user