reactive_graph_graph/instances/flows/
flow_instance.rs

1use crate::EntityInstance;
2use crate::EntityInstances;
3#[cfg(any(test, feature = "test"))]
4use crate::EntityType;
5use crate::EntityTypeId;
6use crate::JSON_SCHEMA_ID_URI_PREFIX;
7use crate::NamespacedTypeGetter;
8use crate::RelationInstances;
9use crate::TYPE_ID_TYPE_SEPARATOR;
10use crate::TypeDefinition;
11use crate::TypeDefinitionGetter;
12use crate::instances::named::NamedInstanceContainer;
13use const_format::formatcp;
14use dashmap::DashMap;
15use dashmap::iter::OwningIter;
16#[cfg(any(test, feature = "test"))]
17use default_test::DefaultTest;
18#[cfg(any(test, feature = "test"))]
19use rand::Rng;
20#[cfg(any(test, feature = "test"))]
21use reactive_graph_utils_test::DefaultFrom;
22use schemars::JsonSchema;
23use schemars::Schema;
24use schemars::SchemaGenerator;
25use schemars::json_schema;
26use serde::Deserialize;
27use serde::Deserializer;
28use serde::Serialize;
29use serde::Serializer;
30use std::borrow::Cow;
31use std::cmp::Ordering;
32use std::fmt::Display;
33use std::fmt::Formatter;
34use std::hash::Hash;
35use std::hash::Hasher;
36use std::ops::Deref;
37use std::ops::DerefMut;
38use typed_builder::TypedBuilder;
39use uuid::Uuid;
40
41pub const JSON_SCHEMA_ID_FLOW_INSTANCE: &str = formatcp!("{}/flow-instance.schema.json", JSON_SCHEMA_ID_URI_PREFIX);
42
43/// A flow instance is a container for entity instances and relation instances.
44///
45/// A flow instance is strictly associated with a wrapper entity instance. The properties
46/// of the wrapper entity instance are the properties of the flow.
47///
48/// Additionally, flows can be nested -  from the perspective of the outer flow
49/// the inner flow acts like an entity instance. The wrapper entity instance of
50/// the inner flow is the interface which can be accessed by the outer flow.
51///
52/// Entity instances and relation instances can be shared with multiple flows.
53///
54/// It's even possible to connect entity instances from different flows with relation
55/// instances.
56///
57#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize, JsonSchema, TypedBuilder)]
58#[serde(tag = "$id", rename = "https://schema.reactive-graph.io/schema/json/flow-instance.schema.json")]
59#[schemars(
60    title = "FlowInstance",
61    deny_unknown_fields,
62    extend("$id" = JSON_SCHEMA_ID_FLOW_INSTANCE),
63    transform = add_json_schema_id_property
64)]
65pub struct FlowInstance {
66    /// The id of the flow corresponds to the id of the wrapper entity instance
67    ///
68    /// This means the vector of entity instances must contain an instance with
69    /// the id of the flow.
70    pub id: Uuid,
71
72    /// The type definition of the entity type of the wrapper entity instance.
73    #[serde(flatten)]
74    #[builder(setter(into))]
75    pub ty: EntityTypeId,
76
77    /// The name of the flow instance.
78    #[serde(default = "String::new")]
79    #[builder(default, setter(into))]
80    pub name: String,
81
82    /// Textual description of the flow instance.
83    #[serde(default = "String::new")]
84    #[builder(default, setter(into))]
85    pub description: String,
86
87    /// The entity instances which are contained in this flow instance.
88    ///
89    /// It can't have a default because the wrapper entity instance must be
90    /// present in the list of entities.
91    #[serde(default = "EntityInstances::new", alias = "entities")]
92    #[builder(default, setter(into))]
93    pub entity_instances: EntityInstances,
94
95    /// The relation instances which are contained in this flow instance.
96    ///
97    /// By default, no relation instances are contained in this flow instance.
98    #[serde(default = "RelationInstances::new", alias = "relations")]
99    #[builder(default, setter(into))]
100    pub relation_instances: RelationInstances,
101}
102
103impl FlowInstance {
104    /// Constructs a new flow instance from the wrapper entity instance.
105    pub fn from_instance_with_name<S: Into<String>>(wrapper_entity_instance: EntityInstance, name: S) -> FlowInstance {
106        FlowInstance {
107            id: wrapper_entity_instance.id,
108            ty: wrapper_entity_instance.ty.clone(),
109            name: name.into(),
110            description: String::new(),
111            entity_instances: EntityInstances::new_with_instance(wrapper_entity_instance),
112            relation_instances: RelationInstances::new(),
113        }
114    }
115}
116
117impl NamedInstanceContainer for FlowInstance {
118    fn name(&self) -> String {
119        self.name.clone()
120    }
121
122    fn description(&self) -> String {
123        self.description.clone()
124    }
125}
126
127impl From<EntityInstance> for FlowInstance {
128    fn from(wrapper_entity_instance: EntityInstance) -> FlowInstance {
129        FlowInstance {
130            id: wrapper_entity_instance.id,
131            ty: wrapper_entity_instance.ty.clone(),
132            name: String::new(),
133            description: String::new(),
134            entity_instances: EntityInstances::new_with_instance(wrapper_entity_instance),
135            relation_instances: RelationInstances::new(),
136        }
137    }
138}
139
140impl NamespacedTypeGetter for FlowInstance {
141    fn namespace(&self) -> String {
142        self.ty.namespace()
143    }
144
145    fn type_name(&self) -> String {
146        self.ty.type_name()
147    }
148}
149
150impl TypeDefinitionGetter for FlowInstance {
151    fn type_definition(&self) -> TypeDefinition {
152        self.ty.type_definition()
153    }
154}
155impl PartialEq<Uuid> for FlowInstance {
156    fn eq(&self, id: &Uuid) -> bool {
157        self.id == *id
158    }
159}
160
161impl PartialOrd<Self> for FlowInstance {
162    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
163        Some(self.cmp(other))
164    }
165}
166
167impl Ord for FlowInstance {
168    fn cmp(&self, other: &Self) -> Ordering {
169        self.id.cmp(&other.id)
170    }
171}
172
173impl Display for FlowInstance {
174    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
175        write!(f, "{}{}{}", &self.ty, TYPE_ID_TYPE_SEPARATOR, self.id)
176    }
177}
178
179#[derive(Clone, Debug, Default)]
180pub struct FlowInstances(DashMap<Uuid, FlowInstance>);
181
182impl FlowInstances {
183    pub fn new() -> Self {
184        FlowInstances(DashMap::new())
185    }
186
187    pub fn new_with_instance<E: Into<FlowInstance>>(flow_instance: E) -> Self {
188        let flow_instances = FlowInstances::new();
189        flow_instances.push(flow_instance.into());
190        flow_instances
191    }
192
193    pub fn push<E: Into<FlowInstance>>(&self, flow_instance: E) {
194        let flow_instance = flow_instance.into();
195        self.0.insert(flow_instance.id, flow_instance);
196    }
197
198    pub fn to_vec(&self) -> Vec<FlowInstance> {
199        let mut items: Vec<_> = self.iter().map(|item| item.value().clone()).collect();
200        items.sort();
201        items
202    }
203
204    // TODO: deduplicate?
205    // pub fn get_type_ids(&self) -> FlowTypeIds {
206    //     self.iter().map(|flow_instance| flow_instance.ty.clone()).collect()
207    // }
208}
209
210impl Deref for FlowInstances {
211    type Target = DashMap<Uuid, FlowInstance>;
212
213    fn deref(&self) -> &Self::Target {
214        &self.0
215    }
216}
217
218impl DerefMut for FlowInstances {
219    fn deref_mut(&mut self) -> &mut Self::Target {
220        &mut self.0
221    }
222}
223
224impl IntoIterator for FlowInstances {
225    type Item = (Uuid, FlowInstance);
226    type IntoIter = OwningIter<Uuid, FlowInstance>;
227
228    fn into_iter(self) -> Self::IntoIter {
229        self.0.into_iter()
230    }
231}
232
233impl PartialEq for FlowInstances {
234    fn eq(&self, other: &Self) -> bool {
235        self.0.iter().all(|self_flow_instance| other.contains_key(&self_flow_instance.id))
236            && other.iter().all(|other_flow_instance| self.contains_key(&other_flow_instance.id))
237    }
238}
239
240impl Eq for FlowInstances {}
241
242impl Hash for FlowInstances {
243    fn hash<H: Hasher>(&self, state: &mut H) {
244        self.to_vec().hash(state);
245    }
246}
247
248impl Serialize for FlowInstances {
249    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
250    where
251        S: Serializer,
252    {
253        serializer.collect_seq(self.iter())
254    }
255}
256
257impl<'de> Deserialize<'de> for FlowInstances {
258    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
259    where
260        D: Deserializer<'de>,
261    {
262        Ok(Vec::<FlowInstance>::deserialize(deserializer)?.into())
263    }
264}
265
266impl JsonSchema for FlowInstances {
267    fn schema_name() -> Cow<'static, str> {
268        "FlowInstances".into()
269    }
270
271    fn json_schema(schema_generator: &mut SchemaGenerator) -> Schema {
272        let sub_schema: Schema = schema_generator.subschema_for::<FlowInstance>();
273        json_schema!({
274            "type": "array",
275            "items": sub_schema,
276            "description": "Flow Instances",
277        })
278    }
279}
280
281impl From<Vec<FlowInstance>> for FlowInstances {
282    fn from(flow_instances: Vec<FlowInstance>) -> Self {
283        Self(flow_instances.into_iter().map(|flow_instance| (flow_instance.id, flow_instance)).collect())
284    }
285}
286
287impl From<FlowInstances> for Vec<FlowInstance> {
288    fn from(flow_instances: FlowInstances) -> Self {
289        flow_instances.to_vec()
290    }
291}
292
293impl From<&FlowInstances> for Vec<FlowInstance> {
294    fn from(flow_instances: &FlowInstances) -> Self {
295        flow_instances.0.iter().map(|flow_instance| flow_instance.clone()).collect()
296    }
297}
298
299impl From<DashMap<Uuid, FlowInstance>> for FlowInstances {
300    fn from(flow_instances: DashMap<Uuid, FlowInstance>) -> Self {
301        Self(flow_instances)
302    }
303}
304
305impl From<&DashMap<Uuid, FlowInstance>> for FlowInstances {
306    fn from(flow_instances: &DashMap<Uuid, FlowInstance>) -> Self {
307        Self(flow_instances.clone())
308    }
309}
310
311impl From<FlowInstances> for DashMap<Uuid, FlowInstance> {
312    fn from(flow_instances: FlowInstances) -> Self {
313        flow_instances.0
314    }
315}
316
317impl FromIterator<FlowInstance> for FlowInstances {
318    fn from_iter<I: IntoIterator<Item = FlowInstance>>(iter: I) -> Self {
319        let flow_instances = Self::new();
320        for flow_instance in iter {
321            flow_instances.insert(flow_instance.id, flow_instance);
322        }
323        flow_instances
324    }
325}
326
327#[cfg(any(test, feature = "test"))]
328impl DefaultTest for FlowInstance {
329    fn default_test() -> Self {
330        let entity_type = EntityType::default_test();
331        let wrapper_entity_instance = EntityInstance::default_from(&entity_type);
332        let id = wrapper_entity_instance.id;
333
334        let entity_instances = EntityInstances::default_test();
335        entity_instances.push(wrapper_entity_instance);
336
337        FlowInstance::builder()
338            .ty(entity_type.ty.clone())
339            .id(id)
340            .entity_instances(entity_instances)
341            .build()
342    }
343}
344
345#[cfg(any(test, feature = "test"))]
346impl DefaultFrom<EntityType> for FlowInstance {
347    fn default_from(entity_type: &EntityType) -> Self {
348        Self::default_from(&EntityInstance::default_from(entity_type))
349    }
350}
351
352#[cfg(any(test, feature = "test"))]
353impl DefaultFrom<EntityInstance> for FlowInstance {
354    fn default_from(wrapper_entity_instance: &EntityInstance) -> Self {
355        FlowInstance::from(wrapper_entity_instance.clone())
356    }
357}
358
359#[cfg(any(test, feature = "test"))]
360impl DefaultTest for FlowInstances {
361    fn default_test() -> Self {
362        let flow_instances = FlowInstances::new();
363        let mut rng = rand::rng();
364        for _ in 0..rng.random_range(0..10) {
365            flow_instances.push(FlowInstance::default_test());
366        }
367        flow_instances
368    }
369}
370
371fn add_json_schema_id_property(schema: &mut Schema) {
372    crate::json_schema::add_json_schema_id_property(schema, JSON_SCHEMA_ID_FLOW_INSTANCE);
373}