pcm_engine/
engine.rs

1//! Main Catalog Engine
2
3use crate::bundling::{validate_bundle, Bundle};
4use crate::eligibility::{is_eligible, EligibilityContext, EligibilityRule};
5use crate::pricing::{calculate_final_price, PricingContext, PricingRule};
6use crate::rules::{evaluate_rule, CatalogRule, RuleContext};
7use uuid::Uuid;
8
9/// Main Product Catalog Engine
10pub struct CatalogEngine {
11    pricing_rules: Vec<PricingRule>,
12    eligibility_rules: Vec<EligibilityRule>,
13    bundles: Vec<Bundle>,
14    catalog_rules: Vec<CatalogRule>,
15}
16
17impl CatalogEngine {
18    /// Create a new catalog engine
19    pub fn new() -> Self {
20        Self {
21            pricing_rules: Vec::new(),
22            eligibility_rules: Vec::new(),
23            bundles: Vec::new(),
24            catalog_rules: Vec::new(),
25        }
26    }
27
28    /// Add a pricing rule
29    pub fn add_pricing_rule(&mut self, rule: PricingRule) {
30        self.pricing_rules.push(rule);
31    }
32
33    /// Add an eligibility rule
34    pub fn add_eligibility_rule(&mut self, rule: EligibilityRule) {
35        self.eligibility_rules.push(rule);
36    }
37
38    /// Add a bundle
39    pub fn add_bundle(&mut self, bundle: Bundle) -> Result<(), String> {
40        validate_bundle(&bundle)?;
41        self.bundles.push(bundle);
42        Ok(())
43    }
44
45    /// Add a catalog rule
46    pub fn add_catalog_rule(&mut self, rule: CatalogRule) {
47        self.catalog_rules.push(rule);
48    }
49
50    /// Check if a product is eligible for a customer
51    pub fn check_eligibility(
52        &self,
53        product_offering_id: Uuid,
54        context: &EligibilityContext,
55    ) -> bool {
56        self.eligibility_rules
57            .iter()
58            .filter(|rule| rule.product_offering_id == product_offering_id)
59            .all(|rule| is_eligible(rule, context))
60    }
61
62    /// Calculate price for a product offering
63    pub fn calculate_price(
64        &self,
65        product_offering_id: Uuid,
66        context: &PricingContext,
67    ) -> Option<crate::pricing::Money> {
68        self.pricing_rules
69            .iter()
70            .find(|rule| rule.product_offering_id == product_offering_id)
71            .map(|rule| calculate_final_price(rule, context))
72    }
73
74    /// Get bundles for a product
75    pub fn get_bundles_for_product(&self, product_offering_id: Uuid) -> Vec<&Bundle> {
76        self.bundles
77            .iter()
78            .filter(|bundle| {
79                bundle
80                    .products
81                    .iter()
82                    .any(|bp| bp.product_offering_id == product_offering_id)
83            })
84            .collect()
85    }
86
87    /// Evaluate catalog rules for a given context
88    pub fn evaluate_rules(&self, context: &RuleContext) -> Vec<&CatalogRule> {
89        self.catalog_rules
90            .iter()
91            .filter(|rule| {
92                matches!(
93                    evaluate_rule(rule, context),
94                    crate::rules::RuleResult::Matched { .. }
95                )
96            })
97            .collect()
98    }
99}
100
101impl Default for CatalogEngine {
102    fn default() -> Self {
103        Self::new()
104    }
105}