links_notation/
nested_format.rs1use crate::{LiNo, escape_reference};
2
3fn parts<T>(link: &LiNo<T>) -> (Option<&T>, &[LiNo<T>]) {
4 match link {
5 LiNo::Ref(id) => (Some(id), &[]),
6 LiNo::Link { id, values } => (id.as_ref(), values),
7 }
8}
9fn atom<T>(link: &LiNo<T>) -> bool {
10 let (id, values) = parts(link);
11 id.is_some() && values.is_empty()
12}
13fn can_nest<T>(link: &LiNo<T>) -> bool {
14 let (id, values) = parts(link);
15 !atom(link) && !(id.is_none() && values.len() == 1 && !atom(&values[0]))
16}
17fn line_holds<T>(link: &LiNo<T>, count: usize) -> bool {
18 let (id, values) = parts(link);
19 id.is_some() || count >= 2 || (count == 1 && atom(&values[0]))
20}
21fn write<T: ToString>(
22 link: &LiNo<T>,
23 pad: &str,
24 indent: &str,
25 under_name: bool,
26 lines: &mut Vec<String>,
27) {
28 let (id, values) = parts(link);
29 if !line_holds(link, values.len()) || (under_name && id.is_none() && values.len() == 1) {
30 lines.push(format!("{pad}{link}"));
31 return;
32 }
33 let mut count = values.len();
34 while count > 0 && can_nest(&values[count - 1]) {
35 count -= 1;
36 }
37 while count < values.len() && !line_holds(link, count) {
38 count += 1;
39 }
40 let mut head = Vec::new();
41 if let Some(id) = id {
42 head.push(format!("{}:", escape_reference(&id.to_string())));
43 }
44 for value in &values[..count] {
45 head.push(if atom(value) {
46 escape_reference(&parts(value).0.unwrap().to_string())
47 } else {
48 value.to_string()
49 });
50 }
51 lines.push(format!("{pad}{}", head.join(" ")));
52 let inner = format!("{pad}{indent}");
53 for value in &values[count..] {
54 write(
55 value,
56 &inner,
57 indent,
58 under_name || (id.is_some() && count == 0),
59 lines,
60 );
61 }
62}
63
64pub fn format_nested<T: ToString>(links: &[LiNo<T>]) -> String {
67 format_nested_with_indent(links, " ")
68}
69
70pub fn format_nested_with_indent<T: ToString>(links: &[LiNo<T>], indent: &str) -> String {
72 assert!(
73 !indent.is_empty() && indent.chars().all(|c| c == ' '),
74 "indent must contain only spaces and be nonempty"
75 );
76 let mut lines = Vec::new();
77 for link in links {
78 write(link, "", indent, false, &mut lines);
79 }
80 lines.join("\n")
81}