2024-10-18 07:26:11 +02:00
|
|
|
package sqlite
|
|
|
|
|
|
|
|
import (
|
2024-10-24 07:29:32 +02:00
|
|
|
"database/sql"
|
|
|
|
"fmt"
|
2024-10-18 07:26:11 +02:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"go-mod.ewintr.nl/planner/item"
|
|
|
|
)
|
|
|
|
|
2024-10-24 07:29:32 +02:00
|
|
|
type SqliteSync struct {
|
|
|
|
db *sql.DB
|
|
|
|
}
|
2024-10-18 07:26:11 +02:00
|
|
|
|
2024-10-24 07:29:32 +02:00
|
|
|
func NewSqliteSync(db *sql.DB) *SqliteSync {
|
|
|
|
return &SqliteSync{db: db}
|
2024-10-18 07:26:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *SqliteSync) FindAll() ([]item.Item, error) {
|
2024-10-24 07:29:32 +02:00
|
|
|
rows, err := s.db.Query("SELECT id, kind, updated, deleted, body FROM items")
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("%w: failed to query items: %v", ErrSqliteFailure, err)
|
|
|
|
}
|
|
|
|
defer rows.Close()
|
|
|
|
|
|
|
|
var items []item.Item
|
|
|
|
for rows.Next() {
|
|
|
|
var i item.Item
|
|
|
|
var updatedStr string
|
|
|
|
err := rows.Scan(&i.ID, &i.Kind, &updatedStr, &i.Deleted, &i.Body)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("%w: failed to scan item: %v", ErrSqliteFailure, err)
|
|
|
|
}
|
|
|
|
i.Updated, err = time.Parse(time.RFC3339, updatedStr)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("%w: failed to parse updated time: %v", ErrSqliteFailure, err)
|
|
|
|
}
|
|
|
|
items = append(items, i)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = rows.Err(); err != nil {
|
|
|
|
return nil, fmt.Errorf("%w: error iterating over rows: %v", ErrSqliteFailure, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return items, nil
|
2024-10-18 07:26:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *SqliteSync) Store(i item.Item) error {
|
2024-10-24 07:29:32 +02:00
|
|
|
// Ensure we have a valid time
|
|
|
|
if i.Updated.IsZero() {
|
|
|
|
i.Updated = time.Now()
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err := s.db.Exec(
|
|
|
|
"INSERT OR REPLACE INTO items (id, kind, updated, deleted, body) VALUES (?, ?, ?, ?, ?)",
|
|
|
|
i.ID,
|
|
|
|
i.Kind,
|
|
|
|
i.Updated.UTC().Format(time.RFC3339),
|
|
|
|
i.Deleted,
|
|
|
|
sql.NullString{String: i.Body, Valid: i.Body != ""}, // This allows empty string but not NULL
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("%w: failed to store item: %v", ErrSqliteFailure, err)
|
|
|
|
}
|
2024-10-18 07:26:11 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *SqliteSync) DeleteAll() error {
|
2024-10-24 07:29:32 +02:00
|
|
|
_, err := s.db.Exec("DELETE FROM items")
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("%w: failed to delete all items: %v", ErrSqliteFailure, err)
|
|
|
|
}
|
2024-10-18 07:26:11 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *SqliteSync) LastUpdate() (time.Time, error) {
|
2024-10-24 07:29:32 +02:00
|
|
|
var updatedStr sql.NullString
|
|
|
|
err := s.db.QueryRow("SELECT MAX(updated) FROM items").Scan(&updatedStr)
|
|
|
|
if err != nil {
|
|
|
|
return time.Time{}, fmt.Errorf("%w: failed to get last update: %v", ErrSqliteFailure, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !updatedStr.Valid {
|
|
|
|
return time.Time{}, nil // Return zero time if NULL or no rows
|
|
|
|
}
|
|
|
|
|
|
|
|
lastUpdate, err := time.Parse(time.RFC3339, updatedStr.String)
|
|
|
|
if err != nil {
|
|
|
|
return time.Time{}, fmt.Errorf("%w: failed to parse last update time: %v", ErrSqliteFailure, err)
|
|
|
|
}
|
|
|
|
return lastUpdate, nil
|
2024-10-18 07:26:11 +02:00
|
|
|
}
|