mabel_eno/elements/
embed.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, 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}