feat(cli): add inventory tracking (add/list) with persistent json storage
This commit is contained in:
parent
0de0686125
commit
33d0c7d073
44
src/pantry/core/inventory.go
Normal file
44
src/pantry/core/inventory.go
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"pantry/util"
|
||||
)
|
||||
|
||||
type InventoryItem struct {
|
||||
Name string
|
||||
Quantity float64
|
||||
Unit string
|
||||
Expiry *time.Time
|
||||
AddedAt time.Time
|
||||
}
|
||||
|
||||
type Inventory struct {
|
||||
Items []InventoryItem
|
||||
}
|
||||
|
||||
func (p *Inventory) AddItem(item InventoryItem) {
|
||||
for i, existing := range p.Items {
|
||||
if existing.Name == item.Name {
|
||||
p.Items[i].Quantity += item.Quantity
|
||||
fmt.Printf("Updated %s, new quantity: %.2f %s\n", item.Name, p.Items[i].Quantity, item.Unit)
|
||||
return
|
||||
}
|
||||
}
|
||||
p.Items = append(p.Items, item)
|
||||
fmt.Printf("Added %s, quantity: %.2f %s\n", item.Name, item.Quantity, item.Unit)
|
||||
}
|
||||
|
||||
func (p *Inventory) ListItems() {
|
||||
if len(p.Items) == 0 {
|
||||
fmt.Printf("Pantry is empty\n")
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Your pantry:\n")
|
||||
for _, item := range p.Items {
|
||||
fmt.Printf("- %s: %.2f %s, (%s)\n", item.Name, item.Quantity, item.Unit, util.DaysUntilExpiry(item.Expiry))
|
||||
}
|
||||
}
|
||||
39
src/pantry/core/storage.go
Normal file
39
src/pantry/core/storage.go
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func SaveInventory(filename string, inventory *Inventory) error {
|
||||
file, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create file: %s", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
encoder := json.NewEncoder(file)
|
||||
encoder.SetIndent("", " ")
|
||||
return encoder.Encode(inventory.Items)
|
||||
}
|
||||
|
||||
func LoadInventory(filename string) (*Inventory, error) {
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return &Inventory{}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("failed to open file: %s", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var items []InventoryItem
|
||||
decoder := json.NewDecoder(file)
|
||||
err = decoder.Decode(&items)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode inventory data: %w", err)
|
||||
}
|
||||
|
||||
return &Inventory{Items: items}, nil
|
||||
}
|
||||
|
|
@ -1 +1,82 @@
|
|||
package pantry
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"pantry/core"
|
||||
"pantry/util"
|
||||
)
|
||||
|
||||
func main() {
|
||||
dataFile := util.GetDefaultPath()
|
||||
err := os.MkdirAll(filepath.Dir(dataFile), 0755)
|
||||
if err != nil {
|
||||
fmt.Println("Failed to create data directory:", err)
|
||||
return
|
||||
}
|
||||
|
||||
inventory, err := core.LoadInventory(dataFile)
|
||||
if err != nil {
|
||||
fmt.Println("Failed to load inventory:", err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(os.Args) < 2 {
|
||||
fmt.Println("Usage: inventory [add|list]")
|
||||
return
|
||||
}
|
||||
|
||||
command := os.Args[1]
|
||||
|
||||
switch command {
|
||||
case "add":
|
||||
if len(os.Args) < 5 {
|
||||
fmt.Println("Usage: inventory add [name] [quantity] [unit] [expiry:YYYY-MM-DD]")
|
||||
return
|
||||
}
|
||||
|
||||
name := os.Args[2]
|
||||
|
||||
quantity, err := strconv.ParseFloat(os.Args[3], 64)
|
||||
if err != nil {
|
||||
fmt.Println("Invalid quantity")
|
||||
return
|
||||
}
|
||||
unit := os.Args[4]
|
||||
|
||||
var expiry *time.Time
|
||||
if len(os.Args) >= 6 {
|
||||
parsed, err := time.Parse("2006-01-02", os.Args[5])
|
||||
if err != nil {
|
||||
fmt.Println("Invalid expiry date format. Expected YYYY-MM-DD")
|
||||
return
|
||||
}
|
||||
expiry = &parsed
|
||||
}
|
||||
|
||||
item := core.InventoryItem{
|
||||
Name: name,
|
||||
Quantity: quantity,
|
||||
Unit: unit,
|
||||
Expiry: expiry,
|
||||
AddedAt: time.Now(),
|
||||
}
|
||||
|
||||
inventory.AddItem(item)
|
||||
err = core.SaveInventory(dataFile, inventory)
|
||||
if err != nil {
|
||||
fmt.Println("Failed to save inventory:", err)
|
||||
return
|
||||
}
|
||||
|
||||
case "list":
|
||||
inventory.ListItems()
|
||||
|
||||
default:
|
||||
fmt.Println("Unknown command:", command)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
23
src/pantry/util/dates.go
Normal file
23
src/pantry/util/dates.go
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// DaysUntilExpiry - takes expiry time and returns as a string.
|
||||
func DaysUntilExpiry(expiry *time.Time) string {
|
||||
if expiry == nil {
|
||||
return "no expiry date"
|
||||
}
|
||||
days := int(expiry.Sub(time.Now()).Hours() / 24)
|
||||
if days < 0 {
|
||||
return "expired"
|
||||
} else if days == 0 {
|
||||
return "expires today"
|
||||
} else if days == 1 {
|
||||
return "in 1 day"
|
||||
} else {
|
||||
return fmt.Sprintf("in %d days", days)
|
||||
}
|
||||
}
|
||||
22
src/pantry/util/paths.go
Normal file
22
src/pantry/util/paths.go
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
package util
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func GetDefaultPath() string {
|
||||
var base string
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
base = os.Getenv("APPDATA")
|
||||
} else {
|
||||
base = os.Getenv("XDG_CONFIG_HOME")
|
||||
if base == "" {
|
||||
base = filepath.Join(os.Getenv("HOME"), ".config")
|
||||
}
|
||||
}
|
||||
|
||||
return filepath.Join(base, "pantry", "inventory.json")
|
||||
}
|
||||
Loading…
Reference in a new issue