Skip to main content

commerce_theory/
catalog.rs

1use 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}