feat(cli): add inventory tracking (add/list) with persistent json storage

This commit is contained in:
rsxri 2025-05-15 21:03:35 +01:00
parent 0de0686125
commit 33d0c7d073
5 changed files with 210 additions and 1 deletions

View 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))
}
}

View 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
}

View file

@ -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
View 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
View 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")
}