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