Skip to main content

link_cli/
named_type_links.rs

1use anyhow::{Context, Result};
2use std::collections::HashSet;
3use std::fs::OpenOptions;
4use std::io::{BufWriter, Write};
5use std::path::Path;
6
7use crate::error::LinkError;
8use crate::link::Link;
9use crate::link_storage::LinkStorage;
10use crate::named_types::{NamedTypes, NamedTypesDecorator};
11
12pub trait NamedTypeLinks {
13    fn create(&mut self, source: u32, target: u32) -> u32;
14    fn ensure_created(&mut self, id: u32) -> u32;
15    fn try_ensure_created(&mut self, id: u32) -> Result<u32> {
16        if id == 0 || id == u32::MAX {
17            return Err(LinkError::InvalidFormat(format!(
18                "Cannot ensure unsupported link address {id}"
19            ))
20            .into());
21        }
22
23        Ok(self.ensure_created(id))
24    }
25    fn get_link(&mut self, id: u32) -> Option<Link>;
26    fn exists(&mut self, id: u32) -> bool;
27    fn update(&mut self, id: u32, source: u32, target: u32) -> Result<Link>;
28    fn delete(&mut self, id: u32) -> Result<Link>;
29    fn all_links(&mut self) -> Vec<Link>;
30    fn search(&mut self, source: u32, target: u32) -> Option<u32>;
31    fn get_or_create(&mut self, source: u32, target: u32) -> u32;
32    fn get_name(&mut self, id: u32) -> Result<Option<String>>;
33    fn set_name(&mut self, id: u32, name: &str) -> Result<u32>;
34    fn get_by_name(&mut self, name: &str) -> Result<Option<u32>>;
35    fn remove_name(&mut self, id: u32) -> Result<()>;
36    fn save(&mut self) -> Result<()>;
37
38    fn get_or_create_named(&mut self, name: &str) -> Result<u32> {
39        if let Some(id) = self.get_by_name(name)? {
40            return Ok(id);
41        }
42
43        let id = self.create(0, 0);
44        self.set_name(id, name)?;
45        self.update(id, id, id)?;
46        Ok(id)
47    }
48
49    fn format_reference(&mut self, id: u32) -> Result<String> {
50        Ok(self
51            .get_name(id)?
52            .map(|name| escape_lino_reference(&name))
53            .unwrap_or_else(|| id.to_string()))
54    }
55
56    fn format_lino(&mut self, link: &Link) -> Result<String> {
57        Ok(format!(
58            "({}: {} {})",
59            self.format_reference(link.index)?,
60            self.format_reference(link.source)?,
61            self.format_reference(link.target)?
62        ))
63    }
64
65    fn lino_lines(&mut self) -> Result<Vec<String>> {
66        let mut links = self.all_links();
67        links.sort_by_key(|link| link.index);
68
69        links
70            .iter()
71            .map(|link| self.format_lino(link))
72            .collect::<Result<Vec<_>>>()
73    }
74
75    fn write_lino_output<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
76        let path = path.as_ref();
77        let file = OpenOptions::new()
78            .write(true)
79            .create(true)
80            .truncate(true)
81            .open(path)
82            .with_context(|| format!("Failed to create LiNo output: {}", path.display()))?;
83
84        let mut writer = BufWriter::new(file);
85        for line in self.lino_lines()? {
86            writeln!(writer, "{line}")?;
87        }
88        writer.flush()?;
89        Ok(())
90    }
91
92    fn print_all_lino(&mut self) -> Result<()> {
93        for line in self.lino_lines()? {
94            println!("{line}");
95        }
96        Ok(())
97    }
98
99    fn print_change_lino(&mut self, before: &Option<Link>, after: &Option<Link>) -> Result<()> {
100        let before_text = before
101            .map(|link| self.format_lino(&link))
102            .transpose()?
103            .unwrap_or_default();
104        let after_text = after
105            .map(|link| self.format_lino(&link))
106            .transpose()?
107            .unwrap_or_default();
108        println!("({before_text}) ({after_text})");
109        Ok(())
110    }
111
112    fn format_structure(&mut self, id: u32) -> Result<String> {
113        let mut visited = HashSet::new();
114        self.format_structure_recursive(id, &mut visited)
115    }
116
117    fn format_structure_recursive(
118        &mut self,
119        id: u32,
120        visited: &mut HashSet<u32>,
121    ) -> Result<String> {
122        let link = self.get_link(id).ok_or(LinkError::NotFound(id))?;
123        if !visited.insert(id) {
124            return self.format_reference(id);
125        }
126
127        let source = if self.exists(link.source) && !visited.contains(&link.source) {
128            self.format_structure_recursive(link.source, visited)?
129        } else {
130            self.format_reference(link.source)?
131        };
132        let target = self.format_reference(link.target)?;
133        let index = self.format_reference(link.index)?;
134        visited.remove(&id);
135
136        Ok(format!("({index}: {source} {target})"))
137    }
138}
139
140impl NamedTypeLinks for LinkStorage {
141    fn create(&mut self, source: u32, target: u32) -> u32 {
142        LinkStorage::create(self, source, target)
143    }
144
145    fn ensure_created(&mut self, id: u32) -> u32 {
146        LinkStorage::ensure_created(self, id)
147    }
148
149    fn get_link(&mut self, id: u32) -> Option<Link> {
150        self.get(id).copied()
151    }
152
153    fn exists(&mut self, id: u32) -> bool {
154        LinkStorage::exists(self, id)
155    }
156
157    fn update(&mut self, id: u32, source: u32, target: u32) -> Result<Link> {
158        LinkStorage::update(self, id, source, target)
159    }
160
161    fn delete(&mut self, id: u32) -> Result<Link> {
162        LinkStorage::delete(self, id)
163    }
164
165    fn all_links(&mut self) -> Vec<Link> {
166        self.all().into_iter().copied().collect()
167    }
168
169    fn search(&mut self, source: u32, target: u32) -> Option<u32> {
170        LinkStorage::search(self, source, target)
171    }
172
173    fn get_or_create(&mut self, source: u32, target: u32) -> u32 {
174        LinkStorage::get_or_create(self, source, target)
175    }
176
177    fn get_name(&mut self, id: u32) -> Result<Option<String>> {
178        Ok(LinkStorage::get_name(self, id).cloned())
179    }
180
181    fn set_name(&mut self, id: u32, name: &str) -> Result<u32> {
182        LinkStorage::set_name(self, id, name);
183        Ok(id)
184    }
185
186    fn get_by_name(&mut self, name: &str) -> Result<Option<u32>> {
187        Ok(LinkStorage::get_by_name(self, name))
188    }
189
190    fn remove_name(&mut self, id: u32) -> Result<()> {
191        LinkStorage::remove_name(self, id);
192        Ok(())
193    }
194
195    fn save(&mut self) -> Result<()> {
196        LinkStorage::save(self)
197    }
198
199    fn get_or_create_named(&mut self, name: &str) -> Result<u32> {
200        Ok(LinkStorage::get_or_create_named(self, name))
201    }
202}
203
204impl NamedTypeLinks for NamedTypesDecorator {
205    fn create(&mut self, source: u32, target: u32) -> u32 {
206        NamedTypesDecorator::create(self, source, target)
207    }
208
209    fn ensure_created(&mut self, id: u32) -> u32 {
210        NamedTypesDecorator::ensure_created(self, id)
211    }
212
213    fn get_link(&mut self, id: u32) -> Option<Link> {
214        self.get(id).copied()
215    }
216
217    fn exists(&mut self, id: u32) -> bool {
218        NamedTypesDecorator::exists(self, id)
219    }
220
221    fn update(&mut self, id: u32, source: u32, target: u32) -> Result<Link> {
222        NamedTypesDecorator::update(self, id, source, target)
223    }
224
225    fn delete(&mut self, id: u32) -> Result<Link> {
226        NamedTypesDecorator::delete(self, id)
227    }
228
229    fn all_links(&mut self) -> Vec<Link> {
230        self.all().into_iter().copied().collect()
231    }
232
233    fn search(&mut self, source: u32, target: u32) -> Option<u32> {
234        NamedTypesDecorator::search(self, source, target)
235    }
236
237    fn get_or_create(&mut self, source: u32, target: u32) -> u32 {
238        NamedTypesDecorator::get_or_create(self, source, target)
239    }
240
241    fn get_name(&mut self, id: u32) -> Result<Option<String>> {
242        NamedTypes::get_name(self, id)
243    }
244
245    fn set_name(&mut self, id: u32, name: &str) -> Result<u32> {
246        NamedTypes::set_name(self, id, name)
247    }
248
249    fn get_by_name(&mut self, name: &str) -> Result<Option<u32>> {
250        NamedTypes::get_by_name(self, name)
251    }
252
253    fn remove_name(&mut self, id: u32) -> Result<()> {
254        NamedTypes::remove_name(self, id)
255    }
256
257    fn save(&mut self) -> Result<()> {
258        NamedTypesDecorator::save(self)
259    }
260}
261
262pub(crate) fn escape_lino_reference(reference: &str) -> String {
263    if reference.is_empty() || reference.trim().is_empty() {
264        return String::new();
265    }
266
267    let has_single_quote = reference.contains('\'');
268    let has_double_quote = reference.contains('"');
269    let needs_quoting = reference.contains(':')
270        || reference.contains('(')
271        || reference.contains(')')
272        || reference.contains(' ')
273        || reference.contains('\t')
274        || reference.contains('\n')
275        || reference.contains('\r')
276        || has_single_quote
277        || has_double_quote;
278
279    if has_single_quote && has_double_quote {
280        return format!("'{}'", reference.replace('\'', "\\'"));
281    }
282
283    if has_double_quote {
284        return format!("'{reference}'");
285    }
286
287    if has_single_quote {
288        return format!("\"{reference}\"");
289    }
290
291    if needs_quoting {
292        return format!("'{reference}'");
293    }
294
295    reference.to_string()
296}