package sqlite import ( "context" "fmt" "strings" ) type Pack struct { ID int64 PackID string Version string Checksum string Vendor string TargetHost string MinHostVersion string MaxHostVersion string ManifestJSON string } type PacksRepo struct { db execQuerier } func newPacksRepo(db execQuerier) *PacksRepo { return &PacksRepo{db: db} } func (r *PacksRepo) GetByID(ctx context.Context, id int64) (Pack, error) { if id <= 0 { return Pack{}, fmt.Errorf("id is required") } var pack Pack if err := r.db.QueryRowContext(ctx, `SELECT id, pack_id, version, checksum, vendor, target_host, min_host_version, max_host_version, manifest_json FROM packs WHERE id = ?`, id).Scan( &pack.ID, &pack.PackID, &pack.Version, &pack.Checksum, &pack.Vendor, &pack.TargetHost, &pack.MinHostVersion, &pack.MaxHostVersion, &pack.ManifestJSON, ); err != nil { return Pack{}, err } return pack, nil } func (r *PacksRepo) GetByPackID(ctx context.Context, packID string) (Pack, error) { packID = strings.TrimSpace(packID) if packID == "" { return Pack{}, fmt.Errorf("pack_id is required") } var pack Pack if err := r.db.QueryRowContext(ctx, `SELECT id, pack_id, version, checksum, vendor, target_host, min_host_version, max_host_version, manifest_json FROM packs WHERE pack_id = ?`, packID).Scan( &pack.ID, &pack.PackID, &pack.Version, &pack.Checksum, &pack.Vendor, &pack.TargetHost, &pack.MinHostVersion, &pack.MaxHostVersion, &pack.ManifestJSON, ); err != nil { return Pack{}, err } return pack, nil } func (r *PacksRepo) Create(ctx context.Context, pack Pack) (int64, error) { packID := strings.TrimSpace(pack.PackID) version := strings.TrimSpace(pack.Version) checksum := strings.TrimSpace(pack.Checksum) manifestJSON := strings.TrimSpace(pack.ManifestJSON) if manifestJSON == "" { manifestJSON = "{}" } switch { case packID == "": return 0, fmt.Errorf("pack_id is required") case version == "": return 0, fmt.Errorf("version is required") case checksum == "": return 0, fmt.Errorf("checksum is required") } result, err := r.db.ExecContext( ctx, `INSERT INTO packs (pack_id, version, checksum, vendor, target_host, min_host_version, max_host_version, manifest_json) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, packID, version, checksum, strings.TrimSpace(pack.Vendor), strings.TrimSpace(pack.TargetHost), strings.TrimSpace(pack.MinHostVersion), strings.TrimSpace(pack.MaxHostVersion), manifestJSON, ) if err != nil { return 0, fmt.Errorf("insert pack %q: %w", packID, err) } id, err := result.LastInsertId() if err != nil { return 0, fmt.Errorf("read inserted pack id for %q: %w", packID, err) } return id, nil } func (r *PacksRepo) ListAll(ctx context.Context) ([]Pack, error) { rows, err := r.db.QueryContext(ctx, `SELECT id, pack_id, version, checksum, vendor, target_host, min_host_version, max_host_version, manifest_json FROM packs ORDER BY id`) if err != nil { return nil, fmt.Errorf("list packs: %w", err) } defer rows.Close() var packs []Pack for rows.Next() { var pack Pack if err := rows.Scan( &pack.ID, &pack.PackID, &pack.Version, &pack.Checksum, &pack.Vendor, &pack.TargetHost, &pack.MinHostVersion, &pack.MaxHostVersion, &pack.ManifestJSON, ); err != nil { return nil, fmt.Errorf("scan pack: %w", err) } packs = append(packs, pack) } if err := rows.Err(); err != nil { return nil, fmt.Errorf("iterate packs: %w", err) } return packs, nil } func (r *PacksRepo) Upsert(ctx context.Context, pack Pack) (int64, error) { packID := strings.TrimSpace(pack.PackID) version := strings.TrimSpace(pack.Version) checksum := strings.TrimSpace(pack.Checksum) manifestJSON := strings.TrimSpace(pack.ManifestJSON) if manifestJSON == "" { manifestJSON = "{}" } switch { case packID == "": return 0, fmt.Errorf("pack_id is required") case version == "": return 0, fmt.Errorf("version is required") case checksum == "": return 0, fmt.Errorf("checksum is required") } result, err := r.db.ExecContext( ctx, `INSERT INTO packs (pack_id, version, checksum, vendor, target_host, min_host_version, max_host_version, manifest_json) VALUES (?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(pack_id) DO UPDATE SET version = excluded.version, checksum = excluded.checksum, vendor = excluded.vendor, target_host = excluded.target_host, min_host_version = excluded.min_host_version, max_host_version = excluded.max_host_version, manifest_json = excluded.manifest_json`, packID, version, checksum, strings.TrimSpace(pack.Vendor), strings.TrimSpace(pack.TargetHost), strings.TrimSpace(pack.MinHostVersion), strings.TrimSpace(pack.MaxHostVersion), manifestJSON, ) if err != nil { return 0, fmt.Errorf("upsert pack %q: %w", packID, err) } id, err := result.LastInsertId() if err == nil && id > 0 { return id, nil } persisted, getErr := r.GetByPackID(ctx, packID) if getErr != nil { if err != nil { return 0, fmt.Errorf("read upserted pack %q: %w", packID, getErr) } return 0, getErr } return persisted.ID, nil }