Skip to main content

graphrecords_core/graphrecord/
mod.rs

1pub mod attributes;
2#[cfg(feature = "connectors")]
3pub mod connector;
4pub mod datatypes;
5mod graph;
6mod group_mapping;
7#[cfg(feature = "plugins")]
8pub mod plugins;
9mod polars;
10pub mod schema;
11
12pub use self::{
13    datatypes::{GraphRecordAttribute, GraphRecordValue},
14    graph::{AttributeMap, EdgeIndex, NodeIndex},
15    group_mapping::Group,
16};
17#[cfg(feature = "serde")]
18use crate::errors::ConversionError;
19#[cfg(feature = "plugins")]
20use crate::graphrecord::plugins::{Plugin, PluginName};
21use crate::{
22    errors::{GraphRecordError, GraphRecordResult, SchemaError},
23    graphrecord::{
24        attributes::{EdgeAttributesMut, NodeAttributesMut},
25        polars::DataFramesExport,
26    },
27};
28use ::polars::frame::DataFrame;
29use graph::Graph;
30#[cfg(feature = "plugins")]
31use graphrecords_utils::aliases::GrHashMap;
32use graphrecords_utils::aliases::GrHashSet;
33use group_mapping::GroupMapping;
34use polars::{dataframe_to_edges, dataframe_to_nodes};
35use schema::{GroupSchema, Schema, SchemaType};
36#[cfg(feature = "serde")]
37use serde::{Deserialize, Serialize};
38#[cfg(feature = "plugins")]
39use std::sync::Arc;
40use std::{
41    collections::{HashMap, hash_map::Entry},
42    mem,
43};
44#[cfg(feature = "serde")]
45use std::{fs, path::Path};
46
47#[derive(Debug, Clone)]
48pub struct NodeDataFrameInput {
49    pub dataframe: DataFrame,
50    pub index_column: String,
51}
52
53#[derive(Debug, Clone)]
54pub struct EdgeDataFrameInput {
55    pub dataframe: DataFrame,
56    pub source_index_column: String,
57    pub target_index_column: String,
58}
59
60impl<D, S> From<(D, S)> for NodeDataFrameInput
61where
62    D: Into<DataFrame>,
63    S: Into<String>,
64{
65    fn from(val: (D, S)) -> Self {
66        Self {
67            dataframe: val.0.into(),
68            index_column: val.1.into(),
69        }
70    }
71}
72
73impl<D, S> From<(D, S, S)> for EdgeDataFrameInput
74where
75    D: Into<DataFrame>,
76    S: Into<String>,
77{
78    fn from(val: (D, S, S)) -> Self {
79        Self {
80            dataframe: val.0.into(),
81            source_index_column: val.1.into(),
82            target_index_column: val.2.into(),
83        }
84    }
85}
86
87fn node_dataframes_to_tuples(
88    nodes_dataframes: impl IntoIterator<Item = impl Into<NodeDataFrameInput>>,
89) -> GraphRecordResult<Vec<(NodeIndex, AttributeMap)>> {
90    let nodes = nodes_dataframes
91        .into_iter()
92        .map(|dataframe_input| {
93            let dataframe_input = dataframe_input.into();
94
95            dataframe_to_nodes(dataframe_input.dataframe, &dataframe_input.index_column)
96        })
97        .collect::<GraphRecordResult<Vec<_>>>()?
98        .into_iter()
99        .flatten()
100        .collect();
101
102    Ok(nodes)
103}
104
105fn edge_dataframes_to_tuples(
106    edges_dataframes: impl IntoIterator<Item = impl Into<EdgeDataFrameInput>>,
107) -> GraphRecordResult<Vec<(NodeIndex, NodeIndex, AttributeMap)>> {
108    let edges = edges_dataframes
109        .into_iter()
110        .map(|dataframe_input| {
111            let dataframe_input = dataframe_input.into();
112
113            dataframe_to_edges(
114                dataframe_input.dataframe,
115                &dataframe_input.source_index_column,
116                &dataframe_input.target_index_column,
117            )
118        })
119        .collect::<GraphRecordResult<Vec<_>>>()?
120        .into_iter()
121        .flatten()
122        .collect();
123
124    Ok(edges)
125}
126
127#[allow(clippy::type_complexity)]
128fn dataframes_to_tuples(
129    nodes_dataframes: impl IntoIterator<Item = impl Into<NodeDataFrameInput>>,
130    edges_dataframes: impl IntoIterator<Item = impl Into<EdgeDataFrameInput>>,
131) -> GraphRecordResult<(
132    Vec<(NodeIndex, AttributeMap)>,
133    Vec<(NodeIndex, NodeIndex, AttributeMap)>,
134)> {
135    let nodes = node_dataframes_to_tuples(nodes_dataframes)?;
136    let edges = edge_dataframes_to_tuples(edges_dataframes)?;
137
138    Ok((nodes, edges))
139}
140
141#[derive(Default, Debug, Clone)]
142#[allow(clippy::unsafe_derive_deserialize)]
143#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
144pub struct GraphRecord {
145    graph: Graph,
146    group_mapping: GroupMapping,
147    schema: Schema,
148
149    #[cfg(feature = "plugins")]
150    plugins: Arc<GrHashMap<PluginName, Box<dyn Plugin>>>,
151}
152
153impl GraphRecord {
154    #[must_use]
155    pub fn new() -> Self {
156        Self::default()
157    }
158
159    #[must_use]
160    pub fn with_schema(schema: Schema) -> Self {
161        Self {
162            schema,
163            ..Default::default()
164        }
165    }
166
167    #[must_use]
168    pub fn with_capacity(nodes: usize, edges: usize, schema: Option<Schema>) -> Self {
169        Self {
170            graph: Graph::with_capacity(nodes, edges),
171            schema: schema.unwrap_or_default(),
172            ..Default::default()
173        }
174    }
175
176    pub fn from_tuples(
177        nodes: Vec<(NodeIndex, AttributeMap)>,
178        edges: Option<Vec<(NodeIndex, NodeIndex, AttributeMap)>>,
179        schema: Option<Schema>,
180    ) -> GraphRecordResult<Self> {
181        let mut graphrecord = Self::with_capacity(
182            nodes.len(),
183            edges.as_ref().map_or(0, std::vec::Vec::len),
184            schema,
185        );
186
187        for (node_index, attributes) in nodes {
188            graphrecord.add_node_impl(node_index, attributes)?;
189        }
190
191        if let Some(edges) = edges {
192            for (source_node_index, target_node_index, attributes) in edges {
193                graphrecord.add_edge_impl(source_node_index, target_node_index, attributes)?;
194            }
195        }
196
197        Ok(graphrecord)
198    }
199
200    pub fn from_dataframes(
201        nodes_dataframes: impl IntoIterator<Item = impl Into<NodeDataFrameInput>>,
202        edges_dataframes: impl IntoIterator<Item = impl Into<EdgeDataFrameInput>>,
203        schema: Option<Schema>,
204    ) -> GraphRecordResult<Self> {
205        let (nodes, edges) = dataframes_to_tuples(nodes_dataframes, edges_dataframes)?;
206
207        Self::from_tuples(nodes, Some(edges), schema)
208    }
209
210    pub fn from_nodes_dataframes(
211        nodes_dataframes: impl IntoIterator<Item = impl Into<NodeDataFrameInput>>,
212        schema: Option<Schema>,
213    ) -> GraphRecordResult<Self> {
214        let nodes = node_dataframes_to_tuples(nodes_dataframes)?;
215
216        Self::from_tuples(nodes, None, schema)
217    }
218
219    #[cfg(feature = "serde")]
220    pub fn from_ron<P>(path: P) -> GraphRecordResult<Self>
221    where
222        P: AsRef<Path>,
223    {
224        let file = fs::read_to_string(&path).map_err(|error| {
225            GraphRecordError::Conversion(ConversionError::FileRead {
226                path: path.as_ref().display().to_string(),
227                kind: error.kind(),
228            })
229        })?;
230
231        ron::from_str(&file).map_err(|_| {
232            GraphRecordError::Conversion(ConversionError::RonDeserialization {
233                path: path.as_ref().display().to_string(),
234            })
235        })
236    }
237
238    #[cfg(feature = "serde")]
239    pub fn to_ron<P>(&self, path: P) -> GraphRecordResult<()>
240    where
241        P: AsRef<Path>,
242    {
243        let ron_string = ron::to_string(self)
244            .map_err(|_| GraphRecordError::Conversion(ConversionError::RonSerialization))?;
245
246        if let Some(parent) = path.as_ref().parent() {
247            fs::create_dir_all(parent).map_err(|error| {
248                GraphRecordError::Conversion(ConversionError::DirectoryCreation {
249                    path: parent.display().to_string(),
250                    kind: error.kind(),
251                })
252            })?;
253        }
254
255        fs::write(&path, ron_string).map_err(|error| {
256            GraphRecordError::Conversion(ConversionError::FileWrite {
257                path: path.as_ref().display().to_string(),
258                kind: error.kind(),
259            })
260        })
261    }
262
263    pub fn to_dataframes(&self) -> GraphRecordResult<DataFramesExport> {
264        DataFramesExport::new(self)
265    }
266
267    #[allow(clippy::too_many_lines)]
268    fn set_schema_impl(&mut self, mut schema: Schema) -> GraphRecordResult<()> {
269        let mut nodes_group_cache = HashMap::<&Group, usize>::new();
270        let mut nodes_ungrouped_visited = false;
271        let mut edges_group_cache = HashMap::<&Group, usize>::new();
272        let mut edges_ungrouped_visited = false;
273
274        for (node_index, node) in &self.graph.nodes {
275            let groups_of_node: Vec<_> = self
276                .groups_of_node(node_index)
277                .expect("groups of node must exist")
278                .collect();
279
280            if groups_of_node.is_empty() {
281                match schema.schema_type() {
282                    SchemaType::Inferred => {
283                        let nodes_in_groups = self.group_mapping.nodes_in_group.len();
284
285                        let nodes_not_in_groups = self.graph.node_count() - nodes_in_groups;
286
287                        schema.update_node(
288                            &node.attributes,
289                            None,
290                            nodes_not_in_groups == 0 || !nodes_ungrouped_visited,
291                        );
292
293                        nodes_ungrouped_visited = true;
294                    }
295                    SchemaType::Provided => {
296                        schema.validate_node(node_index, &node.attributes, None)?;
297                    }
298                }
299            } else {
300                for group in groups_of_node {
301                    match schema.schema_type() {
302                        SchemaType::Inferred => match nodes_group_cache.entry(group) {
303                            Entry::Occupied(entry) => {
304                                schema.update_node(
305                                    &node.attributes,
306                                    Some(group),
307                                    *entry.get() == 0,
308                                );
309                            }
310                            Entry::Vacant(entry) => {
311                                entry.insert(
312                                    self.group_mapping
313                                        .nodes_in_group
314                                        .get(group)
315                                        .map_or(0, GrHashSet::len),
316                                );
317
318                                schema.update_node(&node.attributes, Some(group), true);
319                            }
320                        },
321                        SchemaType::Provided => {
322                            schema.validate_node(node_index, &node.attributes, Some(group))?;
323                        }
324                    }
325                }
326            }
327        }
328
329        for (edge_index, edge) in &self.graph.edges {
330            let groups_of_edge: Vec<_> = self
331                .groups_of_edge(edge_index)
332                .expect("groups of edge must exist")
333                .collect();
334
335            if groups_of_edge.is_empty() {
336                match schema.schema_type() {
337                    SchemaType::Inferred => {
338                        let edges_in_groups = self.group_mapping.edges_in_group.len();
339
340                        let edges_not_in_groups = self.graph.edge_count() - edges_in_groups;
341
342                        schema.update_edge(
343                            &edge.attributes,
344                            None,
345                            edges_not_in_groups == 0 || !edges_ungrouped_visited,
346                        );
347
348                        edges_ungrouped_visited = true;
349                    }
350                    SchemaType::Provided => {
351                        schema.validate_edge(edge_index, &edge.attributes, None)?;
352                    }
353                }
354            } else {
355                for group in groups_of_edge {
356                    match schema.schema_type() {
357                        SchemaType::Inferred => match edges_group_cache.entry(group) {
358                            Entry::Occupied(entry) => {
359                                schema.update_edge(
360                                    &edge.attributes,
361                                    Some(group),
362                                    *entry.get() == 0,
363                                );
364                            }
365                            Entry::Vacant(entry) => {
366                                entry.insert(
367                                    self.group_mapping
368                                        .edges_in_group
369                                        .get(group)
370                                        .map_or(0, GrHashSet::len),
371                                );
372
373                                schema.update_edge(&edge.attributes, Some(group), true);
374                            }
375                        },
376                        SchemaType::Provided => {
377                            schema.validate_edge(edge_index, &edge.attributes, Some(group))?;
378                        }
379                    }
380                }
381            }
382        }
383
384        mem::swap(&mut self.schema, &mut schema);
385
386        Ok(())
387    }
388
389    /// # Safety
390    ///
391    /// This function should only be used if the data has been validated against the schema.
392    /// Using this function with invalid data may lead to undefined behavior.
393    /// This function does not run any plugin hooks.
394    pub const unsafe fn set_schema_unchecked(&mut self, schema: &mut Schema) {
395        mem::swap(&mut self.schema, schema);
396    }
397
398    #[must_use]
399    pub const fn get_schema(&self) -> &Schema {
400        &self.schema
401    }
402
403    const fn freeze_schema_impl(&mut self) {
404        self.schema.freeze();
405    }
406
407    const fn unfreeze_schema_impl(&mut self) {
408        self.schema.unfreeze();
409    }
410
411    pub fn node_indices(&self) -> impl Iterator<Item = &NodeIndex> {
412        self.graph.node_indices()
413    }
414
415    pub fn resolve_node_index(&self, node_index: &NodeIndex) -> GraphRecordResult<&NodeIndex> {
416        self.graph.resolve_node_index(node_index)
417    }
418
419    pub fn node_attributes(&self, node_index: &NodeIndex) -> GraphRecordResult<&AttributeMap> {
420        self.graph.node_attributes(node_index)
421    }
422
423    pub fn node_attributes_mut<'a>(
424        &'a mut self,
425        node_index: &'a NodeIndex,
426    ) -> GraphRecordResult<NodeAttributesMut<'a>> {
427        NodeAttributesMut::new(node_index, self)
428    }
429
430    pub fn outgoing_edges(
431        &self,
432        node_index: &NodeIndex,
433    ) -> GraphRecordResult<impl Iterator<Item = &EdgeIndex> + use<'_>> {
434        self.graph.outgoing_edges(node_index)
435    }
436
437    pub fn incoming_edges(
438        &self,
439        node_index: &NodeIndex,
440    ) -> GraphRecordResult<impl Iterator<Item = &EdgeIndex> + use<'_>> {
441        self.graph.incoming_edges(node_index)
442    }
443
444    pub fn edge_indices(&self) -> impl Iterator<Item = &EdgeIndex> {
445        self.graph.edge_indices()
446    }
447
448    pub fn resolve_edge_index(&self, edge_index: &EdgeIndex) -> GraphRecordResult<&EdgeIndex> {
449        self.graph.resolve_edge_index(edge_index)
450    }
451
452    pub fn edge_attributes(&self, edge_index: &EdgeIndex) -> GraphRecordResult<&AttributeMap> {
453        self.graph.edge_attributes(edge_index)
454    }
455
456    pub fn edge_attributes_mut<'a>(
457        &'a mut self,
458        edge_index: &'a EdgeIndex,
459    ) -> GraphRecordResult<EdgeAttributesMut<'a>> {
460        EdgeAttributesMut::new(edge_index, self)
461    }
462
463    pub fn edge_endpoints(
464        &self,
465        edge_index: &EdgeIndex,
466    ) -> GraphRecordResult<(&NodeIndex, &NodeIndex)> {
467        self.graph.edge_endpoints(edge_index)
468    }
469
470    pub fn edges_connecting<'a>(
471        &'a self,
472        outgoing_node_indices: Vec<&'a NodeIndex>,
473        incoming_node_indices: Vec<&'a NodeIndex>,
474    ) -> impl Iterator<Item = &'a EdgeIndex> + 'a {
475        self.graph
476            .edges_connecting(outgoing_node_indices, incoming_node_indices)
477    }
478
479    pub fn edges_connecting_undirected<'a>(
480        &'a self,
481        first_node_indices: Vec<&'a NodeIndex>,
482        second_node_indices: Vec<&'a NodeIndex>,
483    ) -> impl Iterator<Item = &'a EdgeIndex> + 'a {
484        self.graph
485            .edges_connecting_undirected(first_node_indices, second_node_indices)
486    }
487
488    fn add_node_impl(
489        &mut self,
490        node_index: NodeIndex,
491        attributes: AttributeMap,
492    ) -> GraphRecordResult<()> {
493        match self.schema.schema_type() {
494            SchemaType::Inferred => {
495                let nodes_in_groups = self.group_mapping.nodes_in_group.len();
496
497                let nodes_not_in_groups = self.graph.node_count() - nodes_in_groups;
498
499                self.schema
500                    .update_node(&attributes, None, nodes_not_in_groups == 0);
501            }
502            SchemaType::Provided => {
503                self.schema.validate_node(&node_index, &attributes, None)?;
504            }
505        }
506
507        self.graph.add_node(node_index, attributes)
508    }
509
510    // TODO: Add tests
511    #[allow(clippy::needless_pass_by_value)]
512    fn add_node_with_group_impl(
513        &mut self,
514        node_index: NodeIndex,
515        attributes: AttributeMap,
516        group: Group,
517    ) -> GraphRecordResult<()> {
518        match self.schema.schema_type() {
519            SchemaType::Inferred => {
520                let nodes_in_group = self
521                    .group_mapping
522                    .nodes_in_group
523                    .get(&group)
524                    .map_or(0, GrHashSet::len);
525
526                self.schema
527                    .update_node(&attributes, Some(&group), nodes_in_group == 0);
528            }
529            SchemaType::Provided => {
530                self.schema
531                    .validate_node(&node_index, &attributes, Some(&group))?;
532            }
533        }
534
535        self.graph.add_node(node_index.clone(), attributes)?;
536
537        self.group_mapping
538            .add_node_to_group(group, node_index.clone())
539            .inspect_err(|_| {
540                self.graph
541                    .remove_node(&node_index, &mut self.group_mapping)
542                    .expect("Node must exist");
543            })
544    }
545
546    fn add_node_with_groups_impl(
547        &mut self,
548        node_index: NodeIndex,
549        attributes: AttributeMap,
550        groups: impl AsRef<[Group]>,
551    ) -> GraphRecordResult<()> {
552        let groups = groups.as_ref();
553
554        match groups.split_first() {
555            None => self.add_node_impl(node_index, attributes),
556            Some((first, rest)) => {
557                self.add_node_with_group_impl(node_index.clone(), attributes, first.clone())?;
558
559                for group in rest {
560                    self.add_node_to_group_impl(group.clone(), node_index.clone())
561                        .inspect_err(|_| {
562                            self.graph
563                                .remove_node(&node_index, &mut self.group_mapping)
564                                .expect("Node must exist");
565                        })?;
566                }
567
568                Ok(())
569            }
570        }
571    }
572
573    fn remove_node_impl(&mut self, node_index: &NodeIndex) -> GraphRecordResult<AttributeMap> {
574        self.graph.remove_node(node_index, &mut self.group_mapping)
575    }
576
577    fn add_nodes_impl(&mut self, nodes: Vec<(NodeIndex, AttributeMap)>) -> GraphRecordResult<()> {
578        for (node_index, attributes) in nodes {
579            self.add_node_impl(node_index, attributes)?;
580        }
581
582        Ok(())
583    }
584
585    // TODO: Add tests
586    #[allow(clippy::needless_pass_by_value)]
587    fn add_nodes_with_group_impl(
588        &mut self,
589        nodes: Vec<(NodeIndex, AttributeMap)>,
590        group: Group,
591    ) -> GraphRecordResult<()> {
592        if !self.contains_group(&group) {
593            self.add_group_impl(group.clone(), None, None)?;
594        }
595
596        for (node_index, attributes) in nodes {
597            self.add_node_with_group_impl(node_index, attributes, group.clone())?;
598        }
599
600        Ok(())
601    }
602
603    fn add_nodes_with_groups_impl(
604        &mut self,
605        nodes: Vec<(NodeIndex, AttributeMap)>,
606        groups: impl AsRef<[Group]>,
607    ) -> GraphRecordResult<()> {
608        let groups = groups.as_ref();
609
610        for group in groups {
611            if !self.contains_group(group) {
612                self.add_group_impl(group.clone(), None, None)?;
613            }
614        }
615
616        for (node_index, attributes) in nodes {
617            self.add_node_with_groups_impl(node_index, attributes, groups)?;
618        }
619
620        Ok(())
621    }
622
623    fn add_nodes_dataframes_impl(
624        &mut self,
625        nodes_dataframes: impl IntoIterator<Item = impl Into<NodeDataFrameInput>>,
626    ) -> GraphRecordResult<()> {
627        self.add_nodes_impl(node_dataframes_to_tuples(nodes_dataframes)?)
628    }
629
630    // TODO: Add tests
631    fn add_nodes_dataframes_with_group_impl(
632        &mut self,
633        nodes_dataframes: impl IntoIterator<Item = impl Into<NodeDataFrameInput>>,
634        group: Group,
635    ) -> GraphRecordResult<()> {
636        self.add_nodes_with_group_impl(node_dataframes_to_tuples(nodes_dataframes)?, group)
637    }
638
639    fn add_nodes_dataframes_with_groups_impl(
640        &mut self,
641        nodes_dataframes: impl IntoIterator<Item = impl Into<NodeDataFrameInput>>,
642        groups: impl AsRef<[Group]>,
643    ) -> GraphRecordResult<()> {
644        self.add_nodes_with_groups_impl(node_dataframes_to_tuples(nodes_dataframes)?, groups)
645    }
646
647    #[allow(clippy::needless_pass_by_value)]
648    fn add_edge_impl(
649        &mut self,
650        source_node_index: NodeIndex,
651        target_node_index: NodeIndex,
652        attributes: AttributeMap,
653    ) -> GraphRecordResult<EdgeIndex> {
654        let edge_index =
655            self.graph
656                .add_edge(source_node_index, target_node_index, attributes.clone())?;
657
658        match self.schema.schema_type() {
659            SchemaType::Inferred => {
660                let edges_in_groups = self.group_mapping.edges_in_group.len();
661
662                let edges_not_in_groups = self.graph.edge_count() - edges_in_groups;
663
664                self.schema
665                    .update_edge(&attributes, None, edges_not_in_groups <= 1);
666
667                Ok(edge_index)
668            }
669            SchemaType::Provided => {
670                match self.schema.validate_edge(&edge_index, &attributes, None) {
671                    Ok(()) => Ok(edge_index),
672                    Err(e) => {
673                        self.graph
674                            .remove_edge(&edge_index)
675                            .expect("Edge must exist");
676
677                        Err(e.into())
678                    }
679                }
680            }
681        }
682    }
683
684    // TODO: Add tests
685    #[allow(clippy::needless_pass_by_value)]
686    fn add_edge_with_group_impl(
687        &mut self,
688        source_node_index: NodeIndex,
689        target_node_index: NodeIndex,
690        attributes: AttributeMap,
691        group: Group,
692    ) -> GraphRecordResult<EdgeIndex> {
693        let edge_index =
694            self.graph
695                .add_edge(source_node_index, target_node_index, attributes.clone())?;
696
697        match self.schema.schema_type() {
698            SchemaType::Inferred => {
699                let edges_in_group = self
700                    .group_mapping
701                    .edges_in_group
702                    .get(&group)
703                    .map_or(0, GrHashSet::len);
704
705                self.schema
706                    .update_edge(&attributes, Some(&group), edges_in_group == 0);
707            }
708            SchemaType::Provided => {
709                self.schema
710                    .validate_edge(&edge_index, &attributes, Some(&group))
711                    .inspect_err(|_| {
712                        self.graph
713                            .remove_edge(&edge_index)
714                            .expect("Edge must exist");
715                    })?;
716            }
717        }
718
719        self.group_mapping
720            .add_edge_to_group(group, edge_index)
721            .inspect_err(|_| {
722                self.graph
723                    .remove_edge(&edge_index)
724                    .expect("Edge must exist");
725            })?;
726
727        Ok(edge_index)
728    }
729
730    fn add_edge_with_groups_impl(
731        &mut self,
732        source_node_index: NodeIndex,
733        target_node_index: NodeIndex,
734        attributes: AttributeMap,
735        groups: impl AsRef<[Group]>,
736    ) -> GraphRecordResult<EdgeIndex> {
737        let groups = groups.as_ref();
738
739        match groups.split_first() {
740            None => self.add_edge_impl(source_node_index, target_node_index, attributes),
741            Some((first, rest)) => {
742                let edge_index = self.add_edge_with_group_impl(
743                    source_node_index,
744                    target_node_index,
745                    attributes,
746                    first.clone(),
747                )?;
748
749                for group in rest {
750                    self.add_edge_to_group_impl(group.clone(), edge_index)
751                        .inspect_err(|_| {
752                            self.graph
753                                .remove_edge(&edge_index)
754                                .expect("Edge must exist");
755                        })?;
756                }
757
758                Ok(edge_index)
759            }
760        }
761    }
762
763    #[allow(clippy::trivially_copy_pass_by_ref)]
764    fn remove_edge_impl(&mut self, edge_index: &EdgeIndex) -> GraphRecordResult<AttributeMap> {
765        self.group_mapping.remove_edge(edge_index);
766
767        self.graph.remove_edge(edge_index)
768    }
769
770    fn add_edges_impl(
771        &mut self,
772        edges: Vec<(NodeIndex, NodeIndex, AttributeMap)>,
773    ) -> GraphRecordResult<Vec<EdgeIndex>> {
774        edges
775            .into_iter()
776            .map(|(source_node_index, target_node_index, attributes)| {
777                self.add_edge_impl(source_node_index, target_node_index, attributes)
778            })
779            .collect()
780    }
781
782    // TODO: Add tests
783    fn add_edges_with_group_impl(
784        &mut self,
785        edges: Vec<(NodeIndex, NodeIndex, AttributeMap)>,
786        group: &Group,
787    ) -> GraphRecordResult<Vec<EdgeIndex>> {
788        if !self.contains_group(group) {
789            self.add_group_impl(group.clone(), None, None)?;
790        }
791
792        edges
793            .into_iter()
794            .map(|(source_node_index, target_node_index, attributes)| {
795                self.add_edge_with_group_impl(
796                    source_node_index,
797                    target_node_index,
798                    attributes,
799                    group.clone(),
800                )
801            })
802            .collect()
803    }
804
805    fn add_edges_with_groups_impl(
806        &mut self,
807        edges: Vec<(NodeIndex, NodeIndex, AttributeMap)>,
808        groups: impl AsRef<[Group]>,
809    ) -> GraphRecordResult<Vec<EdgeIndex>> {
810        let groups = groups.as_ref();
811
812        for group in groups {
813            if !self.contains_group(group) {
814                self.add_group_impl(group.clone(), None, None)?;
815            }
816        }
817
818        edges
819            .into_iter()
820            .map(|(source_node_index, target_node_index, attributes)| {
821                self.add_edge_with_groups_impl(
822                    source_node_index,
823                    target_node_index,
824                    attributes,
825                    groups,
826                )
827            })
828            .collect()
829    }
830
831    fn add_edges_dataframes_impl(
832        &mut self,
833        edges_dataframes: impl IntoIterator<Item = impl Into<EdgeDataFrameInput>>,
834    ) -> GraphRecordResult<Vec<EdgeIndex>> {
835        self.add_edges_impl(edge_dataframes_to_tuples(edges_dataframes)?)
836    }
837
838    // TODO: Add tests
839    fn add_edges_dataframes_with_group_impl(
840        &mut self,
841        edges_dataframes: impl IntoIterator<Item = impl Into<EdgeDataFrameInput>>,
842        group: &Group,
843    ) -> GraphRecordResult<Vec<EdgeIndex>> {
844        self.add_edges_with_group_impl(edge_dataframes_to_tuples(edges_dataframes)?, group)
845    }
846
847    fn add_edges_dataframes_with_groups_impl(
848        &mut self,
849        edges_dataframes: impl IntoIterator<Item = impl Into<EdgeDataFrameInput>>,
850        groups: impl AsRef<[Group]>,
851    ) -> GraphRecordResult<Vec<EdgeIndex>> {
852        self.add_edges_with_groups_impl(edge_dataframes_to_tuples(edges_dataframes)?, groups)
853    }
854
855    fn add_group_impl(
856        &mut self,
857        group: Group,
858        node_indices: Option<Vec<NodeIndex>>,
859        edge_indices: Option<Vec<EdgeIndex>>,
860    ) -> GraphRecordResult<()> {
861        if self.group_mapping.contains_group(&group) {
862            return Err(GraphRecordError::GroupAlreadyExists {
863                group: group.clone(),
864            });
865        }
866
867        if let Some(ref node_indices) = node_indices {
868            for node_index in node_indices {
869                if !self.graph.contains_node(node_index) {
870                    return Err(GraphRecordError::NodeNotFound {
871                        node_index: node_index.clone(),
872                    });
873                }
874            }
875        }
876
877        if let Some(ref edge_indices) = edge_indices {
878            for edge_index in edge_indices {
879                if !self.graph.contains_edge(edge_index) {
880                    return Err(GraphRecordError::EdgeNotFound {
881                        edge_index: *edge_index,
882                    });
883                }
884            }
885        }
886
887        match self.schema.schema_type() {
888            SchemaType::Inferred => {
889                if !self.schema.groups().contains_key(&group) {
890                    self.schema
891                        .add_group(group.clone(), GroupSchema::default())?;
892                }
893
894                if let Some(ref node_indices) = node_indices {
895                    let mut empty = true;
896
897                    for node_index in node_indices {
898                        let node_attributes = self.graph.node_attributes(node_index)?;
899
900                        self.schema
901                            .update_node(node_attributes, Some(&group), empty);
902
903                        empty = false;
904                    }
905                }
906
907                if let Some(ref edge_indices) = edge_indices {
908                    let mut empty = true;
909
910                    for edge_index in edge_indices {
911                        let edge_attributes = self.graph.edge_attributes(edge_index)?;
912
913                        self.schema
914                            .update_edge(edge_attributes, Some(&group), empty);
915
916                        empty = false;
917                    }
918                }
919            }
920            SchemaType::Provided => {
921                if !self.schema.groups().contains_key(&group) {
922                    return Err(SchemaError::GroupNotInSchema {
923                        group: group.clone(),
924                    }
925                    .into());
926                }
927
928                if let Some(ref node_indices) = node_indices {
929                    for node_index in node_indices {
930                        let node_attributes = self.graph.node_attributes(node_index)?;
931
932                        self.schema
933                            .validate_node(node_index, node_attributes, Some(&group))?;
934                    }
935                }
936
937                if let Some(ref edge_indices) = edge_indices {
938                    for edge_index in edge_indices {
939                        let edge_attributes = self.graph.edge_attributes(edge_index)?;
940
941                        self.schema
942                            .validate_edge(edge_index, edge_attributes, Some(&group))?;
943                    }
944                }
945            }
946        }
947
948        self.group_mapping
949            .add_group(group, node_indices, edge_indices)
950            .expect("Group must not exist");
951
952        Ok(())
953    }
954
955    fn remove_group_impl(&mut self, group: &Group) -> GraphRecordResult<()> {
956        self.group_mapping.remove_group(group)
957    }
958
959    fn add_node_to_group_impl(
960        &mut self,
961        group: Group,
962        node_index: NodeIndex,
963    ) -> GraphRecordResult<()> {
964        let node_attributes = self.graph.node_attributes(&node_index)?;
965
966        match self.schema.schema_type() {
967            SchemaType::Inferred => {
968                let nodes_in_group = self
969                    .group_mapping
970                    .nodes_in_group
971                    .get(&group)
972                    .map_or(0, GrHashSet::len);
973
974                self.schema
975                    .update_node(node_attributes, Some(&group), nodes_in_group == 0);
976            }
977            SchemaType::Provided => {
978                self.schema
979                    .validate_node(&node_index, node_attributes, Some(&group))?;
980            }
981        }
982
983        self.group_mapping.add_node_to_group(group, node_index)
984    }
985
986    #[allow(clippy::needless_pass_by_value)]
987    fn add_node_to_groups_impl(
988        &mut self,
989        groups: impl AsRef<[Group]>,
990        node_index: NodeIndex,
991    ) -> GraphRecordResult<()> {
992        groups
993            .as_ref()
994            .iter()
995            .try_for_each(|group| self.add_node_to_group_impl(group.clone(), node_index.clone()))
996    }
997
998    fn add_nodes_to_groups_impl(
999        &mut self,
1000        groups: impl AsRef<[Group]>,
1001        node_indices: Vec<NodeIndex>,
1002    ) -> GraphRecordResult<()> {
1003        let groups = groups.as_ref();
1004
1005        node_indices
1006            .into_iter()
1007            .try_for_each(|node_index| self.add_node_to_groups_impl(groups, node_index))
1008    }
1009
1010    fn add_edge_to_group_impl(
1011        &mut self,
1012        group: Group,
1013        edge_index: EdgeIndex,
1014    ) -> GraphRecordResult<()> {
1015        let edge_attributes = self.graph.edge_attributes(&edge_index)?;
1016
1017        match self.schema.schema_type() {
1018            SchemaType::Inferred => {
1019                let edges_in_group = self
1020                    .group_mapping
1021                    .edges_in_group
1022                    .get(&group)
1023                    .map_or(0, GrHashSet::len);
1024
1025                self.schema
1026                    .update_edge(edge_attributes, Some(&group), edges_in_group == 0);
1027            }
1028            SchemaType::Provided => {
1029                self.schema
1030                    .validate_edge(&edge_index, edge_attributes, Some(&group))?;
1031            }
1032        }
1033
1034        self.group_mapping.add_edge_to_group(group, edge_index)
1035    }
1036
1037    fn add_edge_to_groups_impl(
1038        &mut self,
1039        groups: impl AsRef<[Group]>,
1040        edge_index: EdgeIndex,
1041    ) -> GraphRecordResult<()> {
1042        groups
1043            .as_ref()
1044            .iter()
1045            .try_for_each(|group| self.add_edge_to_group_impl(group.clone(), edge_index))
1046    }
1047
1048    fn add_edges_to_groups_impl(
1049        &mut self,
1050        groups: impl AsRef<[Group]>,
1051        edge_indices: Vec<EdgeIndex>,
1052    ) -> GraphRecordResult<()> {
1053        let groups = groups.as_ref();
1054
1055        edge_indices
1056            .into_iter()
1057            .try_for_each(|edge_index| self.add_edge_to_groups_impl(groups, edge_index))
1058    }
1059
1060    fn remove_node_from_group_impl(
1061        &mut self,
1062        group: &Group,
1063        node_index: &NodeIndex,
1064    ) -> GraphRecordResult<()> {
1065        if !self.graph.contains_node(node_index) {
1066            return Err(GraphRecordError::NodeNotFound {
1067                node_index: node_index.clone(),
1068            });
1069        }
1070
1071        self.group_mapping.remove_node_from_group(group, node_index)
1072    }
1073
1074    fn remove_node_from_groups_impl(
1075        &mut self,
1076        groups: impl AsRef<[Group]>,
1077        node_index: &NodeIndex,
1078    ) -> GraphRecordResult<()> {
1079        groups
1080            .as_ref()
1081            .iter()
1082            .try_for_each(|group| self.remove_node_from_group_impl(group, node_index))
1083    }
1084
1085    fn remove_nodes_from_groups_impl(
1086        &mut self,
1087        groups: impl AsRef<[Group]>,
1088        node_indices: &[NodeIndex],
1089    ) -> GraphRecordResult<()> {
1090        let groups = groups.as_ref();
1091
1092        node_indices
1093            .iter()
1094            .try_for_each(|node_index| self.remove_node_from_groups_impl(groups, node_index))
1095    }
1096
1097    #[allow(clippy::trivially_copy_pass_by_ref)]
1098    fn remove_edge_from_group_impl(
1099        &mut self,
1100        group: &Group,
1101        edge_index: &EdgeIndex,
1102    ) -> GraphRecordResult<()> {
1103        if !self.graph.contains_edge(edge_index) {
1104            return Err(GraphRecordError::EdgeNotFound {
1105                edge_index: *edge_index,
1106            });
1107        }
1108
1109        self.group_mapping.remove_edge_from_group(group, edge_index)
1110    }
1111
1112    #[allow(clippy::trivially_copy_pass_by_ref)]
1113    fn remove_edge_from_groups_impl(
1114        &mut self,
1115        groups: impl AsRef<[Group]>,
1116        edge_index: &EdgeIndex,
1117    ) -> GraphRecordResult<()> {
1118        groups
1119            .as_ref()
1120            .iter()
1121            .try_for_each(|group| self.remove_edge_from_group_impl(group, edge_index))
1122    }
1123
1124    fn remove_edges_from_groups_impl(
1125        &mut self,
1126        groups: impl AsRef<[Group]>,
1127        edge_indices: &[EdgeIndex],
1128    ) -> GraphRecordResult<()> {
1129        let groups = groups.as_ref();
1130
1131        edge_indices
1132            .iter()
1133            .try_for_each(|edge_index| self.remove_edge_from_groups_impl(groups, edge_index))
1134    }
1135
1136    pub fn groups(&self) -> impl Iterator<Item = &Group> {
1137        self.group_mapping.groups()
1138    }
1139
1140    pub fn nodes_in_group(
1141        &self,
1142        group: &Group,
1143    ) -> GraphRecordResult<impl Iterator<Item = &NodeIndex> + use<'_>> {
1144        self.group_mapping.nodes_in_group(group)
1145    }
1146
1147    pub fn ungrouped_nodes(&self) -> impl Iterator<Item = &NodeIndex> {
1148        let nodes_in_groups: GrHashSet<_> = self
1149            .group_mapping
1150            .nodes_in_group
1151            .values()
1152            .flat_map(|nodes| nodes.iter())
1153            .collect();
1154
1155        self.graph
1156            .node_indices()
1157            .filter(move |node_index| !nodes_in_groups.contains(*node_index))
1158    }
1159
1160    pub fn edges_in_group(
1161        &self,
1162        group: &Group,
1163    ) -> GraphRecordResult<impl Iterator<Item = &EdgeIndex> + use<'_>> {
1164        self.group_mapping.edges_in_group(group)
1165    }
1166
1167    pub fn ungrouped_edges(&self) -> impl Iterator<Item = &EdgeIndex> {
1168        let edges_in_groups: GrHashSet<_> = self
1169            .group_mapping
1170            .edges_in_group
1171            .values()
1172            .flat_map(|edges| edges.iter())
1173            .collect();
1174
1175        self.graph
1176            .edge_indices()
1177            .filter(move |edge_index| !edges_in_groups.contains(*edge_index))
1178    }
1179
1180    pub fn groups_of_node(
1181        &self,
1182        node_index: &NodeIndex,
1183    ) -> GraphRecordResult<impl Iterator<Item = &Group> + use<'_>> {
1184        if !self.graph.contains_node(node_index) {
1185            return Err(GraphRecordError::NodeNotFound {
1186                node_index: node_index.clone(),
1187            });
1188        }
1189
1190        Ok(self.group_mapping.groups_of_node(node_index))
1191    }
1192
1193    pub fn groups_of_edge(
1194        &self,
1195        edge_index: &EdgeIndex,
1196    ) -> GraphRecordResult<impl Iterator<Item = &Group> + use<'_>> {
1197        if !self.graph.contains_edge(edge_index) {
1198            return Err(GraphRecordError::EdgeNotFound {
1199                edge_index: *edge_index,
1200            });
1201        }
1202
1203        Ok(self.group_mapping.groups_of_edge(edge_index))
1204    }
1205
1206    #[must_use]
1207    pub fn node_count(&self) -> usize {
1208        self.graph.node_count()
1209    }
1210
1211    #[must_use]
1212    pub fn edge_count(&self) -> usize {
1213        self.graph.edge_count()
1214    }
1215
1216    #[must_use]
1217    pub fn group_count(&self) -> usize {
1218        self.group_mapping.group_count()
1219    }
1220
1221    #[must_use]
1222    pub fn contains_node(&self, node_index: &NodeIndex) -> bool {
1223        self.graph.contains_node(node_index)
1224    }
1225
1226    #[must_use]
1227    pub fn contains_edge(&self, edge_index: &EdgeIndex) -> bool {
1228        self.graph.contains_edge(edge_index)
1229    }
1230
1231    #[must_use]
1232    pub fn contains_group(&self, group: &Group) -> bool {
1233        self.group_mapping.contains_group(group)
1234    }
1235
1236    pub fn outgoing_neighbors(
1237        &self,
1238        node_index: &NodeIndex,
1239    ) -> GraphRecordResult<impl Iterator<Item = &NodeIndex> + use<'_>> {
1240        self.graph.outgoing_neighbors(node_index)
1241    }
1242
1243    // TODO: Add tests
1244    pub fn incoming_neighbors(
1245        &self,
1246        node_index: &NodeIndex,
1247    ) -> GraphRecordResult<impl Iterator<Item = &NodeIndex> + use<'_>> {
1248        self.graph.incoming_neighbors(node_index)
1249    }
1250
1251    pub fn neighbors(
1252        &self,
1253        node_index: &NodeIndex,
1254    ) -> GraphRecordResult<impl Iterator<Item = &NodeIndex> + use<'_>> {
1255        self.graph.neighbors(node_index)
1256    }
1257
1258    fn clear_impl(&mut self) {
1259        self.graph.clear();
1260        self.group_mapping.clear();
1261    }
1262}
1263
1264#[cfg(not(feature = "plugins"))]
1265impl GraphRecord {
1266    pub fn set_schema(&mut self, schema: Schema) -> GraphRecordResult<()> {
1267        self.set_schema_impl(schema)
1268    }
1269
1270    pub const fn freeze_schema(&mut self) -> GraphRecordResult<()> {
1271        self.freeze_schema_impl();
1272
1273        Ok(())
1274    }
1275
1276    pub const fn unfreeze_schema(&mut self) -> GraphRecordResult<()> {
1277        self.unfreeze_schema_impl();
1278
1279        Ok(())
1280    }
1281
1282    pub fn add_node(
1283        &mut self,
1284        node_index: NodeIndex,
1285        attributes: AttributeMap,
1286    ) -> GraphRecordResult<()> {
1287        self.add_node_impl(node_index, attributes)
1288    }
1289
1290    #[allow(clippy::needless_pass_by_value)]
1291    pub fn add_node_with_group(
1292        &mut self,
1293        node_index: NodeIndex,
1294        attributes: AttributeMap,
1295        group: Group,
1296    ) -> GraphRecordResult<()> {
1297        self.add_node_with_group_impl(node_index, attributes, group)
1298    }
1299
1300    pub fn add_node_with_groups(
1301        &mut self,
1302        node_index: NodeIndex,
1303        attributes: AttributeMap,
1304        groups: impl AsRef<[Group]>,
1305    ) -> GraphRecordResult<()> {
1306        self.add_node_with_groups_impl(node_index, attributes, groups)
1307    }
1308
1309    pub fn remove_node(&mut self, node_index: &NodeIndex) -> GraphRecordResult<AttributeMap> {
1310        self.remove_node_impl(node_index)
1311    }
1312
1313    pub fn add_nodes(&mut self, nodes: Vec<(NodeIndex, AttributeMap)>) -> GraphRecordResult<()> {
1314        self.add_nodes_impl(nodes)
1315    }
1316
1317    #[allow(clippy::needless_pass_by_value)]
1318    pub fn add_nodes_with_group(
1319        &mut self,
1320        nodes: Vec<(NodeIndex, AttributeMap)>,
1321        group: Group,
1322    ) -> GraphRecordResult<()> {
1323        self.add_nodes_with_group_impl(nodes, group)
1324    }
1325
1326    pub fn add_nodes_with_groups(
1327        &mut self,
1328        nodes: Vec<(NodeIndex, AttributeMap)>,
1329        groups: impl AsRef<[Group]>,
1330    ) -> GraphRecordResult<()> {
1331        self.add_nodes_with_groups_impl(nodes, groups)
1332    }
1333
1334    pub fn add_nodes_dataframes(
1335        &mut self,
1336        nodes_dataframes: impl IntoIterator<Item = impl Into<NodeDataFrameInput>>,
1337    ) -> GraphRecordResult<()> {
1338        self.add_nodes_dataframes_impl(nodes_dataframes)
1339    }
1340
1341    pub fn add_nodes_dataframes_with_group(
1342        &mut self,
1343        nodes_dataframes: impl IntoIterator<Item = impl Into<NodeDataFrameInput>>,
1344        group: Group,
1345    ) -> GraphRecordResult<()> {
1346        self.add_nodes_dataframes_with_group_impl(nodes_dataframes, group)
1347    }
1348
1349    pub fn add_nodes_dataframes_with_groups(
1350        &mut self,
1351        nodes_dataframes: impl IntoIterator<Item = impl Into<NodeDataFrameInput>>,
1352        groups: impl AsRef<[Group]>,
1353    ) -> GraphRecordResult<()> {
1354        self.add_nodes_dataframes_with_groups_impl(nodes_dataframes, groups)
1355    }
1356
1357    #[allow(clippy::needless_pass_by_value)]
1358    pub fn add_edge(
1359        &mut self,
1360        source_node_index: NodeIndex,
1361        target_node_index: NodeIndex,
1362        attributes: AttributeMap,
1363    ) -> GraphRecordResult<EdgeIndex> {
1364        self.add_edge_impl(source_node_index, target_node_index, attributes)
1365    }
1366
1367    #[allow(clippy::needless_pass_by_value)]
1368    pub fn add_edge_with_group(
1369        &mut self,
1370        source_node_index: NodeIndex,
1371        target_node_index: NodeIndex,
1372        attributes: AttributeMap,
1373        group: Group,
1374    ) -> GraphRecordResult<EdgeIndex> {
1375        self.add_edge_with_group_impl(source_node_index, target_node_index, attributes, group)
1376    }
1377
1378    pub fn add_edge_with_groups(
1379        &mut self,
1380        source_node_index: NodeIndex,
1381        target_node_index: NodeIndex,
1382        attributes: AttributeMap,
1383        groups: impl AsRef<[Group]>,
1384    ) -> GraphRecordResult<EdgeIndex> {
1385        self.add_edge_with_groups_impl(source_node_index, target_node_index, attributes, groups)
1386    }
1387
1388    pub fn remove_edge(&mut self, edge_index: &EdgeIndex) -> GraphRecordResult<AttributeMap> {
1389        self.remove_edge_impl(edge_index)
1390    }
1391
1392    pub fn add_edges(
1393        &mut self,
1394        edges: Vec<(NodeIndex, NodeIndex, AttributeMap)>,
1395    ) -> GraphRecordResult<Vec<EdgeIndex>> {
1396        self.add_edges_impl(edges)
1397    }
1398
1399    pub fn add_edges_with_group(
1400        &mut self,
1401        edges: Vec<(NodeIndex, NodeIndex, AttributeMap)>,
1402        group: &Group,
1403    ) -> GraphRecordResult<Vec<EdgeIndex>> {
1404        self.add_edges_with_group_impl(edges, group)
1405    }
1406
1407    pub fn add_edges_with_groups(
1408        &mut self,
1409        edges: Vec<(NodeIndex, NodeIndex, AttributeMap)>,
1410        groups: impl AsRef<[Group]>,
1411    ) -> GraphRecordResult<Vec<EdgeIndex>> {
1412        self.add_edges_with_groups_impl(edges, groups)
1413    }
1414
1415    pub fn add_edges_dataframes(
1416        &mut self,
1417        edges_dataframes: impl IntoIterator<Item = impl Into<EdgeDataFrameInput>>,
1418    ) -> GraphRecordResult<Vec<EdgeIndex>> {
1419        self.add_edges_dataframes_impl(edges_dataframes)
1420    }
1421
1422    pub fn add_edges_dataframes_with_group(
1423        &mut self,
1424        edges_dataframes: impl IntoIterator<Item = impl Into<EdgeDataFrameInput>>,
1425        group: &Group,
1426    ) -> GraphRecordResult<Vec<EdgeIndex>> {
1427        self.add_edges_dataframes_with_group_impl(edges_dataframes, group)
1428    }
1429
1430    pub fn add_edges_dataframes_with_groups(
1431        &mut self,
1432        edges_dataframes: impl IntoIterator<Item = impl Into<EdgeDataFrameInput>>,
1433        groups: impl AsRef<[Group]>,
1434    ) -> GraphRecordResult<Vec<EdgeIndex>> {
1435        self.add_edges_dataframes_with_groups_impl(edges_dataframes, groups)
1436    }
1437
1438    pub fn add_group(
1439        &mut self,
1440        group: Group,
1441        node_indices: Option<Vec<NodeIndex>>,
1442        edge_indices: Option<Vec<EdgeIndex>>,
1443    ) -> GraphRecordResult<()> {
1444        self.add_group_impl(group, node_indices, edge_indices)
1445    }
1446
1447    pub fn remove_group(&mut self, group: &Group) -> GraphRecordResult<()> {
1448        self.remove_group_impl(group)
1449    }
1450
1451    pub fn add_node_to_group(
1452        &mut self,
1453        group: Group,
1454        node_index: NodeIndex,
1455    ) -> GraphRecordResult<()> {
1456        self.add_node_to_group_impl(group, node_index)
1457    }
1458
1459    pub fn add_node_to_groups(
1460        &mut self,
1461        groups: impl AsRef<[Group]>,
1462        node_index: NodeIndex,
1463    ) -> GraphRecordResult<()> {
1464        self.add_node_to_groups_impl(groups, node_index)
1465    }
1466
1467    pub fn add_nodes_to_groups(
1468        &mut self,
1469        groups: impl AsRef<[Group]>,
1470        node_indices: Vec<NodeIndex>,
1471    ) -> GraphRecordResult<()> {
1472        self.add_nodes_to_groups_impl(groups, node_indices)
1473    }
1474
1475    pub fn add_edge_to_group(
1476        &mut self,
1477        group: Group,
1478        edge_index: EdgeIndex,
1479    ) -> GraphRecordResult<()> {
1480        self.add_edge_to_group_impl(group, edge_index)
1481    }
1482
1483    pub fn add_edge_to_groups(
1484        &mut self,
1485        groups: impl AsRef<[Group]>,
1486        edge_index: EdgeIndex,
1487    ) -> GraphRecordResult<()> {
1488        self.add_edge_to_groups_impl(groups, edge_index)
1489    }
1490
1491    pub fn add_edges_to_groups(
1492        &mut self,
1493        groups: impl AsRef<[Group]>,
1494        edge_indices: Vec<EdgeIndex>,
1495    ) -> GraphRecordResult<()> {
1496        self.add_edges_to_groups_impl(groups, edge_indices)
1497    }
1498
1499    pub fn remove_node_from_group(
1500        &mut self,
1501        group: &Group,
1502        node_index: &NodeIndex,
1503    ) -> GraphRecordResult<()> {
1504        self.remove_node_from_group_impl(group, node_index)
1505    }
1506
1507    pub fn remove_node_from_groups(
1508        &mut self,
1509        groups: impl AsRef<[Group]>,
1510        node_index: &NodeIndex,
1511    ) -> GraphRecordResult<()> {
1512        self.remove_node_from_groups_impl(groups, node_index)
1513    }
1514
1515    pub fn remove_nodes_from_groups(
1516        &mut self,
1517        groups: impl AsRef<[Group]>,
1518        node_indices: &[NodeIndex],
1519    ) -> GraphRecordResult<()> {
1520        self.remove_nodes_from_groups_impl(groups, node_indices)
1521    }
1522
1523    pub fn remove_edge_from_group(
1524        &mut self,
1525        group: &Group,
1526        edge_index: &EdgeIndex,
1527    ) -> GraphRecordResult<()> {
1528        self.remove_edge_from_group_impl(group, edge_index)
1529    }
1530
1531    pub fn remove_edge_from_groups(
1532        &mut self,
1533        groups: impl AsRef<[Group]>,
1534        edge_index: &EdgeIndex,
1535    ) -> GraphRecordResult<()> {
1536        self.remove_edge_from_groups_impl(groups, edge_index)
1537    }
1538
1539    pub fn remove_edges_from_groups(
1540        &mut self,
1541        groups: impl AsRef<[Group]>,
1542        edge_indices: &[EdgeIndex],
1543    ) -> GraphRecordResult<()> {
1544        self.remove_edges_from_groups_impl(groups, edge_indices)
1545    }
1546
1547    pub fn clear(&mut self) -> GraphRecordResult<()> {
1548        self.clear_impl();
1549
1550        Ok(())
1551    }
1552}
1553
1554#[cfg(test)]
1555mod test {
1556    use super::{
1557        AttributeMap, EdgeDataFrameInput, GraphRecord, GraphRecordAttribute, NodeDataFrameInput,
1558        NodeIndex,
1559    };
1560    use crate::{
1561        errors::{GraphRecordError, SchemaError},
1562        graphrecord::{
1563            SchemaType,
1564            datatypes::DataType,
1565            schema::{AttributeSchema, GroupSchema, Schema},
1566        },
1567    };
1568    use polars::prelude::{DataFrame, NamedFrom, PolarsError, Series};
1569    use std::collections::HashMap;
1570    #[cfg(feature = "serde")]
1571    use std::fs;
1572
1573    fn create_nodes() -> Vec<(NodeIndex, AttributeMap)> {
1574        vec![
1575            (
1576                "0".into(),
1577                HashMap::from([("lorem".into(), "ipsum".into())]),
1578            ),
1579            (
1580                "1".into(),
1581                HashMap::from([("amet".into(), "consectetur".into())]),
1582            ),
1583            (
1584                "2".into(),
1585                HashMap::from([("adipiscing".into(), "elit".into())]),
1586            ),
1587            ("3".into(), HashMap::new()),
1588        ]
1589    }
1590
1591    fn create_edges() -> Vec<(NodeIndex, NodeIndex, AttributeMap)> {
1592        vec![
1593            (
1594                "0".into(),
1595                "1".into(),
1596                HashMap::from([
1597                    ("sed".into(), "do".into()),
1598                    ("eiusmod".into(), "tempor".into()),
1599                ]),
1600            ),
1601            (
1602                "1".into(),
1603                "0".into(),
1604                HashMap::from([
1605                    ("sed".into(), "do".into()),
1606                    ("eiusmod".into(), "tempor".into()),
1607                ]),
1608            ),
1609            (
1610                "1".into(),
1611                "2".into(),
1612                HashMap::from([("incididunt".into(), "ut".into())]),
1613            ),
1614            ("0".into(), "2".into(), HashMap::new()),
1615        ]
1616    }
1617
1618    fn create_nodes_dataframe() -> Result<DataFrame, PolarsError> {
1619        let s0 = Series::new("index".into(), &["0", "1"]);
1620        let s1 = Series::new("attribute".into(), &[1, 2]);
1621        DataFrame::new(2, vec![s0.into(), s1.into()])
1622    }
1623
1624    fn create_edges_dataframe() -> Result<DataFrame, PolarsError> {
1625        let s0 = Series::new("from".into(), &["0", "1"]);
1626        let s1 = Series::new("to".into(), &["1", "0"]);
1627        let s2 = Series::new("attribute".into(), &[1, 2]);
1628        DataFrame::new(2, vec![s0.into(), s1.into(), s2.into()])
1629    }
1630
1631    fn create_graphrecord() -> GraphRecord {
1632        let nodes = create_nodes();
1633        let edges = create_edges();
1634
1635        GraphRecord::from_tuples(nodes, Some(edges), None).unwrap()
1636    }
1637
1638    #[test]
1639    fn test_from_tuples() {
1640        let graphrecord = create_graphrecord();
1641
1642        assert_eq!(4, graphrecord.node_count());
1643        assert_eq!(4, graphrecord.edge_count());
1644    }
1645
1646    #[test]
1647    fn test_invalid_from_tuples() {
1648        let nodes = create_nodes();
1649
1650        // Adding an edge pointing to a non-existing node should fail
1651        assert!(
1652            GraphRecord::from_tuples(
1653                nodes.clone(),
1654                Some(vec![("0".into(), "50".into(), HashMap::new())]),
1655                None
1656            )
1657            .is_err_and(|e| matches!(e, GraphRecordError::NodeNotFound { .. }))
1658        );
1659
1660        // Adding an edge from a non-existing should fail
1661        assert!(
1662            GraphRecord::from_tuples(
1663                nodes,
1664                Some(vec![("50".into(), "0".into(), HashMap::new())]),
1665                None
1666            )
1667            .is_err_and(|e| matches!(e, GraphRecordError::NodeNotFound { .. }))
1668        );
1669    }
1670
1671    #[test]
1672    fn test_from_dataframes() {
1673        let nodes_dataframe = create_nodes_dataframe().unwrap();
1674        let edges_dataframe = create_edges_dataframe().unwrap();
1675
1676        let graphrecord = GraphRecord::from_dataframes(
1677            vec![(nodes_dataframe, "index".to_string())],
1678            vec![(edges_dataframe, "from".to_string(), "to".to_string())],
1679            None,
1680        )
1681        .unwrap();
1682
1683        assert_eq!(2, graphrecord.node_count());
1684        assert_eq!(2, graphrecord.edge_count());
1685    }
1686
1687    #[test]
1688    fn test_from_nodes_dataframes() {
1689        let nodes_dataframe = create_nodes_dataframe().unwrap();
1690
1691        let graphrecord =
1692            GraphRecord::from_nodes_dataframes(vec![(nodes_dataframe, "index".to_string())], None)
1693                .unwrap();
1694
1695        assert_eq!(2, graphrecord.node_count());
1696    }
1697
1698    #[test]
1699    #[cfg(feature = "serde")]
1700    fn test_ron() {
1701        let graphrecord = create_graphrecord();
1702
1703        let mut file_path = std::env::temp_dir().into_os_string();
1704        file_path.push("/graphrecord_test/");
1705
1706        fs::create_dir_all(&file_path).unwrap();
1707
1708        file_path.push("test.ron");
1709
1710        graphrecord.to_ron(&file_path).unwrap();
1711
1712        let loaded_graphrecord = GraphRecord::from_ron(&file_path).unwrap();
1713
1714        assert_eq!(graphrecord.node_count(), loaded_graphrecord.node_count());
1715        assert_eq!(graphrecord.edge_count(), loaded_graphrecord.edge_count());
1716    }
1717
1718    #[test]
1719    fn test_set_schema() {
1720        let mut graphrecord = GraphRecord::new();
1721
1722        let group_schema = GroupSchema::new(
1723            AttributeSchema::from([("attribute".into(), DataType::Int.into())]),
1724            AttributeSchema::from([("attribute".into(), DataType::Int.into())]),
1725        );
1726
1727        graphrecord
1728            .add_node("0".into(), HashMap::from([("attribute".into(), 1.into())]))
1729            .unwrap();
1730        graphrecord
1731            .add_node("1".into(), HashMap::from([("attribute".into(), 1.into())]))
1732            .unwrap();
1733        graphrecord
1734            .add_edge(
1735                "0".into(),
1736                "1".into(),
1737                HashMap::from([("attribute".into(), 1.into())]),
1738            )
1739            .unwrap();
1740
1741        let schema = Schema::new_provided(HashMap::default(), group_schema.clone());
1742
1743        assert!(graphrecord.set_schema(schema.clone()).is_ok());
1744
1745        assert_eq!(schema, *graphrecord.get_schema());
1746
1747        let mut graphrecord = GraphRecord::new();
1748
1749        graphrecord
1750            .add_node("0".into(), HashMap::from([("attribute".into(), 1.into())]))
1751            .unwrap();
1752        graphrecord
1753            .add_node("1".into(), HashMap::from([("attribute".into(), 1.into())]))
1754            .unwrap();
1755        graphrecord
1756            .add_node("2".into(), HashMap::from([("attribute".into(), 1.into())]))
1757            .unwrap();
1758        graphrecord
1759            .add_edge(
1760                "0".into(),
1761                "1".into(),
1762                HashMap::from([("attribute".into(), 1.into())]),
1763            )
1764            .unwrap();
1765        graphrecord
1766            .add_edge(
1767                "0".into(),
1768                "1".into(),
1769                HashMap::from([("attribute".into(), 1.into())]),
1770            )
1771            .unwrap();
1772        graphrecord
1773            .add_edge(
1774                "0".into(),
1775                "1".into(),
1776                HashMap::from([("attribute".into(), 1.into())]),
1777            )
1778            .unwrap();
1779
1780        let schema = Schema::new_inferred(
1781            HashMap::from([
1782                ("0".into(), group_schema.clone()),
1783                ("1".into(), group_schema.clone()),
1784            ]),
1785            group_schema,
1786        );
1787
1788        graphrecord
1789            .add_group(
1790                "0".into(),
1791                Some(vec!["0".into(), "1".into()]),
1792                Some(vec![0, 1]),
1793            )
1794            .unwrap();
1795        graphrecord
1796            .add_group(
1797                "1".into(),
1798                Some(vec!["0".into(), "1".into()]),
1799                Some(vec![0, 1]),
1800            )
1801            .unwrap();
1802
1803        let inferred_schema = Schema::new_inferred(HashMap::default(), GroupSchema::default());
1804
1805        assert!(graphrecord.set_schema(inferred_schema).is_ok());
1806
1807        assert_eq!(schema, *graphrecord.get_schema());
1808    }
1809
1810    #[test]
1811    fn test_invalid_set_schema() {
1812        let mut graphrecord = GraphRecord::new();
1813
1814        graphrecord
1815            .add_node("0".into(), HashMap::from([("attribute2".into(), 1.into())]))
1816            .unwrap();
1817
1818        let schema = Schema::new_provided(
1819            HashMap::default(),
1820            GroupSchema::new(
1821                AttributeSchema::from([("attribute".into(), DataType::Int.into())]),
1822                AttributeSchema::from([("attribute".into(), DataType::Int.into())]),
1823            ),
1824        );
1825
1826        let previous_schema = graphrecord.get_schema().clone();
1827
1828        assert!(
1829            graphrecord
1830                .set_schema(schema.clone())
1831                .is_err_and(|e| { matches!(e, GraphRecordError::Schema(_)) })
1832        );
1833
1834        assert_eq!(previous_schema, *graphrecord.get_schema());
1835
1836        let mut graphrecord = GraphRecord::new();
1837
1838        graphrecord
1839            .add_node("0".into(), HashMap::from([("attribute".into(), 1.into())]))
1840            .unwrap();
1841        graphrecord
1842            .add_node("1".into(), HashMap::from([("attribute".into(), 1.into())]))
1843            .unwrap();
1844        graphrecord
1845            .add_edge(
1846                "0".into(),
1847                "1".into(),
1848                HashMap::from([("attribute2".into(), 1.into())]),
1849            )
1850            .unwrap();
1851
1852        let previous_schema = graphrecord.get_schema().clone();
1853
1854        assert!(
1855            graphrecord
1856                .set_schema(schema)
1857                .is_err_and(|e| { matches!(e, GraphRecordError::Schema(_)) })
1858        );
1859
1860        assert_eq!(previous_schema, *graphrecord.get_schema());
1861    }
1862
1863    #[test]
1864    fn test_freeze_schema() {
1865        let mut graphrecord = GraphRecord::new();
1866
1867        assert_eq!(
1868            SchemaType::Inferred,
1869            *graphrecord.get_schema().schema_type()
1870        );
1871
1872        graphrecord.freeze_schema().unwrap();
1873
1874        assert_eq!(
1875            SchemaType::Provided,
1876            *graphrecord.get_schema().schema_type()
1877        );
1878    }
1879
1880    #[test]
1881    fn test_unfreeze_schema() {
1882        let schema = Schema::new_provided(HashMap::default(), GroupSchema::default());
1883        let mut graphrecord = GraphRecord::with_schema(schema);
1884
1885        assert_eq!(
1886            *graphrecord.get_schema().schema_type(),
1887            SchemaType::Provided
1888        );
1889
1890        graphrecord.unfreeze_schema().unwrap();
1891
1892        assert_eq!(
1893            *graphrecord.get_schema().schema_type(),
1894            SchemaType::Inferred
1895        );
1896    }
1897
1898    #[test]
1899    fn test_node_indices() {
1900        let graphrecord = create_graphrecord();
1901
1902        let node_indices: Vec<_> = create_nodes()
1903            .into_iter()
1904            .map(|(node_index, _)| node_index)
1905            .collect();
1906
1907        for node_index in graphrecord.node_indices() {
1908            assert!(node_indices.contains(node_index));
1909        }
1910    }
1911
1912    #[test]
1913    fn test_node_attributes() {
1914        let graphrecord = create_graphrecord();
1915
1916        let attributes = graphrecord.node_attributes(&"0".into()).unwrap();
1917
1918        assert_eq!(&create_nodes()[0].1, attributes);
1919    }
1920
1921    #[test]
1922    fn test_invalid_node_attributes() {
1923        let graphrecord = create_graphrecord();
1924
1925        // Querying a non-existing node should fail
1926        assert!(
1927            graphrecord
1928                .node_attributes(&"50".into())
1929                .is_err_and(|e| matches!(e, GraphRecordError::NodeNotFound { .. }))
1930        );
1931    }
1932
1933    #[test]
1934    fn test_node_attributes_mut() {
1935        let mut graphrecord = create_graphrecord();
1936
1937        let node_index = "0".into();
1938        let mut attributes = graphrecord.node_attributes_mut(&node_index).unwrap();
1939
1940        let new_attributes = HashMap::from([("0".into(), "1".into()), ("2".into(), "3".into())]);
1941
1942        attributes
1943            .replace_attributes(new_attributes.clone())
1944            .unwrap();
1945
1946        assert_eq!(
1947            &new_attributes,
1948            graphrecord.node_attributes(&node_index).unwrap()
1949        );
1950    }
1951
1952    #[test]
1953    fn test_invalid_node_attributes_mut() {
1954        let mut graphrecord = create_graphrecord();
1955
1956        // Accessing the node attributes of a non-existing node should fail
1957        assert!(
1958            graphrecord
1959                .node_attributes_mut(&"50".into())
1960                .is_err_and(|e| matches!(e, GraphRecordError::NodeNotFound { .. }))
1961        );
1962    }
1963
1964    #[test]
1965    fn test_outgoing_edges() {
1966        let graphrecord = create_graphrecord();
1967
1968        let edges = graphrecord.outgoing_edges(&"0".into()).unwrap();
1969
1970        assert_eq!(2, edges.count());
1971    }
1972
1973    #[test]
1974    fn test_invalid_outgoing_edges() {
1975        let graphrecord = create_graphrecord();
1976
1977        assert!(
1978            graphrecord
1979                .outgoing_edges(&"50".into())
1980                .is_err_and(|e| matches!(e, GraphRecordError::NodeNotFound { .. }))
1981        );
1982    }
1983
1984    #[test]
1985    fn test_incoming_edges() {
1986        let graphrecord = create_graphrecord();
1987
1988        let edges = graphrecord.incoming_edges(&"2".into()).unwrap();
1989
1990        assert_eq!(2, edges.count());
1991    }
1992
1993    #[test]
1994    fn test_invalid_incoming_edges() {
1995        let graphrecord = create_graphrecord();
1996
1997        assert!(
1998            graphrecord
1999                .incoming_edges(&"50".into())
2000                .is_err_and(|e| matches!(e, GraphRecordError::NodeNotFound { .. }))
2001        );
2002    }
2003
2004    #[test]
2005    fn test_edge_indices() {
2006        let graphrecord = create_graphrecord();
2007        let edges = [0, 1, 2, 3];
2008
2009        for edge in graphrecord.edge_indices() {
2010            assert!(edges.contains(edge));
2011        }
2012    }
2013
2014    #[test]
2015    fn test_edge_attributes() {
2016        let graphrecord = create_graphrecord();
2017
2018        let attributes = graphrecord.edge_attributes(&0).unwrap();
2019
2020        assert_eq!(&create_edges()[0].2, attributes);
2021    }
2022
2023    #[test]
2024    fn test_invalid_edge_attributes() {
2025        let graphrecord = create_graphrecord();
2026
2027        // Querying a non-existing node should fail
2028        assert!(
2029            graphrecord
2030                .edge_attributes(&50)
2031                .is_err_and(|e| matches!(e, GraphRecordError::EdgeNotFound { .. }))
2032        );
2033    }
2034
2035    #[test]
2036    fn test_edge_attributes_mut() {
2037        let mut graphrecord = create_graphrecord();
2038
2039        let mut attributes = graphrecord.edge_attributes_mut(&0).unwrap();
2040
2041        let new_attributes = HashMap::from([("0".into(), "1".into()), ("2".into(), "3".into())]);
2042
2043        attributes
2044            .replace_attributes(new_attributes.clone())
2045            .unwrap();
2046
2047        assert_eq!(&new_attributes, graphrecord.edge_attributes(&0).unwrap());
2048    }
2049
2050    #[test]
2051    fn test_invalid_edge_attributes_mut() {
2052        let mut graphrecord = create_graphrecord();
2053
2054        // Accessing the edge attributes of a non-existing edge should fail
2055        assert!(
2056            graphrecord
2057                .edge_attributes_mut(&50)
2058                .is_err_and(|e| matches!(e, GraphRecordError::EdgeNotFound { .. }))
2059        );
2060    }
2061
2062    #[test]
2063    fn test_edge_endpoints() {
2064        let graphrecord = create_graphrecord();
2065
2066        let edge = &create_edges()[0];
2067
2068        let endpoints = graphrecord.edge_endpoints(&0).unwrap();
2069
2070        assert_eq!(&edge.0, endpoints.0);
2071
2072        assert_eq!(&edge.1, endpoints.1);
2073    }
2074
2075    #[test]
2076    fn test_invalid_edge_endpoints() {
2077        let graphrecord = create_graphrecord();
2078
2079        // Accessing the edge endpoints of a non-existing edge should fail
2080        assert!(
2081            graphrecord
2082                .edge_endpoints(&50)
2083                .is_err_and(|e| matches!(e, GraphRecordError::EdgeNotFound { .. }))
2084        );
2085    }
2086
2087    #[test]
2088    fn test_edges_connecting() {
2089        let graphrecord = create_graphrecord();
2090
2091        let first_index = "0".into();
2092        let second_index = "1".into();
2093        let edges_connecting =
2094            graphrecord.edges_connecting(vec![&first_index], vec![&second_index]);
2095
2096        assert_eq!(vec![&0], edges_connecting.collect::<Vec<_>>());
2097
2098        let first_index = "0".into();
2099        let second_index = "3".into();
2100        let edges_connecting =
2101            graphrecord.edges_connecting(vec![&first_index], vec![&second_index]);
2102
2103        assert_eq!(0, edges_connecting.count());
2104
2105        let first_index = "0".into();
2106        let second_index = "1".into();
2107        let third_index = "2".into();
2108        let mut edges_connecting: Vec<_> = graphrecord
2109            .edges_connecting(vec![&first_index, &second_index], vec![&third_index])
2110            .collect();
2111
2112        edges_connecting.sort();
2113        assert_eq!(vec![&2, &3], edges_connecting);
2114
2115        let first_index = "0".into();
2116        let second_index = "1".into();
2117        let third_index = "2".into();
2118        let fourth_index = "3".into();
2119        let mut edges_connecting: Vec<_> = graphrecord
2120            .edges_connecting(
2121                vec![&first_index, &second_index],
2122                vec![&third_index, &fourth_index],
2123            )
2124            .collect();
2125
2126        edges_connecting.sort();
2127        assert_eq!(vec![&2, &3], edges_connecting);
2128    }
2129
2130    #[test]
2131    fn test_edges_connecting_undirected() {
2132        let graphrecord = create_graphrecord();
2133
2134        let first_index = "0".into();
2135        let second_index = "1".into();
2136        let mut edges_connecting: Vec<_> = graphrecord
2137            .edges_connecting_undirected(vec![&first_index], vec![&second_index])
2138            .collect();
2139
2140        edges_connecting.sort();
2141        assert_eq!(vec![&0, &1], edges_connecting);
2142    }
2143
2144    #[test]
2145    fn test_add_node() {
2146        let mut graphrecord = GraphRecord::new();
2147
2148        assert_eq!(0, graphrecord.node_count());
2149
2150        graphrecord.add_node("0".into(), HashMap::new()).unwrap();
2151
2152        assert_eq!(1, graphrecord.node_count());
2153
2154        graphrecord.freeze_schema().unwrap();
2155
2156        graphrecord.add_node("1".into(), HashMap::new()).unwrap();
2157
2158        assert_eq!(2, graphrecord.node_count());
2159    }
2160
2161    #[test]
2162    fn test_invalid_add_node() {
2163        let mut graphrecord = create_graphrecord();
2164
2165        assert!(
2166            graphrecord
2167                .add_node("0".into(), HashMap::new())
2168                .is_err_and(|e| matches!(e, GraphRecordError::NodeAlreadyExists { .. }))
2169        );
2170
2171        graphrecord.freeze_schema().unwrap();
2172
2173        assert!(
2174            graphrecord
2175                .add_node("4".into(), HashMap::from([("attribute".into(), 1.into())]))
2176                .is_err_and(|e| matches!(e, GraphRecordError::Schema(_)))
2177        );
2178    }
2179
2180    #[test]
2181    fn test_remove_node() {
2182        let mut graphrecord = create_graphrecord();
2183
2184        graphrecord
2185            .add_group("group".into(), Some(vec!["0".into()]), Some(vec![0]))
2186            .unwrap();
2187
2188        let nodes = create_nodes();
2189
2190        assert_eq!(4, graphrecord.node_count());
2191        assert_eq!(4, graphrecord.edge_count());
2192        assert_eq!(
2193            1,
2194            graphrecord
2195                .nodes_in_group(&("group".into()))
2196                .unwrap()
2197                .count()
2198        );
2199        assert_eq!(
2200            1,
2201            graphrecord
2202                .edges_in_group(&("group".into()))
2203                .unwrap()
2204                .count()
2205        );
2206
2207        assert_eq!(nodes[0].1, graphrecord.remove_node(&"0".into()).unwrap());
2208
2209        assert_eq!(3, graphrecord.node_count());
2210        assert_eq!(1, graphrecord.edge_count());
2211        assert_eq!(
2212            0,
2213            graphrecord
2214                .nodes_in_group(&("group".into()))
2215                .unwrap()
2216                .count()
2217        );
2218        assert_eq!(
2219            0,
2220            graphrecord
2221                .edges_in_group(&("group".into()))
2222                .unwrap()
2223                .count()
2224        );
2225
2226        let mut graphrecord = GraphRecord::new();
2227
2228        graphrecord.add_node(0.into(), HashMap::new()).unwrap();
2229        graphrecord
2230            .add_edge(0.into(), 0.into(), HashMap::new())
2231            .unwrap();
2232
2233        assert_eq!(1, graphrecord.node_count());
2234        assert_eq!(1, graphrecord.edge_count());
2235
2236        assert!(graphrecord.remove_node(&0.into()).is_ok());
2237
2238        assert_eq!(0, graphrecord.node_count());
2239        assert_eq!(0, graphrecord.edge_count());
2240    }
2241
2242    #[test]
2243    fn test_invalid_remove_node() {
2244        let mut graphrecord = create_graphrecord();
2245
2246        // Removing a non-existing node should fail
2247        assert!(
2248            graphrecord
2249                .remove_node(&"50".into())
2250                .is_err_and(|e| matches!(e, GraphRecordError::NodeNotFound { .. }))
2251        );
2252    }
2253
2254    #[test]
2255    fn test_add_nodes() {
2256        let mut graphrecord = GraphRecord::new();
2257
2258        assert_eq!(0, graphrecord.node_count());
2259
2260        let nodes = create_nodes();
2261
2262        graphrecord.add_nodes(nodes).unwrap();
2263
2264        assert_eq!(4, graphrecord.node_count());
2265    }
2266
2267    #[test]
2268    fn test_invalid_add_nodes() {
2269        let mut graphrecord = create_graphrecord();
2270
2271        let nodes = create_nodes();
2272
2273        assert!(
2274            graphrecord
2275                .add_nodes(nodes)
2276                .is_err_and(|e| matches!(e, GraphRecordError::NodeAlreadyExists { .. }))
2277        );
2278    }
2279
2280    #[test]
2281    fn test_add_nodes_dataframe() {
2282        let mut graphrecord = GraphRecord::new();
2283
2284        assert_eq!(0, graphrecord.node_count());
2285
2286        let nodes_dataframe = create_nodes_dataframe().unwrap();
2287
2288        graphrecord
2289            .add_nodes_dataframes(vec![(nodes_dataframe, "index".to_string())])
2290            .unwrap();
2291
2292        assert_eq!(2, graphrecord.node_count());
2293    }
2294
2295    #[test]
2296    fn test_add_edge() {
2297        let mut graphrecord = create_graphrecord();
2298
2299        assert_eq!(4, graphrecord.edge_count());
2300
2301        graphrecord
2302            .add_edge("0".into(), "3".into(), HashMap::new())
2303            .unwrap();
2304
2305        assert_eq!(5, graphrecord.edge_count());
2306
2307        graphrecord.freeze_schema().unwrap();
2308
2309        graphrecord
2310            .add_edge("0".into(), "3".into(), HashMap::new())
2311            .unwrap();
2312
2313        assert_eq!(6, graphrecord.edge_count());
2314    }
2315
2316    #[test]
2317    fn test_invalid_add_edge() {
2318        let mut graphrecord = GraphRecord::new();
2319
2320        let nodes = create_nodes();
2321
2322        graphrecord.add_nodes(nodes).unwrap();
2323
2324        // Adding an edge pointing to a non-existing node should fail
2325        assert!(
2326            graphrecord
2327                .add_edge("0".into(), "50".into(), HashMap::new())
2328                .is_err_and(|e| matches!(e, GraphRecordError::NodeNotFound { .. }))
2329        );
2330
2331        // Adding an edge from a non-existing node should fail
2332        assert!(
2333            graphrecord
2334                .add_edge("50".into(), "0".into(), HashMap::new())
2335                .is_err_and(|e| matches!(e, GraphRecordError::NodeNotFound { .. }))
2336        );
2337
2338        graphrecord.freeze_schema().unwrap();
2339
2340        assert!(
2341            graphrecord
2342                .add_edge(
2343                    "0".into(),
2344                    "3".into(),
2345                    HashMap::from([("attribute".into(), 1.into())])
2346                )
2347                .is_err_and(|e| matches!(e, GraphRecordError::Schema(_)))
2348        );
2349    }
2350
2351    #[test]
2352    fn test_remove_edge() {
2353        let mut graphrecord = create_graphrecord();
2354
2355        let edges = create_edges();
2356
2357        assert_eq!(edges[0].2, graphrecord.remove_edge(&0).unwrap());
2358    }
2359
2360    #[test]
2361    fn test_invalid_remove_edge() {
2362        let mut graphrecord = create_graphrecord();
2363
2364        // Removing a non-existing edge should fail
2365        assert!(
2366            graphrecord
2367                .remove_edge(&50)
2368                .is_err_and(|e| matches!(e, GraphRecordError::EdgeNotFound { .. }))
2369        );
2370    }
2371
2372    #[test]
2373    fn test_add_edges() {
2374        let mut graphrecord = GraphRecord::new();
2375
2376        let nodes = create_nodes();
2377
2378        graphrecord.add_nodes(nodes).unwrap();
2379
2380        assert_eq!(0, graphrecord.edge_count());
2381
2382        let edges = create_edges();
2383
2384        graphrecord.add_edges(edges).unwrap();
2385
2386        assert_eq!(4, graphrecord.edge_count());
2387    }
2388
2389    #[test]
2390    fn test_add_edges_dataframe() {
2391        let mut graphrecord = GraphRecord::new();
2392
2393        let nodes = create_nodes();
2394
2395        graphrecord.add_nodes(nodes).unwrap();
2396
2397        assert_eq!(0, graphrecord.edge_count());
2398
2399        let edges = create_edges_dataframe().unwrap();
2400
2401        graphrecord
2402            .add_edges_dataframes(vec![(edges, "from", "to")])
2403            .unwrap();
2404
2405        assert_eq!(2, graphrecord.edge_count());
2406    }
2407
2408    #[test]
2409    fn test_add_group() {
2410        let mut graphrecord = create_graphrecord();
2411
2412        assert_eq!(0, graphrecord.group_count());
2413
2414        graphrecord.add_group("0".into(), None, None).unwrap();
2415
2416        assert_eq!(1, graphrecord.group_count());
2417
2418        graphrecord
2419            .add_group("1".into(), Some(vec!["0".into(), "1".into()]), None)
2420            .unwrap();
2421
2422        assert_eq!(2, graphrecord.group_count());
2423
2424        assert_eq!(2, graphrecord.nodes_in_group(&"1".into()).unwrap().count());
2425    }
2426
2427    #[test]
2428    fn test_invalid_add_group() {
2429        let mut graphrecord = create_graphrecord();
2430
2431        // Adding a group with a non-existing node should fail
2432        assert!(
2433            graphrecord
2434                .add_group("0".into(), Some(vec!["50".into()]), None)
2435                .is_err_and(|e| matches!(e, GraphRecordError::NodeNotFound { .. }))
2436        );
2437
2438        // Adding a group with a non-existing edge should fail
2439        assert!(
2440            graphrecord
2441                .add_group("0".into(), None, Some(vec![50]))
2442                .is_err_and(|e| matches!(e, GraphRecordError::EdgeNotFound { .. }))
2443        );
2444
2445        graphrecord.add_group("0".into(), None, None).unwrap();
2446
2447        // Adding an already existing group should fail
2448        assert!(
2449            graphrecord
2450                .add_group("0".into(), None, None)
2451                .is_err_and(|e| matches!(e, GraphRecordError::GroupAlreadyExists { .. }))
2452        );
2453
2454        graphrecord.freeze_schema().unwrap();
2455
2456        assert!(
2457            graphrecord
2458                .add_group("2".into(), None, None)
2459                .is_err_and(|e| matches!(
2460                    e,
2461                    GraphRecordError::Schema(SchemaError::GroupNotInSchema { .. })
2462                ))
2463        );
2464
2465        graphrecord.remove_group(&"0".into()).unwrap();
2466
2467        assert!(
2468            graphrecord
2469                .add_group("0".into(), Some(vec!["0".into()]), None)
2470                .is_err_and(|e| matches!(e, GraphRecordError::Schema(_)))
2471        );
2472        assert!(
2473            graphrecord
2474                .add_group("0".into(), None, Some(vec![0]))
2475                .is_err_and(|e| matches!(e, GraphRecordError::Schema(_)))
2476        );
2477    }
2478
2479    #[test]
2480    fn test_remove_group() {
2481        let mut graphrecord = create_graphrecord();
2482
2483        graphrecord.add_group("0".into(), None, None).unwrap();
2484
2485        assert_eq!(1, graphrecord.group_count());
2486
2487        graphrecord.remove_group(&"0".into()).unwrap();
2488
2489        assert_eq!(0, graphrecord.group_count());
2490    }
2491
2492    #[test]
2493    fn test_invalid_remove_group() {
2494        let mut graphrecord = GraphRecord::new();
2495
2496        // Removing a non-existing group should fail
2497        assert!(
2498            graphrecord
2499                .remove_group(&"0".into())
2500                .is_err_and(|e| matches!(e, GraphRecordError::GroupNotFound { .. }))
2501        );
2502    }
2503
2504    #[test]
2505    fn test_add_node_to_group() {
2506        let mut graphrecord = create_graphrecord();
2507
2508        graphrecord
2509            .add_group("0".into(), Some(vec!["0".into(), "1".into()]), None)
2510            .unwrap();
2511
2512        assert_eq!(2, graphrecord.nodes_in_group(&"0".into()).unwrap().count());
2513
2514        graphrecord
2515            .add_node_to_group("0".into(), "2".into())
2516            .unwrap();
2517
2518        assert_eq!(3, graphrecord.nodes_in_group(&"0".into()).unwrap().count());
2519
2520        graphrecord
2521            .add_node("4".into(), HashMap::from([("test".into(), "test".into())]))
2522            .unwrap();
2523
2524        graphrecord
2525            .add_group("1".into(), Some(vec!["4".into()]), None)
2526            .unwrap();
2527
2528        graphrecord.freeze_schema().unwrap();
2529
2530        graphrecord
2531            .add_node("5".into(), HashMap::from([("test".into(), "test".into())]))
2532            .unwrap();
2533
2534        assert!(
2535            graphrecord
2536                .add_node_to_group("1".into(), "5".into())
2537                .is_ok()
2538        );
2539
2540        assert_eq!(2, graphrecord.nodes_in_group(&"1".into()).unwrap().count());
2541    }
2542
2543    #[test]
2544    fn test_invalid_add_node_to_group() {
2545        let mut graphrecord = create_graphrecord();
2546
2547        graphrecord
2548            .add_group("0".into(), Some(vec!["0".into()]), None)
2549            .unwrap();
2550
2551        // Adding a non-existing node to a group should fail
2552        assert!(
2553            graphrecord
2554                .add_node_to_group("0".into(), "50".into())
2555                .is_err_and(|e| matches!(e, GraphRecordError::NodeNotFound { .. }))
2556        );
2557
2558        // Adding a node to a group that already is in the group should fail
2559        assert!(
2560            graphrecord
2561                .add_node_to_group("0".into(), "0".into())
2562                .is_err_and(|e| matches!(e, GraphRecordError::NodeAlreadyInGroup { .. }))
2563        );
2564
2565        let mut graphrecord = GraphRecord::new();
2566
2567        graphrecord
2568            .add_node("0".into(), HashMap::from([("test".into(), "test".into())]))
2569            .unwrap();
2570        graphrecord.add_group("group".into(), None, None).unwrap();
2571
2572        graphrecord.freeze_schema().unwrap();
2573
2574        assert!(
2575            graphrecord
2576                .add_node_to_group("group".into(), "0".into())
2577                .is_err_and(|e| matches!(e, GraphRecordError::Schema(_)))
2578        );
2579    }
2580
2581    #[test]
2582    fn test_add_node_to_groups() {
2583        let mut graphrecord = create_graphrecord();
2584
2585        graphrecord
2586            .add_group("0".into(), Some(vec!["0".into()]), None)
2587            .unwrap();
2588        graphrecord
2589            .add_group("1".into(), Some(vec!["1".into()]), None)
2590            .unwrap();
2591
2592        graphrecord
2593            .add_node_to_groups(&["0".into(), "1".into()], "2".into())
2594            .unwrap();
2595
2596        assert_eq!(2, graphrecord.nodes_in_group(&"0".into()).unwrap().count());
2597        assert_eq!(2, graphrecord.nodes_in_group(&"1".into()).unwrap().count());
2598    }
2599
2600    #[test]
2601    fn test_invalid_add_node_to_groups() {
2602        let mut graphrecord = create_graphrecord();
2603
2604        graphrecord
2605            .add_group("0".into(), Some(vec!["0".into()]), None)
2606            .unwrap();
2607        graphrecord
2608            .add_group("1".into(), Some(vec!["1".into()]), None)
2609            .unwrap();
2610
2611        assert!(
2612            graphrecord
2613                .add_node_to_groups(&["0".into(), "1".into()], "50".into())
2614                .is_err_and(|e| matches!(e, GraphRecordError::NodeNotFound { .. }))
2615        );
2616
2617        assert!(
2618            graphrecord
2619                .add_node_to_groups(&["0".into(), "1".into()], "0".into())
2620                .is_err_and(|e| matches!(e, GraphRecordError::NodeAlreadyInGroup { .. }))
2621        );
2622
2623        let mut graphrecord = GraphRecord::new();
2624
2625        graphrecord
2626            .add_node("0".into(), HashMap::from([("test".into(), "test".into())]))
2627            .unwrap();
2628        graphrecord.add_group("group".into(), None, None).unwrap();
2629        graphrecord.add_group("group2".into(), None, None).unwrap();
2630
2631        graphrecord.freeze_schema().unwrap();
2632
2633        assert!(
2634            graphrecord
2635                .add_node_to_groups(&["group".into(), "group2".into()], "0".into())
2636                .is_err_and(|e| matches!(e, GraphRecordError::Schema(_)))
2637        );
2638    }
2639
2640    #[test]
2641    fn test_add_edge_to_group() {
2642        let mut graphrecord = create_graphrecord();
2643
2644        graphrecord
2645            .add_group("0".into(), None, Some(vec![0, 1]))
2646            .unwrap();
2647
2648        assert_eq!(2, graphrecord.edges_in_group(&"0".into()).unwrap().count());
2649
2650        graphrecord.add_edge_to_group("0".into(), 2).unwrap();
2651
2652        assert_eq!(3, graphrecord.edges_in_group(&"0".into()).unwrap().count());
2653
2654        graphrecord
2655            .add_edge("0".into(), "1".into(), HashMap::new())
2656            .unwrap();
2657
2658        graphrecord
2659            .add_group("1".into(), None, Some(vec![3]))
2660            .unwrap();
2661
2662        graphrecord.freeze_schema().unwrap();
2663
2664        let edge_index = graphrecord
2665            .add_edge("0".into(), "1".into(), HashMap::new())
2666            .unwrap();
2667
2668        assert!(
2669            graphrecord
2670                .add_edge_to_group("1".into(), edge_index)
2671                .is_ok()
2672        );
2673
2674        assert_eq!(2, graphrecord.edges_in_group(&"1".into()).unwrap().count());
2675    }
2676
2677    #[test]
2678    fn test_invalid_add_edge_to_group() {
2679        let mut graphrecord = create_graphrecord();
2680
2681        graphrecord
2682            .add_group("0".into(), None, Some(vec![0]))
2683            .unwrap();
2684
2685        // Adding a non-existing edge to a group should fail
2686        assert!(
2687            graphrecord
2688                .add_edge_to_group("0".into(), 50)
2689                .is_err_and(|e| matches!(e, GraphRecordError::EdgeNotFound { .. }))
2690        );
2691
2692        // Adding an edge to a group that already is in the group should fail
2693        assert!(
2694            graphrecord
2695                .add_edge_to_group("0".into(), 0)
2696                .is_err_and(|e| matches!(e, GraphRecordError::EdgeAlreadyInGroup { .. }))
2697        );
2698
2699        let mut graphrecord = GraphRecord::new();
2700
2701        graphrecord.add_node("0".into(), HashMap::new()).unwrap();
2702        graphrecord
2703            .add_edge(
2704                "0".into(),
2705                "0".into(),
2706                HashMap::from([("test".into(), "test".into())]),
2707            )
2708            .unwrap();
2709        graphrecord.add_group("group".into(), None, None).unwrap();
2710
2711        graphrecord.freeze_schema().unwrap();
2712
2713        assert!(
2714            graphrecord
2715                .add_edge_to_group("group".into(), 0)
2716                .is_err_and(|e| matches!(e, GraphRecordError::Schema(_)))
2717        );
2718    }
2719
2720    #[test]
2721    fn test_add_edge_to_groups() {
2722        let mut graphrecord = create_graphrecord();
2723
2724        graphrecord
2725            .add_group("0".into(), None, Some(vec![0]))
2726            .unwrap();
2727        graphrecord
2728            .add_group("1".into(), None, Some(vec![1]))
2729            .unwrap();
2730
2731        graphrecord
2732            .add_edge_to_groups(&["0".into(), "1".into()], 2)
2733            .unwrap();
2734
2735        assert_eq!(2, graphrecord.edges_in_group(&"0".into()).unwrap().count());
2736        assert_eq!(2, graphrecord.edges_in_group(&"1".into()).unwrap().count());
2737    }
2738
2739    #[test]
2740    fn test_invalid_add_edge_to_groups() {
2741        let mut graphrecord = create_graphrecord();
2742
2743        graphrecord
2744            .add_group("0".into(), None, Some(vec![0]))
2745            .unwrap();
2746        graphrecord
2747            .add_group("1".into(), None, Some(vec![1]))
2748            .unwrap();
2749
2750        assert!(
2751            graphrecord
2752                .add_edge_to_groups(&["0".into(), "1".into()], 50)
2753                .is_err_and(|e| matches!(e, GraphRecordError::EdgeNotFound { .. }))
2754        );
2755
2756        assert!(
2757            graphrecord
2758                .add_edge_to_groups(&["0".into(), "1".into()], 0)
2759                .is_err_and(|e| matches!(e, GraphRecordError::EdgeAlreadyInGroup { .. }))
2760        );
2761
2762        let mut graphrecord = GraphRecord::new();
2763
2764        graphrecord.add_node("0".into(), HashMap::new()).unwrap();
2765        graphrecord
2766            .add_edge(
2767                "0".into(),
2768                "0".into(),
2769                HashMap::from([("test".into(), "test".into())]),
2770            )
2771            .unwrap();
2772        graphrecord.add_group("group".into(), None, None).unwrap();
2773        graphrecord.add_group("group2".into(), None, None).unwrap();
2774
2775        graphrecord.freeze_schema().unwrap();
2776
2777        assert!(
2778            graphrecord
2779                .add_edge_to_groups(&["group".into(), "group2".into()], 0)
2780                .is_err_and(|e| matches!(e, GraphRecordError::Schema(_)))
2781        );
2782    }
2783
2784    #[test]
2785    fn test_remove_node_from_group() {
2786        let mut graphrecord = create_graphrecord();
2787
2788        graphrecord
2789            .add_group("0".into(), Some(vec!["0".into(), "1".into()]), None)
2790            .unwrap();
2791
2792        assert_eq!(2, graphrecord.nodes_in_group(&"0".into()).unwrap().count());
2793
2794        graphrecord
2795            .remove_node_from_group(&"0".into(), &"0".into())
2796            .unwrap();
2797
2798        assert_eq!(1, graphrecord.nodes_in_group(&"0".into()).unwrap().count());
2799    }
2800
2801    #[test]
2802    fn test_invalid_remove_node_from_group() {
2803        let mut graphrecord = create_graphrecord();
2804
2805        graphrecord
2806            .add_group("0".into(), Some(vec!["0".into()]), None)
2807            .unwrap();
2808
2809        // Removing a node from a non-existing group should fail
2810        assert!(
2811            graphrecord
2812                .remove_node_from_group(&"50".into(), &"0".into())
2813                .is_err_and(|e| matches!(e, GraphRecordError::GroupNotFound { .. }))
2814        );
2815
2816        // Removing a non-existing node from a group should fail
2817        assert!(
2818            graphrecord
2819                .remove_node_from_group(&"0".into(), &"50".into())
2820                .is_err_and(|e| matches!(e, GraphRecordError::NodeNotFound { .. }))
2821        );
2822
2823        // Removing a node from a group it is not in should fail
2824        assert!(
2825            graphrecord
2826                .remove_node_from_group(&"0".into(), &"1".into())
2827                .is_err_and(|e| matches!(e, GraphRecordError::NodeNotInGroup { .. }))
2828        );
2829    }
2830
2831    #[test]
2832    fn test_remove_node_from_groups() {
2833        let mut graphrecord = create_graphrecord();
2834
2835        graphrecord
2836            .add_group("0".into(), Some(vec!["0".into(), "1".into()]), None)
2837            .unwrap();
2838        graphrecord
2839            .add_group("1".into(), Some(vec!["0".into(), "2".into()]), None)
2840            .unwrap();
2841
2842        graphrecord
2843            .remove_node_from_groups(&["0".into(), "1".into()], &"0".into())
2844            .unwrap();
2845
2846        assert_eq!(1, graphrecord.nodes_in_group(&"0".into()).unwrap().count());
2847        assert_eq!(1, graphrecord.nodes_in_group(&"1".into()).unwrap().count());
2848    }
2849
2850    #[test]
2851    fn test_invalid_remove_node_from_groups() {
2852        let mut graphrecord = create_graphrecord();
2853
2854        graphrecord
2855            .add_group("0".into(), Some(vec!["0".into()]), None)
2856            .unwrap();
2857        graphrecord
2858            .add_group("1".into(), Some(vec!["1".into()]), None)
2859            .unwrap();
2860
2861        assert!(
2862            graphrecord
2863                .remove_node_from_groups(&["0".into(), "1".into()], &"50".into())
2864                .is_err_and(|e| matches!(e, GraphRecordError::NodeNotFound { .. }))
2865        );
2866
2867        assert!(
2868            graphrecord
2869                .remove_node_from_groups(&["0".into(), "1".into()], &"1".into())
2870                .is_err_and(|e| matches!(e, GraphRecordError::NodeNotInGroup { .. }))
2871        );
2872    }
2873
2874    #[test]
2875    fn test_remove_edge_from_group() {
2876        let mut graphrecord = create_graphrecord();
2877
2878        graphrecord
2879            .add_group("0".into(), None, Some(vec![0, 1]))
2880            .unwrap();
2881
2882        assert_eq!(2, graphrecord.edges_in_group(&"0".into()).unwrap().count());
2883
2884        graphrecord.remove_edge_from_group(&"0".into(), &0).unwrap();
2885
2886        assert_eq!(1, graphrecord.edges_in_group(&"0".into()).unwrap().count());
2887    }
2888
2889    #[test]
2890    fn test_invalid_remove_edge_from_group() {
2891        let mut graphrecord = create_graphrecord();
2892
2893        graphrecord
2894            .add_group("0".into(), None, Some(vec![0]))
2895            .unwrap();
2896
2897        // Removing an edge from a non-existing group should fail
2898        assert!(
2899            graphrecord
2900                .remove_edge_from_group(&"50".into(), &0)
2901                .is_err_and(|e| matches!(e, GraphRecordError::GroupNotFound { .. }))
2902        );
2903
2904        // Removing a non-existing edge from a group should fail
2905        assert!(
2906            graphrecord
2907                .remove_edge_from_group(&"0".into(), &50)
2908                .is_err_and(|e| matches!(e, GraphRecordError::EdgeNotFound { .. }))
2909        );
2910
2911        // Removing an edge from a group it is not in should fail
2912        assert!(
2913            graphrecord
2914                .remove_edge_from_group(&"0".into(), &1)
2915                .is_err_and(|e| matches!(e, GraphRecordError::EdgeNotInGroup { .. }))
2916        );
2917    }
2918
2919    #[test]
2920    fn test_remove_edge_from_groups() {
2921        let mut graphrecord = create_graphrecord();
2922
2923        graphrecord
2924            .add_group("0".into(), None, Some(vec![0, 1]))
2925            .unwrap();
2926        graphrecord
2927            .add_group("1".into(), None, Some(vec![0, 2]))
2928            .unwrap();
2929
2930        graphrecord
2931            .remove_edge_from_groups(&["0".into(), "1".into()], &0)
2932            .unwrap();
2933
2934        assert_eq!(1, graphrecord.edges_in_group(&"0".into()).unwrap().count());
2935        assert_eq!(1, graphrecord.edges_in_group(&"1".into()).unwrap().count());
2936    }
2937
2938    #[test]
2939    fn test_invalid_remove_edge_from_groups() {
2940        let mut graphrecord = create_graphrecord();
2941
2942        graphrecord
2943            .add_group("0".into(), None, Some(vec![0]))
2944            .unwrap();
2945        graphrecord
2946            .add_group("1".into(), None, Some(vec![1]))
2947            .unwrap();
2948
2949        assert!(
2950            graphrecord
2951                .remove_edge_from_groups(&["0".into(), "1".into()], &50)
2952                .is_err_and(|e| matches!(e, GraphRecordError::EdgeNotFound { .. }))
2953        );
2954
2955        assert!(
2956            graphrecord
2957                .remove_edge_from_groups(&["0".into(), "1".into()], &1)
2958                .is_err_and(|e| matches!(e, GraphRecordError::EdgeNotInGroup { .. }))
2959        );
2960    }
2961
2962    #[test]
2963    fn test_add_nodes_to_groups() {
2964        let mut graphrecord = create_graphrecord();
2965
2966        graphrecord
2967            .add_group("0".into(), Some(vec!["0".into()]), None)
2968            .unwrap();
2969        graphrecord
2970            .add_group("1".into(), Some(vec!["1".into()]), None)
2971            .unwrap();
2972
2973        graphrecord
2974            .add_nodes_to_groups(&["0".into(), "1".into()], vec!["2".into(), "3".into()])
2975            .unwrap();
2976
2977        assert_eq!(3, graphrecord.nodes_in_group(&"0".into()).unwrap().count());
2978        assert_eq!(3, graphrecord.nodes_in_group(&"1".into()).unwrap().count());
2979    }
2980
2981    #[test]
2982    fn test_invalid_add_nodes_to_groups() {
2983        let mut graphrecord = create_graphrecord();
2984
2985        graphrecord
2986            .add_group("0".into(), Some(vec!["0".into()]), None)
2987            .unwrap();
2988        graphrecord
2989            .add_group("1".into(), Some(vec!["1".into()]), None)
2990            .unwrap();
2991
2992        assert!(
2993            graphrecord
2994                .add_nodes_to_groups(&["0".into(), "1".into()], vec!["50".into()],)
2995                .is_err_and(|e| matches!(e, GraphRecordError::NodeNotFound { .. }))
2996        );
2997
2998        assert!(
2999            graphrecord
3000                .add_nodes_to_groups(&["0".into(), "1".into()], vec!["0".into()],)
3001                .is_err_and(|e| matches!(e, GraphRecordError::NodeAlreadyInGroup { .. }))
3002        );
3003
3004        let mut graphrecord = GraphRecord::new();
3005
3006        graphrecord
3007            .add_node("0".into(), HashMap::from([("test".into(), "test".into())]))
3008            .unwrap();
3009        graphrecord.add_group("group".into(), None, None).unwrap();
3010        graphrecord.add_group("group2".into(), None, None).unwrap();
3011
3012        graphrecord.freeze_schema().unwrap();
3013
3014        assert!(
3015            graphrecord
3016                .add_nodes_to_groups(&["group".into(), "group2".into()], vec!["0".into()],)
3017                .is_err_and(|e| matches!(e, GraphRecordError::Schema(_)))
3018        );
3019    }
3020
3021    #[test]
3022    fn test_add_edges_to_groups() {
3023        let mut graphrecord = create_graphrecord();
3024
3025        graphrecord
3026            .add_group("0".into(), None, Some(vec![0]))
3027            .unwrap();
3028        graphrecord
3029            .add_group("1".into(), None, Some(vec![1]))
3030            .unwrap();
3031
3032        graphrecord
3033            .add_edges_to_groups(&["0".into(), "1".into()], vec![2, 3])
3034            .unwrap();
3035
3036        assert_eq!(3, graphrecord.edges_in_group(&"0".into()).unwrap().count());
3037        assert_eq!(3, graphrecord.edges_in_group(&"1".into()).unwrap().count());
3038    }
3039
3040    #[test]
3041    fn test_invalid_add_edges_to_groups() {
3042        let mut graphrecord = create_graphrecord();
3043
3044        graphrecord
3045            .add_group("0".into(), None, Some(vec![0]))
3046            .unwrap();
3047        graphrecord
3048            .add_group("1".into(), None, Some(vec![1]))
3049            .unwrap();
3050
3051        assert!(
3052            graphrecord
3053                .add_edges_to_groups(&["0".into(), "1".into()], vec![50])
3054                .is_err_and(|e| matches!(e, GraphRecordError::EdgeNotFound { .. }))
3055        );
3056
3057        assert!(
3058            graphrecord
3059                .add_edges_to_groups(&["0".into(), "1".into()], vec![0])
3060                .is_err_and(|e| matches!(e, GraphRecordError::EdgeAlreadyInGroup { .. }))
3061        );
3062
3063        let mut graphrecord = GraphRecord::new();
3064
3065        graphrecord.add_node("0".into(), HashMap::new()).unwrap();
3066        graphrecord
3067            .add_edge(
3068                "0".into(),
3069                "0".into(),
3070                HashMap::from([("test".into(), "test".into())]),
3071            )
3072            .unwrap();
3073        graphrecord.add_group("group".into(), None, None).unwrap();
3074        graphrecord.add_group("group2".into(), None, None).unwrap();
3075
3076        graphrecord.freeze_schema().unwrap();
3077
3078        assert!(
3079            graphrecord
3080                .add_edges_to_groups(&["group".into(), "group2".into()], vec![0])
3081                .is_err_and(|e| matches!(e, GraphRecordError::Schema(_)))
3082        );
3083    }
3084
3085    #[test]
3086    fn test_remove_nodes_from_groups() {
3087        let mut graphrecord = create_graphrecord();
3088
3089        graphrecord
3090            .add_group(
3091                "0".into(),
3092                Some(vec!["0".into(), "1".into(), "2".into()]),
3093                None,
3094            )
3095            .unwrap();
3096        graphrecord
3097            .add_group(
3098                "1".into(),
3099                Some(vec!["0".into(), "1".into(), "2".into()]),
3100                None,
3101            )
3102            .unwrap();
3103
3104        graphrecord
3105            .remove_nodes_from_groups(&["0".into(), "1".into()], &["0".into(), "1".into()])
3106            .unwrap();
3107
3108        assert_eq!(1, graphrecord.nodes_in_group(&"0".into()).unwrap().count());
3109        assert_eq!(1, graphrecord.nodes_in_group(&"1".into()).unwrap().count());
3110    }
3111
3112    #[test]
3113    fn test_invalid_remove_nodes_from_groups() {
3114        let mut graphrecord = create_graphrecord();
3115
3116        graphrecord
3117            .add_group("0".into(), Some(vec!["0".into()]), None)
3118            .unwrap();
3119        graphrecord
3120            .add_group("1".into(), Some(vec!["1".into()]), None)
3121            .unwrap();
3122
3123        assert!(
3124            graphrecord
3125                .remove_nodes_from_groups(&["0".into(), "1".into()], &["50".into()],)
3126                .is_err_and(|e| matches!(e, GraphRecordError::NodeNotFound { .. }))
3127        );
3128
3129        assert!(
3130            graphrecord
3131                .remove_nodes_from_groups(&["0".into(), "1".into()], &["1".into()],)
3132                .is_err_and(|e| matches!(e, GraphRecordError::NodeNotInGroup { .. }))
3133        );
3134    }
3135
3136    #[test]
3137    fn test_remove_edges_from_groups() {
3138        let mut graphrecord = create_graphrecord();
3139
3140        graphrecord
3141            .add_group("0".into(), None, Some(vec![0, 1, 2]))
3142            .unwrap();
3143        graphrecord
3144            .add_group("1".into(), None, Some(vec![0, 1, 2]))
3145            .unwrap();
3146
3147        graphrecord
3148            .remove_edges_from_groups(&["0".into(), "1".into()], &[0, 1])
3149            .unwrap();
3150
3151        assert_eq!(1, graphrecord.edges_in_group(&"0".into()).unwrap().count());
3152        assert_eq!(1, graphrecord.edges_in_group(&"1".into()).unwrap().count());
3153    }
3154
3155    #[test]
3156    fn test_invalid_remove_edges_from_groups() {
3157        let mut graphrecord = create_graphrecord();
3158
3159        graphrecord
3160            .add_group("0".into(), None, Some(vec![0]))
3161            .unwrap();
3162        graphrecord
3163            .add_group("1".into(), None, Some(vec![1]))
3164            .unwrap();
3165
3166        assert!(
3167            graphrecord
3168                .remove_edges_from_groups(&["0".into(), "1".into()], &[50])
3169                .is_err_and(|e| matches!(e, GraphRecordError::EdgeNotFound { .. }))
3170        );
3171
3172        assert!(
3173            graphrecord
3174                .remove_edges_from_groups(&["0".into(), "1".into()], &[1])
3175                .is_err_and(|e| matches!(e, GraphRecordError::EdgeNotInGroup { .. }))
3176        );
3177    }
3178
3179    #[test]
3180    fn test_add_node_with_groups() {
3181        let mut graphrecord = create_graphrecord();
3182
3183        graphrecord.add_group("0".into(), None, None).unwrap();
3184        graphrecord.add_group("1".into(), None, None).unwrap();
3185
3186        graphrecord
3187            .add_node_with_groups(
3188                "4".into(),
3189                HashMap::from([("lorem".into(), "ipsum".into())]),
3190                &["0".into(), "1".into()],
3191            )
3192            .unwrap();
3193
3194        assert_eq!(5, graphrecord.node_count());
3195        assert_eq!(1, graphrecord.nodes_in_group(&"0".into()).unwrap().count());
3196        assert_eq!(1, graphrecord.nodes_in_group(&"1".into()).unwrap().count());
3197    }
3198
3199    #[test]
3200    fn test_invalid_add_node_with_groups() {
3201        let mut graphrecord = create_graphrecord();
3202
3203        graphrecord.add_group("0".into(), None, None).unwrap();
3204        graphrecord.add_group("1".into(), None, None).unwrap();
3205
3206        assert!(
3207            graphrecord
3208                .add_node_with_groups("0".into(), HashMap::new(), &["0".into(), "1".into()],)
3209                .is_err_and(|e| matches!(e, GraphRecordError::NodeAlreadyExists { .. }))
3210        );
3211    }
3212
3213    #[test]
3214    fn test_add_edge_with_groups() {
3215        let mut graphrecord = create_graphrecord();
3216
3217        graphrecord.add_group("0".into(), None, None).unwrap();
3218        graphrecord.add_group("1".into(), None, None).unwrap();
3219
3220        let edge_index = graphrecord
3221            .add_edge_with_groups(
3222                "0".into(),
3223                "1".into(),
3224                HashMap::from([("sed".into(), "do".into())]),
3225                &["0".into(), "1".into()],
3226            )
3227            .unwrap();
3228
3229        assert_eq!(5, graphrecord.edge_count());
3230        assert_eq!(4, edge_index);
3231        assert_eq!(1, graphrecord.edges_in_group(&"0".into()).unwrap().count());
3232        assert_eq!(1, graphrecord.edges_in_group(&"1".into()).unwrap().count());
3233    }
3234
3235    #[test]
3236    fn test_invalid_add_edge_with_groups() {
3237        let mut graphrecord = create_graphrecord();
3238
3239        graphrecord.add_group("0".into(), None, None).unwrap();
3240        graphrecord.add_group("1".into(), None, None).unwrap();
3241
3242        assert!(
3243            graphrecord
3244                .add_edge_with_groups(
3245                    "50".into(),
3246                    "0".into(),
3247                    HashMap::new(),
3248                    &["0".into(), "1".into()],
3249                )
3250                .is_err_and(|e| matches!(e, GraphRecordError::NodeNotFound { .. }))
3251        );
3252
3253        assert!(
3254            graphrecord
3255                .add_edge_with_groups(
3256                    "0".into(),
3257                    "50".into(),
3258                    HashMap::new(),
3259                    &["0".into(), "1".into()],
3260                )
3261                .is_err_and(|e| matches!(e, GraphRecordError::NodeNotFound { .. }))
3262        );
3263    }
3264
3265    #[test]
3266    fn test_add_nodes_with_groups() {
3267        let mut graphrecord = GraphRecord::new();
3268
3269        graphrecord.add_group("0".into(), None, None).unwrap();
3270        graphrecord.add_group("1".into(), None, None).unwrap();
3271
3272        graphrecord
3273            .add_nodes_with_groups(
3274                vec![
3275                    (
3276                        "0".into(),
3277                        HashMap::from([("lorem".into(), "ipsum".into())]),
3278                    ),
3279                    (
3280                        "1".into(),
3281                        HashMap::from([("amet".into(), "consectetur".into())]),
3282                    ),
3283                ],
3284                &["0".into(), "1".into()],
3285            )
3286            .unwrap();
3287
3288        assert_eq!(2, graphrecord.node_count());
3289        assert_eq!(2, graphrecord.nodes_in_group(&"0".into()).unwrap().count());
3290        assert_eq!(2, graphrecord.nodes_in_group(&"1".into()).unwrap().count());
3291    }
3292
3293    #[test]
3294    fn test_invalid_add_nodes_with_groups() {
3295        let mut graphrecord = create_graphrecord();
3296
3297        graphrecord.add_group("0".into(), None, None).unwrap();
3298        graphrecord.add_group("1".into(), None, None).unwrap();
3299
3300        assert!(
3301            graphrecord
3302                .add_nodes_with_groups(
3303                    vec![("0".into(), HashMap::new())],
3304                    &["0".into(), "1".into()],
3305                )
3306                .is_err_and(|e| matches!(e, GraphRecordError::NodeAlreadyExists { .. }))
3307        );
3308    }
3309
3310    #[test]
3311    fn test_add_edges_with_groups() {
3312        let mut graphrecord = create_graphrecord();
3313
3314        graphrecord.add_group("0".into(), None, None).unwrap();
3315        graphrecord.add_group("1".into(), None, None).unwrap();
3316
3317        let edge_indices = graphrecord
3318            .add_edges_with_groups(
3319                vec![
3320                    (
3321                        "0".into(),
3322                        "1".into(),
3323                        HashMap::from([("sed".into(), "do".into())]),
3324                    ),
3325                    (
3326                        "1".into(),
3327                        "0".into(),
3328                        HashMap::from([("sed".into(), "do".into())]),
3329                    ),
3330                ],
3331                &["0".into(), "1".into()],
3332            )
3333            .unwrap();
3334
3335        assert_eq!(6, graphrecord.edge_count());
3336        assert_eq!(vec![4, 5], edge_indices);
3337        assert_eq!(2, graphrecord.edges_in_group(&"0".into()).unwrap().count());
3338        assert_eq!(2, graphrecord.edges_in_group(&"1".into()).unwrap().count());
3339    }
3340
3341    #[test]
3342    fn test_invalid_add_edges_with_groups() {
3343        let mut graphrecord = create_graphrecord();
3344
3345        graphrecord.add_group("0".into(), None, None).unwrap();
3346        graphrecord.add_group("1".into(), None, None).unwrap();
3347
3348        assert!(
3349            graphrecord
3350                .add_edges_with_groups(
3351                    vec![("50".into(), "0".into(), HashMap::new())],
3352                    &["0".into(), "1".into()],
3353                )
3354                .is_err_and(|e| matches!(e, GraphRecordError::NodeNotFound { .. }))
3355        );
3356
3357        assert!(
3358            graphrecord
3359                .add_edges_with_groups(
3360                    vec![("0".into(), "50".into(), HashMap::new())],
3361                    &["0".into(), "1".into()],
3362                )
3363                .is_err_and(|e| matches!(e, GraphRecordError::NodeNotFound { .. }))
3364        );
3365    }
3366
3367    #[test]
3368    fn test_add_nodes_dataframes_with_groups() {
3369        let mut graphrecord = GraphRecord::new();
3370
3371        graphrecord.add_group("0".into(), None, None).unwrap();
3372        graphrecord.add_group("1".into(), None, None).unwrap();
3373
3374        let nodes_dataframe = create_nodes_dataframe().unwrap();
3375
3376        graphrecord
3377            .add_nodes_dataframes_with_groups(
3378                vec![NodeDataFrameInput {
3379                    dataframe: nodes_dataframe,
3380                    index_column: "index".to_string(),
3381                }],
3382                &["0".into(), "1".into()],
3383            )
3384            .unwrap();
3385
3386        assert_eq!(2, graphrecord.node_count());
3387        assert_eq!(2, graphrecord.nodes_in_group(&"0".into()).unwrap().count());
3388        assert_eq!(2, graphrecord.nodes_in_group(&"1".into()).unwrap().count());
3389    }
3390
3391    #[test]
3392    fn test_add_edges_dataframes_with_groups() {
3393        let mut graphrecord = GraphRecord::new();
3394
3395        let nodes = create_nodes();
3396
3397        graphrecord.add_nodes(nodes).unwrap();
3398
3399        graphrecord.add_group("0".into(), None, None).unwrap();
3400        graphrecord.add_group("1".into(), None, None).unwrap();
3401
3402        let edges_dataframe = create_edges_dataframe().unwrap();
3403
3404        let edge_indices = graphrecord
3405            .add_edges_dataframes_with_groups(
3406                vec![EdgeDataFrameInput {
3407                    dataframe: edges_dataframe,
3408                    source_index_column: "from".to_string(),
3409                    target_index_column: "to".to_string(),
3410                }],
3411                &["0".into(), "1".into()],
3412            )
3413            .unwrap();
3414
3415        assert_eq!(2, graphrecord.edge_count());
3416        assert_eq!(2, edge_indices.len());
3417        assert_eq!(2, graphrecord.edges_in_group(&"0".into()).unwrap().count());
3418        assert_eq!(2, graphrecord.edges_in_group(&"1".into()).unwrap().count());
3419    }
3420
3421    #[test]
3422    fn test_groups() {
3423        let mut graphrecord = create_graphrecord();
3424
3425        graphrecord.add_group("0".into(), None, None).unwrap();
3426
3427        let groups: Vec<_> = graphrecord.groups().collect();
3428
3429        assert_eq!(vec![&(GraphRecordAttribute::from("0"))], groups);
3430    }
3431
3432    #[test]
3433    fn test_nodes_in_group() {
3434        let mut graphrecord = create_graphrecord();
3435
3436        graphrecord.add_group("0".into(), None, None).unwrap();
3437
3438        assert_eq!(0, graphrecord.nodes_in_group(&"0".into()).unwrap().count());
3439
3440        graphrecord
3441            .add_group("1".into(), Some(vec!["0".into()]), None)
3442            .unwrap();
3443
3444        assert_eq!(1, graphrecord.nodes_in_group(&"1".into()).unwrap().count());
3445    }
3446
3447    #[test]
3448    fn test_invalid_nodes_in_group() {
3449        let graphrecord = create_graphrecord();
3450
3451        // Querying a non-existing group should fail
3452        assert!(
3453            graphrecord
3454                .nodes_in_group(&"0".into())
3455                .is_err_and(|e| matches!(e, GraphRecordError::GroupNotFound { .. }))
3456        );
3457    }
3458
3459    #[test]
3460    fn test_edges_in_group() {
3461        let mut graphrecord = create_graphrecord();
3462
3463        graphrecord.add_group("0".into(), None, None).unwrap();
3464
3465        assert_eq!(0, graphrecord.edges_in_group(&"0".into()).unwrap().count());
3466
3467        graphrecord
3468            .add_group("1".into(), None, Some(vec![0]))
3469            .unwrap();
3470
3471        assert_eq!(1, graphrecord.edges_in_group(&"1".into()).unwrap().count());
3472    }
3473
3474    #[test]
3475    fn test_invalid_edges_in_group() {
3476        let graphrecord = create_graphrecord();
3477
3478        // Querying a non-existing group should fail
3479        assert!(
3480            graphrecord
3481                .edges_in_group(&"0".into())
3482                .is_err_and(|e| matches!(e, GraphRecordError::GroupNotFound { .. }))
3483        );
3484    }
3485
3486    #[test]
3487    fn test_groups_of_node() {
3488        let mut graphrecord = create_graphrecord();
3489
3490        graphrecord
3491            .add_group("0".into(), Some(vec!["0".into()]), None)
3492            .unwrap();
3493
3494        assert_eq!(1, graphrecord.groups_of_node(&"0".into()).unwrap().count());
3495    }
3496
3497    #[test]
3498    fn test_invalid_groups_of_node() {
3499        let graphrecord = create_graphrecord();
3500
3501        // Querying the groups of a non-existing node should fail
3502        assert!(
3503            graphrecord
3504                .groups_of_node(&"50".into())
3505                .is_err_and(|e| matches!(e, GraphRecordError::NodeNotFound { .. }))
3506        );
3507    }
3508
3509    #[test]
3510    fn test_groups_of_edge() {
3511        let mut graphrecord = create_graphrecord();
3512
3513        graphrecord
3514            .add_group("0".into(), None, Some(vec![0]))
3515            .unwrap();
3516
3517        assert_eq!(1, graphrecord.groups_of_edge(&0).unwrap().count());
3518    }
3519
3520    #[test]
3521    fn test_invalid_groups_of_edge() {
3522        let graphrecord = create_graphrecord();
3523
3524        // Querying the groups of a non-existing edge should fail
3525        assert!(
3526            graphrecord
3527                .groups_of_edge(&50)
3528                .is_err_and(|e| matches!(e, GraphRecordError::EdgeNotFound { .. }))
3529        );
3530    }
3531
3532    #[test]
3533    fn test_node_count() {
3534        let mut graphrecord = GraphRecord::new();
3535
3536        assert_eq!(0, graphrecord.node_count());
3537
3538        graphrecord.add_node("0".into(), HashMap::new()).unwrap();
3539
3540        assert_eq!(1, graphrecord.node_count());
3541    }
3542
3543    #[test]
3544    fn test_edge_count() {
3545        let mut graphrecord = GraphRecord::new();
3546
3547        graphrecord.add_node("0".into(), HashMap::new()).unwrap();
3548        graphrecord.add_node("1".into(), HashMap::new()).unwrap();
3549
3550        assert_eq!(0, graphrecord.edge_count());
3551
3552        graphrecord
3553            .add_edge("0".into(), "1".into(), HashMap::new())
3554            .unwrap();
3555
3556        assert_eq!(1, graphrecord.edge_count());
3557    }
3558
3559    #[test]
3560    fn test_group_count() {
3561        let mut graphrecord = create_graphrecord();
3562
3563        assert_eq!(0, graphrecord.group_count());
3564
3565        graphrecord.add_group("0".into(), None, None).unwrap();
3566
3567        assert_eq!(1, graphrecord.group_count());
3568    }
3569
3570    #[test]
3571    fn test_contains_node() {
3572        let graphrecord = create_graphrecord();
3573
3574        assert!(graphrecord.contains_node(&"0".into()));
3575
3576        assert!(!graphrecord.contains_node(&"50".into()));
3577    }
3578
3579    #[test]
3580    fn test_contains_edge() {
3581        let graphrecord = create_graphrecord();
3582
3583        assert!(graphrecord.contains_edge(&0));
3584
3585        assert!(!graphrecord.contains_edge(&50));
3586    }
3587
3588    #[test]
3589    fn test_contains_group() {
3590        let mut graphrecord = create_graphrecord();
3591
3592        assert!(!graphrecord.contains_group(&"0".into()));
3593
3594        graphrecord.add_group("0".into(), None, None).unwrap();
3595
3596        assert!(graphrecord.contains_group(&"0".into()));
3597    }
3598
3599    #[test]
3600    fn test_outgoing_neighbors() {
3601        let graphrecord = create_graphrecord();
3602
3603        let neighbors = graphrecord.outgoing_neighbors(&"0".into()).unwrap();
3604
3605        assert_eq!(2, neighbors.count());
3606    }
3607
3608    #[test]
3609    fn test_invalid_outgoing_neighbors() {
3610        let graphrecord = GraphRecord::new();
3611
3612        // Querying neighbors of a non-existing node should fail
3613        assert!(
3614            graphrecord
3615                .outgoing_neighbors(&"0".into())
3616                .is_err_and(|e| matches!(e, GraphRecordError::NodeNotFound { .. }))
3617        );
3618    }
3619
3620    #[test]
3621    fn test_neighbors() {
3622        let graphrecord = create_graphrecord();
3623
3624        let neighbors = graphrecord.outgoing_neighbors(&"2".into()).unwrap();
3625        assert_eq!(0, neighbors.count());
3626
3627        let neighbors = graphrecord.neighbors(&"2".into()).unwrap();
3628        assert_eq!(2, neighbors.count());
3629    }
3630
3631    #[test]
3632    fn test_invalid_neighbors() {
3633        let graphrecord = create_graphrecord();
3634
3635        assert!(
3636            graphrecord
3637                .neighbors(&"50".into())
3638                .is_err_and(|e| matches!(e, GraphRecordError::NodeNotFound { .. }))
3639        );
3640    }
3641
3642    #[test]
3643    fn test_clear() {
3644        let mut graphrecord = create_graphrecord();
3645
3646        graphrecord.clear().unwrap();
3647
3648        assert_eq!(0, graphrecord.node_count());
3649        assert_eq!(0, graphrecord.edge_count());
3650        assert_eq!(0, graphrecord.group_count());
3651    }
3652}