package storage import ( "encoding/json" "strings" "time" "gardomatic.kleiax.de/internal/platform/validate" ) // LocationModelInterface persists hierarchical locations within a garden. type LocationModelInterface interface { Insert(location Location) (Location, error) Get(gardenID, id int) (Location, error) GetAllForGarden(gardenID int) ([]Location, error) Update(gardenID int, location Location) (Location, error) Delete(gardenID, id int) error } // ValidateLocation applies persistence-independent location validation rules. func ValidateLocation(v *validate.Validator, location Location) { v.Check(strings.TrimSpace(location.Name) != "", "name", "must be provided") v.Check(len(location.Name) <= 500, "name", "must not be more than 500 bytes long") v.Check(len(location.Description) <= 10_000, "description", "must not be more than 10000 bytes long") v.Check(len(location.Kind) <= 100, "kind", "must not be more than 100 bytes long") v.Check(len(location.Attributes) == 0 || json.Valid(location.Attributes), "attributes", "must be valid JSON") if location.ParentID != nil { v.Check(*location.ParentID > 0, "parent_id", "must be a positive integer") v.Check(*location.ParentID != location.ID, "parent_id", "must not refer to the location itself") } if location.AreaSQM != nil { v.Check(*location.AreaSQM >= 0, "area_sqm", "must be zero or greater") } validateOptionalEnum(v, "sun_exposure", location.SunExposure, "sunny", "partial_shade", "shade") validateOptionalEnum(v, "soil_condition", location.SoilCondition, "dry", "moist", "boggy") validateOptionalEnum(v, "soil_reaction", location.SoilReaction, "alkaline", "acidic", "neutral") } // Location describes a physical place where plants can be assigned. type Location struct { ID int `json:"id"` GardenID int `json:"garden_id"` ParentID *int `json:"parent_id,omitempty"` Name string `json:"name"` Description string `json:"description"` ImageData string `json:"image_data,omitempty"` ImageID *int `json:"image_id,omitempty"` Kind string `json:"kind"` AreaSQM *float64 `json:"area_sqm,omitempty"` SunExposure *string `json:"sun_exposure,omitempty"` SoilCondition *string `json:"soil_condition,omitempty"` SoilReaction *string `json:"soil_reaction,omitempty"` Attributes json.RawMessage `json:"attributes"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` Version int `json:"version"` CreatedBy int `json:"created_by"` UpdatedBy int `json:"updated_by"` }