1use std::io::{BufRead, BufReader, Read, Write};
28
29use crate::core::attributes::AttributeValue;
30use crate::core::{Graph, IgraphError, IgraphResult};
31
32#[derive(Debug, Clone)]
34pub struct LedaGraph {
35 pub graph: Graph,
37 pub labels: Option<Vec<String>>,
39 pub weights: Option<Vec<f64>>,
41}
42
43pub fn read_leda<R: Read>(input: R) -> IgraphResult<LedaGraph> {
61 let reader = BufReader::new(input);
62 let mut content_lines: Vec<String> = Vec::new();
63 for line_result in reader.lines() {
64 let line = line_result?;
65 let trimmed = line.trim().to_string();
66 if !trimmed.is_empty() && !trimmed.starts_with('#') {
67 content_lines.push(trimmed);
68 }
69 }
70
71 let get = |i: usize, msg: &str| -> IgraphResult<&str> {
72 content_lines
73 .get(i)
74 .map(String::as_str)
75 .ok_or_else(|| leda_parse_err(i, msg))
76 };
77
78 if get(0, "empty LEDA file")? != "LEDA.GRAPH" {
79 return Err(leda_parse_err(0, "LEDA file must start with 'LEDA.GRAPH'"));
80 }
81 let has_labels = get(1, "missing vertex attribute type")?.eq_ignore_ascii_case("string");
82 let has_weights = get(2, "missing edge attribute type")?.eq_ignore_ascii_case("double");
83 let directed = match get(3, "missing directedness flag")? {
84 "-1" => true,
85 "-2" => false,
86 other => {
87 return Err(leda_parse_err(
88 3,
89 &format!("invalid directedness flag '{other}', expected -1 or -2"),
90 ));
91 }
92 };
93 let n: u32 = get(4, "missing vertex count")?
94 .parse()
95 .map_err(|e| leda_parse_err(4, &format!("invalid vertex count: {e}")))?;
96
97 let mut pos = 5;
98 let mut labels: Vec<String> = Vec::with_capacity(n as usize);
99 for _ in 0..n {
100 let vline = get(pos, "unexpected end in vertex section")?;
101 let label = extract_leda_label(vline)
102 .ok_or_else(|| leda_parse_err(pos, &format!("invalid vertex entry: {vline}")))?;
103 labels.push(label);
104 pos += 1;
105 }
106
107 let parsed = parse_leda_edges(&content_lines, pos, n, has_weights)?;
108
109 let mut graph = Graph::new(n, directed)?;
110 graph.add_edges(parsed.edges)?;
111
112 if has_labels {
113 graph.set_vertex_attribute_all(
114 "name",
115 labels
116 .iter()
117 .map(|l| AttributeValue::String(l.clone()))
118 .collect(),
119 )?;
120 }
121 if has_weights {
122 graph.set_edge_attribute_all(
123 "weight",
124 parsed
125 .weights
126 .iter()
127 .map(|&w| AttributeValue::Numeric(w))
128 .collect(),
129 )?;
130 }
131
132 Ok(LedaGraph {
133 graph,
134 labels: if has_labels { Some(labels) } else { None },
135 weights: if has_weights {
136 Some(parsed.weights)
137 } else {
138 None
139 },
140 })
141}
142
143struct LedaEdges {
144 edges: Vec<(u32, u32)>,
145 weights: Vec<f64>,
146}
147
148fn parse_leda_edges(
149 lines: &[String],
150 start: usize,
151 n: u32,
152 has_weights: bool,
153) -> IgraphResult<LedaEdges> {
154 let m_str = lines
155 .get(start)
156 .ok_or_else(|| leda_parse_err(start, "missing edge count"))?;
157 let m: usize = m_str
158 .parse()
159 .map_err(|e| leda_parse_err(start, &format!("invalid edge count: {e}")))?;
160
161 let mut edges: Vec<(u32, u32)> = Vec::with_capacity(m);
162 let mut weights: Vec<f64> = Vec::with_capacity(m);
163 for i in 0..m {
164 let pos = start + 1 + i;
165 let eline = lines
166 .get(pos)
167 .ok_or_else(|| leda_parse_err(pos, "unexpected end in edge section"))?;
168 let tokens: Vec<&str> = eline.splitn(4, ' ').collect();
169 if tokens.len() < 3 {
170 return Err(leda_parse_err(
171 pos,
172 &format!("edge line needs at least 3 fields: {eline}"),
173 ));
174 }
175 let from: u32 = tokens[0]
176 .parse()
177 .map_err(|e| leda_parse_err(pos, &format!("invalid source id: {e}")))?;
178 let to: u32 = tokens[1]
179 .parse()
180 .map_err(|e| leda_parse_err(pos, &format!("invalid target id: {e}")))?;
181 if from == 0 || to == 0 || from > n || to > n {
182 return Err(leda_parse_err(
183 pos,
184 &format!("vertex ID out of range: {from}->{to} (n={n})"),
185 ));
186 }
187 edges.push((from - 1, to - 1));
188
189 if has_weights {
190 let label = tokens
191 .get(3)
192 .and_then(|s| extract_leda_label(s))
193 .unwrap_or_default();
194 let w: f64 = if label.is_empty() {
195 0.0
196 } else {
197 label
198 .parse()
199 .map_err(|e| leda_parse_err(pos, &format!("invalid edge weight: {e}")))?
200 };
201 weights.push(w);
202 }
203 }
204 Ok(LedaEdges { edges, weights })
205}
206
207fn leda_parse_err(line: usize, msg: &str) -> IgraphError {
208 IgraphError::Parse {
209 line,
210 message: msg.to_string(),
211 }
212}
213
214fn extract_leda_label(s: &str) -> Option<String> {
215 let trimmed = s.trim();
216 let inner = trimmed.strip_prefix("|{")?.strip_suffix("}|")?;
217 Some(inner.to_string())
218}
219
220pub fn write_leda<W: Write>(
243 graph: &Graph,
244 vertex_labels: Option<&[String]>,
245 edge_weights: Option<&[f64]>,
246 writer: &mut W,
247) -> IgraphResult<()> {
248 if let Some(l) = vertex_labels {
249 if l.len() != graph.vcount() as usize {
250 return Err(IgraphError::InvalidArgument(format!(
251 "vertex_labels length {} does not match vcount {}",
252 l.len(),
253 graph.vcount()
254 )));
255 }
256 for (i, lbl) in l.iter().enumerate() {
257 if lbl.contains('\n') {
258 return Err(IgraphError::InvalidArgument(format!(
259 "vertex label at index {i} contains a newline character"
260 )));
261 }
262 }
263 }
264 if let Some(w) = edge_weights {
265 if w.len() != graph.ecount() {
266 return Err(IgraphError::InvalidArgument(format!(
267 "edge_weights length {} does not match ecount {}",
268 w.len(),
269 graph.ecount()
270 )));
271 }
272 }
273
274 let has_attr_labels =
275 vertex_labels.is_none() && graph.vertex_attribute_names().contains(&"name");
276 let has_attr_weights =
277 edge_weights.is_none() && graph.edge_attribute_names().contains(&"weight");
278
279 writeln!(writer, "LEDA.GRAPH")?;
281
282 if vertex_labels.is_some() || has_attr_labels {
284 writeln!(writer, "string")?;
285 } else {
286 writeln!(writer, "void")?;
287 }
288
289 if edge_weights.is_some() || has_attr_weights {
291 writeln!(writer, "double")?;
292 } else {
293 writeln!(writer, "void")?;
294 }
295
296 if graph.is_directed() {
298 writeln!(writer, "-1")?;
299 } else {
300 writeln!(writer, "-2")?;
301 }
302
303 writeln!(writer, "# Vertices")?;
305 writeln!(writer, "{}", graph.vcount())?;
306
307 for v in 0..graph.vcount() {
308 match vertex_labels {
309 Some(labels) => writeln!(writer, "|{{{}}}|", labels[v as usize])?,
310 None => {
311 if has_attr_labels {
312 let label = graph
313 .vertex_attribute("name", v)
314 .and_then(AttributeValue::as_str)
315 .unwrap_or("");
316 writeln!(writer, "|{{{label}}}|")?;
317 } else {
318 writeln!(writer, "|{{}}|")?;
319 }
320 }
321 }
322 }
323
324 writeln!(writer, "# Edges")?;
326 writeln!(writer, "{}", graph.ecount())?;
327
328 for eid in 0..graph.ecount() {
329 #[allow(clippy::cast_possible_truncation)]
330 let (from, to) = graph.edge(eid as u32)?;
331
332 if let Some(w) = edge_weights {
333 writeln!(writer, "{} {} 0 |{{{}}}|", from + 1, to + 1, w[eid])?;
334 } else {
335 #[allow(clippy::cast_possible_truncation)]
336 let eid_u32 = eid as u32;
337 if let Some(w) = graph
338 .edge_attribute("weight", eid_u32)
339 .and_then(AttributeValue::as_f64)
340 {
341 writeln!(writer, "{} {} 0 |{{{w}}}|", from + 1, to + 1)?;
342 } else {
343 writeln!(writer, "{} {} 0 |{{}}|", from + 1, to + 1)?;
344 }
345 }
346 }
347
348 Ok(())
349}
350
351#[cfg(test)]
352mod tests {
353 use super::*;
354
355 #[test]
356 fn test_basic_undirected() {
357 let mut g = Graph::with_vertices(3);
358 g.add_edge(0, 1).unwrap();
359 g.add_edge(1, 2).unwrap();
360
361 let mut buf = Vec::new();
362 write_leda(&g, None, None, &mut buf).unwrap();
363 let s = String::from_utf8(buf).unwrap();
364
365 assert!(s.starts_with("LEDA.GRAPH\n"));
366 assert!(s.contains("void\nvoid\n-2\n"));
367 assert!(s.contains("# Vertices\n3\n"));
368 assert!(s.contains("|{}|\n|{}|\n|{}|\n"));
369 assert!(s.contains("# Edges\n2\n"));
370 assert!(s.contains("1 2 0 |{}|\n"));
371 assert!(s.contains("2 3 0 |{}|\n"));
372 }
373
374 #[test]
375 fn test_directed() {
376 let mut g = Graph::new(2, true).unwrap();
377 g.add_edge(0, 1).unwrap();
378
379 let mut buf = Vec::new();
380 write_leda(&g, None, None, &mut buf).unwrap();
381 let s = String::from_utf8(buf).unwrap();
382
383 assert!(s.contains("-1\n"));
384 }
385
386 #[test]
387 fn test_with_labels() {
388 let mut g = Graph::with_vertices(3);
389 g.add_edge(0, 1).unwrap();
390
391 let labels = vec!["Alice".to_string(), "Bob".to_string(), "Carol".to_string()];
392 let mut buf = Vec::new();
393 write_leda(&g, Some(&labels), None, &mut buf).unwrap();
394 let s = String::from_utf8(buf).unwrap();
395
396 assert!(s.contains("string\nvoid\n"));
397 assert!(s.contains("|{Alice}|\n"));
398 assert!(s.contains("|{Bob}|\n"));
399 assert!(s.contains("|{Carol}|\n"));
400 }
401
402 #[test]
403 fn test_with_weights() {
404 let mut g = Graph::with_vertices(2);
405 g.add_edge(0, 1).unwrap();
406
407 let weights = vec![3.5];
408 let mut buf = Vec::new();
409 write_leda(&g, None, Some(&weights), &mut buf).unwrap();
410 let s = String::from_utf8(buf).unwrap();
411
412 assert!(s.contains("void\ndouble\n"));
413 assert!(s.contains("1 2 0 |{3.5}|\n"));
414 }
415
416 #[test]
417 fn test_with_labels_and_weights() {
418 let mut g = Graph::with_vertices(2);
419 g.add_edge(0, 1).unwrap();
420
421 let labels = vec!["X".to_string(), "Y".to_string()];
422 let weights = vec![1.25];
423 let mut buf = Vec::new();
424 write_leda(&g, Some(&labels), Some(&weights), &mut buf).unwrap();
425 let s = String::from_utf8(buf).unwrap();
426
427 assert!(s.contains("string\ndouble\n"));
428 assert!(s.contains("|{X}|\n"));
429 assert!(s.contains("|{Y}|\n"));
430 assert!(s.contains("1 2 0 |{1.25}|\n"));
431 }
432
433 #[test]
434 fn test_empty_graph() {
435 let g = Graph::with_vertices(0);
436
437 let mut buf = Vec::new();
438 write_leda(&g, None, None, &mut buf).unwrap();
439 let s = String::from_utf8(buf).unwrap();
440
441 assert!(s.contains("# Vertices\n0\n"));
442 assert!(s.contains("# Edges\n0\n"));
443 }
444
445 #[test]
446 fn test_no_edges() {
447 let g = Graph::with_vertices(3);
448
449 let mut buf = Vec::new();
450 write_leda(&g, None, None, &mut buf).unwrap();
451 let s = String::from_utf8(buf).unwrap();
452
453 assert!(s.contains("# Vertices\n3\n"));
454 assert!(s.contains("# Edges\n0\n"));
455 }
456
457 #[test]
458 fn test_label_mismatch_error() {
459 let g = Graph::with_vertices(3);
460 let labels = vec!["A".to_string()];
461 let mut buf = Vec::new();
462 assert!(write_leda(&g, Some(&labels), None, &mut buf).is_err());
463 }
464
465 #[test]
466 fn test_weight_mismatch_error() {
467 let mut g = Graph::with_vertices(2);
468 g.add_edge(0, 1).unwrap();
469 let weights = vec![1.0, 2.0];
470 let mut buf = Vec::new();
471 assert!(write_leda(&g, None, Some(&weights), &mut buf).is_err());
472 }
473
474 #[test]
475 fn test_newline_in_label_error() {
476 let g = Graph::with_vertices(2);
477 let labels = vec!["hello\nworld".to_string(), "ok".to_string()];
478 let mut buf = Vec::new();
479 assert!(write_leda(&g, Some(&labels), None, &mut buf).is_err());
480 }
481
482 #[test]
483 fn test_self_loop() {
484 let mut g = Graph::with_vertices(2);
485 g.add_edge(0, 0).unwrap();
486
487 let mut buf = Vec::new();
488 write_leda(&g, None, None, &mut buf).unwrap();
489 let s = String::from_utf8(buf).unwrap();
490
491 assert!(s.contains("1 1 0 |{}|\n"));
492 }
493
494 #[test]
495 fn test_one_based_vertex_ids() {
496 let mut g = Graph::with_vertices(4);
497 g.add_edge(2, 3).unwrap();
498
499 let mut buf = Vec::new();
500 write_leda(&g, None, None, &mut buf).unwrap();
501 let s = String::from_utf8(buf).unwrap();
502
503 assert!(s.contains("3 4 0 |{}|\n"));
504 }
505
506 #[test]
509 fn test_read_basic_undirected() {
510 let input = b"LEDA.GRAPH\nvoid\nvoid\n-2\n# Vertices\n3\n|{}|\n|{}|\n|{}|\n# Edges\n2\n1 2 0 |{}|\n2 3 0 |{}|\n";
511 let result = read_leda(&input[..]).unwrap();
512 assert_eq!(result.graph.vcount(), 3);
513 assert_eq!(result.graph.ecount(), 2);
514 assert!(!result.graph.is_directed());
515 assert!(result.labels.is_none());
516 assert!(result.weights.is_none());
517 }
518
519 #[test]
520 fn test_read_directed() {
521 let input =
522 b"LEDA.GRAPH\nvoid\nvoid\n-1\n# Vertices\n2\n|{}|\n|{}|\n# Edges\n1\n1 2 0 |{}|\n";
523 let result = read_leda(&input[..]).unwrap();
524 assert!(result.graph.is_directed());
525 assert_eq!(result.graph.ecount(), 1);
526 }
527
528 #[test]
529 fn test_read_with_labels() {
530 let input = b"LEDA.GRAPH\nstring\nvoid\n-2\n# Vertices\n3\n|{Alice}|\n|{Bob}|\n|{Carol}|\n# Edges\n1\n1 2 0 |{}|\n";
531 let result = read_leda(&input[..]).unwrap();
532 let labels = result.labels.unwrap();
533 assert_eq!(labels, vec!["Alice", "Bob", "Carol"]);
534 }
535
536 #[test]
537 fn test_read_with_weights() {
538 let input =
539 b"LEDA.GRAPH\nvoid\ndouble\n-2\n# Vertices\n2\n|{}|\n|{}|\n# Edges\n1\n1 2 0 |{3.5}|\n";
540 let result = read_leda(&input[..]).unwrap();
541 let w = result.weights.unwrap();
542 assert!((w[0] - 3.5).abs() < 1e-10);
543 }
544
545 #[test]
546 fn test_read_with_labels_and_weights() {
547 let input = b"LEDA.GRAPH\nstring\ndouble\n-2\n# Vertices\n2\n|{X}|\n|{Y}|\n# Edges\n1\n1 2 0 |{1.25}|\n";
548 let result = read_leda(&input[..]).unwrap();
549 let labels = result.labels.unwrap();
550 assert_eq!(labels, vec!["X", "Y"]);
551 let w = result.weights.unwrap();
552 assert!((w[0] - 1.25).abs() < 1e-10);
553 }
554
555 #[test]
556 fn test_read_empty_graph() {
557 let input = b"LEDA.GRAPH\nvoid\nvoid\n-2\n# Vertices\n0\n# Edges\n0\n";
558 let result = read_leda(&input[..]).unwrap();
559 assert_eq!(result.graph.vcount(), 0);
560 assert_eq!(result.graph.ecount(), 0);
561 }
562
563 #[test]
564 fn test_read_no_edges() {
565 let input = b"LEDA.GRAPH\nvoid\nvoid\n-2\n# Vertices\n3\n|{}|\n|{}|\n|{}|\n# Edges\n0\n";
566 let result = read_leda(&input[..]).unwrap();
567 assert_eq!(result.graph.vcount(), 3);
568 assert_eq!(result.graph.ecount(), 0);
569 }
570
571 #[test]
572 fn test_read_self_loop() {
573 let input =
574 b"LEDA.GRAPH\nvoid\nvoid\n-2\n# Vertices\n2\n|{}|\n|{}|\n# Edges\n1\n1 1 0 |{}|\n";
575 let result = read_leda(&input[..]).unwrap();
576 assert_eq!(result.graph.ecount(), 1);
577 let (from, to) = result.graph.edge(0).unwrap();
578 assert_eq!(from, 0);
579 assert_eq!(to, 0);
580 }
581
582 #[test]
583 fn test_read_bad_header() {
584 let input = b"NOT_LEDA\nvoid\nvoid\n-2\n";
585 assert!(read_leda(&input[..]).is_err());
586 }
587
588 #[test]
589 fn test_read_bad_directedness() {
590 let input = b"LEDA.GRAPH\nvoid\nvoid\n-3\n# Vertices\n0\n# Edges\n0\n";
591 assert!(read_leda(&input[..]).is_err());
592 }
593
594 #[test]
595 fn test_read_vertex_id_out_of_range() {
596 let input =
597 b"LEDA.GRAPH\nvoid\nvoid\n-2\n# Vertices\n2\n|{}|\n|{}|\n# Edges\n1\n1 5 0 |{}|\n";
598 assert!(read_leda(&input[..]).is_err());
599 }
600
601 #[test]
602 fn test_roundtrip_undirected() {
603 let mut g = Graph::with_vertices(4);
604 g.add_edge(0, 1).unwrap();
605 g.add_edge(1, 2).unwrap();
606 g.add_edge(2, 3).unwrap();
607
608 let mut buf = Vec::new();
609 write_leda(&g, None, None, &mut buf).unwrap();
610 let result = read_leda(&buf[..]).unwrap();
611
612 assert_eq!(result.graph.vcount(), g.vcount());
613 assert_eq!(result.graph.ecount(), g.ecount());
614 assert_eq!(result.graph.is_directed(), g.is_directed());
615 }
616
617 #[test]
618 fn test_roundtrip_directed() {
619 let mut g = Graph::new(3, true).unwrap();
620 g.add_edge(0, 1).unwrap();
621 g.add_edge(1, 2).unwrap();
622
623 let mut buf = Vec::new();
624 write_leda(&g, None, None, &mut buf).unwrap();
625 let result = read_leda(&buf[..]).unwrap();
626
627 assert_eq!(result.graph.vcount(), g.vcount());
628 assert_eq!(result.graph.ecount(), g.ecount());
629 assert!(result.graph.is_directed());
630 }
631
632 #[test]
633 fn test_roundtrip_with_labels() {
634 let mut g = Graph::with_vertices(3);
635 g.add_edge(0, 1).unwrap();
636 g.add_edge(1, 2).unwrap();
637
638 let labels = vec!["A".to_string(), "B".to_string(), "C".to_string()];
639 let mut buf = Vec::new();
640 write_leda(&g, Some(&labels), None, &mut buf).unwrap();
641 let result = read_leda(&buf[..]).unwrap();
642
643 assert_eq!(result.labels.unwrap(), labels);
644 }
645
646 #[test]
647 fn test_roundtrip_with_weights() {
648 let mut g = Graph::with_vertices(2);
649 g.add_edge(0, 1).unwrap();
650
651 let weights = vec![2.75];
652 let mut buf = Vec::new();
653 write_leda(&g, None, Some(&weights), &mut buf).unwrap();
654 let result = read_leda(&buf[..]).unwrap();
655
656 let w = result.weights.unwrap();
657 assert!((w[0] - 2.75).abs() < 1e-10);
658 }
659
660 #[test]
661 fn test_read_empty_label() {
662 let input = b"LEDA.GRAPH\nstring\nvoid\n-2\n# Vertices\n2\n|{}|\n|{hello}|\n# Edges\n0\n";
663 let result = read_leda(&input[..]).unwrap();
664 let labels = result.labels.unwrap();
665 assert_eq!(labels[0], "");
666 assert_eq!(labels[1], "hello");
667 }
668
669 #[test]
672 fn test_read_stores_label_attribute() {
673 let input =
674 b"LEDA.GRAPH\nstring\nvoid\n-2\n# Vertices\n2\n|{Alice}|\n|{Bob}|\n# Edges\n1\n1 2 0 |{}|\n";
675 let result = read_leda(&input[..]).unwrap();
676 assert_eq!(
677 result
678 .graph
679 .vertex_attribute("name", 0)
680 .and_then(AttributeValue::as_str),
681 Some("Alice")
682 );
683 assert_eq!(
684 result
685 .graph
686 .vertex_attribute("name", 1)
687 .and_then(AttributeValue::as_str),
688 Some("Bob")
689 );
690 }
691
692 #[test]
693 fn test_read_stores_weight_attribute() {
694 let input =
695 b"LEDA.GRAPH\nvoid\ndouble\n-2\n# Vertices\n2\n|{}|\n|{}|\n# Edges\n1\n1 2 0 |{4.5}|\n";
696 let result = read_leda(&input[..]).unwrap();
697 let w = result
698 .graph
699 .edge_attribute("weight", 0)
700 .and_then(AttributeValue::as_f64)
701 .unwrap();
702 assert!((w - 4.5).abs() < 1e-10);
703 }
704
705 #[test]
706 fn test_read_no_label_attribute_when_void() {
707 let input = b"LEDA.GRAPH\nvoid\nvoid\n-2\n# Vertices\n2\n|{}|\n|{}|\n# Edges\n0\n";
708 let result = read_leda(&input[..]).unwrap();
709 assert!(result.graph.vertex_attribute("name", 0).is_none());
710 }
711
712 #[test]
713 fn test_write_fallback_to_label_attribute() {
714 let mut g = Graph::with_vertices(2);
715 g.add_edge(0, 1).unwrap();
716 g.set_vertex_attribute("name", 0, AttributeValue::String("X".into()))
717 .unwrap();
718 g.set_vertex_attribute("name", 1, AttributeValue::String("Y".into()))
719 .unwrap();
720
721 let mut buf = Vec::new();
722 write_leda(&g, None, None, &mut buf).unwrap();
723 let s = String::from_utf8(buf).unwrap();
724 assert!(s.contains("string"));
725 assert!(s.contains("|{X}|"));
726 assert!(s.contains("|{Y}|"));
727 }
728
729 #[test]
730 fn test_write_fallback_to_weight_attribute() {
731 let mut g = Graph::with_vertices(2);
732 g.add_edge(0, 1).unwrap();
733 g.set_edge_attribute("weight", 0, AttributeValue::Numeric(7.5))
734 .unwrap();
735
736 let mut buf = Vec::new();
737 write_leda(&g, None, None, &mut buf).unwrap();
738 let s = String::from_utf8(buf).unwrap();
739 assert!(s.contains("double"));
740 assert!(s.contains("|{7.5}|"));
741 }
742
743 #[test]
744 fn test_roundtrip_via_attributes() {
745 let input = b"LEDA.GRAPH\nstring\ndouble\n-2\n# Vertices\n2\n|{Alice}|\n|{Bob}|\n# Edges\n1\n1 2 0 |{1.5}|\n";
746 let result = read_leda(&input[..]).unwrap();
747
748 let mut buf = Vec::new();
749 write_leda(&result.graph, None, None, &mut buf).unwrap();
750
751 let result2 = read_leda(&buf[..]).unwrap();
752 assert_eq!(result2.graph.vcount(), 2);
753 assert_eq!(result2.graph.ecount(), 1);
754 assert!(result2.labels.is_some());
755 assert!(result2.weights.is_some());
756 let labels = result2.labels.unwrap();
757 assert_eq!(labels, vec!["Alice", "Bob"]);
758 }
759
760 #[test]
761 fn test_explicit_params_override_attributes() {
762 let mut g = Graph::with_vertices(2);
763 g.add_edge(0, 1).unwrap();
764 g.set_vertex_attribute("name", 0, AttributeValue::String("attr_A".into()))
765 .unwrap();
766 g.set_vertex_attribute("name", 1, AttributeValue::String("attr_B".into()))
767 .unwrap();
768
769 let labels = vec!["explicit_A".to_string(), "explicit_B".to_string()];
770 let mut buf = Vec::new();
771 write_leda(&g, Some(&labels), None, &mut buf).unwrap();
772 let s = String::from_utf8(buf).unwrap();
773 assert!(s.contains("|{explicit_A}|"));
774 assert!(!s.contains("attr_A"));
775 }
776}