1pub use ftd::p1::{Error, Result};
2
3#[derive(Debug, PartialEq, Default, Clone, serde::Serialize, serde::Deserialize)]
4pub struct Header(pub Vec<(usize, String, String)>);
5
6impl Header {
7 pub fn uncommented_headers(&self) -> Header {
14 let mut headers: Vec<(usize, String, String)> = vec![];
15 for (ln, key, val) in self.0.iter() {
16 if !key.trim().starts_with('/') {
17 match key.trim().starts_with(r"\/") {
18 true => headers.push((*ln, key.trim().replacen('\\', "", 1), val.to_string())),
19 false => headers.push((*ln, key.to_string(), val.to_string())),
20 }
21 }
22 }
23 Header(headers)
24 }
25
26 pub fn without_line_number(&self) -> Self {
27 let mut header: Header = Default::default();
28 for (_, k, v) in self.0.iter() {
29 header.add(&0, k, v);
30 }
31 header
32 }
33
34 pub fn add(&mut self, line_number: &usize, name: &str, value: &str) {
35 self.0
36 .push((*line_number, name.to_string(), value.to_string()))
37 }
38
39 pub fn bool_with_default(
40 &self,
41 doc_id: &str,
42 line_number: usize,
43 name: &str,
44 def: bool,
45 ) -> Result<bool> {
46 match self.bool(doc_id, line_number, name) {
47 Ok(b) => Ok(b),
48 Err(Error::NotFound { .. }) => Ok(def),
49 e => e,
50 }
51 }
52
53 pub fn bool_optional(
54 &self,
55 doc_id: &str,
56 line_number: usize,
57 name: &str,
58 ) -> Result<Option<bool>> {
59 match self.bool(doc_id, line_number, name) {
60 Ok(b) => Ok(Some(b)),
61 Err(Error::NotFound { .. }) => Ok(None),
62 Err(e) => Err(e),
63 }
64 }
65
66 pub fn bool(&self, doc_id: &str, line_number: usize, name: &str) -> Result<bool> {
67 for (l, k, v) in self.0.iter() {
68 if k == name {
69 return if v == "true" || v == "false" {
70 Ok(v == "true")
71 } else {
72 Err(ftd::p1::Error::ParseError {
73 message: "can't parse bool".to_string(),
74 doc_id: doc_id.to_string(),
75 line_number: *l,
76 })
77 };
78 }
79 }
80 Err(Error::NotFound {
81 doc_id: doc_id.to_string(),
82 line_number,
83 key: name.to_string(),
84 })
85 }
86
87 pub fn i32_with_default(
88 &self,
89 doc_id: &str,
90 line_number: usize,
91 name: &str,
92 def: i32,
93 ) -> Result<i32> {
94 match self.i32(doc_id, line_number, name) {
95 Ok(b) => Ok(b),
96 Err(Error::NotFound { .. }) => Ok(def),
97 e => e,
98 }
99 }
100
101 pub fn i32_optional(
102 &self,
103 doc_id: &str,
104 line_number: usize,
105 name: &str,
106 ) -> Result<Option<i32>> {
107 match self.i32(doc_id, line_number, name) {
108 Ok(b) => Ok(Some(b)),
109 Err(Error::NotFound { .. }) => Ok(None),
110 Err(e) => Err(e),
111 }
112 }
113
114 pub fn i32(&self, doc_id: &str, line_number: usize, name: &str) -> Result<i32> {
115 for (l, k, v) in self.0.iter() {
116 if k == name {
117 return v.parse().map_err(|e: std::num::ParseIntError| {
118 ftd::p1::Error::ParseError {
119 message: format!("{:?}", e),
120 doc_id: doc_id.to_string(),
121 line_number: *l,
122 }
123 });
124 }
125 }
126 Err(Error::NotFound {
127 doc_id: doc_id.to_string(),
128 line_number,
129 key: name.to_string(),
130 })
131 }
132
133 pub fn i64(&self, doc_id: &str, line_number: usize, name: &str) -> Result<i64> {
134 for (l, k, v) in self.0.iter() {
135 if k == name {
136 return v.parse().map_err(|e: std::num::ParseIntError| {
137 ftd::p1::Error::ParseError {
138 message: format!("{:?}", e),
139 doc_id: doc_id.to_string(),
140 line_number: *l,
141 }
142 });
143 }
144 }
145 Err(Error::NotFound {
146 doc_id: doc_id.to_string(),
147 line_number,
148 key: name.to_string(),
149 })
150 }
151
152 pub fn i64_optional(
153 &self,
154 doc_id: &str,
155 line_number: usize,
156 name: &str,
157 ) -> Result<Option<i64>> {
158 match self.i64(doc_id, line_number, name) {
159 Ok(b) => Ok(Some(b)),
160 Err(Error::NotFound { .. }) => Ok(None),
161 Err(e) => Err(e),
162 }
163 }
164
165 pub fn f64(&self, doc_id: &str, line_number: usize, name: &str) -> Result<f64> {
166 for (l, k, v) in self.0.iter() {
167 if k == name {
168 return v.parse().map_err(|e: std::num::ParseFloatError| {
169 ftd::p1::Error::ParseError {
170 message: format!("{:?}", e),
171 doc_id: doc_id.to_string(),
172 line_number: *l,
173 }
174 });
175 }
176 }
177 Err(Error::NotFound {
178 doc_id: doc_id.to_string(),
179 line_number,
180 key: name.to_string(),
181 })
182 }
183
184 pub fn f64_optional(
185 &self,
186 doc_id: &str,
187 line_number: usize,
188 name: &str,
189 ) -> Result<Option<f64>> {
190 match self.f64(doc_id, line_number, name) {
191 Ok(b) => Ok(Some(b)),
192 Err(Error::NotFound { .. }) => Ok(None),
193 Err(e) => Err(e),
194 }
195 }
196
197 pub fn str_with_default<'a>(
198 &'a self,
199 doc_id: &str,
200 line_number: usize,
201 name: &str,
202 def: &'a str,
203 ) -> Result<&'a str> {
204 match self.str(doc_id, line_number, name) {
205 Ok(b) => Ok(b),
206 Err(Error::NotFound { .. }) => Ok(def),
207 e => e,
208 }
209 }
210
211 pub fn get_events(
212 &self,
213 line_number: usize,
214 doc: &ftd::p2::TDoc,
215 arguments: &ftd::Map<ftd::p2::Kind>,
216 ) -> ftd::p1::Result<Vec<ftd::p2::Event>> {
217 let events = {
218 let mut events = vec![];
219 for (_, k, v) in self.0.iter() {
220 if k.starts_with("$on-") && k.ends_with('$') {
221 let mut event = k.replace("$on-", "");
222 event = event[..event.len() - 1].to_string();
223 events.push((event, v.to_string()));
224 }
225 }
226 events
227 };
228 let mut event = vec![];
229 for (e, a) in events {
230 event.push(ftd::p2::Event::to_event(
231 line_number,
232 &e,
233 &a,
234 doc,
235 arguments,
236 )?);
237 }
238 Ok(event)
239 }
240
241 pub fn str_optional(
242 &self,
243 doc_id: &str,
244 line_number: usize,
245 name: &str,
246 ) -> Result<Option<&str>> {
247 match self.str(doc_id, line_number, name) {
248 Ok(b) => Ok(Some(b)),
249 Err(Error::NotFound { .. }) => Ok(None),
250 Err(e) => Err(e),
251 }
252 }
253
254 #[allow(clippy::type_complexity)]
255 pub fn conditional_str(
256 &self,
257 doc: &ftd::p2::TDoc,
258 line_number: usize,
259 name: &str,
260 arguments: &ftd::Map<ftd::p2::Kind>,
261 ) -> Result<Vec<(usize, String, Option<String>, bool)>> {
262 let mut conditional_vector = vec![];
263 for (idx, (_, k, v)) in self.0.iter().enumerate() {
264 let v = doc.resolve_reference_name(line_number, v, arguments)?;
265 let (k, is_referenced) = if let Some(k) = k.strip_prefix('$') {
266 (k.to_string(), true)
267 } else {
268 (k.to_string(), false)
269 };
270 if k.eq(name) {
271 conditional_vector.push((idx, v.to_string(), None, is_referenced));
272 }
273 if k.contains(" if ") {
274 let mut parts = k.splitn(2, " if ");
275 let property_name = parts.next().unwrap().trim();
276 if property_name == name {
277 let conditional_attribute = parts.next().unwrap().trim();
278 conditional_vector.push((
279 idx,
280 v.to_string(),
281 Some(conditional_attribute.to_string()),
282 is_referenced,
283 ));
284 }
285 }
286 }
287 if conditional_vector.is_empty() {
288 Err(Error::NotFound {
289 doc_id: doc.name.to_string(),
290 line_number,
291 key: format!("`{}` header is missing", name),
292 })
293 } else {
294 Ok(conditional_vector)
295 }
296 }
297
298 pub fn str(&self, doc_id: &str, line_number: usize, name: &str) -> Result<&str> {
299 for (_, k, v) in self.0.iter() {
300 if k == name {
301 return Ok(v.as_str());
302 }
303 }
304
305 Err(Error::NotFound {
306 doc_id: doc_id.to_string(),
307 line_number,
308 key: format!("`{}` header is missing", name),
309 })
310 }
311
312 pub fn string(&self, doc_id: &str, line_number: usize, name: &str) -> Result<String> {
313 self.str(doc_id, line_number, name).map(ToString::to_string)
314 }
315
316 pub fn string_optional(
317 &self,
318 doc_id: &str,
319 line_number: usize,
320 name: &str,
321 ) -> Result<Option<String>> {
322 Ok(self
323 .str_optional(doc_id, line_number, name)?
324 .map(ToString::to_string))
325 }
326
327 pub fn string_with_default(
328 &self,
329 doc_id: &str,
330 line_number: usize,
331 name: &str,
332 def: &str,
333 ) -> Result<String> {
334 self.str_with_default(doc_id, line_number, name, def)
335 .map(ToString::to_string)
336 }
337}