1pub mod parser;
2pub mod format_config;
3
4use std::fmt;
5use std::error::Error as StdError;
6
7#[derive(Debug)]
9pub enum ParseError {
10 EmptyInput,
12 SyntaxError(String),
14 InternalError(String),
16}
17
18impl fmt::Display for ParseError {
19 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20 match self {
21 ParseError::EmptyInput => write!(f, "Empty input"),
22 ParseError::SyntaxError(msg) => write!(f, "Syntax error: {}", msg),
23 ParseError::InternalError(msg) => write!(f, "Internal error: {}", msg),
24 }
25 }
26}
27
28impl StdError for ParseError {}
29
30#[derive(Debug, Clone, PartialEq)]
31pub enum LiNo<T> {
32 Link { id: Option<T>, values: Vec<Self> },
33 Ref(T),
34}
35
36impl<T> LiNo<T> {
37 pub fn is_ref(&self) -> bool {
38 matches!(self, LiNo::Ref(_))
39 }
40
41 pub fn is_link(&self) -> bool {
42 matches!(self, LiNo::Link { .. })
43 }
44}
45
46impl<T: ToString> fmt::Display for LiNo<T> {
47 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48 match self {
49 LiNo::Ref(value) => write!(f, "{}", value.to_string()),
50 LiNo::Link { id, values } => {
51 let id_str = id
52 .as_ref()
53 .map(|id| format!("{}: ", id.to_string()))
54 .unwrap_or_default();
55
56 if f.alternate() {
57 let lines = values
59 .iter()
60 .map(|value| {
61 match value {
64 LiNo::Ref(_) => format!("{}({})", id_str, value),
65 _ => format!("{}{}", id_str, value),
66 }
67 })
68 .collect::<Vec<_>>()
69 .join("\n");
70 write!(f, "{}", lines)
71 } else {
72 let values_str = values
73 .iter()
74 .map(|value| value.to_string())
75 .collect::<Vec<_>>()
76 .join(" ");
77 write!(f, "({}{})", id_str, values_str)
78 }
79 }
80 }
81 }
82}
83
84impl From<parser::Link> for LiNo<String> {
86 fn from(link: parser::Link) -> Self {
87 if link.values.is_empty() && link.children.is_empty() {
88 if let Some(id) = link.id {
89 LiNo::Ref(id)
90 } else {
91 LiNo::Link { id: None, values: vec![] }
92 }
93 } else {
94 let values: Vec<LiNo<String>> = link.values.into_iter().map(|v| v.into()).collect();
95 LiNo::Link { id: link.id, values }
96 }
97 }
98}
99
100fn flatten_links(links: Vec<parser::Link>) -> Vec<LiNo<String>> {
102 let mut result = vec![];
103
104 for link in links {
105 flatten_link_recursive(&link, None, &mut result);
106 }
107
108 result
109}
110
111fn flatten_link_recursive(link: &parser::Link, parent: Option<&LiNo<String>>, result: &mut Vec<LiNo<String>>) {
112 if link.is_indented_id && link.id.is_some() && link.values.is_empty() && !link.children.is_empty() {
115 let child_values: Vec<LiNo<String>> = link.children.iter().map(|child| {
116 if child.values.len() == 1 && child.values[0].values.is_empty() && child.values[0].children.is_empty() {
118 if let Some(ref id) = child.values[0].id {
120 LiNo::Ref(id.clone())
121 } else {
122 parser::Link {
124 id: child.id.clone(),
125 values: child.values.clone(),
126 children: vec![],
127 is_indented_id: false,
128 }.into()
129 }
130 } else {
131 parser::Link {
132 id: child.id.clone(),
133 values: child.values.clone(),
134 children: vec![],
135 is_indented_id: false,
136 }.into()
137 }
138 }).collect();
139
140 let current = LiNo::Link {
141 id: link.id.clone(),
142 values: child_values
143 };
144
145 let combined = if let Some(parent) = parent {
146 let wrapped_parent = match parent {
148 LiNo::Ref(ref_id) => LiNo::Link { id: None, values: vec![LiNo::Ref(ref_id.clone())] },
149 link => link.clone()
150 };
151
152 LiNo::Link {
153 id: None,
154 values: vec![wrapped_parent, current]
155 }
156 } else {
157 current
158 };
159
160 result.push(combined);
161 return; }
163
164 let current = if link.values.is_empty() {
166 if let Some(id) = &link.id {
167 LiNo::Ref(id.clone())
168 } else {
169 LiNo::Link { id: None, values: vec![] }
170 }
171 } else {
172 let values: Vec<LiNo<String>> = link.values.iter().map(|v| {
173 parser::Link {
174 id: v.id.clone(),
175 values: v.values.clone(),
176 children: vec![],
177 is_indented_id: false,
178 }.into()
179 }).collect();
180 LiNo::Link { id: link.id.clone(), values }
181 };
182
183 let combined = if let Some(parent) = parent {
185 let wrapped_parent = match parent {
187 LiNo::Ref(ref_id) => LiNo::Link { id: None, values: vec![LiNo::Ref(ref_id.clone())] },
188 link => link.clone()
189 };
190
191 let wrapped_current = match ¤t {
193 LiNo::Ref(ref_id) => LiNo::Link { id: None, values: vec![LiNo::Ref(ref_id.clone())] },
194 link => link.clone()
195 };
196
197 LiNo::Link {
198 id: None,
199 values: vec![wrapped_parent, wrapped_current]
200 }
201 } else {
202 current.clone()
203 };
204
205 result.push(combined.clone());
206
207 for child in &link.children {
209 flatten_link_recursive(child, Some(&combined), result);
210 }
211}
212
213pub fn parse_lino(document: &str) -> Result<LiNo<String>, ParseError> {
214 if document.trim().is_empty() {
216 return Ok(LiNo::Link { id: None, values: vec![] });
217 }
218
219 match parser::parse_document(document) {
220 Ok((_, links)) => {
221 if links.is_empty() {
222 Ok(LiNo::Link { id: None, values: vec![] })
223 } else {
224 let flattened = flatten_links(links);
226 Ok(LiNo::Link { id: None, values: flattened })
227 }
228 }
229 Err(e) => Err(ParseError::SyntaxError(format!("{:?}", e)))
230 }
231}
232
233pub fn parse_lino_to_links(document: &str) -> Result<Vec<LiNo<String>>, ParseError> {
235 if document.trim().is_empty() {
237 return Ok(vec![]);
238 }
239
240 match parser::parse_document(document) {
241 Ok((_, links)) => {
242 if links.is_empty() {
243 Ok(vec![])
244 } else {
245 let flattened = flatten_links(links);
247 Ok(flattened)
248 }
249 }
250 Err(e) => Err(ParseError::SyntaxError(format!("{:?}", e)))
251 }
252}
253
254pub fn format_links(links: &[LiNo<String>]) -> String {
257 links.iter()
258 .map(|link| format!("{}", link))
259 .collect::<Vec<_>>()
260 .join("\n")
261}
262