mabel_eno/elements/
attribute.rs

1use std::cell::Cell;
2use std::ops::{Range, RangeInclusive};
3use std::rc::Rc;
4use std::str::FromStr;
5
6use crate::elements::{DocumentInternals, ElementImpl};
7use crate::{Element, Error, Printer};
8
9#[derive(Debug, Clone)]
10pub struct Attribute {
11    document_internals: Rc<DocumentInternals>,
12    escape_operator_ranges: Option<(Range<usize>, Range<usize>)>,
13    key_range: Range<usize>,
14    line_begin_index: usize,
15    pub line_number: u32,
16    operator_index: usize,
17    touched: Cell<bool>,
18    value_range: Option<Range<usize>>,
19}
20
21pub trait AttributeImpl {
22    fn as_element(&self) -> &dyn Element;
23    fn get_value(&self) -> Option<String>;
24    #[allow(clippy::new_ret_no_self)]
25    fn new(
26        document_internals: Rc<DocumentInternals>,
27        escape_operator_ranges: Option<(Range<usize>, Range<usize>)>,
28        key_range: Range<usize>,
29        line_begin_index: usize,
30        line_number: u32,
31        operator_index: usize,
32        value_range: Option<Range<usize>>,
33    ) -> Attribute;
34}
35
36impl Attribute {
37    pub fn key(&self) -> &str {
38        &self.document_internals.content[self.key_range.clone()]
39    }
40
41    pub fn optional_value<T: FromStr>(&self) -> Option<Result<T, Error>>
42    where
43        <T as FromStr>::Err: std::fmt::Display,
44        <T as FromStr>::Err: std::error::Error,
45        <T as FromStr>::Err: 'static,
46    {
47        match self.get_value() {
48            Some(value) => match value.parse::<T>() {
49                Ok(converted) => Some(Ok(converted)),
50                Err(err) => Some(Err(Error::with_source(
51                    format!("{}", err),
52                    self.line_number,
53                    Box::new(err),
54                ))),
55            },
56            None => None,
57        }
58    }
59
60    pub fn required_value<T: FromStr>(&self) -> Result<T, Error>
61    where
62        <T as FromStr>::Err: std::fmt::Display,
63        <T as FromStr>::Err: std::error::Error,
64        <T as FromStr>::Err: 'static,
65    {
66        match self.get_value() {
67            Some(value) => match value.parse::<T>() {
68                Ok(converted) => Ok(converted),
69                Err(err) => Err(Error::with_source(
70                    format!("{}", err),
71                    self.line_number,
72                    Box::new(err),
73                )),
74            },
75            None => Err(Error::new("Value expected".to_owned(), self.line_number)),
76        }
77    }
78}
79
80impl AttributeImpl for Attribute {
81    fn as_element(&self) -> &dyn Element {
82        self
83    }
84
85    fn get_value(&self) -> Option<String> {
86        self.value_range
87            .as_ref()
88            .map(|range| self.document_internals.content[range.clone()].to_owned())
89    }
90
91    fn new(
92        document_internals: Rc<DocumentInternals>,
93        escape_operator_ranges: Option<(Range<usize>, Range<usize>)>,
94        key_range: Range<usize>,
95        line_begin_index: usize,
96        line_number: u32,
97        operator_index: usize,
98        value_range: Option<Range<usize>>,
99    ) -> Attribute {
100        Attribute {
101            document_internals,
102            escape_operator_ranges,
103            key_range,
104            line_begin_index,
105            line_number,
106            operator_index,
107            touched: Cell::new(false),
108            value_range,
109        }
110    }
111}
112
113impl Element for Attribute {
114    fn as_attribute(&self) -> Option<&Attribute> {
115        Some(self)
116    }
117
118    fn is_attribute(&self) -> bool {
119        true
120    }
121
122    fn line_number(&self) -> u32 {
123        self.line_number
124    }
125
126    fn snippet(&self) -> String {
127        self.snippet_with_options(&*self.document_internals.default_printer, true)
128    }
129
130    fn snippet_with_options(&self, printer: &dyn Printer, gutter: bool) -> String {
131        let mut out = String::new();
132
133        if gutter {
134            out.push_str(&printer.gutter(self.line_number));
135        }
136
137        if let Some((escape_begin_range, escape_end_range)) = &self.escape_operator_ranges {
138            out.push_str(
139                &self.document_internals.content[self.line_begin_index..escape_begin_range.start],
140            );
141            out.push_str(
142                &printer.operator(&self.document_internals.content[escape_begin_range.clone()]),
143            );
144            out.push_str(
145                &self.document_internals.content[escape_begin_range.end..self.key_range.start],
146            );
147            out.push_str(&printer.key(&self.document_internals.content[self.key_range.clone()]));
148            out.push_str(
149                &self.document_internals.content[self.key_range.end..escape_end_range.start],
150            );
151            out.push_str(
152                &printer.operator(&self.document_internals.content[escape_end_range.clone()]),
153            );
154            out.push_str(
155                &self.document_internals.content[escape_end_range.end..self.operator_index],
156            );
157        } else {
158            out.push_str(
159                &self.document_internals.content[self.line_begin_index..self.key_range.start],
160            );
161            out.push_str(&printer.key(&self.document_internals.content[self.key_range.clone()]));
162            out.push_str(&self.document_internals.content[self.key_range.end..self.operator_index]);
163        }
164
165        out.push_str(&printer.operator("="));
166
167        if let Some(value_range) = &self.value_range {
168            out.push_str(
169                &self.document_internals.content[(self.operator_index + 1)..value_range.start],
170            );
171            out.push_str(&printer.value(&self.document_internals.content[value_range.clone()]));
172        }
173
174        out
175    }
176
177    fn touch(&self) {
178        self.touched.set(true);
179    }
180}
181
182impl ElementImpl for Attribute {
183    fn line_range(&self) -> RangeInclusive<u32> {
184        self.line_number..=self.line_number
185    }
186
187    fn touched(&self) -> bool {
188        self.touched.get()
189    }
190}