mabel_eno/elements/
field.rs

1use std::cell::Cell;
2use std::ops::{Range, RangeInclusive};
3use std::rc::Rc;
4use std::str::FromStr;
5
6use crate::elements::{AttributeImpl, CommentImpl, DocumentInternals, ElementImpl, ItemImpl};
7use crate::{Attribute, Element, Error, Item, Printer, SectionElement};
8
9#[derive(Debug)]
10pub struct Field {
11    content: FieldContent,
12    document_internals: Rc<DocumentInternals>,
13    escape_operator_ranges: Option<(Range<usize>, Range<usize>)>,
14    key_range: Range<usize>,
15    line_begin_index: usize,
16    pub line_number: u32,
17    operator_index: usize,
18    touched: Cell<bool>,
19}
20
21#[derive(Debug)]
22pub enum FieldContent {
23    Attributes(Vec<Attribute>),
24    Items(Vec<Item>),
25    None,
26    Value(Range<usize>),
27}
28
29pub trait FieldImpl {
30    fn get_content(&self) -> &FieldContent;
31    fn get_content_mut(&mut self) -> &mut FieldContent;
32    fn get_document_content(&self) -> &str;
33    #[allow(clippy::new_ret_no_self)]
34    fn new(
35        content: FieldContent,
36        document_internals: Rc<DocumentInternals>,
37        escape_operator_ranges: Option<(Range<usize>, Range<usize>)>,
38        key_range: Range<usize>,
39        line_begin_index: usize,
40        line_number: u32,
41        operator_index: usize,
42    ) -> Field;
43    fn set_content(&mut self, content: FieldContent);
44}
45
46impl Field {
47    pub fn attributes(&self) -> Result<&[Attribute], Error> {
48        match &self.content {
49            FieldContent::Attributes(attributes) => {
50                for attribute in attributes.iter() {
51                    attribute.touch();
52                }
53                Ok(attributes.as_slice())
54            }
55            FieldContent::Items(_) => Err(Error::new(
56                "Attributes expected, Items found".to_string(),
57                self.line_number,
58            )),
59            FieldContent::Value { .. } => Err(Error::new(
60                "Attributes expected, Value found".to_string(),
61                self.line_number,
62            )),
63            FieldContent::None => Ok(&[]),
64        }
65    }
66
67    pub fn items(&self) -> Result<&[Item], Error> {
68        match &self.content {
69            FieldContent::Attributes(_) => Err(Error::new(
70                "Items expected, Attributes found".to_string(),
71                self.line_number,
72            )),
73            FieldContent::Items(items) => {
74                for item in items.iter() {
75                    item.touch();
76                }
77                Ok(items.as_slice())
78            }
79            FieldContent::Value { .. } => Err(Error::new(
80                "Items expected, Value found".to_string(),
81                self.line_number,
82            )),
83            FieldContent::None => Ok(&[]),
84        }
85    }
86
87    pub fn optional_value(&self) -> Result<Option<String>, Error> {
88        match &self.content {
89            FieldContent::Attributes(attributes) => Err(Error::new(
90                "Value expected, Attributes found".to_string(),
91                attributes[0].line_number,
92            )),
93            FieldContent::Items(items) => Err(Error::new(
94                "Value expected, Items found".to_string(),
95                items[0].line_number,
96            )),
97            FieldContent::Value(range) => Ok(Some(
98                self.document_internals.content[range.clone()].to_owned(),
99            )),
100            FieldContent::None => Ok(None),
101        }
102    }
103
104    pub fn required_attribute(&self, key: &str) -> Result<&Attribute, Error> {
105        match &self.content {
106            FieldContent::Attributes(attributes) => {
107                let mut attribute_option = None;
108
109                for attribute in attributes {
110                    if attribute.key() == key {
111                        match attribute_option {
112                            Some(_) => {
113                                return Err(Error::new(
114                                    format!("Multiple attributes with key {} found", key),
115                                    self.line_number,
116                                ))
117                            }
118                            None => attribute_option = Some(attribute),
119                        }
120                    }
121                }
122
123                match attribute_option {
124                    Some(attribute) => {
125                        attribute.touch();
126                        Ok(attribute)
127                    }
128                    None => Err(Error::new(
129                        format!("Missing attribute {}", key),
130                        self.line_number,
131                    )),
132                }
133            }
134            FieldContent::Items(_) => Err(Error::new(
135                "Attributes expected, Items found".to_string(),
136                self.line_number,
137            )),
138            FieldContent::Value { .. } => Err(Error::new(
139                "Attributes expected, Value found".to_string(),
140                self.line_number,
141            )),
142            FieldContent::None => Err(Error::new(
143                format!("Missing attribute {}", key),
144                self.line_number,
145            )),
146        }
147    }
148
149    pub fn required_value<T: FromStr>(&self) -> Result<T, Error>
150    where
151        <T as FromStr>::Err: std::fmt::Display,
152    {
153        match &self.content {
154            FieldContent::Attributes(_) => Err(Error::new(
155                "Value expected, Attributes found".to_string(),
156                self.line_number,
157            )),
158            FieldContent::Items(_) => Err(Error::new(
159                "Value expected, Items found".to_string(),
160                self.line_number,
161            )),
162            FieldContent::Value(range) => {
163                let value = &self.document_internals.content[range.clone()].to_owned();
164                match value.parse::<T>() {
165                    Ok(converted) => Ok(converted),
166                    Err(err) => Err(Error::new(format!("{}", err), self.line_number)),
167                }
168            }
169            FieldContent::None => Err(Error::new("Missing value".to_string(), self.line_number)),
170        }
171    }
172
173    pub fn untouched_elements(&self) -> Vec<&dyn Element> {
174        match &self.content {
175            FieldContent::Attributes(attributes) => attributes
176                .iter()
177                .filter(|attribute| !attribute.touched())
178                .map(|attribute| attribute.as_element())
179                .collect(),
180            FieldContent::Items(items) => items
181                .iter()
182                .filter(|item| !item.touched())
183                .map(|item| item.as_element())
184                .collect(),
185            _ => Vec::new(),
186        }
187    }
188}
189
190impl Element for Field {
191    fn as_field(&self) -> Option<&Field> {
192        Some(self)
193    }
194
195    fn is_field(&self) -> bool {
196        true
197    }
198
199    fn line_number(&self) -> u32 {
200        self.line_number
201    }
202
203    fn snippet(&self) -> String {
204        self.snippet_with_options(&*self.document_internals.default_printer, true)
205    }
206
207    fn snippet_with_options(&self, printer: &dyn Printer, gutter: bool) -> String {
208        let mut out = String::new();
209
210        if gutter {
211            out.push_str(&printer.gutter(self.line_number));
212        }
213
214        if let Some((escape_begin_range, escape_end_range)) = &self.escape_operator_ranges {
215            out.push_str(
216                &self.document_internals.content[self.line_begin_index..escape_begin_range.start],
217            );
218            out.push_str(
219                &printer.operator(&self.document_internals.content[escape_begin_range.clone()]),
220            );
221            out.push_str(
222                &self.document_internals.content[escape_begin_range.end..self.key_range.start],
223            );
224            out.push_str(&printer.key(&self.document_internals.content[self.key_range.clone()]));
225            out.push_str(
226                &self.document_internals.content[self.key_range.end..escape_end_range.start],
227            );
228            out.push_str(
229                &printer.operator(&self.document_internals.content[escape_end_range.clone()]),
230            );
231            out.push_str(
232                &self.document_internals.content[escape_end_range.end..self.operator_index],
233            );
234        } else {
235            out.push_str(
236                &self.document_internals.content[self.line_begin_index..self.key_range.start],
237            );
238            out.push_str(&printer.key(&self.document_internals.content[self.key_range.clone()]));
239            out.push_str(&self.document_internals.content[self.key_range.end..self.operator_index]);
240        }
241
242        out.push_str(&self.document_internals.content[self.key_range.end..self.operator_index]);
243        out.push_str(&printer.operator(":"));
244
245        let mut line_number = self.line_number + 1;
246
247        match &self.content {
248            FieldContent::Attributes(attributes) => {
249                for attribute in attributes {
250                    out.push('\n');
251
252                    let attribute_line_range = attribute.line_range();
253
254                    while line_number < *attribute_line_range.start() {
255                        if let Some(comment) = self
256                            .document_internals
257                            .comments
258                            .borrow()
259                            .iter()
260                            .find(|comment| comment.line_number == line_number)
261                        {
262                            out.push_str(&comment.snippet_with_options(printer, gutter));
263                        } else if gutter {
264                            out.push_str(&printer.gutter(line_number));
265                        }
266
267                        out.push('\n');
268
269                        line_number += 1;
270                    }
271
272                    out.push_str(&attribute.snippet_with_options(printer, gutter));
273
274                    line_number = *attribute_line_range.end() + 1;
275                }
276            }
277            FieldContent::Items(items) => {
278                for item in items {
279                    out.push('\n');
280
281                    let item_line_range = item.line_range();
282
283                    while line_number < *item_line_range.start() {
284                        if let Some(comment) = self
285                            .document_internals
286                            .comments
287                            .borrow()
288                            .iter()
289                            .find(|comment| comment.line_number == line_number)
290                        {
291                            out.push_str(&comment.snippet_with_options(printer, gutter));
292                        } else if gutter {
293                            out.push_str(&printer.gutter(line_number));
294                        }
295
296                        out.push('\n');
297
298                        line_number += 1;
299                    }
300
301                    out.push_str(&item.snippet_with_options(printer, gutter));
302
303                    line_number = *item_line_range.end() + 1;
304                }
305            }
306            FieldContent::Value(value_range) => {
307                out.push_str(
308                    &self.document_internals.content[(self.operator_index + 1)..value_range.start],
309                );
310                out.push_str(&printer.value(&self.document_internals.content[value_range.clone()]));
311            }
312            FieldContent::None => (),
313        }
314
315        out
316    }
317
318    fn touch(&self) {
319        self.touched.set(true);
320    }
321}
322
323impl ElementImpl for Field {
324    fn line_range(&self) -> RangeInclusive<u32> {
325        let end = match &self.content {
326            FieldContent::Attributes(attributes) => *attributes.last().unwrap().line_range().end(),
327            FieldContent::Items(items) => *items.last().unwrap().line_range().end(),
328            FieldContent::Value(_) | FieldContent::None => self.line_number,
329        };
330
331        self.line_number..=end
332    }
333
334    fn touched(&self) -> bool {
335        self.touched.get()
336    }
337}
338
339impl FieldImpl for Field {
340    fn get_content(&self) -> &FieldContent {
341        &self.content
342    }
343
344    fn get_content_mut(&mut self) -> &mut FieldContent {
345        &mut self.content
346    }
347
348    fn get_document_content(&self) -> &str {
349        &self.document_internals.content
350    }
351
352    fn new(
353        content: FieldContent,
354        document_internals: Rc<DocumentInternals>,
355        escape_operator_ranges: Option<(Range<usize>, Range<usize>)>,
356        key_range: Range<usize>,
357        line_begin_index: usize,
358        line_number: u32,
359        operator_index: usize,
360    ) -> Field {
361        Field {
362            content,
363            document_internals,
364            escape_operator_ranges,
365            key_range,
366            line_begin_index,
367            line_number,
368            operator_index,
369            touched: Cell::new(false),
370        }
371    }
372
373    fn set_content(&mut self, content: FieldContent) {
374        self.content = content;
375    }
376}
377
378impl SectionElement for Field {
379    fn as_element(&self) -> &dyn Element {
380        self
381    }
382
383    fn as_mut_field(&mut self) -> Option<&mut Field> {
384        Some(self)
385    }
386
387    fn key(&self) -> &str {
388        &self.document_internals.content[self.key_range.clone()]
389    }
390}