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 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 #[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 #[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 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 #[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 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 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 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 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 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 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 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 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 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 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 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 assert!(
2326 graphrecord
2327 .add_edge("0".into(), "50".into(), HashMap::new())
2328 .is_err_and(|e| matches!(e, GraphRecordError::NodeNotFound { .. }))
2329 );
2330
2331 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 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 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 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 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 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 assert!(
2553 graphrecord
2554 .add_node_to_group("0".into(), "50".into())
2555 .is_err_and(|e| matches!(e, GraphRecordError::NodeNotFound { .. }))
2556 );
2557
2558 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 assert!(
2687 graphrecord
2688 .add_edge_to_group("0".into(), 50)
2689 .is_err_and(|e| matches!(e, GraphRecordError::EdgeNotFound { .. }))
2690 );
2691
2692 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 assert!(
2811 graphrecord
2812 .remove_node_from_group(&"50".into(), &"0".into())
2813 .is_err_and(|e| matches!(e, GraphRecordError::GroupNotFound { .. }))
2814 );
2815
2816 assert!(
2818 graphrecord
2819 .remove_node_from_group(&"0".into(), &"50".into())
2820 .is_err_and(|e| matches!(e, GraphRecordError::NodeNotFound { .. }))
2821 );
2822
2823 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 assert!(
2899 graphrecord
2900 .remove_edge_from_group(&"50".into(), &0)
2901 .is_err_and(|e| matches!(e, GraphRecordError::GroupNotFound { .. }))
2902 );
2903
2904 assert!(
2906 graphrecord
2907 .remove_edge_from_group(&"0".into(), &50)
2908 .is_err_and(|e| matches!(e, GraphRecordError::EdgeNotFound { .. }))
2909 );
2910
2911 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 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 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 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 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 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}