package db import ( "context" "database/sql" "errors" "strings" "github.com/jmoiron/sqlx" "golang.org/x/sync/errgroup" ) type System struct { ID string Title string Description string URL string License string Bugs string Changelog string Version string Manifest string Download string Background string PrimaryTokenAttribute string Availability int Socket bool Protected bool Exclusive bool PersistentStorage bool Locked bool Owned bool HasStorage bool Compatibility Compatibility Relationships Relationships DocumentTypes DocumentTypes Grid *Grid Esmodules []string Scripts []string Tags []string Authors []*Author Media []*Media Packs []*Pack Styles []*Style Languages []*Language PackFolders []*Folder } func (s *System) Query(data *InsertId[string]) { data.query = ` INSERT INTO system (setup_id, id, title, description, url, license, bugs, changelog, version, manifest, download, background, primary_token_attribute, availability, socket, protected, exclusive_, persistent_storage, locked, owned, has_storage) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21) ON CONFLICT(id) DO NOTHING` } func (s *System) ConnectGameQuery(data *InsertId[string]) { data.query = ` INSERT INTO game_to_systems (game_id, system_id) VALUES ($1, $2)` } func (s *System) InsertObjects(tx *sqlx.Tx) error { relId := InsertId[string]{id: s.ID, fieldName: "system_id"} group, ctx := errgroup.WithContext(context.Background()) InsertWithCtxParallel(group, ctx, tx, s.Compatibility, relId) InsertWithCtxParallel(group, ctx, tx, s.Relationships, relId) InsertWithCtxParallel(group, ctx, tx, s.DocumentTypes, relId) InsertWithCtxParallel(group, ctx, tx, s.Grid, relId) esModulesRelId := &InsertId[string]{id: s.ID, fieldName: "system_id", tableName: "es_modules"} InsertSimpleSliceParallel(group, tx, s.Esmodules, esModulesRelId) scriptRelId := &InsertId[string]{id: s.ID, fieldName: "system_id", tableName: "scripts"} InsertSimpleSliceParallel(group, tx, s.Scripts, scriptRelId) tagsRelId := &InsertId[string]{id: s.ID, fieldName: "system_id", tableName: "tags"} InsertSimpleSliceParallel(group, tx, s.Tags, tagsRelId) InsertSliceParallel(group, tx, s.Authors, relId) InsertSliceParallel(group, tx, s.Media, relId) InsertSliceParallel(group, tx, s.Styles, relId) InsertSliceParallel(group, tx, s.Languages, relId) InsertSliceParallel(group, tx, s.Packs, relId) InsertSliceParallel(group, tx, s.PackFolders, relId) err := group.Wait() if err != nil && !errors.Is(err, sql.ErrNoRows) { return err } return nil } func (s *System) ConnectGame(tx *sqlx.Tx, data *InsertId[string]) error { if data.query == "" { return ErrNoQuery } args := []any{data.id, s.ID} _, err := tx.Exec(data.query, args...) return err } func (s *System) Insert(tx *sqlx.Tx, data *InsertId[string]) error { if data.query == "" { return ErrNoQuery } var dataId *string if strings.EqualFold(data.fieldName, "setup_id") { dataId = &data.id } args := []any{dataId, s.ID, s.Title, s.Description, s.URL, s.License, s.Bugs, s.Changelog, s.Version, s.Manifest, s.Download, s.Background, s.PrimaryTokenAttribute, s.Availability, s.Socket, s.Protected, s.Exclusive, s.PersistentStorage, s.Locked, s.Owned, s.HasStorage} res, err := tx.Exec(data.query, args...) if err != nil { return err } rowsAffected, err := res.RowsAffected() if rowsAffected != 0 { err = s.InsertObjects(tx) if err != nil { return err } } if dataId == nil { dataCopy := *data s.ConnectGameQuery(&dataCopy) err = s.ConnectGame(tx, &dataCopy) } return err } func (s *System) ConnectGameCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error { if data.query == "" { return ErrNoQuery } args := []any{data.id, s.ID} _, err := tx.ExecContext(ctx, data.query, args...) return err } func (s *System) InsertCtx(ctx context.Context, tx *sqlx.Tx, data *InsertId[string]) error { if data.query == "" { return ErrNoQuery } var dataId *string if strings.EqualFold(data.fieldName, "setup_id") { dataId = &data.id } args := []any{dataId, s.ID, s.Title, s.Description, s.URL, s.License, s.Bugs, s.Changelog, s.Version, s.Manifest, s.Download, s.Background, s.PrimaryTokenAttribute, s.Availability, s.Socket, s.Protected, s.Exclusive, s.PersistentStorage, s.Locked, s.Owned, s.HasStorage} res, err := tx.ExecContext(ctx, data.query, args...) if err != nil { return err } rowsAffected, err := res.RowsAffected() if rowsAffected != 0 { err = s.InsertObjects(tx) if err != nil { return err } } if dataId == nil { dataCopy := *data s.ConnectGameQuery(&dataCopy) err = s.ConnectGameCtx(ctx, tx, &dataCopy) } return err }