1use crate::{
2 annostorage::{Match, ValueSearch},
3 errors::{GraphAnnisCoreError, Result},
4 graph::{
5 ANNIS_NS, Graph, NODE_NAME, NODE_NAME_KEY, NODE_TYPE, NODE_TYPE_KEY,
6 update::{GraphUpdate, UpdateEvent},
7 },
8 types::{AnnoKey, Annotation, Component, ComponentType, Edge},
9 util::{join_qname, split_qname},
10};
11use itertools::Itertools;
12use quick_xml::{
13 Reader, Writer,
14 events::{
15 BytesCData, BytesDecl, BytesEnd, BytesStart, BytesText, Event, attributes::Attributes,
16 },
17};
18use std::{
19 cmp::Ordering,
20 collections::{BTreeMap, BTreeSet, HashMap},
21 io::{BufReader, BufWriter, Read, Write},
22 str::FromStr,
23};
24
25fn write_annotation_keys<CT: ComponentType, W: std::io::Write>(
26 graph: &Graph<CT>,
27 has_graph_configuration: bool,
28 sorted: bool,
29 writer: &mut Writer<W>,
30) -> Result<BTreeMap<AnnoKey, String>> {
31 let mut key_id_mapping = BTreeMap::new();
32 let mut id_counter = 0;
33
34 if has_graph_configuration {
35 let new_id = format!("k{}", id_counter);
36 id_counter += 1;
37
38 let mut key_start = BytesStart::new("key");
39 key_start.push_attribute(("id", new_id.as_str()));
40 key_start.push_attribute(("for", "graph"));
41 key_start.push_attribute(("attr.name", "configuration"));
42 key_start.push_attribute(("attr.type", "string"));
43
44 writer.write_event(Event::Empty(key_start))?;
45 }
46
47 let mut anno_keys = graph.get_node_annos().annotation_keys()?;
49 if sorted {
50 anno_keys.sort_unstable();
51 }
52 for key in anno_keys {
53 if (key.ns != ANNIS_NS || key.name != NODE_NAME) && !key_id_mapping.contains_key(&key) {
54 let new_id = format!("k{}", id_counter);
55 id_counter += 1;
56
57 let qname = join_qname(&key.ns, &key.name);
58
59 let mut key_start = BytesStart::new("key");
60 key_start.push_attribute(("id", new_id.as_str()));
61 key_start.push_attribute(("for", "node"));
62 key_start.push_attribute(("attr.name", qname.as_str()));
63 key_start.push_attribute(("attr.type", "string"));
64
65 writer.write_event(Event::Empty(key_start))?;
66
67 key_id_mapping.insert(key, new_id);
68 }
69 }
70
71 let autogenerated_components: BTreeSet<Component<CT>> =
73 CT::update_graph_index_components(graph)
74 .into_iter()
75 .collect();
76 let mut all_components = graph.get_all_components(None, None);
77 if sorted {
78 all_components.sort_unstable();
79 }
80 for c in all_components {
81 if !autogenerated_components.contains(&c)
82 && let Some(gs) = graph.get_graphstorage(&c)
83 {
84 for key in gs.get_anno_storage().annotation_keys()? {
85 #[allow(clippy::map_entry)]
86 if !key_id_mapping.contains_key(&key) {
87 let new_id = format!("k{}", id_counter);
88 id_counter += 1;
89
90 let qname = join_qname(&key.ns, &key.name);
91
92 let mut key_start = BytesStart::new("key");
93 key_start.push_attribute(("id", new_id.as_str()));
94 key_start.push_attribute(("for", "node"));
95 key_start.push_attribute(("attr.name", qname.as_str()));
96 key_start.push_attribute(("attr.type", "string"));
97
98 writer.write_event(Event::Empty(key_start))?;
99
100 key_id_mapping.insert(key, new_id);
101 }
102 }
103 }
104 }
105
106 Ok(key_id_mapping)
107}
108
109fn write_data<W: std::io::Write>(
110 anno: Annotation,
111 writer: &mut Writer<W>,
112 key_id_mapping: &BTreeMap<AnnoKey, String>,
113) -> Result<()> {
114 let mut data_start = BytesStart::new("data");
115
116 let key_id = key_id_mapping
117 .get(&anno.key)
118 .ok_or_else(|| GraphAnnisCoreError::GraphMLMissingAnnotationKey(anno.key.clone()))?;
119
120 data_start.push_attribute(("key", key_id.as_str()));
121 writer.write_event(Event::Start(data_start))?;
122 writer.write_event(Event::Text(BytesText::new(&anno.val)))?;
124 writer.write_event(Event::End(BytesEnd::new("data")))?;
125
126 Ok(())
127}
128
129fn compare_results<T: Ord>(a: &Result<T>, b: &Result<T>) -> Ordering {
130 if let (Ok(a), Ok(b)) = (a, b) {
131 a.cmp(b)
132 } else if a.is_err() {
133 Ordering::Less
134 } else if b.is_err() {
135 Ordering::Greater
136 } else {
137 Ordering::Equal
139 }
140}
141
142fn write_nodes<CT: ComponentType, W: std::io::Write>(
143 graph: &Graph<CT>,
144 writer: &mut Writer<W>,
145 sorted: bool,
146 key_id_mapping: &BTreeMap<AnnoKey, String>,
147) -> Result<()> {
148 let base_node_iterator =
149 graph
150 .get_node_annos()
151 .exact_anno_search(Some(ANNIS_NS), NODE_TYPE, ValueSearch::Any);
152 let node_iterator: Box<dyn Iterator<Item = Result<Match>>> = if sorted {
153 let it = base_node_iterator.sorted_unstable_by(compare_results);
154 Box::new(it)
155 } else {
156 Box::new(base_node_iterator)
157 };
158
159 for m in node_iterator {
160 let m = m?;
161 let mut node_start = BytesStart::new("node");
162
163 if let Some(id) = graph
164 .get_node_annos()
165 .get_value_for_item(&m.node, &NODE_NAME_KEY)?
166 {
167 node_start.push_attribute(("id", id.as_ref()));
168 let mut node_annotations = graph.get_node_annos().get_annotations_for_item(&m.node)?;
169 if node_annotations.is_empty() {
170 writer.write_event(Event::Empty(node_start))?;
172 } else {
173 writer.write_event(Event::Start(node_start))?;
174 node_annotations.sort_unstable_by_key(|anno| {
177 key_id_mapping
178 .get(&anno.key)
179 .map(|internal_key| internal_key.as_str())
180 .unwrap_or("")
181 });
182
183 for anno in node_annotations {
184 if anno.key.ns != ANNIS_NS || anno.key.name != NODE_NAME {
185 write_data(anno, writer, key_id_mapping)?;
186 }
187 }
188 writer.write_event(Event::End(BytesEnd::new("node")))?;
189 }
190 }
191 }
192 Ok(())
193}
194
195fn write_edges<CT: ComponentType, W: std::io::Write>(
196 graph: &Graph<CT>,
197 writer: &mut Writer<W>,
198 sorted: bool,
199 key_id_mapping: &BTreeMap<AnnoKey, String>,
200) -> Result<()> {
201 let mut edge_counter = 0;
202 let autogenerated_components: BTreeSet<Component<CT>> =
203 CT::update_graph_index_components(graph)
204 .into_iter()
205 .collect();
206
207 let mut all_components = graph.get_all_components(None, None);
208 if sorted {
209 all_components.sort_unstable();
210 }
211
212 for c in all_components {
213 if !autogenerated_components.contains(&c)
215 && let Some(gs) = graph.get_graphstorage(&c)
216 {
217 let source_nodes_iterator = if sorted {
218 Box::new(gs.source_nodes().sorted_unstable_by(compare_results))
219 } else {
220 gs.source_nodes()
221 };
222 for source in source_nodes_iterator {
223 let source = source?;
224 if let Some(source_id) = graph
225 .get_node_annos()
226 .get_value_for_item(&source, &NODE_NAME_KEY)?
227 {
228 let target_nodes_iterator = if sorted {
229 Box::new(
230 gs.get_outgoing_edges(source)
231 .sorted_unstable_by(compare_results),
232 )
233 } else {
234 gs.get_outgoing_edges(source)
235 };
236 for target in target_nodes_iterator {
237 let target = target?;
238 if let Some(target_id) = graph
239 .get_node_annos()
240 .get_value_for_item(&target, &NODE_NAME_KEY)?
241 {
242 let edge = Edge { source, target };
243
244 let mut edge_id = edge_counter.to_string();
245 edge_counter += 1;
246 edge_id.insert(0, 'e');
247
248 let mut edge_start = BytesStart::new("edge");
249 edge_start.push_attribute(("id", edge_id.as_str()));
250 edge_start.push_attribute(("source", source_id.as_ref()));
251 edge_start.push_attribute(("target", target_id.as_ref()));
252 edge_start.push_attribute(("label", c.to_string().as_ref()));
254
255 writer.write_event(Event::Start(edge_start))?;
256
257 let mut edge_annotations =
260 gs.get_anno_storage().get_annotations_for_item(&edge)?;
261 edge_annotations.sort_unstable_by_key(|anno| {
262 key_id_mapping
263 .get(&anno.key)
264 .map(|internal_key| internal_key.as_str())
265 .unwrap_or("")
266 });
267 for anno in edge_annotations {
268 write_data(anno, writer, key_id_mapping)?;
269 }
270 writer.write_event(Event::End(BytesEnd::new("edge")))?;
271 }
272 }
273 }
274 }
275 }
276 }
277 Ok(())
278}
279
280pub fn export<CT: ComponentType, W: std::io::Write, F>(
281 graph: &Graph<CT>,
282 graph_configuration: Option<&str>,
283 output: W,
284 progress_callback: F,
285) -> Result<()>
286where
287 F: Fn(&str),
288{
289 let output = BufWriter::new(output);
291 let mut writer = Writer::new_with_indent(output, b' ', 4);
292
293 let xml_decl = BytesDecl::new("1.0", Some("UTF-8"), None);
295 writer.write_event(Event::Decl(xml_decl))?;
296
297 writer.write_event(Event::Start(BytesStart::new("graphml")))?;
299
300 progress_callback("exporting all available annotation keys");
302 let key_id_mapping =
303 write_annotation_keys(graph, graph_configuration.is_some(), false, &mut writer)?;
304
305 let mut graph_start = BytesStart::new("graph");
307 graph_start.push_attribute(("edgedefault", "directed"));
308 graph_start.push_attribute(("parse.order", "nodesfirst"));
310 graph_start.push_attribute(("parse.nodeids", "free"));
311 graph_start.push_attribute(("parse.edgeids", "canonical"));
312
313 writer.write_event(Event::Start(graph_start))?;
314
315 if let Some(config) = graph_configuration {
317 let mut data_start = BytesStart::new("data");
318 data_start.push_attribute(("key", "k0"));
320 writer.write_event(Event::Start(data_start))?;
321 writer.write_event(Event::CData(BytesCData::new(config)))?;
323 writer.write_event(Event::End(BytesEnd::new("data")))?;
324 }
325
326 progress_callback("exporting nodes");
328 write_nodes(graph, &mut writer, false, &key_id_mapping)?;
329
330 progress_callback("exporting edges");
332 write_edges(graph, &mut writer, false, &key_id_mapping)?;
333
334 writer.write_event(Event::End(BytesEnd::new("graph")))?;
335 writer.write_event(Event::End(BytesEnd::new("graphml")))?;
336
337 writer.into_inner().flush()?;
339
340 Ok(())
341}
342
343pub fn export_stable_order<CT: ComponentType, W: std::io::Write, F>(
348 graph: &Graph<CT>,
349 graph_configuration: Option<&str>,
350 output: W,
351 progress_callback: F,
352) -> Result<()>
353where
354 F: Fn(&str),
355{
356 let output = BufWriter::new(output);
358 let mut writer = Writer::new_with_indent(output, b' ', 4);
359
360 let xml_decl = BytesDecl::new("1.0", Some("UTF-8"), None);
362 writer.write_event(Event::Decl(xml_decl))?;
363
364 writer.write_event(Event::Start(BytesStart::new("graphml")))?;
366
367 progress_callback("exporting all available annotation keys");
369 let key_id_mapping =
370 write_annotation_keys(graph, graph_configuration.is_some(), true, &mut writer)?;
371
372 let mut graph_start = BytesStart::new("graph");
374 graph_start.push_attribute(("edgedefault", "directed"));
375 graph_start.push_attribute(("parse.order", "nodesfirst"));
377 graph_start.push_attribute(("parse.nodeids", "free"));
378 graph_start.push_attribute(("parse.edgeids", "canonical"));
379
380 writer.write_event(Event::Start(graph_start))?;
381
382 if let Some(config) = graph_configuration {
384 let mut data_start = BytesStart::new("data");
385 data_start.push_attribute(("key", "k0"));
387 writer.write_event(Event::Start(data_start))?;
388 writer.write_event(Event::CData(BytesCData::new(config)))?;
390 writer.write_event(Event::End(BytesEnd::new("data")))?;
391 }
392
393 progress_callback("exporting nodes");
395 write_nodes(graph, &mut writer, true, &key_id_mapping)?;
396
397 progress_callback("exporting edges");
399 write_edges(graph, &mut writer, true, &key_id_mapping)?;
400
401 writer.write_event(Event::End(BytesEnd::new("graph")))?;
402 writer.write_event(Event::End(BytesEnd::new("graphml")))?;
403
404 writer.into_inner().flush()?;
406
407 Ok(())
408}
409
410fn add_annotation_key(keys: &mut BTreeMap<String, AnnoKey>, attributes: Attributes) -> Result<()> {
411 let mut id: Option<String> = None;
413 let mut anno_key: Option<AnnoKey> = None;
414
415 for att in attributes {
416 let att = att?;
417
418 let att_value = String::from_utf8_lossy(&att.value);
419
420 match att.key.0 {
421 b"id" => {
422 id = Some(att_value.to_string());
423 }
424 b"attr.name" => {
425 let (ns, name) = split_qname(att_value.as_ref());
426 anno_key = Some(AnnoKey {
427 ns: ns.unwrap_or("").into(),
428 name: name.into(),
429 });
430 }
431 _ => {}
432 }
433 }
434
435 if let (Some(id), Some(anno_key)) = (id, anno_key) {
436 keys.insert(id, anno_key);
437 }
438 Ok(())
439}
440
441fn add_node(
442 node_updates: &mut GraphUpdate,
443 current_node_id: &Option<String>,
444 data: &mut HashMap<AnnoKey, String>,
445) -> Result<()> {
446 if let Some(node_name) = current_node_id {
447 let node_type = data
449 .remove(&NODE_TYPE_KEY)
450 .unwrap_or_else(|| "node".to_string());
451 node_updates.add_event(UpdateEvent::AddNode {
452 node_name: node_name.clone(),
453 node_type,
454 })?;
455 for (key, value) in data.drain() {
457 node_updates.add_event(UpdateEvent::AddNodeLabel {
458 node_name: node_name.clone(),
459 anno_ns: key.ns,
460 anno_name: key.name,
461 anno_value: value,
462 })?;
463 }
464 }
465 Ok(())
466}
467
468fn add_edge<CT: ComponentType>(
469 edge_updates: &mut GraphUpdate,
470 current_source_id: &Option<String>,
471 current_target_id: &Option<String>,
472 current_component: &Option<String>,
473 data: &mut HashMap<AnnoKey, String>,
474) -> Result<()> {
475 if let (Some(source), Some(target), Some(component)) =
476 (current_source_id, current_target_id, current_component)
477 {
478 if let Ok(component) = Component::<CT>::from_str(component) {
480 edge_updates.add_event(UpdateEvent::AddEdge {
481 source_node: source.clone(),
482 target_node: target.clone(),
483 layer: component.layer.clone(),
484 component_type: component.get_type().to_string(),
485 component_name: component.name.clone(),
486 })?;
487
488 for (key, value) in data.drain() {
490 edge_updates.add_event(UpdateEvent::AddEdgeLabel {
491 source_node: source.clone(),
492 target_node: target.clone(),
493 layer: component.layer.clone(),
494 component_type: component.get_type().to_string(),
495 component_name: component.name.clone(),
496 anno_ns: key.ns,
497 anno_name: key.name,
498 anno_value: value,
499 })?;
500 }
501 }
502 }
503 Ok(())
504}
505
506fn read_graphml<CT: ComponentType, R: std::io::BufRead, F: Fn(&str)>(
507 input: &mut R,
508 node_updates: &mut GraphUpdate,
509 edge_updates: &mut GraphUpdate,
510 progress_callback: &F,
511) -> Result<Option<String>> {
512 let mut reader = Reader::from_reader(input);
513 reader.expand_empty_elements(true);
514
515 let mut keys = BTreeMap::new();
516
517 let mut level = 0;
518 let mut in_graph = false;
519 let mut current_node_id: Option<String> = None;
520 let mut current_data_key: Option<String> = None;
521 let mut current_source_id: Option<String> = None;
522 let mut current_target_id: Option<String> = None;
523 let mut current_component: Option<String> = None;
524 let mut current_data_value: Option<String> = None;
525 let mut data: HashMap<AnnoKey, String> = HashMap::new();
526
527 let mut config = None;
528
529 let mut processed_updates = 0;
530
531 let mut buf = Vec::new();
532 loop {
533 match reader.read_event_into(&mut buf)? {
534 Event::Start(ref e) => {
535 level += 1;
536
537 match e.name().0 {
538 b"graph" => {
539 if level == 2 {
540 in_graph = true;
541 }
542 }
543 b"key" => {
544 if level == 2 {
545 add_annotation_key(&mut keys, e.attributes())?;
546 }
547 }
548 b"node" => {
549 if in_graph && level == 3 {
550 data.clear();
551 for att in e.attributes() {
553 let att = att?;
554 if att.key.0 == b"id" {
555 current_node_id =
556 Some(String::from_utf8_lossy(&att.value).to_string());
557 }
558 }
559 }
560 }
561 b"edge" => {
562 if in_graph && level == 3 {
563 data.clear();
564 for att in e.attributes() {
566 let att = att?;
567 if att.key.0 == b"source" {
568 current_source_id =
569 Some(String::from_utf8_lossy(&att.value).to_string());
570 } else if att.key.0 == b"target" {
571 current_target_id =
572 Some(String::from_utf8_lossy(&att.value).to_string());
573 } else if att.key.0 == b"label" {
574 current_component =
575 Some(String::from_utf8_lossy(&att.value).to_string());
576 }
577 }
578 }
579 }
580 b"data" => {
581 for att in e.attributes() {
582 let att = att?;
583 if att.key.0 == b"key" {
584 current_data_key =
585 Some(String::from_utf8_lossy(&att.value).to_string());
586 }
587 }
588 }
589 _ => {}
590 }
591 }
592 Event::Text(t) => {
593 if in_graph && level == 4 && current_data_key.is_some() {
594 current_data_value = Some(t.unescape()?.to_string());
595 }
596 }
597 Event::CData(t) => {
598 if let Some(current_data_key) = ¤t_data_key
599 && in_graph
600 && level == 3
601 && current_data_key == "k0"
602 {
603 config = Some(String::from_utf8_lossy(&t).to_string());
605 }
606 }
607 Event::End(ref e) => {
608 match e.name().0 {
609 b"graph" => {
610 in_graph = false;
611 }
612 b"node" => {
613 add_node(node_updates, ¤t_node_id, &mut data)?;
614 current_node_id = None;
615 processed_updates += 1;
616 if processed_updates % 1_000_000 == 0 {
617 progress_callback(&format!(
618 "Processed {} GraphML nodes and edges",
619 processed_updates
620 ));
621 }
622 }
623 b"edge" => {
624 add_edge::<CT>(
625 edge_updates,
626 ¤t_source_id,
627 ¤t_target_id,
628 ¤t_component,
629 &mut data,
630 )?;
631 current_source_id = None;
632 current_target_id = None;
633 current_component = None;
634 processed_updates += 1;
635 if processed_updates % 1_000_000 == 0 {
636 progress_callback(&format!(
637 "Processed {} GraphML nodes and edges",
638 processed_updates
639 ));
640 }
641 }
642 b"data" => {
643 if let Some(current_data_key) = current_data_key
644 && let Some(anno_key) = keys.get(¤t_data_key)
645 {
646 if let Some(v) = current_data_value.take() {
648 data.insert(anno_key.clone(), v);
649 } else {
650 data.insert(anno_key.clone(), String::default());
654 }
655 }
656
657 current_data_value = None;
658 current_data_key = None;
659 }
660 _ => {}
661 }
662
663 level -= 1;
664 }
665 Event::Eof => {
666 break;
667 }
668 _ => {}
669 }
670 buf.clear();
672 }
673 Ok(config)
674}
675
676pub fn import<CT: ComponentType, R: Read, F>(
677 input: R,
678 disk_based: bool,
679 progress_callback: F,
680) -> Result<(Graph<CT>, Option<String>)>
681where
682 F: Fn(&str),
683{
684 let mut input = BufReader::new(input);
686 let mut g = Graph::with_default_graphstorages(disk_based)?;
687 let mut updates = GraphUpdate::default();
688 let mut edge_updates = GraphUpdate::default();
689
690 progress_callback("reading GraphML");
692 let config = read_graphml::<CT, BufReader<R>, F>(
693 &mut input,
694 &mut updates,
695 &mut edge_updates,
696 &progress_callback,
697 )?;
698
699 progress_callback("merging generated events");
702 for event in edge_updates.iter()? {
703 let (_, event) = event?;
704 updates.add_event(event)?;
705 }
706
707 progress_callback("applying imported changes");
708 g.apply_update(&mut updates, &progress_callback)?;
709
710 progress_callback("calculating graph statistics");
711 g.calculate_all_statistics()?;
712
713 for c in g.get_all_components(None, None) {
714 progress_callback(&format!("optimizing implementation for component {}", c));
715 g.optimize_gs_impl(&c)?;
716 }
717
718 Ok((g, config))
719}
720
721#[cfg(test)]
722mod tests {
723 use super::*;
724 use crate::{
725 graph::{DEFAULT_NS, GraphUpdate},
726 types::DefaultComponentType,
727 };
728 use pretty_assertions::assert_eq;
729 use std::borrow::Cow;
730
731 const TEST_CONFIG: &str = r#"[some]
732key = "<value>"
733
734[some.another]
735value = "test""#;
736
737 #[test]
738 fn export_graphml() {
739 let mut u = GraphUpdate::new();
741 u.add_event(UpdateEvent::AddNode {
742 node_name: "first_node".to_string(),
743 node_type: "node".to_string(),
744 })
745 .unwrap();
746 u.add_event(UpdateEvent::AddNode {
747 node_name: "second_node".to_string(),
748 node_type: "node".to_string(),
749 })
750 .unwrap();
751 u.add_event(UpdateEvent::AddNodeLabel {
752 node_name: "first_node".to_string(),
753 anno_ns: DEFAULT_NS.to_string(),
754 anno_name: "an_annotation".to_string(),
755 anno_value: "something".to_string(),
756 })
757 .unwrap();
758
759 u.add_event(UpdateEvent::AddEdge {
760 source_node: "first_node".to_string(),
761 target_node: "second_node".to_string(),
762 component_type: "Edge".to_string(),
763 layer: "some_ns".to_string(),
764 component_name: "test_component".to_string(),
765 })
766 .unwrap();
767
768 let mut g: Graph<DefaultComponentType> = Graph::new(false).unwrap();
769 g.apply_update(&mut u, |_| {}).unwrap();
770
771 let mut xml_data: Vec<u8> = Vec::default();
773 export(&g, Some(TEST_CONFIG), &mut xml_data, |_| {}).unwrap();
774 let expected = include_str!("graphml_example.graphml");
775 let actual = String::from_utf8(xml_data).unwrap();
776 assert_eq!(expected, actual);
777 }
778
779 #[test]
780 fn export_graphml_sorted() {
781 let mut u = GraphUpdate::new();
783
784 u.add_event(UpdateEvent::AddNode {
785 node_name: "1".to_string(),
786 node_type: "node".to_string(),
787 })
788 .unwrap();
789 u.add_event(UpdateEvent::AddNode {
790 node_name: "2".to_string(),
791 node_type: "node".to_string(),
792 })
793 .unwrap();
794 u.add_event(UpdateEvent::AddNodeLabel {
795 node_name: "1".to_string(),
796 anno_ns: DEFAULT_NS.to_string(),
797 anno_name: "an_annotation".to_string(),
798 anno_value: "something".to_string(),
799 })
800 .unwrap();
801
802 u.add_event(UpdateEvent::AddEdge {
803 source_node: "1".to_string(),
804 target_node: "2".to_string(),
805 component_type: "Edge".to_string(),
806 layer: "some_ns".to_string(),
807 component_name: "test_component".to_string(),
808 })
809 .unwrap();
810
811 let mut g: Graph<DefaultComponentType> = Graph::new(false).unwrap();
812 g.apply_update(&mut u, |_| {}).unwrap();
813
814 let mut xml_data: Vec<u8> = Vec::default();
816 export_stable_order(&g, Some(TEST_CONFIG), &mut xml_data, |_| {}).unwrap();
817 let expected = include_str!("graphml_example sorted.graphml");
818 let actual = String::from_utf8(xml_data).unwrap();
819 assert_eq!(expected, actual);
820 }
821
822 #[test]
823 fn import_graphml() {
824 let input_xml = std::io::Cursor::new(
825 include_str!("graphml_example.graphml")
826 .as_bytes()
827 .to_owned(),
828 );
829 let (g, config_str) = import(input_xml, false, |_| {}).unwrap();
830
831 let first_node_id = g
833 .node_annos
834 .get_node_id_from_name("first_node")
835 .unwrap()
836 .unwrap();
837 let second_node_id = g
838 .node_annos
839 .get_node_id_from_name("second_node")
840 .unwrap()
841 .unwrap();
842
843 let first_node_annos = g
844 .get_node_annos()
845 .get_annotations_for_item(&first_node_id)
846 .unwrap();
847 assert_eq!(3, first_node_annos.len());
848 assert_eq!(
849 Some(Cow::Borrowed("something")),
850 g.get_node_annos()
851 .get_value_for_item(
852 &first_node_id,
853 &AnnoKey {
854 ns: DEFAULT_NS.into(),
855 name: "an_annotation".into(),
856 }
857 )
858 .unwrap()
859 );
860
861 assert_eq!(
862 2,
863 g.get_node_annos()
864 .get_annotations_for_item(&second_node_id)
865 .unwrap()
866 .len()
867 );
868
869 let component = g.get_all_components(Some(DefaultComponentType::Edge), None);
870 assert_eq!(1, component.len());
871 assert_eq!("some_ns", component[0].layer);
872 assert_eq!("test_component", component[0].name);
873
874 let test_gs = g.get_graphstorage_as_ref(&component[0]).unwrap();
875 assert_eq!(
876 Some(1),
877 test_gs.distance(first_node_id, second_node_id).unwrap()
878 );
879
880 assert_eq!(Some(TEST_CONFIG), config_str.as_deref());
881 }
882}