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 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 let wrapped_parent = match parent {
109 LiNo::Ref(ref_id) => LiNo::Link { id: None, values: vec![LiNo::Ref(ref_id)] },
110 link => link
111 };
112
113 let wrapped_current = match current.clone() {
115 LiNo::Ref(ref_id) => LiNo::Link { id: None, values: vec![LiNo::Ref(ref_id)] },
116 link => link
117 };
118
119 LiNo::Link {
120 id: None,
121 values: vec![wrapped_parent, wrapped_current]
122 }
123 } else {
124 current.clone()
125 };
126
127 result.push(combined.clone());
128
129 for child in &link.children {
131 flatten_link_recursive(child, Some(combined.clone()), result);
132 }
133}
134
135pub fn parse_lino(document: &str) -> Result<LiNo<String>, String> {
136 if document.trim().is_empty() {
138 return Ok(LiNo::Link { id: None, values: vec![] });
139 }
140
141 match parser::parse_document(document) {
142 Ok((_, links)) => {
143 if links.is_empty() {
144 Ok(LiNo::Link { id: None, values: vec![] })
145 } else {
146 let flattened = flatten_links(links);
148 Ok(LiNo::Link { id: None, values: flattened })
149 }
150 }
151 Err(e) => Err(format!("Parse error: {:?}", e))
152 }
153}
154
155pub fn parse_lino_to_links(document: &str) -> Result<Vec<LiNo<String>>, String> {
157 if document.trim().is_empty() {
159 return Ok(vec![]);
160 }
161
162 match parser::parse_document(document) {
163 Ok((_, links)) => {
164 if links.is_empty() {
165 Ok(vec![])
166 } else {
167 let flattened = flatten_links(links);
169 Ok(flattened)
170 }
171 }
172 Err(e) => Err(format!("Parse error: {:?}", e))
173 }
174}
175