1use crate::{
2 annostorage::{Match, ValueSearch},
3 errors::{GraphAnnisCoreError, Result},
4 graph::{
5 update::{GraphUpdate, UpdateEvent},
6 Graph, ANNIS_NS, NODE_NAME, NODE_NAME_KEY, NODE_TYPE, NODE_TYPE_KEY,
7 },
8 types::{AnnoKey, Annotation, Component, ComponentType, Edge},
9 util::{join_qname, split_qname},
10};
11use itertools::Itertools;
12use quick_xml::{
13 events::{
14 attributes::Attributes, BytesCData, BytesDecl, BytesEnd, BytesStart, BytesText, Event,
15 },
16 Reader, Writer,
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 if let Some(gs) = graph.get_graphstorage(&c) {
83 for key in gs.get_anno_storage().annotation_keys()? {
84 #[allow(clippy::map_entry)]
85 if !key_id_mapping.contains_key(&key) {
86 let new_id = format!("k{}", id_counter);
87 id_counter += 1;
88
89 let qname = join_qname(&key.ns, &key.name);
90
91 let mut key_start = BytesStart::new("key");
92 key_start.push_attribute(("id", new_id.as_str()));
93 key_start.push_attribute(("for", "node"));
94 key_start.push_attribute(("attr.name", qname.as_str()));
95 key_start.push_attribute(("attr.type", "string"));
96
97 writer.write_event(Event::Empty(key_start))?;
98
99 key_id_mapping.insert(key, new_id);
100 }
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 if let Some(gs) = graph.get_graphstorage(&c) {
216 let source_nodes_iterator = if sorted {
217 Box::new(gs.source_nodes().sorted_unstable_by(compare_results))
218 } else {
219 gs.source_nodes()
220 };
221 for source in source_nodes_iterator {
222 let source = source?;
223 if let Some(source_id) = graph
224 .get_node_annos()
225 .get_value_for_item(&source, &NODE_NAME_KEY)?
226 {
227 let target_nodes_iterator = if sorted {
228 Box::new(
229 gs.get_outgoing_edges(source)
230 .sorted_unstable_by(compare_results),
231 )
232 } else {
233 gs.get_outgoing_edges(source)
234 };
235 for target in target_nodes_iterator {
236 let target = target?;
237 if let Some(target_id) = graph
238 .get_node_annos()
239 .get_value_for_item(&target, &NODE_NAME_KEY)?
240 {
241 let edge = Edge { source, target };
242
243 let mut edge_id = edge_counter.to_string();
244 edge_counter += 1;
245 edge_id.insert(0, 'e');
246
247 let mut edge_start = BytesStart::new("edge");
248 edge_start.push_attribute(("id", edge_id.as_str()));
249 edge_start.push_attribute(("source", source_id.as_ref()));
250 edge_start.push_attribute(("target", target_id.as_ref()));
251 edge_start.push_attribute(("label", c.to_string().as_ref()));
253
254 writer.write_event(Event::Start(edge_start))?;
255
256 let mut edge_annotations =
259 gs.get_anno_storage().get_annotations_for_item(&edge)?;
260 edge_annotations.sort_unstable_by_key(|anno| {
261 key_id_mapping
262 .get(&anno.key)
263 .map(|internal_key| internal_key.as_str())
264 .unwrap_or("")
265 });
266 for anno in edge_annotations {
267 write_data(anno, writer, key_id_mapping)?;
268 }
269 writer.write_event(Event::End(BytesEnd::new("edge")))?;
270 }
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.into(),
460 anno_name: key.name.into(),
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().into(),
484 component_type: component.get_type().to_string(),
485 component_name: component.name.clone().into(),
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().into(),
494 component_type: component.get_type().to_string(),
495 component_name: component.name.clone().into(),
496 anno_ns: key.ns.into(),
497 anno_name: key.name.into(),
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 if in_graph && level == 3 && current_data_key == "k0" {
600 config = Some(String::from_utf8_lossy(&t).to_string());
602 }
603 }
604 }
605 Event::End(ref e) => {
606 match e.name().0 {
607 b"graph" => {
608 in_graph = false;
609 }
610 b"node" => {
611 add_node(node_updates, ¤t_node_id, &mut data)?;
612 current_node_id = None;
613 processed_updates += 1;
614 if processed_updates % 1_000_000 == 0 {
615 progress_callback(&format!(
616 "Processed {} GraphML nodes and edges",
617 processed_updates
618 ));
619 }
620 }
621 b"edge" => {
622 add_edge::<CT>(
623 edge_updates,
624 ¤t_source_id,
625 ¤t_target_id,
626 ¤t_component,
627 &mut data,
628 )?;
629 current_source_id = None;
630 current_target_id = None;
631 current_component = None;
632 processed_updates += 1;
633 if processed_updates % 1_000_000 == 0 {
634 progress_callback(&format!(
635 "Processed {} GraphML nodes and edges",
636 processed_updates
637 ));
638 }
639 }
640 b"data" => {
641 if let Some(current_data_key) = current_data_key {
642 if let Some(anno_key) = keys.get(¤t_data_key) {
643 if let Some(v) = current_data_value.take() {
645 data.insert(anno_key.clone(), v);
646 } else {
647 data.insert(anno_key.clone(), String::default());
651 }
652 }
653 }
654
655 current_data_value = None;
656 current_data_key = None;
657 }
658 _ => {}
659 }
660
661 level -= 1;
662 }
663 Event::Eof => {
664 break;
665 }
666 _ => {}
667 }
668 buf.clear();
670 }
671 Ok(config)
672}
673
674pub fn import<CT: ComponentType, R: Read, F>(
675 input: R,
676 disk_based: bool,
677 progress_callback: F,
678) -> Result<(Graph<CT>, Option<String>)>
679where
680 F: Fn(&str),
681{
682 let mut input = BufReader::new(input);
684 let mut g = Graph::with_default_graphstorages(disk_based)?;
685 let mut updates = GraphUpdate::default();
686 let mut edge_updates = GraphUpdate::default();
687
688 progress_callback("reading GraphML");
690 let config = read_graphml::<CT, BufReader<R>, F>(
691 &mut input,
692 &mut updates,
693 &mut edge_updates,
694 &progress_callback,
695 )?;
696
697 progress_callback("merging generated events");
700 for event in edge_updates.iter()? {
701 let (_, event) = event?;
702 updates.add_event(event)?;
703 }
704
705 progress_callback("applying imported changes");
706 g.apply_update(&mut updates, &progress_callback)?;
707
708 progress_callback("calculating graph statistics");
709 g.calculate_all_statistics()?;
710
711 for c in g.get_all_components(None, None) {
712 progress_callback(&format!("optimizing implementation for component {}", c));
713 g.optimize_gs_impl(&c)?;
714 }
715
716 Ok((g, config))
717}
718
719#[cfg(test)]
720mod tests {
721 use super::*;
722 use crate::{
723 graph::{GraphUpdate, DEFAULT_NS},
724 types::DefaultComponentType,
725 };
726 use pretty_assertions::assert_eq;
727 use std::borrow::Cow;
728
729 const TEST_CONFIG: &str = r#"[some]
730key = "<value>"
731
732[some.another]
733value = "test""#;
734
735 #[test]
736 fn export_graphml() {
737 let mut u = GraphUpdate::new();
739 u.add_event(UpdateEvent::AddNode {
740 node_name: "first_node".to_string(),
741 node_type: "node".to_string(),
742 })
743 .unwrap();
744 u.add_event(UpdateEvent::AddNode {
745 node_name: "second_node".to_string(),
746 node_type: "node".to_string(),
747 })
748 .unwrap();
749 u.add_event(UpdateEvent::AddNodeLabel {
750 node_name: "first_node".to_string(),
751 anno_ns: DEFAULT_NS.to_string(),
752 anno_name: "an_annotation".to_string(),
753 anno_value: "something".to_string(),
754 })
755 .unwrap();
756
757 u.add_event(UpdateEvent::AddEdge {
758 source_node: "first_node".to_string(),
759 target_node: "second_node".to_string(),
760 component_type: "Edge".to_string(),
761 layer: "some_ns".to_string(),
762 component_name: "test_component".to_string(),
763 })
764 .unwrap();
765
766 let mut g: Graph<DefaultComponentType> = Graph::new(false).unwrap();
767 g.apply_update(&mut u, |_| {}).unwrap();
768
769 let mut xml_data: Vec<u8> = Vec::default();
771 export(&g, Some(TEST_CONFIG), &mut xml_data, |_| {}).unwrap();
772 let expected = include_str!("graphml_example.graphml");
773 let actual = String::from_utf8(xml_data).unwrap();
774 assert_eq!(expected, actual);
775 }
776
777 #[test]
778 fn export_graphml_sorted() {
779 let mut u = GraphUpdate::new();
781
782 u.add_event(UpdateEvent::AddNode {
783 node_name: "1".to_string(),
784 node_type: "node".to_string(),
785 })
786 .unwrap();
787 u.add_event(UpdateEvent::AddNode {
788 node_name: "2".to_string(),
789 node_type: "node".to_string(),
790 })
791 .unwrap();
792 u.add_event(UpdateEvent::AddNodeLabel {
793 node_name: "1".to_string(),
794 anno_ns: DEFAULT_NS.to_string(),
795 anno_name: "an_annotation".to_string(),
796 anno_value: "something".to_string(),
797 })
798 .unwrap();
799
800 u.add_event(UpdateEvent::AddEdge {
801 source_node: "1".to_string(),
802 target_node: "2".to_string(),
803 component_type: "Edge".to_string(),
804 layer: "some_ns".to_string(),
805 component_name: "test_component".to_string(),
806 })
807 .unwrap();
808
809 let mut g: Graph<DefaultComponentType> = Graph::new(false).unwrap();
810 g.apply_update(&mut u, |_| {}).unwrap();
811
812 let mut xml_data: Vec<u8> = Vec::default();
814 export_stable_order(&g, Some(TEST_CONFIG), &mut xml_data, |_| {}).unwrap();
815 let expected = include_str!("graphml_example sorted.graphml");
816 let actual = String::from_utf8(xml_data).unwrap();
817 assert_eq!(expected, actual);
818 }
819
820 #[test]
821 fn import_graphml() {
822 let input_xml = std::io::Cursor::new(
823 include_str!("graphml_example.graphml")
824 .as_bytes()
825 .to_owned(),
826 );
827 let (g, config_str) = import(input_xml, false, |_| {}).unwrap();
828
829 let first_node_id = g
831 .node_annos
832 .get_node_id_from_name("first_node")
833 .unwrap()
834 .unwrap();
835 let second_node_id = g
836 .node_annos
837 .get_node_id_from_name("second_node")
838 .unwrap()
839 .unwrap();
840
841 let first_node_annos = g
842 .get_node_annos()
843 .get_annotations_for_item(&first_node_id)
844 .unwrap();
845 assert_eq!(3, first_node_annos.len());
846 assert_eq!(
847 Some(Cow::Borrowed("something")),
848 g.get_node_annos()
849 .get_value_for_item(
850 &first_node_id,
851 &AnnoKey {
852 ns: DEFAULT_NS.into(),
853 name: "an_annotation".into(),
854 }
855 )
856 .unwrap()
857 );
858
859 assert_eq!(
860 2,
861 g.get_node_annos()
862 .get_annotations_for_item(&second_node_id)
863 .unwrap()
864 .len()
865 );
866
867 let component = g.get_all_components(Some(DefaultComponentType::Edge), None);
868 assert_eq!(1, component.len());
869 assert_eq!("some_ns", component[0].layer);
870 assert_eq!("test_component", component[0].name);
871
872 let test_gs = g.get_graphstorage_as_ref(&component[0]).unwrap();
873 assert_eq!(
874 Some(1),
875 test_gs.distance(first_node_id, second_node_id).unwrap()
876 );
877
878 assert_eq!(Some(TEST_CONFIG), config_str.as_deref());
879 }
880}