Skip to main content

graphrecords_core/graphrecord/
schema.rs

1use super::{Attributes, EdgeIndex, GraphRecord, Group, NodeIndex};
2use crate::{
3    errors::GraphError,
4    graphrecord::{GraphRecordAttribute, datatypes::DataType},
5};
6use graphrecords_utils::aliases::GrHashMap;
7#[cfg(feature = "serde")]
8use serde::{Deserialize, Serialize};
9use std::{
10    collections::{HashMap, hash_map::Entry},
11    ops::Deref,
12};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
16pub enum AttributeType {
17    Categorical,
18    Continuous,
19    Temporal,
20    Unstructured,
21}
22
23impl AttributeType {
24    #[must_use]
25    pub fn infer(data_type: &DataType) -> Self {
26        match data_type {
27            DataType::String | DataType::Null | DataType::Any => Self::Unstructured,
28            DataType::Int | DataType::Float => Self::Continuous,
29            DataType::Bool => Self::Categorical,
30            DataType::DateTime | DataType::Duration => Self::Temporal,
31            DataType::Union((first_dataype, second_dataype)) => {
32                Self::infer(first_dataype).merge(Self::infer(second_dataype))
33            }
34            DataType::Option(dataype) => Self::infer(dataype),
35        }
36    }
37
38    const fn merge(self, other: Self) -> Self {
39        match (self, other) {
40            (Self::Categorical, Self::Unstructured) | (Self::Unstructured, Self::Categorical) => {
41                Self::Unstructured
42            }
43            (Self::Categorical, _) | (_, Self::Categorical) => Self::Categorical,
44            (Self::Continuous, Self::Continuous) => Self::Continuous,
45            (Self::Temporal, Self::Temporal) => Self::Temporal,
46            _ => Self::Unstructured,
47        }
48    }
49}
50
51impl DataType {
52    fn merge(&self, other: &Self) -> Self {
53        if self.evaluate(other) {
54            self.clone()
55        } else {
56            match (self, other) {
57                (Self::Null, _) => Self::Option(Box::new(other.clone())),
58                (_, Self::Null) => Self::Option(Box::new(self.clone())),
59                (_, Self::Any) => Self::Any,
60                (Self::Option(option1), Self::Option(option2)) => {
61                    Self::Option(Box::new(option1.merge(option2)))
62                }
63                (Self::Option(option), _) => Self::Option(Box::new(option.merge(other))),
64                (_, Self::Option(option)) => Self::Option(Box::new(self.merge(option))),
65                _ => Self::Union((Box::new(self.clone()), Box::new(other.clone()))),
66            }
67        }
68    }
69}
70
71#[derive(Debug, Clone, PartialEq)]
72#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
73pub struct AttributeDataType {
74    data_type: DataType,
75    attribute_type: AttributeType,
76}
77
78impl AttributeDataType {
79    fn validate(data_type: &DataType, attribute_type: AttributeType) -> Result<(), GraphError> {
80        match (attribute_type, data_type) {
81            (AttributeType::Categorical | AttributeType::Unstructured, _)
82            | (AttributeType::Continuous, DataType::Int | DataType::Float | DataType::Null)
83            | (AttributeType::Temporal, DataType::DateTime | DataType::Duration | DataType::Null) => {
84                Ok(())
85            }
86
87            (_, DataType::Option(option)) => Self::validate(option, attribute_type),
88            (_, DataType::Union((first_datatype, second_datatype))) => {
89                Self::validate(first_datatype, attribute_type)?;
90                Self::validate(second_datatype, attribute_type)
91            }
92
93            (AttributeType::Continuous, _) => Err(GraphError::SchemaError(
94                "Continuous attribute must be of (sub-)type Int or Float.".to_string(),
95            )),
96
97            (AttributeType::Temporal, _) => Err(GraphError::SchemaError(
98                "Temporal attribute must be of (sub-)type DateTime or Duration.".to_string(),
99            )),
100        }
101    }
102
103    pub fn new(data_type: DataType, attribute_type: AttributeType) -> Result<Self, GraphError> {
104        Self::validate(&data_type, attribute_type)?;
105
106        Ok(Self {
107            data_type,
108            attribute_type,
109        })
110    }
111
112    #[must_use]
113    pub const fn data_type(&self) -> &DataType {
114        &self.data_type
115    }
116
117    #[must_use]
118    pub const fn attribute_type(&self) -> &AttributeType {
119        &self.attribute_type
120    }
121
122    fn merge(&mut self, other: &Self) {
123        match (self.data_type.clone(), other.data_type.clone()) {
124            (DataType::Null, _) => {
125                self.data_type = self.data_type.merge(&other.data_type);
126                self.attribute_type = other.attribute_type;
127            }
128            (_, DataType::Null) => {
129                self.data_type = self.data_type.merge(&other.data_type);
130            }
131            _ => {
132                self.data_type = self.data_type.merge(&other.data_type);
133                self.attribute_type = self.attribute_type.merge(other.attribute_type);
134            }
135        }
136    }
137}
138
139impl From<DataType> for AttributeDataType {
140    fn from(value: DataType) -> Self {
141        let attribute_type = AttributeType::infer(&value);
142
143        Self {
144            data_type: value,
145            attribute_type,
146        }
147    }
148}
149
150impl From<(DataType, AttributeType)> for AttributeDataType {
151    fn from(value: (DataType, AttributeType)) -> Self {
152        Self {
153            data_type: value.0,
154            attribute_type: value.1,
155        }
156    }
157}
158
159#[derive(Debug, Clone, Copy)]
160enum AttributeSchemaKind<'a> {
161    Node(&'a NodeIndex),
162    Edge(&'a EdgeIndex),
163}
164
165impl AttributeSchemaKind<'_> {
166    fn error_message(&self, key: &GraphRecordAttribute, data_type: &DataType) -> String {
167        match self {
168            Self::Node(index) => {
169                format!("Attribute {key} of type {data_type} not found on node with index {index}")
170            }
171            Self::Edge(index) => {
172                format!("Attribute {key} of type {data_type} not found on edge with index {index}")
173            }
174        }
175    }
176
177    fn error_message_expected(
178        &self,
179        key: &GraphRecordAttribute,
180        data_type: &DataType,
181        expected_data_type: &DataType,
182    ) -> String {
183        match self {
184            Self::Node(index) => format!(
185                "Attribute {key} of node with index {index} is of type {data_type}. Expected {expected_data_type}."
186            ),
187            Self::Edge(index) => format!(
188                "Attribute {key} of edge with index {index} is of type {data_type}. Expected {expected_data_type}."
189            ),
190        }
191    }
192
193    fn error_message_too_many(&self, attributes: &[String]) -> String {
194        match self {
195            Self::Node(index) => format!(
196                "Attributes [{}] of node with index {} do not exist in schema.",
197                attributes.join(", "),
198                index
199            ),
200            Self::Edge(index) => format!(
201                "Attributes [{}] of edge with index {} do not exist in schema.",
202                attributes.join(", "),
203                index
204            ),
205        }
206    }
207}
208
209type AttributeSchemaMapping = HashMap<GraphRecordAttribute, AttributeDataType>;
210
211#[derive(Debug, Clone, PartialEq, Default)]
212#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
213pub struct AttributeSchema(AttributeSchemaMapping);
214
215impl Deref for AttributeSchema {
216    type Target = AttributeSchemaMapping;
217
218    fn deref(&self) -> &Self::Target {
219        &self.0
220    }
221}
222
223impl<T> From<T> for AttributeSchema
224where
225    T: Into<AttributeSchemaMapping>,
226{
227    fn from(value: T) -> Self {
228        Self(value.into())
229    }
230}
231
232impl AttributeSchema {
233    #[must_use]
234    pub const fn new(mapping: HashMap<GraphRecordAttribute, AttributeDataType>) -> Self {
235        Self(mapping)
236    }
237
238    fn validate(
239        &self,
240        attributes: &Attributes,
241        kind: &AttributeSchemaKind,
242    ) -> Result<(), GraphError> {
243        let mut matched_count = 0;
244        let mut attributes_not_in_schema = Vec::new();
245
246        for (key, value) in attributes {
247            match self.0.get(key) {
248                Some(schema) => {
249                    let data_type = DataType::from(value);
250
251                    if !schema.data_type.evaluate(&data_type) {
252                        return Err(GraphError::SchemaError(kind.error_message_expected(
253                            key,
254                            &data_type,
255                            &schema.data_type,
256                        )));
257                    }
258
259                    matched_count += 1;
260                }
261                None => {
262                    attributes_not_in_schema.push(key.to_string());
263                }
264            }
265        }
266
267        if matched_count < self.0.len() {
268            for (key, schema) in &self.0 {
269                if !attributes.contains_key(key) && !matches!(schema.data_type, DataType::Option(_))
270                {
271                    return Err(GraphError::SchemaError(
272                        kind.error_message(key, &schema.data_type),
273                    ));
274                }
275            }
276        }
277
278        if !attributes_not_in_schema.is_empty() {
279            return Err(GraphError::SchemaError(
280                kind.error_message_too_many(&attributes_not_in_schema),
281            ));
282        }
283
284        Ok(())
285    }
286
287    fn update(&mut self, attributes: &Attributes, empty: bool) {
288        for (attribute, data_type) in &mut self.0 {
289            if !attributes.contains_key(attribute) {
290                data_type.data_type = data_type.data_type.merge(&DataType::Null);
291            }
292        }
293
294        for (attribute, value) in attributes {
295            let data_type = DataType::from(value);
296            let attribute_type = AttributeType::infer(&data_type);
297
298            let mut attribute_data_type = AttributeDataType::new(data_type, attribute_type)
299                .expect("AttributeType was inferred from DataType.");
300
301            match self.0.entry(attribute.clone()) {
302                Entry::Occupied(entry) => {
303                    entry.into_mut().merge(&attribute_data_type);
304                }
305                Entry::Vacant(entry) => {
306                    if !empty {
307                        attribute_data_type.data_type =
308                            attribute_data_type.data_type.merge(&DataType::Null);
309                    }
310
311                    entry.insert(attribute_data_type);
312                }
313            }
314        }
315    }
316
317    #[must_use]
318    pub fn infer(attributes: Vec<&Attributes>) -> Self {
319        let mut schema = Self::default();
320
321        let mut empty = true;
322
323        for attributes in attributes {
324            schema.update(attributes, empty);
325
326            empty = false;
327        }
328
329        schema
330    }
331}
332
333#[derive(Debug, Clone, PartialEq, Default)]
334#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
335pub struct GroupSchema {
336    nodes: AttributeSchema,
337    edges: AttributeSchema,
338}
339
340impl GroupSchema {
341    #[must_use]
342    pub const fn new(nodes: AttributeSchema, edges: AttributeSchema) -> Self {
343        Self { nodes, edges }
344    }
345
346    #[must_use]
347    pub fn nodes(&self) -> &AttributeSchemaMapping {
348        &self.nodes
349    }
350
351    #[must_use]
352    pub fn edges(&self) -> &AttributeSchemaMapping {
353        &self.edges
354    }
355
356    pub fn validate_node<'a>(
357        &self,
358        index: &'a NodeIndex,
359        attributes: &'a Attributes,
360    ) -> Result<(), GraphError> {
361        self.nodes
362            .validate(attributes, &AttributeSchemaKind::Node(index))
363    }
364
365    pub fn validate_edge<'a>(
366        &self,
367        index: &'a EdgeIndex,
368        attributes: &'a Attributes,
369    ) -> Result<(), GraphError> {
370        self.edges
371            .validate(attributes, &AttributeSchemaKind::Edge(index))
372    }
373
374    #[must_use]
375    pub fn infer(nodes: Vec<&Attributes>, edges: Vec<&Attributes>) -> Self {
376        Self {
377            nodes: AttributeSchema::infer(nodes),
378            edges: AttributeSchema::infer(edges),
379        }
380    }
381
382    pub(crate) fn update_node(&mut self, attributes: &Attributes, empty: bool) {
383        self.nodes.update(attributes, empty);
384    }
385
386    pub(crate) fn update_edge(&mut self, attributes: &Attributes, empty: bool) {
387        self.edges.update(attributes, empty);
388    }
389}
390
391#[derive(Debug, Clone, PartialEq, Eq, Default)]
392#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
393pub enum SchemaType {
394    #[default]
395    Inferred,
396    Provided,
397}
398
399#[derive(Debug, Clone, PartialEq, Default)]
400#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
401pub struct Schema {
402    groups: HashMap<Group, GroupSchema>,
403    ungrouped: GroupSchema,
404    r#type: SchemaType,
405}
406
407impl Schema {
408    #[must_use]
409    pub const fn new_inferred(groups: HashMap<Group, GroupSchema>, ungrouped: GroupSchema) -> Self {
410        Self {
411            groups,
412            ungrouped,
413            r#type: SchemaType::Inferred,
414        }
415    }
416
417    #[must_use]
418    pub const fn new_provided(groups: HashMap<Group, GroupSchema>, ungrouped: GroupSchema) -> Self {
419        Self {
420            groups,
421            ungrouped,
422            r#type: SchemaType::Provided,
423        }
424    }
425
426    #[must_use]
427    pub fn infer(graphrecord: &GraphRecord) -> Self {
428        let mut group_mapping: GrHashMap<_, _> = graphrecord
429            .groups()
430            .map(|group| (group, (Vec::new(), Vec::new())))
431            .collect();
432
433        let mut ungrouped = (Vec::new(), Vec::new());
434
435        for node_index in graphrecord.node_indices() {
436            #[expect(clippy::missing_panics_doc, reason = "infallible")]
437            let mut groups_of_node = graphrecord
438                .groups_of_node(node_index)
439                .expect("Node must exist.")
440                .peekable();
441
442            if groups_of_node.peek().is_none() {
443                ungrouped.0.push(node_index);
444                continue;
445            }
446
447            for group in groups_of_node {
448                #[expect(clippy::missing_panics_doc, reason = "infallible")]
449                let group_nodes = &mut group_mapping.get_mut(&group).expect("Group must exist.").0;
450
451                group_nodes.push(node_index);
452            }
453        }
454
455        for edge_index in graphrecord.edge_indices() {
456            #[expect(clippy::missing_panics_doc, reason = "infallible")]
457            let mut groups_of_edge = graphrecord
458                .groups_of_edge(edge_index)
459                .expect("Edge must exist.")
460                .peekable();
461
462            if groups_of_edge.peek().is_none() {
463                ungrouped.1.push(edge_index);
464                continue;
465            }
466
467            for group in groups_of_edge {
468                #[expect(clippy::missing_panics_doc, reason = "infallible")]
469                let group_edges = &mut group_mapping.get_mut(&group).expect("Group must exist.").1;
470
471                group_edges.push(edge_index);
472            }
473        }
474
475        let group_schemas =
476            group_mapping
477                .into_iter()
478                .map(|(group, (nodes_in_group, edges_in_group))| {
479                    #[expect(clippy::missing_panics_doc, reason = "infallible")]
480                    let node_attributes: Vec<_> = nodes_in_group
481                        .into_iter()
482                        .map(|node| graphrecord.node_attributes(node).expect("Node must exist."))
483                        .collect();
484                    #[expect(clippy::missing_panics_doc, reason = "infallible")]
485                    let edge_attributes: Vec<_> = edges_in_group
486                        .into_iter()
487                        .map(|edge| graphrecord.edge_attributes(edge).expect("Edge must exist."))
488                        .collect();
489
490                    let schema = GroupSchema::infer(node_attributes, edge_attributes);
491
492                    (group.clone(), schema)
493                });
494
495        #[expect(clippy::missing_panics_doc, reason = "infallible")]
496        let ungrouped_schema = GroupSchema::infer(
497            ungrouped
498                .0
499                .into_iter()
500                .map(|node| graphrecord.node_attributes(node).expect("Node must exist."))
501                .collect::<Vec<_>>(),
502            ungrouped
503                .1
504                .into_iter()
505                .map(|edge| graphrecord.edge_attributes(edge).expect("Edge must exist."))
506                .collect::<Vec<_>>(),
507        );
508
509        Self {
510            groups: group_schemas.collect(),
511            ungrouped: ungrouped_schema,
512            r#type: SchemaType::Inferred,
513        }
514    }
515
516    #[must_use]
517    pub const fn groups(&self) -> &HashMap<Group, GroupSchema> {
518        &self.groups
519    }
520
521    pub fn group(&self, group: &Group) -> Result<&GroupSchema, GraphError> {
522        self.groups
523            .get(group)
524            .ok_or_else(|| GraphError::SchemaError(format!("Group {group} not found in schema.")))
525    }
526
527    #[must_use]
528    pub const fn ungrouped(&self) -> &GroupSchema {
529        &self.ungrouped
530    }
531
532    #[must_use]
533    pub const fn schema_type(&self) -> &SchemaType {
534        &self.r#type
535    }
536
537    pub fn validate_node<'a>(
538        &self,
539        index: &'a NodeIndex,
540        attributes: &'a Attributes,
541        group: Option<&'a Group>,
542    ) -> Result<(), GraphError> {
543        match group {
544            Some(group) => {
545                let schema = self.groups.get(group).ok_or_else(|| {
546                    GraphError::SchemaError(format!("Group {group} not found in schema."))
547                })?;
548
549                schema.validate_node(index, attributes)
550            }
551            None => self.ungrouped.validate_node(index, attributes),
552        }
553    }
554
555    pub fn validate_edge<'a>(
556        &self,
557        index: &'a EdgeIndex,
558        attributes: &'a Attributes,
559        group: Option<&'a Group>,
560    ) -> Result<(), GraphError> {
561        match group {
562            Some(group) => {
563                let schema = self.groups.get(group).ok_or_else(|| {
564                    GraphError::SchemaError(format!("Group {group} not found in schema."))
565                })?;
566
567                schema.validate_edge(index, attributes)
568            }
569            None => self.ungrouped.validate_edge(index, attributes),
570        }
571    }
572
573    pub(crate) fn update_node(
574        &mut self,
575        attributes: &Attributes,
576        group: Option<&Group>,
577        empty: bool,
578    ) {
579        match group {
580            Some(group) => {
581                self.groups
582                    .entry(group.clone())
583                    .or_default()
584                    .update_node(attributes, empty);
585            }
586            None => self.ungrouped.update_node(attributes, empty),
587        }
588    }
589
590    pub(crate) fn update_edge(
591        &mut self,
592        attributes: &Attributes,
593        group: Option<&Group>,
594        empty: bool,
595    ) {
596        match group {
597            Some(group) => {
598                self.groups
599                    .entry(group.clone())
600                    .or_default()
601                    .update_edge(attributes, empty);
602            }
603            None => self.ungrouped.update_edge(attributes, empty),
604        }
605    }
606
607    pub fn set_node_attribute(
608        &mut self,
609        attribute: &GraphRecordAttribute,
610        data_type: DataType,
611        attribute_type: AttributeType,
612        group: Option<&Group>,
613    ) -> Result<(), GraphError> {
614        let attribute_data_type = AttributeDataType::new(data_type, attribute_type)?;
615
616        match group {
617            Some(group) => {
618                let group_schema = self.groups.entry(group.clone()).or_default();
619                group_schema
620                    .nodes
621                    .0
622                    .insert(attribute.clone(), attribute_data_type);
623            }
624            None => {
625                self.ungrouped
626                    .nodes
627                    .0
628                    .insert(attribute.clone(), attribute_data_type);
629            }
630        }
631
632        Ok(())
633    }
634
635    pub fn set_edge_attribute(
636        &mut self,
637        attribute: &GraphRecordAttribute,
638        data_type: DataType,
639        attribute_type: AttributeType,
640        group: Option<&Group>,
641    ) -> Result<(), GraphError> {
642        let attribute_data_type = AttributeDataType::new(data_type, attribute_type)?;
643
644        match group {
645            Some(group) => {
646                let group_schema = self.groups.entry(group.clone()).or_default();
647                group_schema
648                    .edges
649                    .0
650                    .insert(attribute.clone(), attribute_data_type);
651            }
652            None => {
653                self.ungrouped
654                    .edges
655                    .0
656                    .insert(attribute.clone(), attribute_data_type);
657            }
658        }
659
660        Ok(())
661    }
662
663    pub fn update_node_attribute(
664        &mut self,
665        attribute: &GraphRecordAttribute,
666        data_type: DataType,
667        attribute_type: AttributeType,
668        group: Option<&Group>,
669    ) -> Result<(), GraphError> {
670        let attribute_data_type = AttributeDataType::new(data_type, attribute_type)?;
671
672        match group {
673            Some(group) => {
674                let group_schema = self.groups.entry(group.clone()).or_default();
675                group_schema
676                    .nodes
677                    .0
678                    .entry(attribute.clone())
679                    .and_modify(|value| value.merge(&attribute_data_type))
680                    .or_insert(attribute_data_type);
681            }
682            None => {
683                self.ungrouped
684                    .nodes
685                    .0
686                    .entry(attribute.clone())
687                    .and_modify(|value| value.merge(&attribute_data_type))
688                    .or_insert(attribute_data_type);
689            }
690        }
691
692        Ok(())
693    }
694
695    pub fn update_edge_attribute(
696        &mut self,
697        attribute: &GraphRecordAttribute,
698        data_type: DataType,
699        attribute_type: AttributeType,
700        group: Option<&Group>,
701    ) -> Result<(), GraphError> {
702        let attribute_data_type = AttributeDataType::new(data_type, attribute_type)?;
703
704        match group {
705            Some(group) => {
706                let group_schema = self.groups.entry(group.clone()).or_default();
707                group_schema
708                    .edges
709                    .0
710                    .entry(attribute.clone())
711                    .and_modify(|value| value.merge(&attribute_data_type))
712                    .or_insert(attribute_data_type);
713            }
714            None => {
715                self.ungrouped
716                    .edges
717                    .0
718                    .entry(attribute.clone())
719                    .and_modify(|value| value.merge(&attribute_data_type))
720                    .or_insert(attribute_data_type);
721            }
722        }
723
724        Ok(())
725    }
726
727    pub fn remove_node_attribute(
728        &mut self,
729        attribute: &GraphRecordAttribute,
730        group: Option<&Group>,
731    ) {
732        match group {
733            Some(group) => {
734                if let Some(group_schema) = self.groups.get_mut(group) {
735                    group_schema.nodes.0.remove(attribute);
736                }
737            }
738            None => {
739                self.ungrouped.nodes.0.remove(attribute);
740            }
741        }
742    }
743
744    pub fn remove_edge_attribute(
745        &mut self,
746        attribute: &GraphRecordAttribute,
747        group: Option<&Group>,
748    ) {
749        match group {
750            Some(group) => {
751                if let Some(group_schema) = self.groups.get_mut(group) {
752                    group_schema.edges.0.remove(attribute);
753                }
754            }
755            None => {
756                self.ungrouped.edges.0.remove(attribute);
757            }
758        }
759    }
760
761    pub fn add_group(&mut self, group: Group, schema: GroupSchema) -> Result<(), GraphError> {
762        if self.groups.contains_key(&group) {
763            return Err(GraphError::SchemaError(format!(
764                "Group {group} already exists in schema."
765            )));
766        }
767
768        self.groups.insert(group, schema);
769
770        Ok(())
771    }
772
773    pub fn remove_group(&mut self, group: &Group) {
774        self.groups.remove(group);
775    }
776
777    pub const fn freeze(&mut self) {
778        self.r#type = SchemaType::Provided;
779    }
780
781    pub const fn unfreeze(&mut self) {
782        self.r#type = SchemaType::Inferred;
783    }
784}
785
786#[cfg(test)]
787mod test {
788    use super::{AttributeDataType, GroupSchema};
789    use crate::{
790        GraphRecord,
791        graphrecord::{
792            Attributes, Schema, SchemaType,
793            datatypes::DataType,
794            schema::{AttributeSchema, AttributeSchemaKind, AttributeType},
795        },
796    };
797    use std::collections::HashMap;
798
799    #[test]
800    fn test_attribute_type_infer() {
801        assert_eq!(
802            AttributeType::infer(&DataType::String),
803            AttributeType::Unstructured
804        );
805        assert_eq!(
806            AttributeType::infer(&DataType::Int),
807            AttributeType::Continuous
808        );
809        assert_eq!(
810            AttributeType::infer(&DataType::Float),
811            AttributeType::Continuous
812        );
813        assert_eq!(
814            AttributeType::infer(&DataType::Bool),
815            AttributeType::Categorical
816        );
817        assert_eq!(
818            AttributeType::infer(&DataType::DateTime),
819            AttributeType::Temporal
820        );
821        assert_eq!(
822            AttributeType::infer(&DataType::Duration),
823            AttributeType::Temporal
824        );
825        assert_eq!(
826            AttributeType::infer(&DataType::Null),
827            AttributeType::Unstructured
828        );
829        assert_eq!(
830            AttributeType::infer(&DataType::Any),
831            AttributeType::Unstructured
832        );
833        assert_eq!(
834            AttributeType::infer(&DataType::Union((
835                Box::new(DataType::Int),
836                Box::new(DataType::Float)
837            ))),
838            AttributeType::Continuous
839        );
840        assert_eq!(
841            AttributeType::infer(&DataType::Option(Box::new(DataType::Int))),
842            AttributeType::Continuous
843        );
844    }
845
846    #[test]
847    fn test_attribute_type_merge() {
848        assert_eq!(
849            AttributeType::Categorical.merge(AttributeType::Unstructured),
850            AttributeType::Unstructured
851        );
852        assert_eq!(
853            AttributeType::Unstructured.merge(AttributeType::Categorical),
854            AttributeType::Unstructured
855        );
856
857        assert_eq!(
858            AttributeType::Categorical.merge(AttributeType::Categorical),
859            AttributeType::Categorical
860        );
861        assert_eq!(
862            AttributeType::Categorical.merge(AttributeType::Continuous),
863            AttributeType::Categorical
864        );
865        assert_eq!(
866            AttributeType::Categorical.merge(AttributeType::Temporal),
867            AttributeType::Categorical
868        );
869
870        assert_eq!(
871            AttributeType::Continuous.merge(AttributeType::Categorical),
872            AttributeType::Categorical
873        );
874        assert_eq!(
875            AttributeType::Temporal.merge(AttributeType::Categorical),
876            AttributeType::Categorical
877        );
878
879        assert_eq!(
880            AttributeType::Continuous.merge(AttributeType::Continuous),
881            AttributeType::Continuous
882        );
883
884        assert_eq!(
885            AttributeType::Temporal.merge(AttributeType::Temporal),
886            AttributeType::Temporal
887        );
888
889        assert_eq!(
890            AttributeType::Continuous.merge(AttributeType::Temporal),
891            AttributeType::Unstructured
892        );
893        assert_eq!(
894            AttributeType::Continuous.merge(AttributeType::Unstructured),
895            AttributeType::Unstructured
896        );
897
898        assert_eq!(
899            AttributeType::Temporal.merge(AttributeType::Continuous),
900            AttributeType::Unstructured
901        );
902        assert_eq!(
903            AttributeType::Temporal.merge(AttributeType::Unstructured),
904            AttributeType::Unstructured
905        );
906
907        assert_eq!(
908            AttributeType::Unstructured.merge(AttributeType::Continuous),
909            AttributeType::Unstructured
910        );
911        assert_eq!(
912            AttributeType::Unstructured.merge(AttributeType::Temporal),
913            AttributeType::Unstructured
914        );
915        assert_eq!(
916            AttributeType::Unstructured.merge(AttributeType::Unstructured),
917            AttributeType::Unstructured
918        );
919    }
920
921    #[test]
922    fn test_data_type_merge() {
923        assert_eq!(DataType::Int.merge(&DataType::Int), DataType::Int);
924        assert_eq!(
925            DataType::Int.merge(&DataType::Float),
926            DataType::Union((Box::new(DataType::Int), Box::new(DataType::Float)))
927        );
928        assert_eq!(
929            DataType::Int.merge(&DataType::Null),
930            DataType::Option(Box::new(DataType::Int))
931        );
932        assert_eq!(
933            DataType::Null.merge(&DataType::Int),
934            DataType::Option(Box::new(DataType::Int))
935        );
936        assert_eq!(DataType::Null.merge(&DataType::Null), DataType::Null);
937        assert_eq!(DataType::Int.merge(&DataType::Any), DataType::Any);
938        assert_eq!(DataType::Any.merge(&DataType::Int), DataType::Any);
939        assert_eq!(
940            DataType::Option(Box::new(DataType::Int)).merge(&DataType::String),
941            DataType::Option(Box::new(DataType::Union((
942                Box::new(DataType::Int),
943                Box::new(DataType::String)
944            ))))
945        );
946        assert_eq!(
947            DataType::Int.merge(&DataType::Option(Box::new(DataType::Int))),
948            DataType::Option(Box::new(DataType::Int))
949        );
950        assert_eq!(
951            DataType::Option(Box::new(DataType::Int))
952                .merge(&DataType::Option(Box::new(DataType::String))),
953            DataType::Option(Box::new(DataType::Union((
954                Box::new(DataType::Int),
955                Box::new(DataType::String)
956            ))))
957        );
958    }
959
960    #[test]
961    fn test_attribute_data_type_new() {
962        assert!(AttributeDataType::new(DataType::String, AttributeType::Categorical).is_ok());
963        assert!(AttributeDataType::new(DataType::String, AttributeType::Continuous).is_err());
964        assert!(AttributeDataType::new(DataType::String, AttributeType::Temporal).is_err());
965        assert!(AttributeDataType::new(DataType::String, AttributeType::Unstructured).is_ok());
966
967        assert!(AttributeDataType::new(DataType::Int, AttributeType::Categorical).is_ok());
968        assert!(AttributeDataType::new(DataType::Int, AttributeType::Continuous).is_ok());
969        assert!(AttributeDataType::new(DataType::Int, AttributeType::Temporal).is_err());
970        assert!(AttributeDataType::new(DataType::Int, AttributeType::Unstructured).is_ok());
971
972        assert!(AttributeDataType::new(DataType::Float, AttributeType::Categorical).is_ok());
973        assert!(AttributeDataType::new(DataType::Float, AttributeType::Continuous).is_ok());
974        assert!(AttributeDataType::new(DataType::Float, AttributeType::Temporal).is_err());
975        assert!(AttributeDataType::new(DataType::Float, AttributeType::Unstructured).is_ok());
976
977        assert!(AttributeDataType::new(DataType::Bool, AttributeType::Categorical).is_ok());
978        assert!(AttributeDataType::new(DataType::Bool, AttributeType::Continuous).is_err());
979        assert!(AttributeDataType::new(DataType::Bool, AttributeType::Temporal).is_err());
980        assert!(AttributeDataType::new(DataType::Bool, AttributeType::Unstructured).is_ok());
981
982        assert!(AttributeDataType::new(DataType::DateTime, AttributeType::Categorical).is_ok());
983        assert!(AttributeDataType::new(DataType::DateTime, AttributeType::Continuous).is_err());
984        assert!(AttributeDataType::new(DataType::DateTime, AttributeType::Temporal).is_ok());
985        assert!(AttributeDataType::new(DataType::DateTime, AttributeType::Unstructured).is_ok());
986
987        assert!(AttributeDataType::new(DataType::Duration, AttributeType::Categorical).is_ok());
988        assert!(AttributeDataType::new(DataType::Duration, AttributeType::Continuous).is_err());
989        assert!(AttributeDataType::new(DataType::Duration, AttributeType::Temporal).is_ok());
990        assert!(AttributeDataType::new(DataType::Duration, AttributeType::Unstructured).is_ok());
991
992        assert!(AttributeDataType::new(DataType::Null, AttributeType::Categorical).is_ok());
993        assert!(AttributeDataType::new(DataType::Null, AttributeType::Continuous).is_ok());
994        assert!(AttributeDataType::new(DataType::Null, AttributeType::Temporal).is_ok());
995        assert!(AttributeDataType::new(DataType::Null, AttributeType::Unstructured).is_ok());
996
997        assert!(AttributeDataType::new(DataType::Any, AttributeType::Categorical).is_ok());
998        assert!(AttributeDataType::new(DataType::Any, AttributeType::Continuous).is_err());
999        assert!(AttributeDataType::new(DataType::Any, AttributeType::Temporal).is_err());
1000        assert!(AttributeDataType::new(DataType::Any, AttributeType::Unstructured).is_ok());
1001
1002        assert!(
1003            AttributeDataType::new(
1004                DataType::Option(Box::new(DataType::Int)),
1005                AttributeType::Categorical
1006            )
1007            .is_ok()
1008        );
1009        assert!(
1010            AttributeDataType::new(
1011                DataType::Option(Box::new(DataType::Int)),
1012                AttributeType::Continuous
1013            )
1014            .is_ok()
1015        );
1016        assert!(
1017            AttributeDataType::new(
1018                DataType::Option(Box::new(DataType::Int)),
1019                AttributeType::Temporal
1020            )
1021            .is_err()
1022        );
1023        assert!(
1024            AttributeDataType::new(
1025                DataType::Option(Box::new(DataType::Int)),
1026                AttributeType::Unstructured
1027            )
1028            .is_ok()
1029        );
1030
1031        assert!(
1032            AttributeDataType::new(
1033                DataType::Union((Box::new(DataType::Int), Box::new(DataType::Float))),
1034                AttributeType::Categorical
1035            )
1036            .is_ok()
1037        );
1038        assert!(
1039            AttributeDataType::new(
1040                DataType::Union((Box::new(DataType::Int), Box::new(DataType::Float))),
1041                AttributeType::Continuous
1042            )
1043            .is_ok()
1044        );
1045        assert!(
1046            AttributeDataType::new(
1047                DataType::Union((Box::new(DataType::Int), Box::new(DataType::Float))),
1048                AttributeType::Temporal
1049            )
1050            .is_err()
1051        );
1052        assert!(
1053            AttributeDataType::new(
1054                DataType::Union((Box::new(DataType::Int), Box::new(DataType::Float))),
1055                AttributeType::Unstructured
1056            )
1057            .is_ok()
1058        );
1059    }
1060
1061    #[test]
1062    fn test_attribute_data_type_data_type() {
1063        let attribute_data_type = AttributeDataType::new(DataType::Int, AttributeType::Categorical)
1064            .expect("AttributeType was inferred from DataType.");
1065
1066        assert_eq!(attribute_data_type.data_type(), &DataType::Int);
1067    }
1068
1069    #[test]
1070    fn test_attribute_data_type_attribute_type() {
1071        let attribute_data_type = AttributeDataType::new(DataType::Int, AttributeType::Categorical)
1072            .expect("AttributeType was inferred from DataType.");
1073
1074        assert_eq!(
1075            attribute_data_type.attribute_type(),
1076            &AttributeType::Categorical
1077        );
1078    }
1079
1080    #[test]
1081    fn test_attribute_data_type_merge() {
1082        let mut attribute_data_type =
1083            AttributeDataType::new(DataType::Int, AttributeType::Categorical)
1084                .expect("AttributeType was inferred from DataType.");
1085
1086        attribute_data_type.merge(
1087            &AttributeDataType::new(DataType::Float, AttributeType::Continuous)
1088                .expect("AttributeType was inferred from DataType."),
1089        );
1090
1091        assert_eq!(
1092            attribute_data_type.data_type(),
1093            &DataType::Union((Box::new(DataType::Int), Box::new(DataType::Float)))
1094        );
1095        assert_eq!(
1096            attribute_data_type.attribute_type(),
1097            &AttributeType::Categorical
1098        );
1099    }
1100
1101    #[test]
1102    fn test_attribute_data_type_from_data_type() {
1103        let attribute_data_type: AttributeDataType = DataType::Int.into();
1104
1105        assert_eq!(attribute_data_type.data_type(), &DataType::Int);
1106        assert_eq!(
1107            attribute_data_type.attribute_type(),
1108            &AttributeType::Continuous
1109        );
1110    }
1111
1112    #[test]
1113    fn test_attribute_data_type_from_tuple() {
1114        let attribute_data_type: AttributeDataType =
1115            (DataType::Int, AttributeType::Categorical).into();
1116
1117        assert_eq!(attribute_data_type.data_type(), &DataType::Int);
1118        assert_eq!(
1119            attribute_data_type.attribute_type(),
1120            &AttributeType::Categorical
1121        );
1122    }
1123
1124    #[test]
1125    fn test_attribute_schema_kind_error_message() {
1126        let index = 0;
1127        let key = "key";
1128        let data_type = DataType::Int;
1129
1130        assert_eq!(
1131            AttributeSchemaKind::Node(&(index.into())).error_message(&(key.into()), &data_type),
1132            "Attribute key of type Int not found on node with index 0"
1133        );
1134        assert_eq!(
1135            AttributeSchemaKind::Edge(&(index as u32)).error_message(&(key.into()), &data_type),
1136            "Attribute key of type Int not found on edge with index 0"
1137        );
1138    }
1139
1140    #[test]
1141    fn test_attribute_schema_kind_error_message_expected() {
1142        let index = 0;
1143        let key = "key";
1144        let data_type = DataType::Int;
1145        let expected_data_type = DataType::Float;
1146
1147        assert_eq!(
1148            AttributeSchemaKind::Node(&(index.into())).error_message_expected(
1149                &(key.into()),
1150                &data_type,
1151                &expected_data_type
1152            ),
1153            "Attribute key of node with index 0 is of type Int. Expected Float."
1154        );
1155        assert_eq!(
1156            AttributeSchemaKind::Edge(&(index as u32)).error_message_expected(
1157                &(key.into()),
1158                &data_type,
1159                &expected_data_type
1160            ),
1161            "Attribute key of edge with index 0 is of type Int. Expected Float."
1162        );
1163    }
1164
1165    #[test]
1166    fn test_attribute_schema_kind_error_message_too_many() {
1167        let index = 0;
1168        let attributes = vec!["key1".to_string(), "key2".to_string()];
1169
1170        assert_eq!(
1171            AttributeSchemaKind::Node(&(index.into())).error_message_too_many(&attributes),
1172            "Attributes [key1, key2] of node with index 0 do not exist in schema."
1173        );
1174        assert_eq!(
1175            AttributeSchemaKind::Edge(&(index as u32)).error_message_too_many(&attributes),
1176            "Attributes [key1, key2] of edge with index 0 do not exist in schema."
1177        );
1178    }
1179
1180    #[test]
1181    fn test_attribute_schema_deref() {
1182        let schema = AttributeSchema::new(
1183            vec![
1184                (
1185                    "key1".into(),
1186                    AttributeDataType::new(DataType::Int, AttributeType::Categorical)
1187                        .expect("AttributeType was inferred from DataType."),
1188                ),
1189                (
1190                    "key2".into(),
1191                    AttributeDataType::new(DataType::Float, AttributeType::Continuous)
1192                        .expect("AttributeType was inferred from DataType."),
1193                ),
1194            ]
1195            .into_iter()
1196            .collect(),
1197        );
1198
1199        assert_eq!(
1200            schema.get(&"key1".into()).unwrap().data_type(),
1201            &DataType::Int
1202        );
1203        assert_eq!(
1204            schema.get(&"key2".into()).unwrap().data_type(),
1205            &DataType::Float
1206        );
1207    }
1208
1209    #[test]
1210    fn test_attribute_schema_validate() {
1211        let attribute_schema = AttributeSchema::new(
1212            vec![
1213                (
1214                    "key1".into(),
1215                    AttributeDataType::new(DataType::Int, AttributeType::Categorical)
1216                        .expect("AttributeType was inferred from DataType."),
1217                ),
1218                (
1219                    "key2".into(),
1220                    AttributeDataType::new(DataType::Float, AttributeType::Continuous)
1221                        .expect("AttributeType was inferred from DataType."),
1222                ),
1223            ]
1224            .into_iter()
1225            .collect(),
1226        );
1227
1228        let attributes: Attributes = vec![("key1".into(), 0.into()), ("key2".into(), 0.0.into())]
1229            .into_iter()
1230            .collect();
1231
1232        assert!(
1233            attribute_schema
1234                .validate(&attributes, &AttributeSchemaKind::Node(&0.into()))
1235                .is_ok()
1236        );
1237
1238        let attributes: Attributes = vec![("key1".into(), 0.0.into()), ("key2".into(), 0.into())]
1239            .into_iter()
1240            .collect();
1241
1242        assert!(
1243            attribute_schema
1244                .validate(&attributes, &AttributeSchemaKind::Node(&0.into()))
1245                .is_err_and(|error| { matches!(error, crate::errors::GraphError::SchemaError(_)) })
1246        );
1247
1248        let attributes: Attributes = vec![
1249            ("key1".into(), 0.into()),
1250            ("key2".into(), 0.0.into()),
1251            ("key3".into(), 0.0.into()),
1252        ]
1253        .into_iter()
1254        .collect();
1255
1256        assert!(
1257            attribute_schema
1258                .validate(&attributes, &AttributeSchemaKind::Node(&0.into()))
1259                .is_err_and(|error| { matches!(error, crate::errors::GraphError::SchemaError(_)) })
1260        );
1261    }
1262
1263    #[test]
1264    fn test_attribute_schema_update() {
1265        let mut schema = AttributeSchema::default();
1266        let attributes: Attributes =
1267            vec![("key1".into(), 0.into()), ("key2".into(), "test".into())]
1268                .into_iter()
1269                .collect();
1270
1271        schema.update(&attributes, true);
1272
1273        assert_eq!(schema.0.len(), 2);
1274        assert_eq!(
1275            schema.0.get(&"key1".into()).unwrap().data_type(),
1276            &DataType::Int
1277        );
1278        assert_eq!(
1279            schema.0.get(&"key2".into()).unwrap().data_type(),
1280            &DataType::String
1281        );
1282
1283        let new_attributes: Attributes =
1284            vec![("key1".into(), 0.5.into()), ("key3".into(), true.into())]
1285                .into_iter()
1286                .collect();
1287
1288        schema.update(&new_attributes, false);
1289
1290        assert_eq!(schema.0.len(), 3);
1291        assert_eq!(
1292            schema.0.get(&"key1".into()).unwrap().data_type(),
1293            &DataType::Union((Box::new(DataType::Int), Box::new(DataType::Float)))
1294        );
1295        assert_eq!(
1296            schema.0.get(&"key2".into()).unwrap().data_type(),
1297            &DataType::Option(Box::new(DataType::String))
1298        );
1299        assert_eq!(
1300            schema.0.get(&"key3".into()).unwrap().data_type(),
1301            &DataType::Option(Box::new(DataType::Bool))
1302        );
1303    }
1304
1305    #[test]
1306    fn test_attribute_schema_infer() {
1307        let attributes1: Attributes =
1308            vec![("key1".into(), 0.into()), ("key2".into(), "test".into())]
1309                .into_iter()
1310                .collect();
1311
1312        let attributes2: Attributes = vec![("key1".into(), 1.into()), ("key3".into(), true.into())]
1313            .into_iter()
1314            .collect();
1315
1316        let schema = AttributeSchema::infer(vec![&attributes1, &attributes2]);
1317
1318        assert_eq!(schema.0.len(), 3);
1319        assert_eq!(
1320            schema.0.get(&"key1".into()).unwrap().data_type(),
1321            &DataType::Int
1322        );
1323        assert_eq!(
1324            schema.0.get(&"key2".into()).unwrap().data_type(),
1325            &DataType::Option(Box::new(DataType::String))
1326        );
1327        assert_eq!(
1328            schema.0.get(&"key3".into()).unwrap().data_type(),
1329            &DataType::Option(Box::new(DataType::Bool))
1330        );
1331    }
1332
1333    #[test]
1334    fn test_group_schema_nodes() {
1335        let nodes = AttributeSchema::new(
1336            vec![
1337                (
1338                    "key1".into(),
1339                    AttributeDataType::new(DataType::Int, AttributeType::Categorical)
1340                        .expect("AttributeType was inferred from DataType."),
1341                ),
1342                (
1343                    "key2".into(),
1344                    AttributeDataType::new(DataType::Float, AttributeType::Continuous)
1345                        .expect("AttributeType was inferred from DataType."),
1346                ),
1347            ]
1348            .into_iter()
1349            .collect(),
1350        );
1351
1352        let group_schema = GroupSchema::new(nodes.clone(), AttributeSchema::default());
1353
1354        assert_eq!(group_schema.nodes(), &nodes.0);
1355    }
1356
1357    #[test]
1358    fn test_group_schema_edges() {
1359        let edges = AttributeSchema::new(
1360            vec![
1361                (
1362                    "key1".into(),
1363                    AttributeDataType::new(DataType::Int, AttributeType::Categorical)
1364                        .expect("AttributeType was inferred from DataType."),
1365                ),
1366                (
1367                    "key2".into(),
1368                    AttributeDataType::new(DataType::Float, AttributeType::Continuous)
1369                        .expect("AttributeType was inferred from DataType."),
1370                ),
1371            ]
1372            .into_iter()
1373            .collect(),
1374        );
1375
1376        let group_schema = GroupSchema::new(AttributeSchema::default(), edges.clone());
1377
1378        assert_eq!(group_schema.edges(), &edges.0);
1379    }
1380
1381    #[test]
1382    fn test_group_schema_validate_node() {
1383        let nodes = AttributeSchema::new(
1384            vec![
1385                (
1386                    "key1".into(),
1387                    AttributeDataType::new(DataType::Int, AttributeType::Categorical)
1388                        .expect("AttributeType was inferred from DataType."),
1389                ),
1390                (
1391                    "key2".into(),
1392                    AttributeDataType::new(DataType::Float, AttributeType::Continuous)
1393                        .expect("AttributeType was inferred from DataType."),
1394                ),
1395            ]
1396            .into_iter()
1397            .collect(),
1398        );
1399
1400        let group_schema = GroupSchema::new(nodes, AttributeSchema::default());
1401
1402        let attributes: Attributes = vec![("key1".into(), 0.into()), ("key2".into(), 0.0.into())]
1403            .into_iter()
1404            .collect();
1405
1406        assert!(group_schema.validate_node(&0.into(), &attributes).is_ok());
1407
1408        let attributes: Attributes = vec![("key1".into(), 0.0.into()), ("key2".into(), 0.into())]
1409            .into_iter()
1410            .collect();
1411
1412        assert!(
1413            group_schema
1414                .validate_node(&0.into(), &attributes)
1415                .is_err_and(|error| { matches!(error, crate::errors::GraphError::SchemaError(_)) })
1416        );
1417    }
1418
1419    #[test]
1420    fn test_group_schema_validate_edge() {
1421        let edges = AttributeSchema::new(
1422            vec![
1423                (
1424                    "key1".into(),
1425                    AttributeDataType::new(DataType::Int, AttributeType::Categorical)
1426                        .expect("AttributeType was inferred from DataType."),
1427                ),
1428                (
1429                    "key2".into(),
1430                    AttributeDataType::new(DataType::Float, AttributeType::Continuous)
1431                        .expect("AttributeType was inferred from DataType."),
1432                ),
1433            ]
1434            .into_iter()
1435            .collect(),
1436        );
1437
1438        let group_schema = GroupSchema::new(AttributeSchema::default(), edges);
1439
1440        let attributes: Attributes = vec![("key1".into(), 0.into()), ("key2".into(), 0.0.into())]
1441            .into_iter()
1442            .collect();
1443
1444        assert!(group_schema.validate_edge(&0, &attributes).is_ok());
1445
1446        let attributes: Attributes = vec![("key1".into(), 0.0.into()), ("key2".into(), 0.into())]
1447            .into_iter()
1448            .collect();
1449
1450        assert!(
1451            group_schema
1452                .validate_edge(&0, &attributes)
1453                .is_err_and(|error| { matches!(error, crate::errors::GraphError::SchemaError(_)) })
1454        );
1455    }
1456
1457    #[test]
1458    fn test_group_schema_infer() {
1459        let node_attributes1: Attributes =
1460            vec![("key1".into(), 0.into()), ("key2".into(), "test".into())]
1461                .into_iter()
1462                .collect();
1463
1464        let node_attributes2: Attributes =
1465            vec![("key1".into(), 1.into()), ("key3".into(), true.into())]
1466                .into_iter()
1467                .collect();
1468
1469        let edge_attributes: Attributes =
1470            vec![("key4".into(), 0.5.into()), ("key5".into(), "edge".into())]
1471                .into_iter()
1472                .collect();
1473
1474        let group_schema = GroupSchema::infer(
1475            vec![&node_attributes1, &node_attributes2],
1476            vec![&edge_attributes],
1477        );
1478
1479        assert_eq!(group_schema.nodes().len(), 3);
1480        assert_eq!(group_schema.edges().len(), 2);
1481
1482        assert_eq!(
1483            group_schema
1484                .nodes()
1485                .get(&"key1".into())
1486                .unwrap()
1487                .data_type(),
1488            &DataType::Int
1489        );
1490        assert_eq!(
1491            group_schema
1492                .nodes()
1493                .get(&"key2".into())
1494                .unwrap()
1495                .data_type(),
1496            &DataType::Option(Box::new(DataType::String))
1497        );
1498        assert_eq!(
1499            group_schema
1500                .nodes()
1501                .get(&"key3".into())
1502                .unwrap()
1503                .data_type(),
1504            &DataType::Option(Box::new(DataType::Bool))
1505        );
1506
1507        assert_eq!(
1508            group_schema
1509                .edges()
1510                .get(&"key4".into())
1511                .unwrap()
1512                .data_type(),
1513            &DataType::Float
1514        );
1515        assert_eq!(
1516            group_schema
1517                .edges()
1518                .get(&"key5".into())
1519                .unwrap()
1520                .data_type(),
1521            &DataType::String
1522        );
1523    }
1524
1525    #[test]
1526    fn test_group_schema_update_node() {
1527        let mut group_schema = GroupSchema::default();
1528        let attributes = Attributes::from([("key1".into(), 0.into()), ("key2".into(), 0.0.into())]);
1529
1530        group_schema.update_node(&attributes, true);
1531
1532        assert_eq!(group_schema.nodes().len(), 2);
1533        assert_eq!(
1534            group_schema
1535                .nodes()
1536                .get(&"key1".into())
1537                .unwrap()
1538                .data_type(),
1539            &DataType::Int
1540        );
1541        assert_eq!(
1542            group_schema
1543                .nodes()
1544                .get(&"key2".into())
1545                .unwrap()
1546                .data_type(),
1547            &DataType::Float
1548        );
1549    }
1550
1551    #[test]
1552    fn test_group_schema_update_edge() {
1553        let mut group_schema = GroupSchema::default();
1554        let attributes =
1555            Attributes::from([("key3".into(), true.into()), ("key4".into(), "test".into())]);
1556
1557        group_schema.update_edge(&attributes, true);
1558
1559        assert_eq!(group_schema.edges().len(), 2);
1560        assert_eq!(
1561            group_schema
1562                .edges()
1563                .get(&"key3".into())
1564                .unwrap()
1565                .data_type(),
1566            &DataType::Bool
1567        );
1568        assert_eq!(
1569            group_schema
1570                .edges()
1571                .get(&"key4".into())
1572                .unwrap()
1573                .data_type(),
1574            &DataType::String
1575        );
1576    }
1577
1578    #[test]
1579    fn test_schema_infer() {
1580        let mut graphrecord = GraphRecord::new();
1581        graphrecord
1582            .add_node(0.into(), Attributes::from([("key1".into(), 0.into())]))
1583            .unwrap();
1584        graphrecord
1585            .add_node(1.into(), Attributes::from([("key2".into(), 0.0.into())]))
1586            .unwrap();
1587        graphrecord
1588            .add_edge(
1589                0.into(),
1590                1.into(),
1591                Attributes::from([("key3".into(), true.into())]),
1592            )
1593            .unwrap();
1594
1595        let schema = Schema::infer(&graphrecord);
1596
1597        assert_eq!(schema.ungrouped().nodes().len(), 2);
1598        assert_eq!(schema.ungrouped().edges().len(), 1);
1599
1600        graphrecord
1601            .add_group("test".into(), Some(vec![0.into(), 1.into()]), Some(vec![0]))
1602            .unwrap();
1603
1604        let schema = Schema::infer(&graphrecord);
1605
1606        assert_eq!(schema.groups().len(), 1);
1607        assert_eq!(schema.group(&"test".into()).unwrap().nodes().len(), 2);
1608        assert_eq!(schema.group(&"test".into()).unwrap().edges().len(), 1);
1609    }
1610
1611    #[test]
1612    fn test_schema_groups() {
1613        let schema = Schema::new_inferred(
1614            vec![("group1".into(), GroupSchema::default())]
1615                .into_iter()
1616                .collect(),
1617            GroupSchema::default(),
1618        );
1619        assert_eq!(schema.groups().len(), 1);
1620        assert!(schema.groups().contains_key(&"group1".into()));
1621    }
1622
1623    #[test]
1624    fn test_schema_group() {
1625        let schema = Schema::new_inferred(
1626            vec![("group1".into(), GroupSchema::default())]
1627                .into_iter()
1628                .collect(),
1629            GroupSchema::default(),
1630        );
1631        assert!(schema.group(&"group1".into()).is_ok());
1632        assert!(schema.group(&"non_existent".into()).is_err());
1633    }
1634
1635    #[test]
1636    fn test_schema_default() {
1637        let default_schema = GroupSchema::default();
1638        let schema = Schema::new_inferred(HashMap::new(), default_schema.clone());
1639        assert_eq!(schema.ungrouped(), &default_schema);
1640    }
1641
1642    #[test]
1643    fn test_schema_schema_type() {
1644        let schema = Schema::new_inferred(HashMap::new(), GroupSchema::default());
1645        assert_eq!(schema.schema_type(), &SchemaType::Inferred);
1646    }
1647
1648    #[test]
1649    fn test_schema_validate_node() {
1650        let mut schema = Schema::new_inferred(
1651            HashMap::new(),
1652            GroupSchema::new(AttributeSchema::default(), AttributeSchema::default()),
1653        );
1654        schema
1655            .set_node_attribute(
1656                &"key1".into(),
1657                DataType::Int,
1658                AttributeType::Continuous,
1659                None,
1660            )
1661            .unwrap();
1662
1663        let attributes = Attributes::from([("key1".into(), 0.into())]);
1664        assert!(schema.validate_node(&0.into(), &attributes, None).is_ok());
1665
1666        let invalid_attributes = Attributes::from([("key1".into(), "invalid".into())]);
1667        assert!(
1668            schema
1669                .validate_node(&0.into(), &invalid_attributes, None)
1670                .is_err()
1671        );
1672    }
1673
1674    #[test]
1675    fn test_schema_validate_edge() {
1676        let mut schema = Schema::new_inferred(
1677            HashMap::new(),
1678            GroupSchema::new(AttributeSchema::default(), AttributeSchema::default()),
1679        );
1680        schema
1681            .set_edge_attribute(
1682                &"key1".into(),
1683                DataType::Bool,
1684                AttributeType::Categorical,
1685                None,
1686            )
1687            .unwrap();
1688
1689        let attributes = Attributes::from([("key1".into(), true.into())]);
1690        assert!(schema.validate_edge(&0, &attributes, None).is_ok());
1691
1692        let invalid_attributes = Attributes::from([("key1".into(), 0.into())]);
1693        assert!(schema.validate_edge(&0, &invalid_attributes, None).is_err());
1694    }
1695
1696    #[test]
1697    fn test_schema_update_node() {
1698        let mut schema = Schema::new_inferred(
1699            HashMap::new(),
1700            GroupSchema::new(AttributeSchema::default(), AttributeSchema::default()),
1701        );
1702        let attributes = Attributes::from([("key1".into(), 0.into()), ("key2".into(), 0.0.into())]);
1703
1704        schema.update_node(&attributes, None, true);
1705
1706        assert_eq!(schema.ungrouped().nodes().len(), 2);
1707        assert_eq!(
1708            schema
1709                .ungrouped()
1710                .nodes()
1711                .get(&"key1".into())
1712                .unwrap()
1713                .data_type(),
1714            &DataType::Int
1715        );
1716        assert_eq!(
1717            schema
1718                .ungrouped()
1719                .nodes()
1720                .get(&"key2".into())
1721                .unwrap()
1722                .data_type(),
1723            &DataType::Float
1724        );
1725    }
1726
1727    #[test]
1728    fn test_schema_update_edge() {
1729        let mut schema = Schema::new_inferred(
1730            HashMap::new(),
1731            GroupSchema::new(AttributeSchema::default(), AttributeSchema::default()),
1732        );
1733        let attributes =
1734            Attributes::from([("key3".into(), true.into()), ("key4".into(), "test".into())]);
1735
1736        schema.update_edge(&attributes, None, true);
1737
1738        assert_eq!(schema.ungrouped().edges().len(), 2);
1739        assert_eq!(
1740            schema
1741                .ungrouped()
1742                .edges()
1743                .get(&"key3".into())
1744                .unwrap()
1745                .data_type(),
1746            &DataType::Bool
1747        );
1748        assert_eq!(
1749            schema
1750                .ungrouped()
1751                .edges()
1752                .get(&"key4".into())
1753                .unwrap()
1754                .data_type(),
1755            &DataType::String
1756        );
1757    }
1758
1759    #[test]
1760    fn test_schema_set_node_attribute() {
1761        let mut schema = Schema::new_inferred(HashMap::new(), GroupSchema::default());
1762        assert!(
1763            schema
1764                .set_node_attribute(
1765                    &"key1".into(),
1766                    DataType::Int,
1767                    AttributeType::Continuous,
1768                    None
1769                )
1770                .is_ok()
1771        );
1772        assert_eq!(
1773            schema
1774                .ungrouped()
1775                .nodes()
1776                .get(&"key1".into())
1777                .unwrap()
1778                .data_type(),
1779            &DataType::Int
1780        );
1781        assert!(
1782            schema
1783                .set_node_attribute(
1784                    &"key1".into(),
1785                    DataType::Float,
1786                    AttributeType::Continuous,
1787                    None
1788                )
1789                .is_ok()
1790        );
1791        assert_eq!(
1792            schema
1793                .ungrouped()
1794                .nodes()
1795                .get(&"key1".into())
1796                .unwrap()
1797                .data_type(),
1798            &DataType::Float
1799        );
1800
1801        assert!(
1802            schema
1803                .set_node_attribute(
1804                    &"key1".into(),
1805                    DataType::Float,
1806                    AttributeType::Continuous,
1807                    Some(&"group1".into())
1808                )
1809                .is_ok()
1810        );
1811        assert_eq!(
1812            schema
1813                .group(&"group1".into())
1814                .unwrap()
1815                .nodes()
1816                .get(&"key1".into())
1817                .unwrap()
1818                .data_type(),
1819            &DataType::Float
1820        );
1821    }
1822
1823    #[test]
1824    fn test_schema_set_edge_attribute() {
1825        let mut schema = Schema::new_inferred(HashMap::new(), GroupSchema::default());
1826        assert!(
1827            schema
1828                .set_edge_attribute(
1829                    &"key1".into(),
1830                    DataType::Bool,
1831                    AttributeType::Categorical,
1832                    None
1833                )
1834                .is_ok()
1835        );
1836        assert_eq!(
1837            schema
1838                .ungrouped()
1839                .edges()
1840                .get(&"key1".into())
1841                .unwrap()
1842                .data_type(),
1843            &DataType::Bool
1844        );
1845        assert!(
1846            schema
1847                .set_edge_attribute(
1848                    &"key1".into(),
1849                    DataType::Float,
1850                    AttributeType::Continuous,
1851                    None
1852                )
1853                .is_ok()
1854        );
1855        assert_eq!(
1856            schema
1857                .ungrouped()
1858                .edges()
1859                .get(&"key1".into())
1860                .unwrap()
1861                .data_type(),
1862            &DataType::Float
1863        );
1864
1865        assert!(
1866            schema
1867                .set_edge_attribute(
1868                    &"key1".into(),
1869                    DataType::Float,
1870                    AttributeType::Continuous,
1871                    Some(&"group1".into())
1872                )
1873                .is_ok()
1874        );
1875        assert_eq!(
1876            schema
1877                .group(&"group1".into())
1878                .unwrap()
1879                .edges()
1880                .get(&"key1".into())
1881                .unwrap()
1882                .data_type(),
1883            &DataType::Float
1884        );
1885    }
1886
1887    #[test]
1888    fn test_schema_update_node_attribute() {
1889        let mut schema = Schema::new_inferred(HashMap::new(), GroupSchema::default());
1890        schema
1891            .set_node_attribute(
1892                &"key1".into(),
1893                DataType::Int,
1894                AttributeType::Continuous,
1895                None,
1896            )
1897            .unwrap();
1898        assert!(
1899            schema
1900                .update_node_attribute(
1901                    &"key1".into(),
1902                    DataType::Float,
1903                    AttributeType::Continuous,
1904                    None
1905                )
1906                .is_ok()
1907        );
1908        assert_eq!(
1909            schema
1910                .ungrouped()
1911                .nodes()
1912                .get(&"key1".into())
1913                .unwrap()
1914                .data_type(),
1915            &DataType::Union((Box::new(DataType::Int), Box::new(DataType::Float)))
1916        );
1917
1918        schema
1919            .set_node_attribute(
1920                &"key1".into(),
1921                DataType::Int,
1922                AttributeType::Continuous,
1923                Some(&"group1".into()),
1924            )
1925            .unwrap();
1926        assert!(
1927            schema
1928                .update_node_attribute(
1929                    &"key1".into(),
1930                    DataType::Float,
1931                    AttributeType::Continuous,
1932                    Some(&"group1".into())
1933                )
1934                .is_ok()
1935        );
1936        assert_eq!(
1937            schema
1938                .group(&"group1".into())
1939                .unwrap()
1940                .nodes()
1941                .get(&"key1".into())
1942                .unwrap()
1943                .data_type(),
1944            &DataType::Union((Box::new(DataType::Int), Box::new(DataType::Float)))
1945        );
1946    }
1947
1948    #[test]
1949    fn test_schema_update_edge_attribute() {
1950        let mut schema = Schema::new_inferred(HashMap::new(), GroupSchema::default());
1951        schema
1952            .set_edge_attribute(
1953                &"key1".into(),
1954                DataType::Bool,
1955                AttributeType::Categorical,
1956                None,
1957            )
1958            .unwrap();
1959        assert!(
1960            schema
1961                .update_edge_attribute(
1962                    &"key1".into(),
1963                    DataType::String,
1964                    AttributeType::Unstructured,
1965                    None
1966                )
1967                .is_ok()
1968        );
1969        assert_eq!(
1970            schema
1971                .ungrouped()
1972                .edges()
1973                .get(&"key1".into())
1974                .unwrap()
1975                .data_type(),
1976            &DataType::Union((Box::new(DataType::Bool), Box::new(DataType::String)))
1977        );
1978
1979        schema
1980            .set_edge_attribute(
1981                &"key1".into(),
1982                DataType::Bool,
1983                AttributeType::Categorical,
1984                Some(&"group1".into()),
1985            )
1986            .unwrap();
1987        assert!(
1988            schema
1989                .update_edge_attribute(
1990                    &"key1".into(),
1991                    DataType::String,
1992                    AttributeType::Unstructured,
1993                    Some(&"group1".into())
1994                )
1995                .is_ok()
1996        );
1997        assert_eq!(
1998            schema
1999                .group(&"group1".into())
2000                .unwrap()
2001                .edges()
2002                .get(&"key1".into())
2003                .unwrap()
2004                .data_type(),
2005            &DataType::Union((Box::new(DataType::Bool), Box::new(DataType::String)))
2006        );
2007    }
2008
2009    #[test]
2010    fn test_schema_remove_node_attribute() {
2011        let mut schema = Schema::new_inferred(HashMap::new(), GroupSchema::default());
2012        schema
2013            .set_node_attribute(
2014                &"key1".into(),
2015                DataType::Int,
2016                AttributeType::Continuous,
2017                None,
2018            )
2019            .unwrap();
2020        schema.remove_node_attribute(&"key1".into(), None);
2021        assert!(!schema.ungrouped().nodes().contains_key(&"key1".into()));
2022
2023        schema
2024            .set_node_attribute(
2025                &"key1".into(),
2026                DataType::Int,
2027                AttributeType::Continuous,
2028                Some(&"group1".into()),
2029            )
2030            .unwrap();
2031        schema.remove_node_attribute(&"key1".into(), Some(&"group1".into()));
2032        assert!(
2033            !schema
2034                .group(&"group1".into())
2035                .unwrap()
2036                .nodes()
2037                .contains_key(&"key1".into())
2038        );
2039    }
2040
2041    #[test]
2042    fn test_schema_remove_edge_attribute() {
2043        let mut schema = Schema::new_inferred(HashMap::new(), GroupSchema::default());
2044        schema
2045            .set_edge_attribute(
2046                &"key1".into(),
2047                DataType::Bool,
2048                AttributeType::Categorical,
2049                None,
2050            )
2051            .unwrap();
2052        schema.remove_edge_attribute(&"key1".into(), None);
2053        assert!(!schema.ungrouped().edges().contains_key(&"key1".into()));
2054
2055        schema
2056            .set_edge_attribute(
2057                &"key1".into(),
2058                DataType::Bool,
2059                AttributeType::Categorical,
2060                Some(&"group1".into()),
2061            )
2062            .unwrap();
2063        schema.remove_edge_attribute(&"key1".into(), Some(&"group1".into()));
2064        assert!(
2065            !schema
2066                .group(&"group1".into())
2067                .unwrap()
2068                .edges()
2069                .contains_key(&"key1".into())
2070        );
2071    }
2072
2073    #[test]
2074    fn test_schema_add_group() {
2075        let attribute_schema = AttributeSchema::new(
2076            vec![
2077                (
2078                    "key1".into(),
2079                    AttributeDataType::new(DataType::Int, AttributeType::Categorical)
2080                        .expect("AttributeType was inferred from DataType."),
2081                ),
2082                (
2083                    "key2".into(),
2084                    AttributeDataType::new(DataType::Float, AttributeType::Continuous)
2085                        .expect("AttributeType was inferred from DataType."),
2086                ),
2087            ]
2088            .into_iter()
2089            .collect(),
2090        );
2091
2092        let mut schema = Schema::new_inferred(HashMap::new(), GroupSchema::default());
2093        schema
2094            .add_group(
2095                "group1".into(),
2096                GroupSchema::new(attribute_schema.clone(), AttributeSchema::default()),
2097            )
2098            .unwrap();
2099        assert_eq!(
2100            attribute_schema,
2101            schema.group(&"group1".into()).unwrap().nodes
2102        );
2103
2104        assert!(
2105            schema
2106                .add_group("group1".into(), GroupSchema::default())
2107                .is_err_and(|error| { matches!(error, crate::errors::GraphError::SchemaError(_)) })
2108        );
2109    }
2110
2111    #[test]
2112    fn test_schema_remove_group() {
2113        let mut schema = Schema::new_inferred(
2114            vec![("group1".into(), GroupSchema::default())]
2115                .into_iter()
2116                .collect(),
2117            GroupSchema::default(),
2118        );
2119        schema.remove_group(&"group1".into());
2120        assert!(!schema.groups().contains_key(&"group1".into()));
2121    }
2122
2123    #[test]
2124    fn test_schema_freeze_unfreeze() {
2125        let mut schema = Schema::new_inferred(HashMap::new(), GroupSchema::default());
2126        assert_eq!(schema.schema_type(), &SchemaType::Inferred);
2127
2128        schema.freeze();
2129        assert_eq!(schema.schema_type(), &SchemaType::Provided);
2130
2131        schema.unfreeze();
2132        assert_eq!(schema.schema_type(), &SchemaType::Inferred);
2133    }
2134}