package pocketbase import ( "database/sql" "encoding/json" "errors" "fmt" "github.com/pocketbase/dbx" "github.com/pocketbase/pocketbase/core" "git.vakhrushev.me/av/transcriber/internal/adapter/repo/pocketbase/migrations" "git.vakhrushev.me/av/transcriber/internal/entity" ) type TextRepository struct { app core.App } func NewTextRepository(app core.App) *TextRepository { return &TextRepository{app: app} } // Put кладёт текст записи, заменяя прежний того же вида. // // Замена, а не вставка: пара «запись и вид» уникальна, и повтор прерванного шага // иначе завёл бы второй комплект строк — тогда вопрос «какой текст отдавать // человеку» стал бы вопросом порядка записи, а не состояния. func (repo *TextRepository) Put(recordID, kind, contents string) (*entity.Text, error) { collection, err := findCollection(repo.app, migrations.TextsCollection) if err != nil { return nil, err } record, err := repo.app.FindFirstRecordByFilter( migrations.TextsCollection, "record = {:record} && kind = {:kind}", dbx.Params{"record": recordID, "kind": kind}, ) switch { case err == nil: // Строка есть — заменяем содержимое. case errors.Is(err, sql.ErrNoRows): record = core.NewRecord(collection) record.Set("record", recordID) record.Set("kind", kind) default: // Отказ хранилища «строкой нет» не является, и подменять его вставкой // нельзя: она упрётся в уникальный индекс, и наверх уедет жалоба на // запись вместо правды о недоступной базе. return nil, fmt.Errorf("failed to look up text of kind %s for record %s: %w", kind, recordID, err) } record.Set("contents", contents) if err := repo.app.Save(record); err != nil { // Текст расшифровки наружу не выходит даже отказом: цепочка `%w` от // хранилища несёт значение поля. return nil, fmt.Errorf("failed to store text of kind %s for record %s", kind, recordID) } return textFromRecord(record), nil } func (repo *TextRepository) GetByID(id string) (*entity.Text, error) { record, err := repo.app.FindRecordById(migrations.TextsCollection, id) if err != nil { return nil, fmt.Errorf("failed to get text %s: %w", id, err) } return textFromRecord(record), nil } func textFromRecord(record *core.Record) *entity.Text { return &entity.Text{ Id: record.Id, RecordID: record.GetString("record"), Kind: record.GetString("kind"), Contents: record.GetString("contents"), } } type StructureRepository struct { app core.App } func NewStructureRepository(app core.App) *StructureRepository { return &StructureRepository{app: app} } // Put кладёт структуру реплик, заменяя прежнюю той же версии разбора. Довод тот // же, что и у текста: повтор шага не должен заводить второй строки. func (repo *StructureRepository) Put(recordID string, version int, replicas []entity.Replica) (*entity.Structure, error) { collection, err := findCollection(repo.app, migrations.StructuresCollection) if err != nil { return nil, err } contents, err := json.Marshal(replicas) if err != nil { return nil, fmt.Errorf("failed to encode structure of record %s", recordID) } record, err := repo.app.FindFirstRecordByFilter( migrations.StructuresCollection, "record = {:record} && version = {:version}", dbx.Params{"record": recordID, "version": version}, ) switch { case err == nil: // Строка есть — заменяем содержимое. case errors.Is(err, sql.ErrNoRows): record = core.NewRecord(collection) record.Set("record", recordID) record.Set("version", version) default: return nil, fmt.Errorf("failed to look up structure of record %s: %w", recordID, err) } record.Set("contents", string(contents)) if err := repo.app.Save(record); err != nil { return nil, fmt.Errorf("failed to store structure of record %s", recordID) } return &entity.Structure{ Id: record.Id, RecordID: recordID, Version: version, Replicas: replicas, }, nil } func (repo *StructureRepository) GetByID(id string) (*entity.Structure, error) { record, err := repo.app.FindRecordById(migrations.StructuresCollection, id) if err != nil { return nil, fmt.Errorf("failed to get structure %s: %w", id, err) } var replicas []entity.Replica raw := record.GetString("contents") if raw != "" { if err := json.Unmarshal([]byte(raw), &replicas); err != nil { return nil, fmt.Errorf("failed to decode structure %s", id) } } return &entity.Structure{ Id: record.Id, RecordID: record.GetString("record"), Version: record.GetInt("version"), Replicas: replicas, }, nil }