package model import ( "database/sql" "errors" "fmt" ) type Ingredient struct { Id string `json:"id"` Index string `json:"index"` Amount string `json:"amount"` Unit string `json:"unit"` Type string `json:"type"` Step string `json:"step"` } func (i *Ingredient) Validate() error { var err error if i.Id != "" { err = isPositiveOrZeroInt(i.Id) if err != nil { return fmt.Errorf("Invalid ingredient ID: %w", err) } } err = isPositiveOrZeroInt(i.Index) if err != nil { return fmt.Errorf("Invalid ingredient index: %w", err) } if i.Amount != "" { err = isPositiveOrZeroFloat(i.Amount) if err != nil { return fmt.Errorf("Invalid ingredient amount: %w", err) } } err = isPositiveOrZeroInt(i.Step) if err != nil { return fmt.Errorf("Ingredient does not reference a valid step ID: %w", err) } return nil } func (i Ingredient) String() string { str := "" if i.Amount != "" { str += i.Amount + " " } if i.Unit != "" { str += i.Unit + " " } str += i.Type return str } func (i *Ingredient) Create(tx *sql.Tx) error { if i.Id != "" { return fmt.Errorf("Cannot create ingredient if ID is given") } err := i.Validate() if err != nil { return err } cmd := ` INSERT INTO ingredients ('index', amount, unit, 'type', step) VALUES (?, ?, ?, ?, ?) ` result, err := tx.Exec(cmd, i.Index, i.Amount, i.Unit, i.Type, i.Step) if err != nil { return err } id, err := result.LastInsertId() if err != nil { return err } i.Id = fmt.Sprint(id) return nil } func (i *Ingredient) Read(tx *sql.Tx) error { cmd := ` SELECT "index", amount, unit, "type", step FROM ingredients WHERE id = ? ` rows, err := tx.Query(cmd, i.Id) if err != nil { return err } defer rows.Close() if !rows.Next() { return sql.ErrNoRows } err = rows.Scan(&i.Index, &i.Amount, &i.Unit, &i.Type, &i.Step) if err != nil { return err } return i.Validate() } func (i *Ingredient) Update(tx *sql.Tx) error { err := i.Validate() if err != nil { return err } cmd := ` UPDATE ingredients SET index = ?, amount = ?, unit = ?, type = ?, step = ? WHERE id = ?` res, err := tx.Exec(cmd, i.Index, i.Amount, i.Unit, i.Type, i.Step, i.Id) if err != nil { return err } affected, err := res.RowsAffected() if err != nil { return err } if affected != 1 { return fmt.Errorf("Ingredient update affected %d rows instead of 1", affected) } return nil } func (i *Ingredient) Delete(tx *sql.Tx) error { cmd := ` DELETE FROM ingredients WHERE id = ? ` result, err := tx.Exec(cmd, i.Id) if err != nil { return err } rows, err := result.RowsAffected() if rows != 1 { return errors.New("Ingredient deletion did not affect exactly one row") } return nil }