1pub mod parser;
2
3use std::fmt;
4
5#[derive(Debug, Clone)]
6pub enum LiNo<T> {
7 Link { id: Option<T>, values: Vec<Self> },
8 Ref(T),
9}
10
11impl<T> LiNo<T> {
12 pub fn is_ref(&self) -> bool {
13 matches!(self, LiNo::Ref(_))
14 }
15
16 pub fn is_link(&self) -> bool {
17 matches!(self, LiNo::Link { .. })
18 }
19}
20
21impl<T: ToString> fmt::Display for LiNo<T> {
22 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23 match self {
24 LiNo::Ref(value) => write!(f, "{}", value.to_string()),
25 LiNo::Link { id, values } => {
26 let id_str = id
27 .as_ref()
28 .map(|id| format!("{}: ", id.to_string()))
29 .unwrap_or_default();
30
31 if f.alternate() {
32 let lines = values
34 .iter()
35 .map(|value| {
36 match value {
39 LiNo::Ref(_) => format!("{}({})", id_str, value),
40 _ => format!("{}{}", id_str, value),
41 }
42 })
43 .collect::<Vec<_>>()
44 .join("\n");
45 write!(f, "{}", lines)
46 } else {
47 let values_str = values
48 .iter()
49 .map(|value| value.to_string())
50 .collect::<Vec<_>>()
51 .join(" ");
52 write!(f, "({}{})", id_str, values_str)
53 }
54 }
55 }
56 }
57}
58
59impl From<parser::Link> for LiNo<String> {
61 fn from(link: parser::Link) -> Self {
62 if link.values.is_empty() && link.children.is_empty() {
63 if let Some(id) = link.id {
64 LiNo::Ref(id)
65 } else {
66 LiNo::Link { id: None, values: vec![] }
67 }
68 } else {
69 let values: Vec<LiNo<String>> = link.values.into_iter().map(|v| v.into()).collect();
70 LiNo::Link { id: link.id, values }
71 }
72 }
73}
74
75fn flatten_links(links: Vec<parser::Link>) -> Vec<LiNo<String>> {
77 let mut result = vec![];
78
79 for link in links {
80 flatten_link_recursive(&link, None, &mut result);
81 }
82
83 result
84}
85
86fn flatten_link_recursive(link: &parser::Link, parent: Option<LiNo<String>>, result: &mut Vec<LiNo<String>>) {
87 let current = if link.values.is_empty() {
89 if let Some(id) = &link.id {
90 LiNo::Ref(id.clone())
91 } else {
92 LiNo::Link { id: None, values: vec![] }
93 }
94 } else {
95 let values: Vec<LiNo<String>> = link.values.iter().map(|v| {
96 parser::Link {
97 id: v.id.clone(),
98 values: v.values.clone(),
99 children: vec![]
100 }.into()
101 }).collect();
102 LiNo::Link { id: link.id.clone(), values }
103 };
104
105 let combined = if let Some(parent) = parent {
107 LiNo::Link {
108 id: None,
109 values: vec![parent.clone(), current.clone()]
110 }
111 } else {
112 current.clone()
113 };
114
115 result.push(combined.clone());
116
117 for child in &link.children {
119 flatten_link_recursive(child, Some(combined.clone()), result);
120 }
121}
122
123pub fn parse_lino(document: &str) -> Result<LiNo<String>, String> {
124 if document.trim().is_empty() {
126 return Ok(LiNo::Link { id: None, values: vec![] });
127 }
128
129 match parser::parse_document(document) {
130 Ok((_, links)) => {
131 if links.is_empty() {
132 Ok(LiNo::Link { id: None, values: vec![] })
133 } else {
134 let flattened = flatten_links(links);
136 Ok(LiNo::Link { id: None, values: flattened })
137 }
138 }
139 Err(e) => Err(format!("Parse error: {:?}", e))
140 }
141}
142