commerce_theory/
catalog.rs1use crate::foundation::*;
2
3#[derive(Clone, Copy, Debug, PartialEq, Eq)]
4#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
5pub enum ProductStatus {
6 Draft,
7 Active,
8 Archived,
9 Discontinued,
10}
11
12domain_struct! {
13 pub struct Brand {
14 id: Id,
15 name: String,
16 }
17}
18
19domain_struct! {
20 pub struct Category {
21 id: Id,
22 name: String,
23 }
24}
25
26domain_struct! {
27 pub struct Product {
28 id: ProductId,
29 brand: Brand,
30 category: Category,
31 status: ProductStatus,
32 }
33}
34
35domain_struct! {
36 pub struct ProductVariant {
37 id: VariantId,
38 product_id: ProductId,
39 sku: Sku,
40 active: bool,
41 }
42}
43
44#[derive(Clone, Debug, PartialEq, Eq)]
45#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
46pub struct ProductCatalogEntry {
47 pub(crate) product: Product,
48 pub(crate) variant: ProductVariant,
49}
50
51impl ProductCatalogEntry {
52 pub fn try_new(product: Product, variant: ProductVariant) -> DomainResult<Self> {
53 if variant.product_id != product.id {
54 return Err(ValidationError::Invariant("variant must belong to product"));
55 }
56 Ok(Self { product, variant })
57 }
58
59 #[must_use]
60 pub const fn product(&self) -> &Product {
61 &self.product
62 }
63
64 #[must_use]
65 pub const fn variant(&self) -> &ProductVariant {
66 &self.variant
67 }
68}
69
70domain_struct! {
71 pub struct ImageAsset {
72 id: Id,
73 width: Nat,
74 height: Nat,
75 }
76}
77
78domain_struct! {
79 pub struct ListingContent {
80 title_length: Nat,
81 image_count: Nat,
82 required_attributes_filled: bool,
83 }
84}
85
86domain_struct! {
87 pub struct MarketplaceContentPolicy {
88 max_title_length: Nat,
89 min_image_count: Nat,
90 }
91}
92
93#[derive(Clone, Debug, PartialEq, Eq)]
94#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
95pub struct ValidListingContent {
96 pub(crate) content: ListingContent,
97 pub(crate) policy: MarketplaceContentPolicy,
98}
99
100impl ValidListingContent {
101 pub const fn try_new(
102 content: ListingContent,
103 policy: MarketplaceContentPolicy,
104 ) -> DomainResult<Self> {
105 if content.title_length > policy.max_title_length {
106 return Err(ValidationError::Invariant("listing title exceeds policy"));
107 }
108 if content.image_count < policy.min_image_count {
109 return Err(ValidationError::Invariant("listing has too few images"));
110 }
111 if !content.required_attributes_filled {
112 return Err(ValidationError::Invariant(
113 "listing attributes must be filled",
114 ));
115 }
116 Ok(Self { content, policy })
117 }
118
119 #[must_use]
120 pub const fn content(&self) -> &ListingContent {
121 &self.content
122 }
123
124 #[must_use]
125 pub const fn policy(&self) -> &MarketplaceContentPolicy {
126 &self.policy
127 }
128}