1pub mod parser;
2
3use std::fmt;
4
5#[derive(Debug, Clone, PartialEq)]
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 if link.is_indented_id && link.id.is_some() && link.values.is_empty() && !link.children.is_empty() {
90 let child_values: Vec<LiNo<String>> = link.children.iter().map(|child| {
91 if child.values.len() == 1 && child.values[0].id.is_some() && child.values[0].values.is_empty() && child.values[0].children.is_empty() {
93 LiNo::Ref(child.values[0].id.clone().unwrap())
94 } else {
95 parser::Link {
96 id: child.id.clone(),
97 values: child.values.clone(),
98 children: vec![],
99 is_indented_id: false,
100 }.into()
101 }
102 }).collect();
103
104 let current = LiNo::Link {
105 id: link.id.clone(),
106 values: child_values
107 };
108
109 let combined = if let Some(parent) = parent {
110 let wrapped_parent = match parent {
112 LiNo::Ref(ref_id) => LiNo::Link { id: None, values: vec![LiNo::Ref(ref_id)] },
113 link => link
114 };
115
116 LiNo::Link {
117 id: None,
118 values: vec![wrapped_parent, current]
119 }
120 } else {
121 current
122 };
123
124 result.push(combined);
125 return; }
127
128 let current = if link.values.is_empty() {
130 if let Some(id) = &link.id {
131 LiNo::Ref(id.clone())
132 } else {
133 LiNo::Link { id: None, values: vec![] }
134 }
135 } else {
136 let values: Vec<LiNo<String>> = link.values.iter().map(|v| {
137 parser::Link {
138 id: v.id.clone(),
139 values: v.values.clone(),
140 children: vec![],
141 is_indented_id: false,
142 }.into()
143 }).collect();
144 LiNo::Link { id: link.id.clone(), values }
145 };
146
147 let combined = if let Some(parent) = parent {
149 let wrapped_parent = match parent {
151 LiNo::Ref(ref_id) => LiNo::Link { id: None, values: vec![LiNo::Ref(ref_id)] },
152 link => link
153 };
154
155 let wrapped_current = match current.clone() {
157 LiNo::Ref(ref_id) => LiNo::Link { id: None, values: vec![LiNo::Ref(ref_id)] },
158 link => link
159 };
160
161 LiNo::Link {
162 id: None,
163 values: vec![wrapped_parent, wrapped_current]
164 }
165 } else {
166 current.clone()
167 };
168
169 result.push(combined.clone());
170
171 for child in &link.children {
173 flatten_link_recursive(child, Some(combined.clone()), result);
174 }
175}
176
177pub fn parse_lino(document: &str) -> Result<LiNo<String>, String> {
178 if document.trim().is_empty() {
180 return Ok(LiNo::Link { id: None, values: vec![] });
181 }
182
183 match parser::parse_document(document) {
184 Ok((_, links)) => {
185 if links.is_empty() {
186 Ok(LiNo::Link { id: None, values: vec![] })
187 } else {
188 let flattened = flatten_links(links);
190 Ok(LiNo::Link { id: None, values: flattened })
191 }
192 }
193 Err(e) => Err(format!("Parse error: {:?}", e))
194 }
195}
196
197pub fn parse_lino_to_links(document: &str) -> Result<Vec<LiNo<String>>, String> {
199 if document.trim().is_empty() {
201 return Ok(vec![]);
202 }
203
204 match parser::parse_document(document) {
205 Ok((_, links)) => {
206 if links.is_empty() {
207 Ok(vec![])
208 } else {
209 let flattened = flatten_links(links);
211 Ok(flattened)
212 }
213 }
214 Err(e) => Err(format!("Parse error: {:?}", e))
215 }
216}
217