mabel_eno/elements/
attribute.rs1use 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}