Files
spellbook/internal/adapters/sqlite/sqlite.go
T
abelith 3673d318a1 feat
- sqlite adapter
2026-08-03 22:15:26 +05:00

137 lines
2.8 KiB
Go

package sqlite
import (
"context"
"database/sql"
"fmt"
"spellbook/internal/models"
"strings"
)
const schema = `
CREATE TABLE IF NOT EXISTS spells (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL UNIQUE,
spell TEXT NOT NULL,
short_desc TEXT NOT NULL DEFAULT '',
description TEXT NOT NULL DEFAULT '',
tags TEXT NOT NULL DEFAULT ''
);
`
type scanner interface {
Scan(dst ...any) error
}
type Repo struct {
db *sql.DB
}
func NewRepo(db *sql.DB) *Repo {
return &Repo{db: db}
}
func (r *Repo) Init(ctx context.Context) error {
if r.db == nil {
return fmt.Errorf("no db provided")
}
if _, err := r.db.ExecContext(ctx, schema); err != nil {
return err
}
return nil
}
func (r *Repo) Add(ctx context.Context, spell models.Spell) error {
const query = `INSERT INTO spells (name, spell, short_desc, description, tags) VALUES (?, ?, ?, ?, ?)`
tags := strings.Join(spell.Tags, ",")
_, err := r.db.ExecContext(
ctx, query,
spell.Name, spell.Spell,
spell.ShortDesc, spell.Description,
tags,
)
if err != nil {
return err
}
return nil
}
func (r *Repo) Delete(ctx context.Context, spellName string) error {
const query = `DELETE FROM spells WHERE name = ?`
if _, err := r.db.ExecContext(ctx, query, spellName); err != nil {
return err
}
return nil
}
func (r *Repo) Get(ctx context.Context, spellName string) (models.Spell, error) {
const query = `SELECT id, name, spell, short_desc, description, tags FROM spells WHERE name = ?`
row := r.db.QueryRowContext(ctx, query, spellName)
if err := row.Err(); err != nil {
return models.Spell{}, err
}
return scanSpell(row)
}
func (r *Repo) GetMany(ctx context.Context, query models.SearchQuery) ([]models.Spell, error) {
sqlQuery := `
SELECT id, name, spell, short_desc, description, tags
FROM spells
WHERE id > ?`
args := []any{query.Cursor}
for _, tag := range query.Tags {
if trimmed := strings.TrimSpace(tag); trimmed != "" {
sqlQuery += " AND tags LIKE ?"
args = append(args, "%"+trimmed+"%")
}
}
sqlQuery += " ORDER BY id ASC LIMIT ?"
limit := query.Limit
if limit <= 0 {
limit = 20
}
args = append(args, limit)
rows, err := r.db.QueryContext(ctx, sqlQuery, args...)
if err != nil {
return nil, err
}
spells := make([]models.Spell, 0)
for rows.Next() {
spell, err := scanSpell(rows)
if err != nil {
return nil, err
}
spells = append(spells, spell)
}
if err := rows.Err(); err != nil {
return nil, err
}
return spells, nil
}
func scanSpell(row scanner) (models.Spell, error) {
spell := models.Spell{}
tagsString := ""
if err := row.Scan(&spell.Id, &spell.Name, &spell.Spell, &spell.ShortDesc, &spell.Description, &tagsString); err != nil {
return models.Spell{}, err
}
if tagsString != "" {
spell.Tags = strings.Split(tagsString, ",")
}
return spell, nil
}