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