sea_core/primitives/
flow.rs

1use crate::ConceptId;
2use rust_decimal::Decimal;
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5use std::collections::HashMap;
6use uuid;
7
8const DEFAULT_NAMESPACE: &str = "default";
9
10/// Represents a transfer of a resource between two entities.
11///
12/// Flows are the "MOVEMENT" in enterprise models - they capture
13/// the transfer of resources from one entity to another.
14///
15/// # Examples
16///
17/// ```
18/// use sea_core::primitives::{Entity, Resource, Flow};
19/// use sea_core::units::unit_from_string;
20/// use rust_decimal::Decimal;
21///
22/// let warehouse = Entity::new_with_namespace("Warehouse".to_string(), "default".to_string());
23/// let factory = Entity::new_with_namespace("Factory".to_string(), "default".to_string());
24/// let product = Resource::new_with_namespace("Widget", unit_from_string("units"), "default".to_string());
25///
26/// let flow = Flow::new(
27///     product.id().clone(),
28///     warehouse.id().clone(),
29///     factory.id().clone(),
30///     Decimal::from(100)
31/// );
32///
33/// assert_eq!(flow.quantity(), Decimal::from(100));
34/// ```
35#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
36pub struct Flow {
37    id: ConceptId,
38    resource_id: ConceptId,
39    from_id: ConceptId,
40    to_id: ConceptId,
41    quantity: Decimal,
42    namespace: String,
43    attributes: HashMap<String, Value>,
44}
45
46impl Flow {
47    /// Creates a new Flow using the default namespace.
48    pub fn new(
49        resource_id: ConceptId,
50        from_id: ConceptId,
51        to_id: ConceptId,
52        quantity: Decimal,
53    ) -> Self {
54        Self::new_with_namespace(resource_id, from_id, to_id, quantity, DEFAULT_NAMESPACE)
55    }
56
57    /// Creates a new Flow with namespace.
58    pub fn new_with_namespace(
59        resource_id: ConceptId,
60        from_id: ConceptId,
61        to_id: ConceptId,
62        quantity: Decimal,
63        namespace: impl Into<String>,
64    ) -> Self {
65        let namespace = namespace.into();
66        // Use UUID v4 for flows to ensure uniqueness (flows are events, not concepts)
67        let id = ConceptId::from_uuid(uuid::Uuid::new_v4());
68
69        Self {
70            id,
71            resource_id,
72            from_id,
73            to_id,
74            quantity,
75            namespace,
76            attributes: HashMap::new(),
77        }
78    }
79
80    pub fn id(&self) -> &ConceptId {
81        &self.id
82    }
83    pub fn resource_id(&self) -> &ConceptId {
84        &self.resource_id
85    }
86    pub fn from_id(&self) -> &ConceptId {
87        &self.from_id
88    }
89    pub fn to_id(&self) -> &ConceptId {
90        &self.to_id
91    }
92    pub fn quantity(&self) -> Decimal {
93        self.quantity
94    }
95    pub fn namespace(&self) -> &str {
96        &self.namespace
97    }
98
99    pub fn set_attribute(&mut self, key: impl Into<String>, value: Value) {
100        self.attributes.insert(key.into(), value);
101    }
102
103    pub fn get_attribute(&self, key: &str) -> Option<&Value> {
104        self.attributes.get(key)
105    }
106
107    /// Returns all attributes as a reference.
108    pub fn attributes(&self) -> &HashMap<String, Value> {
109        &self.attributes
110    }
111}