package main import ( messages "git.tornberg.me/go-cart-actor/proto" ) // cart_state_mapper.go // // Utilities to translate between internal CartGrain state and the gRPC // (typed) protobuf representation CartState. This replaces the previous // JSON blob framing and enables type-safe replies over gRPC, as well as // internal reuse for HTTP handlers without an extra marshal / unmarshal // hop (you can marshal CartState directly for JSON responses if desired). // // Only the one‑way mapping (CartGrain -> CartState) is strictly required // for mutation / state replies. A reverse helper is included in case // future features (e.g. snapshot import, replay, or migration) need it. // ToCartState converts the in‑memory CartGrain into a protobuf CartState. func ToCartState(c *CartGrain) *messages.CartState { if c == nil { return nil } items := make([]*messages.CartItemState, 0, len(c.Items)) for _, it := range c.Items { if it == nil { continue } itemDiscountPerUnit := max(0, it.OrgPrice-it.Price) itemTotalDiscount := itemDiscountPerUnit * int64(it.Quantity) items = append(items, &messages.CartItemState{ Id: int64(it.Id), SourceItemId: int64(it.ItemId), Sku: it.Sku, Name: it.Name, UnitPrice: it.Price, Quantity: int32(it.Quantity), TotalPrice: it.TotalPrice, TotalTax: it.TotalTax, OrgPrice: it.OrgPrice, TaxRate: int32(it.TaxRate), TotalDiscount: itemTotalDiscount, Brand: it.Brand, Category: it.Category, Category2: it.Category2, Category3: it.Category3, Category4: it.Category4, Category5: it.Category5, Image: it.Image, ArticleType: it.ArticleType, SellerId: it.SellerId, SellerName: it.SellerName, Disclaimer: it.Disclaimer, Outlet: deref(it.Outlet), StoreId: deref(it.StoreId), Stock: int32(it.Stock), }) } deliveries := make([]*messages.DeliveryState, 0, len(c.Deliveries)) for _, d := range c.Deliveries { if d == nil { continue } itemIds := make([]int64, 0, len(d.Items)) for _, id := range d.Items { itemIds = append(itemIds, int64(id)) } var pp *messages.PickupPoint if d.PickupPoint != nil { // Copy to avoid accidental shared mutation (proto points are fine but explicit). pp = &messages.PickupPoint{ Id: d.PickupPoint.Id, Name: d.PickupPoint.Name, Address: d.PickupPoint.Address, City: d.PickupPoint.City, Zip: d.PickupPoint.Zip, Country: d.PickupPoint.Country, } } deliveries = append(deliveries, &messages.DeliveryState{ Id: int64(d.Id), Provider: d.Provider, Price: d.Price, ItemIds: itemIds, PickupPoint: pp, }) } return &messages.CartState{ CartId: c.Id.String(), Items: items, TotalPrice: c.TotalPrice, TotalTax: c.TotalTax, TotalDiscount: c.TotalDiscount, Deliveries: deliveries, PaymentInProgress: c.PaymentInProgress, OrderReference: c.OrderReference, PaymentStatus: c.PaymentStatus, } } // FromCartState merges a protobuf CartState into an existing CartGrain. // This is optional and primarily useful for snapshot import or testing. func FromCartState(cs *messages.CartState, g *CartGrain) *CartGrain { if cs == nil { return g } if g == nil { g = &CartGrain{} } g.Id = ToCartId(cs.CartId) g.TotalPrice = cs.TotalPrice g.TotalTax = cs.TotalTax g.TotalDiscount = cs.TotalDiscount g.PaymentInProgress = cs.PaymentInProgress g.OrderReference = cs.OrderReference g.PaymentStatus = cs.PaymentStatus // Items g.Items = g.Items[:0] for _, it := range cs.Items { if it == nil { continue } outlet := toPtr(it.Outlet) storeId := toPtr(it.StoreId) g.Items = append(g.Items, &CartItem{ Id: int(it.Id), ItemId: int(it.SourceItemId), Sku: it.Sku, Name: it.Name, Price: it.UnitPrice, Quantity: int(it.Quantity), TotalPrice: it.TotalPrice, TotalTax: it.TotalTax, OrgPrice: it.OrgPrice, TaxRate: int(it.TaxRate), Brand: it.Brand, Category: it.Category, Category2: it.Category2, Category3: it.Category3, Category4: it.Category4, Category5: it.Category5, Image: it.Image, ArticleType: it.ArticleType, SellerId: it.SellerId, SellerName: it.SellerName, Disclaimer: it.Disclaimer, Outlet: outlet, StoreId: storeId, Stock: StockStatus(it.Stock), // Tax, TaxRate already set via Price / Totals if needed }) if it.Id > int64(g.lastItemId) { g.lastItemId = int(it.Id) } } // Deliveries g.Deliveries = g.Deliveries[:0] for _, d := range cs.Deliveries { if d == nil { continue } intIds := make([]int, 0, len(d.ItemIds)) for _, id := range d.ItemIds { intIds = append(intIds, int(id)) } var pp *messages.PickupPoint if d.PickupPoint != nil { pp = &messages.PickupPoint{ Id: d.PickupPoint.Id, Name: d.PickupPoint.Name, Address: d.PickupPoint.Address, City: d.PickupPoint.City, Zip: d.PickupPoint.Zip, Country: d.PickupPoint.Country, } } g.Deliveries = append(g.Deliveries, &CartDelivery{ Id: int(d.Id), Provider: d.Provider, Price: d.Price, Items: intIds, PickupPoint: pp, }) if d.Id > int64(g.lastDeliveryId) { g.lastDeliveryId = int(d.Id) } } return g } // Helper to safely de-reference optional string pointers to value or "". func deref(p *string) string { if p == nil { return "" } return *p } func toPtr(s string) *string { if s == "" { return nil } return &s }