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