From 89af7644ba38c68b45f15aaee0727feb32ed0943 Mon Sep 17 00:00:00 2001 From: Sky Johnson Date: Fri, 8 Aug 2025 23:27:35 -0500 Subject: [PATCH] split builder components out --- internal/drops/builder.go | 88 +++++++++++++++++++ internal/drops/drops.go | 80 ----------------- internal/items/builder.go | 95 +++++++++++++++++++++ internal/items/items.go | 116 ++++--------------------- internal/monsters/builder.go | 117 +++++++++++++++++++++++++ internal/monsters/monsters.go | 156 ++++++---------------------------- 6 files changed, 339 insertions(+), 313 deletions(-) create mode 100644 internal/drops/builder.go create mode 100644 internal/items/builder.go create mode 100644 internal/monsters/builder.go diff --git a/internal/drops/builder.go b/internal/drops/builder.go new file mode 100644 index 0000000..8b6db8f --- /dev/null +++ b/internal/drops/builder.go @@ -0,0 +1,88 @@ +package drops + +import ( + "dk/internal/database" + "fmt" + + "zombiezen.com/go/sqlite" +) + +// Builder provides a fluent interface for creating drops +type Builder struct { + drop *Drop + db *database.DB +} + +// NewBuilder creates a new drop builder +func NewBuilder(db *database.DB) *Builder { + return &Builder{ + drop: &Drop{db: db}, + db: db, + } +} + +// WithName sets the drop name +func (b *Builder) WithName(name string) *Builder { + b.drop.Name = name + return b +} + +// WithLevel sets the drop level requirement +func (b *Builder) WithLevel(level int) *Builder { + b.drop.Level = level + return b +} + +// WithType sets the drop type +func (b *Builder) WithType(dropType int) *Builder { + b.drop.Type = dropType + return b +} + +// WithAtt sets the attributes +func (b *Builder) WithAtt(att string) *Builder { + b.drop.Att = att + return b +} + +// Create saves the drop to the database and returns it +func (b *Builder) Create() (*Drop, error) { + // Use a transaction to ensure we can get the ID + var drop *Drop + err := b.db.Transaction(func(tx *database.Tx) error { + query := `INSERT INTO drops (name, level, type, att) + VALUES (?, ?, ?, ?)` + + if err := tx.Exec(query, b.drop.Name, b.drop.Level, b.drop.Type, b.drop.Att); err != nil { + return fmt.Errorf("failed to insert drop: %w", err) + } + + // Get the last inserted ID within the same transaction + var lastID int + err := tx.Query("SELECT last_insert_rowid()", func(stmt *sqlite.Stmt) error { + lastID = stmt.ColumnInt(0) + return nil + }) + if err != nil { + return fmt.Errorf("failed to get last insert ID: %w", err) + } + + // Create the drop with the ID + drop = &Drop{ + ID: lastID, + Name: b.drop.Name, + Level: b.drop.Level, + Type: b.drop.Type, + Att: b.drop.Att, + db: b.db, + } + + return nil + }) + + if err != nil { + return nil, fmt.Errorf("failed to create drop: %w", err) + } + + return drop, nil +} diff --git a/internal/drops/drops.go b/internal/drops/drops.go index 666d356..2afd2f3 100644 --- a/internal/drops/drops.go +++ b/internal/drops/drops.go @@ -124,86 +124,6 @@ func ByType(db *database.DB, dropType int) ([]*Drop, error) { return drops, nil } -// Builder provides a fluent interface for creating drops -type Builder struct { - drop *Drop - db *database.DB -} - -// NewBuilder creates a new drop builder -func NewBuilder(db *database.DB) *Builder { - return &Builder{ - drop: &Drop{db: db}, - db: db, - } -} - -// WithName sets the drop name -func (b *Builder) WithName(name string) *Builder { - b.drop.Name = name - return b -} - -// WithLevel sets the drop level requirement -func (b *Builder) WithLevel(level int) *Builder { - b.drop.Level = level - return b -} - -// WithType sets the drop type -func (b *Builder) WithType(dropType int) *Builder { - b.drop.Type = dropType - return b -} - -// WithAtt sets the attributes -func (b *Builder) WithAtt(att string) *Builder { - b.drop.Att = att - return b -} - -// Create saves the drop to the database and returns it -func (b *Builder) Create() (*Drop, error) { - // Use a transaction to ensure we can get the ID - var drop *Drop - err := b.db.Transaction(func(tx *database.Tx) error { - query := `INSERT INTO drops (name, level, type, att) - VALUES (?, ?, ?, ?)` - - if err := tx.Exec(query, b.drop.Name, b.drop.Level, b.drop.Type, b.drop.Att); err != nil { - return fmt.Errorf("failed to insert drop: %w", err) - } - - // Get the last inserted ID within the same transaction - var lastID int - err := tx.Query("SELECT last_insert_rowid()", func(stmt *sqlite.Stmt) error { - lastID = stmt.ColumnInt(0) - return nil - }) - if err != nil { - return fmt.Errorf("failed to get last insert ID: %w", err) - } - - // Create the drop with the ID - drop = &Drop{ - ID: lastID, - Name: b.drop.Name, - Level: b.drop.Level, - Type: b.drop.Type, - Att: b.drop.Att, - db: b.db, - } - - return nil - }) - - if err != nil { - return nil, fmt.Errorf("failed to create drop: %w", err) - } - - return drop, nil -} - // Save updates an existing drop in the database func (d *Drop) Save() error { if d.ID == 0 { diff --git a/internal/items/builder.go b/internal/items/builder.go new file mode 100644 index 0000000..59e4121 --- /dev/null +++ b/internal/items/builder.go @@ -0,0 +1,95 @@ +package items + +import ( + "dk/internal/database" + "fmt" + + "zombiezen.com/go/sqlite" +) + +// Builder provides a fluent interface for creating items +type Builder struct { + item *Item + db *database.DB +} + +// NewBuilder creates a new item builder +func NewBuilder(db *database.DB) *Builder { + return &Builder{ + item: &Item{db: db}, + db: db, + } +} + +// WithType sets the item type +func (b *Builder) WithType(itemType int) *Builder { + b.item.Type = itemType + return b +} + +// WithName sets the item name +func (b *Builder) WithName(name string) *Builder { + b.item.Name = name + return b +} + +// WithValue sets the item value +func (b *Builder) WithValue(value int) *Builder { + b.item.Value = value + return b +} + +// WithAtt sets the item attack/defense value +func (b *Builder) WithAtt(att int) *Builder { + b.item.Att = att + return b +} + +// WithSpecial sets the item special attributes +func (b *Builder) WithSpecial(special string) *Builder { + b.item.Special = special + return b +} + +// Create saves the item to the database and returns it +func (b *Builder) Create() (*Item, error) { + // Use a transaction to ensure we can get the ID + var item *Item + err := b.db.Transaction(func(tx *database.Tx) error { + query := `INSERT INTO items (type, name, value, att, special) + VALUES (?, ?, ?, ?, ?)` + + if err := tx.Exec(query, b.item.Type, b.item.Name, b.item.Value, b.item.Att, b.item.Special); err != nil { + return fmt.Errorf("failed to insert item: %w", err) + } + + // Get the last inserted ID within the same transaction + var lastID int + err := tx.Query("SELECT last_insert_rowid()", func(stmt *sqlite.Stmt) error { + lastID = stmt.ColumnInt(0) + return nil + }) + if err != nil { + return fmt.Errorf("failed to get last insert ID: %w", err) + } + + // Create the item with the ID + item = &Item{ + ID: lastID, + Type: b.item.Type, + Name: b.item.Name, + Value: b.item.Value, + Att: b.item.Att, + Special: b.item.Special, + db: b.db, + } + + return nil + }) + + if err != nil { + return nil, fmt.Errorf("failed to create item: %w", err) + } + + return item, nil +} diff --git a/internal/items/items.go b/internal/items/items.go index a165515..98240f2 100644 --- a/internal/items/items.go +++ b/internal/items/items.go @@ -4,6 +4,7 @@ import ( "fmt" "dk/internal/database" + "zombiezen.com/go/sqlite" ) @@ -15,7 +16,7 @@ type Item struct { Value int `json:"value"` Att int `json:"att"` Special string `json:"special"` - + db *database.DB } @@ -29,7 +30,7 @@ const ( // Find retrieves an item by ID func Find(db *database.DB, id int) (*Item, error) { item := &Item{db: db} - + query := "SELECT id, type, name, value, att, special FROM items WHERE id = ?" err := db.Query(query, func(stmt *sqlite.Stmt) error { item.ID = stmt.ColumnInt(0) @@ -40,22 +41,22 @@ func Find(db *database.DB, id int) (*Item, error) { item.Special = stmt.ColumnText(5) return nil }, id) - + if err != nil { return nil, fmt.Errorf("failed to find item: %w", err) } - + if item.ID == 0 { return nil, fmt.Errorf("item with ID %d not found", id) } - + return item, nil } // All retrieves all items func All(db *database.DB) ([]*Item, error) { var items []*Item - + query := "SELECT id, type, name, value, att, special FROM items ORDER BY id" err := db.Query(query, func(stmt *sqlite.Stmt) error { item := &Item{ @@ -70,18 +71,18 @@ func All(db *database.DB) ([]*Item, error) { items = append(items, item) return nil }) - + if err != nil { return nil, fmt.Errorf("failed to retrieve all items: %w", err) } - + return items, nil } // ByType retrieves items by type func ByType(db *database.DB, itemType int) ([]*Item, error) { var items []*Item - + query := "SELECT id, type, name, value, att, special FROM items WHERE type = ? ORDER BY id" err := db.Query(query, func(stmt *sqlite.Stmt) error { item := &Item{ @@ -96,107 +97,20 @@ func ByType(db *database.DB, itemType int) ([]*Item, error) { items = append(items, item) return nil }, itemType) - + if err != nil { return nil, fmt.Errorf("failed to retrieve items by type: %w", err) } - + return items, nil } -// Builder provides a fluent interface for creating items -type Builder struct { - item *Item - db *database.DB -} - -// NewBuilder creates a new item builder -func NewBuilder(db *database.DB) *Builder { - return &Builder{ - item: &Item{db: db}, - db: db, - } -} - -// WithType sets the item type -func (b *Builder) WithType(itemType int) *Builder { - b.item.Type = itemType - return b -} - -// WithName sets the item name -func (b *Builder) WithName(name string) *Builder { - b.item.Name = name - return b -} - -// WithValue sets the item value -func (b *Builder) WithValue(value int) *Builder { - b.item.Value = value - return b -} - -// WithAtt sets the item attack/defense value -func (b *Builder) WithAtt(att int) *Builder { - b.item.Att = att - return b -} - -// WithSpecial sets the item special attributes -func (b *Builder) WithSpecial(special string) *Builder { - b.item.Special = special - return b -} - -// Create saves the item to the database and returns it -func (b *Builder) Create() (*Item, error) { - // Use a transaction to ensure we can get the ID - var item *Item - err := b.db.Transaction(func(tx *database.Tx) error { - query := `INSERT INTO items (type, name, value, att, special) - VALUES (?, ?, ?, ?, ?)` - - if err := tx.Exec(query, b.item.Type, b.item.Name, b.item.Value, b.item.Att, b.item.Special); err != nil { - return fmt.Errorf("failed to insert item: %w", err) - } - - // Get the last inserted ID within the same transaction - var lastID int - err := tx.Query("SELECT last_insert_rowid()", func(stmt *sqlite.Stmt) error { - lastID = stmt.ColumnInt(0) - return nil - }) - if err != nil { - return fmt.Errorf("failed to get last insert ID: %w", err) - } - - // Create the item with the ID - item = &Item{ - ID: lastID, - Type: b.item.Type, - Name: b.item.Name, - Value: b.item.Value, - Att: b.item.Att, - Special: b.item.Special, - db: b.db, - } - - return nil - }) - - if err != nil { - return nil, fmt.Errorf("failed to create item: %w", err) - } - - return item, nil -} - // Save updates an existing item in the database func (i *Item) Save() error { if i.ID == 0 { return fmt.Errorf("cannot save item without ID") } - + query := `UPDATE items SET type = ?, name = ?, value = ?, att = ?, special = ? WHERE id = ?` return i.db.Exec(query, i.Type, i.Name, i.Value, i.Att, i.Special, i.ID) } @@ -206,7 +120,7 @@ func (i *Item) Delete() error { if i.ID == 0 { return fmt.Errorf("cannot delete item without ID") } - + query := "DELETE FROM items WHERE id = ?" return i.db.Exec(query, i.ID) } @@ -238,4 +152,4 @@ func (i *Item) TypeName() string { default: return "Unknown" } -} \ No newline at end of file +} diff --git a/internal/monsters/builder.go b/internal/monsters/builder.go new file mode 100644 index 0000000..bd4679c --- /dev/null +++ b/internal/monsters/builder.go @@ -0,0 +1,117 @@ +package monsters + +import ( + "dk/internal/database" + "fmt" + + "zombiezen.com/go/sqlite" +) + +// Builder provides a fluent interface for creating monsters +type Builder struct { + monster *Monster + db *database.DB +} + +// NewBuilder creates a new monster builder +func NewBuilder(db *database.DB) *Builder { + return &Builder{ + monster: &Monster{db: db}, + db: db, + } +} + +// WithName sets the monster name +func (b *Builder) WithName(name string) *Builder { + b.monster.Name = name + return b +} + +// WithMaxHP sets the monster's maximum hit points +func (b *Builder) WithMaxHP(maxHP int) *Builder { + b.monster.MaxHP = maxHP + return b +} + +// WithMaxDmg sets the monster's maximum damage +func (b *Builder) WithMaxDmg(maxDmg int) *Builder { + b.monster.MaxDmg = maxDmg + return b +} + +// WithArmor sets the monster's armor value +func (b *Builder) WithArmor(armor int) *Builder { + b.monster.Armor = armor + return b +} + +// WithLevel sets the monster's level +func (b *Builder) WithLevel(level int) *Builder { + b.monster.Level = level + return b +} + +// WithMaxExp sets the monster's maximum experience reward +func (b *Builder) WithMaxExp(maxExp int) *Builder { + b.monster.MaxExp = maxExp + return b +} + +// WithMaxGold sets the monster's maximum gold reward +func (b *Builder) WithMaxGold(maxGold int) *Builder { + b.monster.MaxGold = maxGold + return b +} + +// WithImmunity sets the monster's immunity type +func (b *Builder) WithImmunity(immunity int) *Builder { + b.monster.Immune = immunity + return b +} + +// Create saves the monster to the database and returns it +func (b *Builder) Create() (*Monster, error) { + // Use a transaction to ensure we can get the ID + var monster *Monster + err := b.db.Transaction(func(tx *database.Tx) error { + query := `INSERT INTO monsters (name, max_hp, max_dmg, armor, level, max_exp, max_gold, immune) + VALUES (?, ?, ?, ?, ?, ?, ?, ?)` + + if err := tx.Exec(query, b.monster.Name, b.monster.MaxHP, b.monster.MaxDmg, b.monster.Armor, + b.monster.Level, b.monster.MaxExp, b.monster.MaxGold, b.monster.Immune); err != nil { + return fmt.Errorf("failed to insert monster: %w", err) + } + + // Get the last inserted ID within the same transaction + var lastID int + err := tx.Query("SELECT last_insert_rowid()", func(stmt *sqlite.Stmt) error { + lastID = stmt.ColumnInt(0) + return nil + }) + if err != nil { + return fmt.Errorf("failed to get last insert ID: %w", err) + } + + // Create the monster with the ID + monster = &Monster{ + ID: lastID, + Name: b.monster.Name, + MaxHP: b.monster.MaxHP, + MaxDmg: b.monster.MaxDmg, + Armor: b.monster.Armor, + Level: b.monster.Level, + MaxExp: b.monster.MaxExp, + MaxGold: b.monster.MaxGold, + Immune: b.monster.Immune, + db: b.db, + } + + return nil + }) + + if err != nil { + return nil, fmt.Errorf("failed to create monster: %w", err) + } + + return monster, nil +} diff --git a/internal/monsters/monsters.go b/internal/monsters/monsters.go index cdc65e1..7d0fad0 100644 --- a/internal/monsters/monsters.go +++ b/internal/monsters/monsters.go @@ -4,6 +4,7 @@ import ( "fmt" "dk/internal/database" + "zombiezen.com/go/sqlite" ) @@ -18,21 +19,21 @@ type Monster struct { MaxExp int `json:"max_exp"` MaxGold int `json:"max_gold"` Immune int `json:"immune"` - + db *database.DB } // Immunity constants for monster immunity types const ( - ImmuneNone = 0 - ImmuneHurt = 1 // Immune to Hurt spells - ImmuneSleep = 2 // Immune to Sleep spells + ImmuneNone = 0 + ImmuneHurt = 1 // Immune to Hurt spells + ImmuneSleep = 2 // Immune to Sleep spells ) // Find retrieves a monster by ID func Find(db *database.DB, id int) (*Monster, error) { monster := &Monster{db: db} - + query := "SELECT id, name, max_hp, max_dmg, armor, level, max_exp, max_gold, immune FROM monsters WHERE id = ?" err := db.Query(query, func(stmt *sqlite.Stmt) error { monster.ID = stmt.ColumnInt(0) @@ -46,22 +47,22 @@ func Find(db *database.DB, id int) (*Monster, error) { monster.Immune = stmt.ColumnInt(8) return nil }, id) - + if err != nil { return nil, fmt.Errorf("failed to find monster: %w", err) } - + if monster.ID == 0 { return nil, fmt.Errorf("monster with ID %d not found", id) } - + return monster, nil } // All retrieves all monsters func All(db *database.DB) ([]*Monster, error) { var monsters []*Monster - + query := "SELECT id, name, max_hp, max_dmg, armor, level, max_exp, max_gold, immune FROM monsters ORDER BY level, id" err := db.Query(query, func(stmt *sqlite.Stmt) error { monster := &Monster{ @@ -79,18 +80,18 @@ func All(db *database.DB) ([]*Monster, error) { monsters = append(monsters, monster) return nil }) - + if err != nil { return nil, fmt.Errorf("failed to retrieve all monsters: %w", err) } - + return monsters, nil } // ByLevel retrieves monsters by level func ByLevel(db *database.DB, level int) ([]*Monster, error) { var monsters []*Monster - + query := "SELECT id, name, max_hp, max_dmg, armor, level, max_exp, max_gold, immune FROM monsters WHERE level = ? ORDER BY id" err := db.Query(query, func(stmt *sqlite.Stmt) error { monster := &Monster{ @@ -108,18 +109,18 @@ func ByLevel(db *database.DB, level int) ([]*Monster, error) { monsters = append(monsters, monster) return nil }, level) - + if err != nil { return nil, fmt.Errorf("failed to retrieve monsters by level: %w", err) } - + return monsters, nil } // ByLevelRange retrieves monsters within a level range (inclusive) func ByLevelRange(db *database.DB, minLevel, maxLevel int) ([]*Monster, error) { var monsters []*Monster - + query := "SELECT id, name, max_hp, max_dmg, armor, level, max_exp, max_gold, immune FROM monsters WHERE level BETWEEN ? AND ? ORDER BY level, id" err := db.Query(query, func(stmt *sqlite.Stmt) error { monster := &Monster{ @@ -137,18 +138,18 @@ func ByLevelRange(db *database.DB, minLevel, maxLevel int) ([]*Monster, error) { monsters = append(monsters, monster) return nil }, minLevel, maxLevel) - + if err != nil { return nil, fmt.Errorf("failed to retrieve monsters by level range: %w", err) } - + return monsters, nil } // ByImmunity retrieves monsters by immunity type func ByImmunity(db *database.DB, immunityType int) ([]*Monster, error) { var monsters []*Monster - + query := "SELECT id, name, max_hp, max_dmg, armor, level, max_exp, max_gold, immune FROM monsters WHERE immune = ? ORDER BY level, id" err := db.Query(query, func(stmt *sqlite.Stmt) error { monster := &Monster{ @@ -166,129 +167,20 @@ func ByImmunity(db *database.DB, immunityType int) ([]*Monster, error) { monsters = append(monsters, monster) return nil }, immunityType) - + if err != nil { return nil, fmt.Errorf("failed to retrieve monsters by immunity: %w", err) } - + return monsters, nil } -// Builder provides a fluent interface for creating monsters -type Builder struct { - monster *Monster - db *database.DB -} - -// NewBuilder creates a new monster builder -func NewBuilder(db *database.DB) *Builder { - return &Builder{ - monster: &Monster{db: db}, - db: db, - } -} - -// WithName sets the monster name -func (b *Builder) WithName(name string) *Builder { - b.monster.Name = name - return b -} - -// WithMaxHP sets the monster's maximum hit points -func (b *Builder) WithMaxHP(maxHP int) *Builder { - b.monster.MaxHP = maxHP - return b -} - -// WithMaxDmg sets the monster's maximum damage -func (b *Builder) WithMaxDmg(maxDmg int) *Builder { - b.monster.MaxDmg = maxDmg - return b -} - -// WithArmor sets the monster's armor value -func (b *Builder) WithArmor(armor int) *Builder { - b.monster.Armor = armor - return b -} - -// WithLevel sets the monster's level -func (b *Builder) WithLevel(level int) *Builder { - b.monster.Level = level - return b -} - -// WithMaxExp sets the monster's maximum experience reward -func (b *Builder) WithMaxExp(maxExp int) *Builder { - b.monster.MaxExp = maxExp - return b -} - -// WithMaxGold sets the monster's maximum gold reward -func (b *Builder) WithMaxGold(maxGold int) *Builder { - b.monster.MaxGold = maxGold - return b -} - -// WithImmunity sets the monster's immunity type -func (b *Builder) WithImmunity(immunity int) *Builder { - b.monster.Immune = immunity - return b -} - -// Create saves the monster to the database and returns it -func (b *Builder) Create() (*Monster, error) { - // Use a transaction to ensure we can get the ID - var monster *Monster - err := b.db.Transaction(func(tx *database.Tx) error { - query := `INSERT INTO monsters (name, max_hp, max_dmg, armor, level, max_exp, max_gold, immune) - VALUES (?, ?, ?, ?, ?, ?, ?, ?)` - - if err := tx.Exec(query, b.monster.Name, b.monster.MaxHP, b.monster.MaxDmg, b.monster.Armor, - b.monster.Level, b.monster.MaxExp, b.monster.MaxGold, b.monster.Immune); err != nil { - return fmt.Errorf("failed to insert monster: %w", err) - } - - // Get the last inserted ID within the same transaction - var lastID int - err := tx.Query("SELECT last_insert_rowid()", func(stmt *sqlite.Stmt) error { - lastID = stmt.ColumnInt(0) - return nil - }) - if err != nil { - return fmt.Errorf("failed to get last insert ID: %w", err) - } - - // Create the monster with the ID - monster = &Monster{ - ID: lastID, - Name: b.monster.Name, - MaxHP: b.monster.MaxHP, - MaxDmg: b.monster.MaxDmg, - Armor: b.monster.Armor, - Level: b.monster.Level, - MaxExp: b.monster.MaxExp, - MaxGold: b.monster.MaxGold, - Immune: b.monster.Immune, - db: b.db, - } - - return nil - }) - - if err != nil { - return nil, fmt.Errorf("failed to create monster: %w", err) - } - - return monster, nil -} - // Save updates an existing monster in the database func (m *Monster) Save() error { if m.ID == 0 { return fmt.Errorf("cannot save monster without ID") } - + query := `UPDATE monsters SET name = ?, max_hp = ?, max_dmg = ?, armor = ?, level = ?, max_exp = ?, max_gold = ?, immune = ? WHERE id = ?` return m.db.Exec(query, m.Name, m.MaxHP, m.MaxDmg, m.Armor, m.Level, m.MaxExp, m.MaxGold, m.Immune, m.ID) } @@ -298,7 +190,7 @@ func (m *Monster) Delete() error { if m.ID == 0 { return fmt.Errorf("cannot delete monster without ID") } - + query := "DELETE FROM monsters WHERE id = ?" return m.db.Exec(query, m.ID) } @@ -356,4 +248,4 @@ func (m *Monster) GoldPerHP() float64 { return 0 } return float64(m.MaxGold) / float64(m.MaxHP) -} \ No newline at end of file +}