diff --git a/plan/storage/sqlite.go b/plan/storage/sqlite.go index 86c13e5..b889c1c 100644 --- a/plan/storage/sqlite.go +++ b/plan/storage/sqlite.go @@ -4,9 +4,7 @@ import ( "database/sql" "errors" "fmt" - "time" - "go-mod.ewintr.nl/planner/item" _ "modernc.org/sqlite" ) @@ -28,115 +26,31 @@ var ( ErrSqliteFailure = errors.New("sqlite returned an error") ) -type Sqlite struct { +type SqliteLocal struct { db *sql.DB } -func NewSqlite(dbPath string) (*Sqlite, error) { +func NewSqlites(dbPath string) (*SqliteLocal, *SqliteEvent, error) { db, err := sql.Open("sqlite", dbPath) if err != nil { - return &Sqlite{}, fmt.Errorf("%w: %v", ErrInvalidConfiguration, err) + return nil, nil, fmt.Errorf("%w: %v", ErrInvalidConfiguration, err) } - s := &Sqlite{ + sl := &SqliteLocal{ + db: db, + } + se := &SqliteEvent{ db: db, } - if err := s.migrate(migrations); err != nil { - return &Sqlite{}, err + if err := sl.migrate(migrations); err != nil { + return nil, nil, err } - return s, nil + return sl, se, nil } -func (s *Sqlite) Store(event item.Event) error { - if _, err := s.db.Exec(` -INSERT INTO events -(id, title, start, duration) -VALUES -(?, ?, ?, ?) -ON CONFLICT(id) DO UPDATE -SET -title=?, -start=?, -duration=?`, - event.ID, event.Title, event.Start.Format(timestampFormat), event.Duration.String(), - event.Title, event.Start.Format(timestampFormat), event.Duration.String()); err != nil { - return fmt.Errorf("%w: %v", ErrSqliteFailure, err) - } - return nil -} - -func (s *Sqlite) Find(id string) (item.Event, error) { - var event item.Event - var durStr string - err := s.db.QueryRow(` -SELECT id, title, start, duration -FROM events -WHERE id = ?`, id).Scan(&event.ID, &event.Title, &event.Start, &durStr) - switch { - case err == sql.ErrNoRows: - return item.Event{}, fmt.Errorf("event not found: %w", err) - case err != nil: - return item.Event{}, fmt.Errorf("%w: %v", ErrSqliteFailure, err) - } - dur, err := time.ParseDuration(durStr) - if err != nil { - return item.Event{}, fmt.Errorf("%w: %v", ErrSqliteFailure, err) - } - event.Duration = dur - - return event, nil -} - -func (s *Sqlite) FindAll() ([]item.Event, error) { - rows, err := s.db.Query(` -SELECT id, title, start, duration -FROM events`) - if err != nil { - return nil, fmt.Errorf("%w: %v", ErrSqliteFailure, err) - } - - result := make([]item.Event, 0) - defer rows.Close() - for rows.Next() { - var event item.Event - var durStr string - if err := rows.Scan(&event.ID, &event.Title, &event.Start, &durStr); err != nil { - return nil, fmt.Errorf("%w: %v", ErrSqliteFailure, err) - } - dur, err := time.ParseDuration(durStr) - if err != nil { - return nil, fmt.Errorf("%w: %v", ErrSqliteFailure, err) - } - event.Duration = dur - result = append(result, event) - } - - return result, nil -} - -func (s *Sqlite) Delete(id string) error { - result, err := s.db.Exec(` -DELETE FROM events -WHERE id = ?`, id) - if err != nil { - return fmt.Errorf("%w: %v", ErrSqliteFailure, err) - } - - rowsAffected, err := result.RowsAffected() - if err != nil { - return fmt.Errorf("%w: %v", ErrSqliteFailure, err) - } - - if rowsAffected == 0 { - return fmt.Errorf("event not found: %s", id) - } - - return nil -} - -func (s *Sqlite) migrate(wanted []string) error { +func (s *SqliteLocal) migrate(wanted []string) error { // admin table if _, err := s.db.Exec(` CREATE TABLE IF NOT EXISTS migration diff --git a/plan/storage/sqliteevent.go b/plan/storage/sqliteevent.go new file mode 100644 index 0000000..fa11792 --- /dev/null +++ b/plan/storage/sqliteevent.go @@ -0,0 +1,100 @@ +package storage + +import ( + "database/sql" + "fmt" + "time" + + "go-mod.ewintr.nl/planner/item" +) + +type SqliteEvent struct { + db *sql.DB +} + +func (s *SqliteEvent) Store(event item.Event) error { + if _, err := s.db.Exec(` +INSERT INTO events +(id, title, start, duration) +VALUES +(?, ?, ?, ?) +ON CONFLICT(id) DO UPDATE +SET +title=?, +start=?, +duration=?`, + event.ID, event.Title, event.Start.Format(timestampFormat), event.Duration.String(), + event.Title, event.Start.Format(timestampFormat), event.Duration.String()); err != nil { + return fmt.Errorf("%w: %v", ErrSqliteFailure, err) + } + return nil +} + +func (s *SqliteEvent) Find(id string) (item.Event, error) { + var event item.Event + var durStr string + err := s.db.QueryRow(` +SELECT id, title, start, duration +FROM events +WHERE id = ?`, id).Scan(&event.ID, &event.Title, &event.Start, &durStr) + switch { + case err == sql.ErrNoRows: + return item.Event{}, fmt.Errorf("event not found: %w", err) + case err != nil: + return item.Event{}, fmt.Errorf("%w: %v", ErrSqliteFailure, err) + } + dur, err := time.ParseDuration(durStr) + if err != nil { + return item.Event{}, fmt.Errorf("%w: %v", ErrSqliteFailure, err) + } + event.Duration = dur + + return event, nil +} + +func (s *SqliteEvent) FindAll() ([]item.Event, error) { + rows, err := s.db.Query(` +SELECT id, title, start, duration +FROM events`) + if err != nil { + return nil, fmt.Errorf("%w: %v", ErrSqliteFailure, err) + } + + result := make([]item.Event, 0) + defer rows.Close() + for rows.Next() { + var event item.Event + var durStr string + if err := rows.Scan(&event.ID, &event.Title, &event.Start, &durStr); err != nil { + return nil, fmt.Errorf("%w: %v", ErrSqliteFailure, err) + } + dur, err := time.ParseDuration(durStr) + if err != nil { + return nil, fmt.Errorf("%w: %v", ErrSqliteFailure, err) + } + event.Duration = dur + result = append(result, event) + } + + return result, nil +} + +func (s *SqliteEvent) Delete(id string) error { + result, err := s.db.Exec(` +DELETE FROM events +WHERE id = ?`, id) + if err != nil { + return fmt.Errorf("%w: %v", ErrSqliteFailure, err) + } + + rowsAffected, err := result.RowsAffected() + if err != nil { + return fmt.Errorf("%w: %v", ErrSqliteFailure, err) + } + + if rowsAffected == 0 { + return fmt.Errorf("event not found: %s", id) + } + + return nil +} diff --git a/plan/storage/storage.go b/plan/storage/storage.go index 335e220..00087db 100644 --- a/plan/storage/storage.go +++ b/plan/storage/storage.go @@ -6,6 +6,12 @@ import ( "go-mod.ewintr.nl/planner/item" ) +type LocalIDRepo interface { + FindAll() (map[string]int, error) + Store(id string, localID int) error + Delete(id string) error +} + type EventRepo interface { Store(event item.Event) error Find(id string) (item.Event, error)