ftd/p2/
utils.rs

1pub fn parse_import(
2    c: &Option<String>,
3    doc_id: &str,
4    line_number: usize,
5) -> ftd::p1::Result<(String, String)> {
6    let v = match c {
7        Some(v) => v.trim(),
8        None => {
9            return ftd::p2::utils::e2(
10                "caption is missing in import statement",
11                doc_id,
12                line_number,
13            )
14        }
15    };
16
17    if v.contains(" as ") {
18        let mut parts = v.splitn(2, " as ");
19        return match (parts.next(), parts.next()) {
20            (Some(n), Some(a)) => Ok((n.to_string(), a.to_string())),
21            _ => ftd::p2::utils::e2(
22                "invalid use of keyword as in import statement",
23                doc_id,
24                line_number,
25            ),
26        };
27    }
28
29    if v.contains('/') {
30        let mut parts = v.rsplitn(2, '/');
31        return match (parts.next(), parts.next()) {
32            (Some(t), Some(_)) => Ok((v.to_string(), t.to_string())),
33            _ => ftd::p2::utils::e2("doc id must contain /", doc_id, line_number),
34        };
35    }
36
37    if let Some((t, _)) = v.split_once('.') {
38        return Ok((v.to_string(), t.to_string()));
39    }
40
41    Ok((v.to_string(), v.to_string()))
42}
43
44pub fn get_name<'a, 'b>(prefix: &'a str, s: &'b str, doc_id: &str) -> ftd::p1::Result<&'b str> {
45    match s.split_once(' ') {
46        Some((p1, p2)) => {
47            if p1 != prefix {
48                return ftd::p2::utils::e2(format!("must start with {}", prefix), doc_id, 0);
49                // TODO
50            }
51            Ok(p2)
52        }
53        None => ftd::p2::utils::e2(
54            format!("{} does not contain space (prefix={})", s, prefix),
55            doc_id,
56            0, // TODO
57        ),
58    }
59}
60
61pub fn boolean_and_ref(
62    line_number: usize,
63    name: &str,
64    properties: &ftd::Map<ftd::component::Property>,
65    doc: &ftd::p2::TDoc,
66    condition: &Option<ftd::p2::Boolean>, // todo: check the string_and_source_and_ref and use
67) -> ftd::p1::Result<(bool, Option<String>)> {
68    let properties = ftd::component::resolve_properties_with_ref(line_number, properties, doc)?;
69    match properties.get(name) {
70        Some((ftd::Value::Boolean { value }, reference)) => {
71            Ok((value.to_owned(), complete_reference(reference)))
72        }
73        Some((ftd::Value::Optional { data, kind }, reference)) => {
74            if !matches!(kind, ftd::p2::Kind::Boolean { .. })
75                && !matches!(kind, ftd::p2::Kind::Element)
76            {
77                return ftd::p2::utils::e2(
78                    format!("expected boolean, found: {:?}", kind),
79                    doc.name,
80                    line_number,
81                );
82            };
83            match data.as_ref() {
84                None => {
85                    let reference = match reference {
86                        Some(reference) => reference,
87                        None => {
88                            return ftd::p2::utils::e2(
89                                format!("expected boolean, found: {:?}", kind),
90                                doc.name,
91                                line_number,
92                            )
93                        }
94                    };
95
96                    if let Some(ftd::p2::Boolean::IsNotNull { value }) = condition {
97                        match value {
98                            ftd::PropertyValue::Reference { name, .. }
99                            | ftd::PropertyValue::Variable { name, .. } => {
100                                if name.eq(reference) {
101                                    return Ok((
102                                        false,
103                                        complete_reference(&Some(reference.to_owned())),
104                                    ));
105                                }
106                            }
107                            _ => {}
108                        }
109                    }
110
111                    // In case when the optional string is null.
112                    // Return the empty string
113
114                    Ok((false, complete_reference(&Some(reference.to_owned()))))
115                }
116                Some(ftd::Value::Boolean { value }) => {
117                    Ok((value.to_owned(), complete_reference(reference)))
118                }
119                _ => ftd::p2::utils::e2(
120                    format!("expected boolean, found: {:?}", kind),
121                    doc.name,
122                    line_number,
123                ),
124            }
125        }
126        Some((ftd::Value::None { kind }, reference)) if condition.is_some() => {
127            let kind = kind.inner();
128            if !matches!(kind, ftd::p2::Kind::Boolean { .. })
129                && !matches!(kind, ftd::p2::Kind::Element)
130            {
131                return ftd::p2::utils::e2(
132                    format!("expected boolean, found: {:?}", kind),
133                    doc.name,
134                    line_number,
135                );
136            };
137
138            let reference = match reference {
139                Some(reference) => reference,
140                None => {
141                    return ftd::p2::utils::e2(
142                        format!("expected integer, found 7: {:?}", kind),
143                        doc.name,
144                        line_number,
145                    )
146                }
147            };
148            if let Some(ftd::p2::Boolean::IsNotNull { value }) = condition {
149                match value {
150                    ftd::PropertyValue::Reference { name, .. }
151                    | ftd::PropertyValue::Variable { name, .. } => {
152                        if name.eq({
153                            if let Some(reference) = reference.strip_prefix('@') {
154                                reference
155                            } else {
156                                reference
157                            }
158                        }) {
159                            return Ok((false, complete_reference(&Some(reference.to_owned()))));
160                        }
161                    }
162                    _ => {}
163                }
164            }
165            ftd::p2::utils::e2(
166                format!("expected boolean, found: {:?}", kind),
167                doc.name,
168                line_number,
169            )
170        }
171        Some(v) => ftd::p2::utils::e2(
172            format!("expected boolean, found: {:?}", v),
173            doc.name,
174            line_number,
175        ),
176        None => ftd::p2::utils::e2(format!("'{}' not found", name), doc.name, line_number),
177    }
178}
179
180pub fn integer_and_ref(
181    line_number: usize,
182    name: &str,
183    properties: &ftd::Map<ftd::component::Property>,
184    doc: &ftd::p2::TDoc,
185    condition: &Option<ftd::p2::Boolean>, // todo: check the string_and_source_and_ref and use
186) -> ftd::p1::Result<(i64, Option<String>)> {
187    let properties = ftd::component::resolve_properties_with_ref(line_number, properties, doc)?;
188    match properties.get(name) {
189        Some((ftd::Value::Integer { value }, reference)) => {
190            Ok((value.to_owned(), complete_reference(reference)))
191        }
192        Some((ftd::Value::Optional { data, kind }, reference)) => {
193            if !matches!(kind, ftd::p2::Kind::Integer { .. })
194                && !matches!(kind, ftd::p2::Kind::Element)
195            {
196                return ftd::p2::utils::e2(
197                    format!("expected integer, found 8: {:?}", kind),
198                    doc.name,
199                    line_number,
200                );
201            };
202            match data.as_ref() {
203                None => {
204                    let reference = match reference {
205                        Some(reference) => reference,
206                        None => {
207                            return ftd::p2::utils::e2(
208                                format!("expected integer, found 9: {:?}", kind),
209                                doc.name,
210                                line_number,
211                            )
212                        }
213                    };
214
215                    if let Some(ftd::p2::Boolean::IsNotNull { value }) = condition {
216                        match value {
217                            ftd::PropertyValue::Reference { name, .. }
218                            | ftd::PropertyValue::Variable { name, .. } => {
219                                if name.eq(reference) {
220                                    return Ok((
221                                        0,
222                                        complete_reference(&Some(reference.to_owned())),
223                                    ));
224                                }
225                            }
226                            _ => {}
227                        }
228                    }
229
230                    // In case when the optional string is null.
231                    // Return the empty string
232
233                    Ok((0, complete_reference(&Some(reference.to_owned()))))
234                }
235                Some(ftd::Value::Integer { value }) => {
236                    Ok((value.to_owned(), complete_reference(reference)))
237                }
238                _ => ftd::p2::utils::e2(
239                    format!("expected integer, found 10: {:?}", kind),
240                    doc.name,
241                    line_number,
242                ),
243            }
244        }
245        Some((ftd::Value::None { kind }, reference)) if condition.is_some() => {
246            let kind = kind.inner();
247            if !matches!(kind, ftd::p2::Kind::Integer { .. })
248                && !matches!(kind, ftd::p2::Kind::Element)
249            {
250                return ftd::p2::utils::e2(
251                    format!("expected integer, found 11: {:?}", kind),
252                    doc.name,
253                    line_number,
254                );
255            };
256
257            let reference = match reference {
258                Some(reference) => reference,
259                None => {
260                    return ftd::p2::utils::e2(
261                        format!("expected integer, found 1: {:?}", kind),
262                        doc.name,
263                        line_number,
264                    )
265                }
266            };
267            if let Some(ftd::p2::Boolean::IsNotNull { value }) = condition {
268                match value {
269                    ftd::PropertyValue::Reference { name, .. }
270                    | ftd::PropertyValue::Variable { name, .. } => {
271                        if name.eq({
272                            if let Some(reference) = reference.strip_prefix('@') {
273                                reference
274                            } else {
275                                reference
276                            }
277                        }) {
278                            return Ok((0, complete_reference(&Some(reference.to_owned()))));
279                        }
280                    }
281                    _ => {}
282                }
283            }
284            ftd::p2::utils::e2(
285                format!("expected integer, found 2: {:?}", kind),
286                doc.name,
287                line_number,
288            )
289        }
290        Some(v) => ftd::p2::utils::e2(
291            format!("expected integer, found 3: {:?}", v),
292            doc.name,
293            line_number,
294        ),
295        None => ftd::p2::utils::e2(format!("'{}' not found", name), doc.name, line_number),
296    }
297}
298
299pub fn decimal_and_ref(
300    line_number: usize,
301    name: &str,
302    properties: &ftd::Map<ftd::component::Property>,
303    doc: &ftd::p2::TDoc,
304    condition: &Option<ftd::p2::Boolean>, // todo: check the string_and_source_and_ref and use
305) -> ftd::p1::Result<(f64, Option<String>)> {
306    let properties = ftd::component::resolve_properties_with_ref(line_number, properties, doc)?;
307    match properties.get(name) {
308        Some((ftd::Value::Decimal { value }, reference)) => {
309            Ok((value.to_owned(), complete_reference(reference)))
310        }
311        Some((ftd::Value::Optional { data, kind }, reference)) => {
312            if !matches!(kind, ftd::p2::Kind::Decimal { .. })
313                && !matches!(kind, ftd::p2::Kind::Element)
314            {
315                return ftd::p2::utils::e2(
316                    format!("expected decimal, found: {:?}", kind),
317                    doc.name,
318                    line_number,
319                );
320            };
321            match data.as_ref() {
322                None => {
323                    let reference = match reference {
324                        Some(reference) => reference,
325                        None => {
326                            return ftd::p2::utils::e2(
327                                format!("expected decimal, found: {:?}", kind),
328                                doc.name,
329                                line_number,
330                            )
331                        }
332                    };
333
334                    if let Some(ftd::p2::Boolean::IsNotNull { value }) = condition {
335                        match value {
336                            ftd::PropertyValue::Reference { name, .. }
337                            | ftd::PropertyValue::Variable { name, .. } => {
338                                if name.eq(reference) {
339                                    return Ok((
340                                        0.0,
341                                        complete_reference(&Some(reference.to_owned())),
342                                    ));
343                                }
344                            }
345                            _ => {}
346                        }
347                    }
348
349                    // In case when the optional string is null.
350                    // Return the empty string
351
352                    Ok((0.0, complete_reference(&Some(reference.to_owned()))))
353                }
354                Some(ftd::Value::Decimal { value }) => {
355                    Ok((value.to_owned(), complete_reference(reference)))
356                }
357                _ => ftd::p2::utils::e2(
358                    format!("expected decimal, found: {:?}", kind),
359                    doc.name,
360                    line_number,
361                ),
362            }
363        }
364        Some((ftd::Value::None { kind }, reference)) if condition.is_some() => {
365            let kind = kind.inner();
366            if !matches!(kind, ftd::p2::Kind::Decimal { .. })
367                && !matches!(kind, ftd::p2::Kind::Element)
368            {
369                return ftd::p2::utils::e2(
370                    format!("expected integer, found 4: {:?}", kind),
371                    doc.name,
372                    line_number,
373                );
374            };
375
376            let reference = match reference {
377                Some(reference) => reference,
378                None => {
379                    return ftd::p2::utils::e2(
380                        format!("expected integer, found 5: {:?}", kind),
381                        doc.name,
382                        line_number,
383                    )
384                }
385            };
386            if let Some(ftd::p2::Boolean::IsNotNull { value }) = condition {
387                match value {
388                    ftd::PropertyValue::Reference { name, .. }
389                    | ftd::PropertyValue::Variable { name, .. } => {
390                        if name.eq({
391                            if let Some(reference) = reference.strip_prefix('@') {
392                                reference
393                            } else {
394                                reference
395                            }
396                        }) {
397                            return Ok((0.0, complete_reference(&Some(reference.to_owned()))));
398                        }
399                    }
400                    _ => {}
401                }
402            }
403            ftd::p2::utils::e2(
404                format!("expected decimal, found: {:?}", kind),
405                doc.name,
406                line_number,
407            )
408        }
409        Some(v) => ftd::p2::utils::e2(
410            format!("expected decimal, found: {:?}", v),
411            doc.name,
412            line_number,
413        ),
414        None => ftd::p2::utils::e2(format!("'{}' not found", name), doc.name, line_number),
415    }
416}
417
418pub fn string_and_source_and_ref(
419    line_number: usize,
420    name: &str,
421    properties: &ftd::Map<ftd::component::Property>,
422    doc: &ftd::p2::TDoc,
423    condition: &Option<ftd::p2::Boolean>,
424) -> ftd::p1::Result<(String, ftd::TextSource, Option<String>)> {
425    let properties = ftd::component::resolve_properties_with_ref(line_number, properties, doc)?;
426    match properties.get(name) {
427        Some((ftd::Value::String { text, source }, reference)) => {
428            Ok((text.to_string(), source.to_owned(), (*reference).to_owned()))
429        }
430        Some((ftd::Value::Optional { data, kind }, reference)) => {
431            let source = match kind {
432                _ if matches!(kind, ftd::p2::Kind::String { .. })
433                    || matches!(kind, ftd::p2::Kind::Element) =>
434                {
435                    ftd::TextSource::from_kind(kind, doc.name, line_number)?
436                }
437                _ => {
438                    return ftd::p2::utils::e2(
439                        format!("expected string, found 1: {:?}", kind),
440                        doc.name,
441                        line_number,
442                    )
443                }
444            };
445
446            match data.as_ref() {
447                None => {
448                    let reference = match reference {
449                        Some(reference) => reference,
450                        None => {
451                            return ftd::p2::utils::e2(
452                                format!("expected string, found 2: {:?}", kind),
453                                doc.name,
454                                line_number,
455                            )
456                        }
457                    };
458
459                    if let Some(ftd::p2::Boolean::IsNotNull { value }) = condition {
460                        match value {
461                            ftd::PropertyValue::Reference { name, .. }
462                            | ftd::PropertyValue::Variable { name, .. } => {
463                                if name.eq(reference) {
464                                    return Ok((
465                                        "".to_string(),
466                                        source,
467                                        complete_reference(&Some(reference.to_owned())),
468                                    ));
469                                }
470                            }
471                            _ => {}
472                        }
473                    }
474
475                    // In case when the optional string is null.
476                    // Return the empty string
477
478                    Ok((
479                        "".to_string(),
480                        source,
481                        complete_reference(&Some(reference.to_owned())),
482                    ))
483                }
484                Some(ftd::Value::String { text, source }) => Ok((
485                    text.to_string(),
486                    source.to_owned(),
487                    complete_reference(reference),
488                )),
489                _ => ftd::p2::utils::e2(
490                    format!("expected string, found 3: {:?}", kind),
491                    doc.name,
492                    line_number,
493                ),
494            }
495        }
496        Some((ftd::Value::None { kind }, reference)) if condition.is_some() => {
497            let kind = kind.inner();
498            let source = match kind {
499                _ if matches!(kind, ftd::p2::Kind::String { .. })
500                    || matches!(kind, ftd::p2::Kind::Element) =>
501                {
502                    ftd::TextSource::from_kind(kind, doc.name, line_number)?
503                }
504                _ => {
505                    return ftd::p2::utils::e2(
506                        format!("expected string, found 4: {:?}", kind),
507                        doc.name,
508                        line_number,
509                    )
510                }
511            };
512
513            let reference = match reference {
514                Some(reference) => reference,
515                None => {
516                    return ftd::p2::utils::e2(
517                        format!("expected string, found 5: {:?}", kind),
518                        doc.name,
519                        line_number,
520                    )
521                }
522            };
523            if let Some(ftd::p2::Boolean::IsNotNull { value }) = condition {
524                match value {
525                    ftd::PropertyValue::Reference { name, .. }
526                    | ftd::PropertyValue::Variable { name, .. } => {
527                        if name.eq({
528                            if let Some(reference) = reference.strip_prefix('@') {
529                                reference
530                            } else {
531                                reference
532                            }
533                        }) {
534                            return Ok((
535                                "".to_string(),
536                                source,
537                                complete_reference(&Some(reference.to_owned())),
538                            ));
539                        }
540                    }
541                    _ => {}
542                }
543            }
544            ftd::p2::utils::e2(
545                format!("expected string, found 6: {:?}", kind),
546                doc.name,
547                line_number,
548            )
549        }
550        Some(v) => ftd::p2::utils::e2(
551            format!("expected string, found 7: {:?}", v),
552            doc.name,
553            line_number,
554        ),
555        None => ftd::p2::utils::e2(format!("'{}' not found", name), doc.name, line_number),
556    }
557}
558
559// todo: remove this
560pub fn complete_reference(reference: &Option<String>) -> Option<String> {
561    let mut reference = (*reference).to_owned();
562    if let Some(ref r) = reference {
563        if let Some(name) = r.strip_prefix('@') {
564            if name.eq("$loop$") {
565                return None;
566            } else if name.eq("MOUSE-IN") {
567                reference = Some("$MOUSE-IN".to_string());
568            }
569        }
570    }
571    reference
572}
573
574pub fn record_and_ref(
575    line_number: usize,
576    name: &str,
577    properties: &ftd::Map<ftd::component::Property>,
578    doc: &ftd::p2::TDoc,
579    condition: &Option<ftd::p2::Boolean>,
580) -> ftd::p1::Result<(ftd::Map<ftd::PropertyValue>, Option<String>)> {
581    let properties = ftd::component::resolve_properties_with_ref(line_number, properties, doc)?;
582    match properties.get(name) {
583        Some((ftd::Value::Record { fields, .. }, reference)) => {
584            Ok((fields.to_owned(), (*reference).to_owned()))
585        }
586        Some((ftd::Value::Optional { data, kind }, reference)) => {
587            if !matches!(kind, ftd::p2::Kind::Record { .. })
588                && !matches!(kind, ftd::p2::Kind::Element)
589            {
590                return ftd::p2::utils::e2(
591                    format!("expected record, found: {:?}", kind),
592                    doc.name,
593                    line_number,
594                );
595            }
596
597            match data.as_ref() {
598                None => {
599                    let reference = match reference {
600                        Some(reference) => reference,
601                        None => {
602                            return ftd::p2::utils::e2(
603                                format!("expected record, found: {:?}", kind),
604                                doc.name,
605                                line_number,
606                            )
607                        }
608                    };
609
610                    if let Some(ftd::p2::Boolean::IsNotNull { value }) = condition {
611                        match value {
612                            ftd::PropertyValue::Reference { name, .. }
613                            | ftd::PropertyValue::Variable { name, .. } => {
614                                if name.eq(reference) {
615                                    return Ok((
616                                        Default::default(),
617                                        complete_reference(&Some(reference.to_owned())),
618                                    ));
619                                }
620                            }
621                            _ => {}
622                        }
623                    }
624
625                    // In case when the optional string is null.
626                    // Return the empty string
627
628                    Ok((
629                        Default::default(),
630                        complete_reference(&Some(reference.to_owned())),
631                    ))
632                }
633                Some(ftd::Value::Record { fields, .. }) => {
634                    Ok((fields.to_owned(), complete_reference(reference)))
635                }
636                _ => ftd::p2::utils::e2(
637                    format!("expected record, found: {:?}", kind),
638                    doc.name,
639                    line_number,
640                ),
641            }
642        }
643        Some((ftd::Value::None { kind }, reference)) if condition.is_some() => {
644            let kind = kind.inner();
645            if !matches!(kind, ftd::p2::Kind::Record { .. })
646                && !matches!(kind, ftd::p2::Kind::Element)
647            {
648                return ftd::p2::utils::e2(
649                    format!("expected record, found: {:?}", kind),
650                    doc.name,
651                    line_number,
652                );
653            }
654
655            let reference = match reference {
656                Some(reference) => reference,
657                None => {
658                    return ftd::p2::utils::e2(
659                        format!("expected record, found: {:?}", kind),
660                        doc.name,
661                        line_number,
662                    )
663                }
664            };
665            if let Some(ftd::p2::Boolean::IsNotNull { value }) = condition {
666                match value {
667                    ftd::PropertyValue::Reference { name, .. }
668                    | ftd::PropertyValue::Variable { name, .. } => {
669                        if name.eq({
670                            if let Some(reference) = reference.strip_prefix('@') {
671                                reference
672                            } else {
673                                reference
674                            }
675                        }) {
676                            return Ok((
677                                Default::default(),
678                                complete_reference(&Some(reference.to_owned())),
679                            ));
680                        }
681                    }
682                    _ => {}
683                }
684            }
685            ftd::p2::utils::e2(
686                format!("expected record, found: {:?}", kind),
687                doc.name,
688                line_number,
689            )
690        }
691        Some(v) => ftd::p2::utils::e2(
692            format!("expected record, found: {:?}", v),
693            doc.name,
694            line_number,
695        ),
696        None => ftd::p2::utils::e2(format!("'{}' not found", name), doc.name, line_number),
697    }
698}
699
700#[allow(clippy::type_complexity)]
701pub fn record_optional_with_ref(
702    name: &str,
703    properties: &ftd::Map<ftd::component::Property>,
704    doc: &ftd::p2::TDoc,
705    line_number: usize,
706) -> ftd::p1::Result<(Option<ftd::Map<ftd::PropertyValue>>, Option<String>)> {
707    let properties = ftd::component::resolve_properties_with_ref(line_number, properties, doc)?;
708    match properties.get(name) {
709        Some((ftd::Value::Record { fields, .. }, reference)) => {
710            Ok((Some(fields.to_owned()), (*reference).to_owned()))
711        }
712        Some((
713            ftd::Value::None {
714                kind: ftd::p2::Kind::Record { .. },
715            },
716            _,
717        )) => Ok((None, None)),
718        Some((ftd::Value::None { .. }, _)) => Ok((None, None)),
719        Some((
720            ftd::Value::Optional {
721                data,
722                kind: ftd::p2::Kind::Record { .. },
723            },
724            reference,
725        )) => match data.as_ref() {
726            Some(ftd::Value::Record { fields, .. }) => {
727                Ok((Some(fields.to_owned()), (*reference).to_owned()))
728            }
729            None => Ok((None, None)),
730            v => ftd::p2::utils::e2(
731                format!("expected record, for: `{}` found: {:?}", name, v),
732                doc.name,
733                line_number,
734            ),
735        },
736        Some(v) => ftd::p2::utils::e2(
737            format!("expected record, for: `{}` found: {:?}", name, v),
738            doc.name,
739            line_number,
740        ),
741        None => Ok((None, None)),
742    }
743}
744
745pub fn record_optional(
746    name: &str,
747    properties: &ftd::Map<ftd::Value>,
748    doc_id: &str,
749    line_number: usize,
750) -> ftd::p1::Result<Option<ftd::Map<ftd::PropertyValue>>> {
751    match properties.get(name) {
752        Some(ftd::Value::Record { fields, .. }) => Ok(Some(fields.to_owned())),
753        Some(ftd::Value::None {
754            kind: ftd::p2::Kind::Record { .. },
755        }) => Ok(None),
756        Some(ftd::Value::None { .. }) => Ok(None),
757        Some(ftd::Value::Optional {
758            data,
759            kind: ftd::p2::Kind::Record { .. },
760        }) => match data.as_ref() {
761            Some(ftd::Value::Record { fields, .. }) => Ok(Some(fields.to_owned())),
762            None => Ok(None),
763            v => ftd::p2::utils::e2(
764                format!("expected record, for: `{}` found: {:?}", name, v),
765                doc_id,
766                line_number,
767            ),
768        },
769        Some(v) => ftd::p2::utils::e2(
770            format!("expected record, for: `{}` found: {:?}", name, v),
771            doc_id,
772            line_number,
773        ),
774        None => Ok(None),
775    }
776}
777
778pub fn string_optional(
779    name: &str,
780    properties: &ftd::Map<ftd::Value>,
781    doc_id: &str,
782    line_number: usize,
783) -> ftd::p1::Result<Option<String>> {
784    match properties.get(name) {
785        Some(ftd::Value::String { text: v, .. }) => Ok(Some(v.to_string())),
786        Some(ftd::Value::None {
787            kind: ftd::p2::Kind::String { .. },
788        }) => Ok(None),
789        Some(ftd::Value::None { .. }) => Ok(None),
790        Some(ftd::Value::Optional {
791            data,
792            kind: ftd::p2::Kind::String { .. },
793        }) => match data.as_ref() {
794            Some(ftd::Value::String { text: v, .. }) => Ok(Some(v.to_string())),
795            None => Ok(None),
796            v => ftd::p2::utils::e2(
797                format!("expected string, for: `{}` found: {:?}", name, v),
798                doc_id,
799                line_number,
800            ),
801        },
802        Some(v) => ftd::p2::utils::e2(
803            format!("expected string, for: `{}` found: {:?}", name, v),
804            doc_id,
805            line_number,
806        ),
807        None => Ok(None),
808    }
809}
810
811pub fn string(
812    name: &str,
813    properties: &ftd::Map<ftd::Value>,
814    doc_id: &str,
815    line_number: usize,
816) -> ftd::p1::Result<String> {
817    match properties.get(name) {
818        Some(ftd::Value::String { text: v, .. }) => Ok(v.to_string()),
819        Some(ftd::Value::Optional {
820            data,
821            kind: ftd::p2::Kind::String { .. },
822        }) => match data.as_ref() {
823            Some(ftd::Value::String { text: v, .. }) => Ok(v.to_string()),
824            v => ftd::p2::utils::e2(
825                format!("expected string, for: `{}` found: {:?}", name, v),
826                doc_id,
827                line_number,
828            ),
829        },
830        v => ftd::p2::utils::e2(
831            format!("expected string, for: `{}` found: {:?}", name, v),
832            doc_id,
833            line_number,
834        ),
835    }
836}
837
838pub fn string_with_default(
839    name: &str,
840    def: &str,
841    properties: &ftd::Map<ftd::Value>,
842    doc_id: &str,
843    line_number: usize,
844) -> ftd::p1::Result<String> {
845    match properties.get(name) {
846        Some(ftd::Value::String { text: v, .. }) => Ok(v.to_string()),
847        Some(ftd::Value::None {
848            kind: ftd::p2::Kind::String { .. },
849        }) => Ok(def.to_string()),
850        Some(ftd::Value::None { .. }) => Ok(def.to_string()),
851        Some(v) => ftd::p2::utils::e2(
852            format!("expected bool, found: {:?}", v),
853            doc_id,
854            line_number,
855        ),
856        None => Ok(def.to_string()),
857    }
858}
859
860pub fn int(
861    name: &str,
862    properties: &ftd::Map<ftd::Value>,
863    doc_id: &str,
864    line_number: usize,
865) -> ftd::p1::Result<i64> {
866    match properties.get(name) {
867        Some(ftd::Value::Integer { value: v, .. }) => Ok(*v),
868        Some(v) => ftd::p2::utils::e2(
869            format!("[{}] expected int, found1: {:?}", name, v),
870            doc_id,
871            line_number,
872        ),
873        None => ftd::p2::utils::e2(format!("'{}' not found", name), doc_id, line_number),
874    }
875}
876
877pub fn int_optional(
878    name: &str,
879    properties: &ftd::Map<ftd::Value>,
880    doc_id: &str,
881    line_number: usize,
882) -> ftd::p1::Result<Option<i64>> {
883    match properties.get(name) {
884        Some(ftd::Value::Integer { value: v }) => Ok(Some(*v)),
885        Some(ftd::Value::None {
886            kind: ftd::p2::Kind::Integer { .. },
887        }) => Ok(None),
888        Some(ftd::Value::None { .. }) => Ok(None),
889        Some(ftd::Value::Optional {
890            data,
891            kind: ftd::p2::Kind::Integer { .. },
892        }) => match data.as_ref() {
893            Some(ftd::Value::Integer { value }) => Ok(Some(*value)),
894            None => Ok(None),
895            v => ftd::p2::utils::e2(
896                format!("expected integer, for: `{}` found: {:?}", name, v),
897                doc_id,
898                line_number,
899            ),
900        },
901        Some(v) => ftd::p2::utils::e2(
902            format!("expected integer, found 6: {:?}", v),
903            doc_id,
904            line_number,
905        ),
906        None => Ok(None),
907    }
908}
909
910pub fn int_with_default(
911    name: &str,
912    def: i64,
913    properties: &ftd::Map<ftd::Value>,
914    doc_id: &str,
915    line_number: usize,
916) -> ftd::p1::Result<i64> {
917    match properties.get(name) {
918        Some(ftd::Value::Integer { value: v }) => Ok(*v),
919        Some(ftd::Value::None {
920            kind: ftd::p2::Kind::Integer { .. },
921        }) => Ok(def),
922        Some(ftd::Value::None { .. }) => Ok(def),
923        Some(v) => ftd::p2::utils::e2(
924            format!("expected int, found2: {:?}", v),
925            doc_id,
926            line_number,
927        ),
928        None => Ok(def),
929    }
930}
931
932// pub fn elements(
933//     name: &str,
934//     properties: &ftd::Map<ftd::Value>,
935// ) -> ftd::p1::Result<Vec<ftd::Element>> {
936//     match properties.get(name) {
937//         Some(ftd::Value::Elements(v)) => Ok((*v).clone()),
938//         Some(v) => ftd::e(format!("expected elements, found: {:?}", v)),
939//         None => ftd::e(format!("'{}' not found", name)),
940//     }
941// }
942
943pub fn bool_with_default(
944    name: &str,
945    def: bool,
946    properties: &ftd::Map<ftd::Value>,
947    doc_id: &str,
948    line_number: usize,
949) -> ftd::p1::Result<bool> {
950    match properties.get(name) {
951        Some(ftd::Value::Boolean { value: v }) => Ok(*v),
952        Some(ftd::Value::None {
953            kind: ftd::p2::Kind::Boolean { .. },
954        }) => Ok(def),
955        Some(ftd::Value::None { .. }) => Ok(def),
956        Some(v) => ftd::p2::utils::e2(
957            format!("expected bool, found: {:?}", v),
958            doc_id,
959            line_number,
960        ),
961        None => Ok(def),
962    }
963}
964
965pub fn bool_(
966    name: &str,
967    properties: &ftd::Map<ftd::Value>,
968    doc_id: &str,
969    line_number: usize,
970) -> ftd::p1::Result<bool> {
971    match properties.get(name) {
972        Some(ftd::Value::Boolean { value: v, .. }) => Ok(*v),
973        Some(v) => ftd::p2::utils::e2(
974            format!("[{}] expected bool, found: {:?}", name, v),
975            doc_id,
976            line_number,
977        ),
978        None => ftd::p2::utils::e2(format!("'{}' not found", name), doc_id, line_number),
979    }
980}
981
982pub fn bool_optional(
983    name: &str,
984    properties: &ftd::Map<ftd::Value>,
985    doc_id: &str,
986    line_number: usize,
987) -> ftd::p1::Result<Option<bool>> {
988    match properties.get(name) {
989        Some(ftd::Value::Boolean { value: v }) => Ok(Some(*v)),
990        Some(ftd::Value::None {
991            kind: ftd::p2::Kind::Boolean { .. },
992        }) => Ok(None),
993        Some(ftd::Value::Optional {
994            data,
995            kind: ftd::p2::Kind::Boolean { .. },
996        }) => match data.as_ref() {
997            Some(ftd::Value::Boolean { value: v }) => Ok(Some(*v)),
998            None => Ok(None),
999            v => ftd::p2::utils::e2(
1000                format!("expected bool, for: `{}` found: {:?}", name, v),
1001                doc_id,
1002                line_number,
1003            ),
1004        },
1005        Some(v) => ftd::p2::utils::e2(
1006            format!("expected bool, found: {:?}", v),
1007            doc_id,
1008            line_number,
1009        ),
1010        None => Ok(None),
1011    }
1012}
1013
1014#[cfg(test)]
1015mod test {
1016    macro_rules! p {
1017        ($s:expr, $id: expr, $alias: expr) => {
1018            assert_eq!(
1019                super::parse_import(&Some($s.to_string()), $id, 0)
1020                    .unwrap_or_else(|e| panic!("{}", e)),
1021                ($id.to_string(), $alias.to_string())
1022            )
1023        };
1024    }
1025
1026    #[test]
1027    fn parse_import() {
1028        p!("a/b/c as foo", "a/b/c", "foo");
1029        p!("a/b as foo", "a/b", "foo");
1030        p!("a/b/c", "a/b/c", "c");
1031        p!("a/b", "a/b", "b");
1032        p!("a", "a", "a");
1033        p!("a as b", "a", "b");
1034    }
1035}
1036
1037pub fn decimal(
1038    name: &str,
1039    properties: &ftd::Map<ftd::Value>,
1040    doc_id: &str,
1041    line_number: usize,
1042) -> ftd::p1::Result<f64> {
1043    match properties.get(name) {
1044        Some(ftd::Value::Decimal { value: v, .. }) => Ok(*v),
1045        Some(v) => ftd::p2::utils::e2(
1046            format!("[{}] expected Decimal, found: {:?}", name, v),
1047            doc_id,
1048            line_number,
1049        ),
1050        None => ftd::p2::utils::e2(format!("'{}' not found", name), doc_id, line_number),
1051    }
1052}
1053
1054pub fn decimal_optional(
1055    name: &str,
1056    properties: &ftd::Map<ftd::Value>,
1057    doc_id: &str,
1058    line_number: usize,
1059) -> ftd::p1::Result<Option<f64>> {
1060    match properties.get(name) {
1061        Some(ftd::Value::Decimal { value: v }) => Ok(Some(*v)),
1062        Some(ftd::Value::None {
1063            kind: ftd::p2::Kind::Decimal { .. },
1064        }) => Ok(None),
1065        Some(ftd::Value::None { .. }) => Ok(None),
1066        Some(ftd::Value::Optional {
1067            data,
1068            kind: ftd::p2::Kind::Decimal { .. },
1069        }) => match data.as_ref() {
1070            Some(ftd::Value::Decimal { value: v }) => Ok(Some(*v)),
1071            None => Ok(None),
1072            v => ftd::p2::utils::e2(
1073                format!("expected decimal, for: `{}` found: {:?}", name, v),
1074                doc_id,
1075                line_number,
1076            ),
1077        },
1078        Some(v) => ftd::p2::utils::e2(
1079            format!("expected decimal, found: {:?}", v),
1080            doc_id,
1081            line_number,
1082        ),
1083        None => Ok(None),
1084    }
1085}
1086
1087pub(crate) fn get_string_container(local_container: &[usize]) -> String {
1088    local_container
1089        .iter()
1090        .map(|v| v.to_string())
1091        .collect::<Vec<String>>()
1092        .join(",")
1093}
1094
1095pub(crate) fn get_doc_name_and_remaining(s: &str) -> ftd::p1::Result<(String, Option<String>)> {
1096    let mut part1 = "".to_string();
1097    let mut pattern_to_split_at = s.to_string();
1098    if let Some((p1, p2)) = s.split_once('#') {
1099        part1 = format!("{}#", p1);
1100        pattern_to_split_at = p2.to_string();
1101    }
1102    Ok(if pattern_to_split_at.contains('.') {
1103        let (p1, p2) = ftd::p2::utils::split(pattern_to_split_at, ".")?;
1104        (format!("{}{}", part1, p1), Some(p2))
1105    } else {
1106        (s.to_string(), None)
1107    })
1108}
1109
1110pub(crate) fn resolve_local_variable_name(
1111    line_number: usize,
1112    name: &str,
1113    container: &str,
1114    doc_name: &str,
1115    aliases: &ftd::Map<String>,
1116) -> ftd::p1::Result<String> {
1117    if name.contains('@') {
1118        return Ok(name.to_string());
1119    }
1120    let (part1, part2) = ftd::p2::utils::get_doc_name_and_remaining(name)?;
1121    Ok(if let Some(ref p2) = part2 {
1122        ftd::p2::utils::resolve_name(
1123            line_number,
1124            format!("{}@{}.{}", part1, container, p2).as_str(),
1125            doc_name,
1126            aliases,
1127        )?
1128    } else {
1129        ftd::p2::utils::resolve_name(
1130            line_number,
1131            format!("{}@{}", part1, container).as_str(),
1132            doc_name,
1133            aliases,
1134        )?
1135    })
1136}
1137
1138pub fn resolve_name(
1139    line_number: usize,
1140    name: &str,
1141    doc_name: &str,
1142    aliases: &ftd::Map<String>,
1143) -> ftd::p1::Result<String> {
1144    if name.contains('#') {
1145        return Ok(name.to_string());
1146    }
1147    Ok(
1148        match ftd::p2::utils::split_module(name, doc_name, line_number)? {
1149            (Some(m), v, None) => match aliases.get(m) {
1150                Some(m) => format!("{}#{}", m, v),
1151                None => format!("{}#{}.{}", doc_name, m, v),
1152            },
1153            (Some(m), v, Some(c)) => match aliases.get(m) {
1154                Some(m) => format!("{}#{}.{}", m, v, c),
1155                None => format!("{}#{}.{}.{}", doc_name, m, v, c),
1156            },
1157            (None, v, None) => format!("{}#{}", doc_name, v),
1158            _ => unimplemented!(),
1159        },
1160    )
1161}
1162
1163pub fn split(name: String, split_at: &str) -> ftd::p1::Result<(String, String)> {
1164    if !name.contains(split_at) {
1165        return ftd::p2::utils::e2(format!("{} is not found in {}", split_at, name), "", 0);
1166    }
1167    let mut part = name.splitn(2, split_at);
1168    let part_1 = part.next().unwrap().trim();
1169    let part_2 = part.next().unwrap().trim();
1170    Ok((part_1.to_string(), part_2.to_string()))
1171}
1172
1173pub fn reorder(
1174    p1: &[ftd::p1::Section],
1175    doc: &ftd::p2::TDoc,
1176) -> ftd::p1::Result<(Vec<ftd::p1::Section>, Vec<String>)> {
1177    fn is_kernel_component(comp: String) -> bool {
1178        if ["ftd.row", "ftd.column"].contains(&comp.as_str()) {
1179            return true;
1180        }
1181        false
1182    }
1183
1184    fn reorder_component(
1185        p1_map: &ftd::Map<ftd::p1::Section>,
1186        new_p1: &mut Vec<ftd::p1::Section>,
1187        dependent_p1: Option<String>,
1188        inserted: &mut Vec<String>,
1189        doc: &ftd::p2::TDoc,
1190        var_types: &[String],
1191    ) -> ftd::p1::Result<()> {
1192        if let Some(p1) = dependent_p1 {
1193            if inserted.contains(&p1) {
1194                return Ok(());
1195            }
1196            if let Some(v) = p1_map.get(&p1) {
1197                for sub_section in v.sub_sections.0.iter() {
1198                    if inserted.contains(&sub_section.name) || p1 == sub_section.name {
1199                        continue;
1200                    }
1201                    reorder_component(
1202                        p1_map,
1203                        new_p1,
1204                        Some(sub_section.name.to_string()),
1205                        inserted,
1206                        doc,
1207                        var_types,
1208                    )?;
1209                }
1210                let var_data = ftd::variable::VariableData::get_name_kind(
1211                    &v.name,
1212                    doc,
1213                    v.line_number,
1214                    var_types,
1215                )?;
1216                if !is_kernel_component(var_data.kind.to_string())
1217                    && !inserted.contains(&var_data.kind)
1218                {
1219                    reorder_component(
1220                        p1_map,
1221                        new_p1,
1222                        Some(var_data.kind),
1223                        inserted,
1224                        doc,
1225                        var_types,
1226                    )?;
1227                }
1228                new_p1.push(v.to_owned());
1229                inserted.push(p1.to_string());
1230            }
1231            return Ok(());
1232        }
1233
1234        for (k, v) in p1_map {
1235            if inserted.contains(k) {
1236                continue;
1237            }
1238            for sub_section in v.sub_sections.0.iter() {
1239                for (_, _, v) in sub_section.header.0.iter() {
1240                    if v.contains(':') {
1241                        let (name, _) = ftd::p2::utils::split(v.to_string(), ":")?;
1242                        if inserted.contains(&name) || k == &name {
1243                            continue;
1244                        }
1245                        reorder_component(
1246                            p1_map,
1247                            new_p1,
1248                            Some(name.to_string()),
1249                            inserted,
1250                            doc,
1251                            var_types,
1252                        )?;
1253                    }
1254                }
1255                if inserted.contains(&sub_section.name) || k == &sub_section.name {
1256                    continue;
1257                }
1258                reorder_component(
1259                    p1_map,
1260                    new_p1,
1261                    Some(sub_section.name.to_string()),
1262                    inserted,
1263                    doc,
1264                    var_types,
1265                )?;
1266            }
1267            for (_, _, v) in v.header.0.iter() {
1268                if v.contains(':') {
1269                    let (name, _) = ftd::p2::utils::split(v.to_string(), ":")?;
1270                    if inserted.contains(&name) || k == &name {
1271                        continue;
1272                    }
1273                    reorder_component(
1274                        p1_map,
1275                        new_p1,
1276                        Some(name.to_string()),
1277                        inserted,
1278                        doc,
1279                        var_types,
1280                    )?;
1281                }
1282            }
1283            let var_data =
1284                ftd::variable::VariableData::get_name_kind(&v.name, doc, v.line_number, var_types)?;
1285            if !is_kernel_component(var_data.kind.to_string()) && !inserted.contains(&var_data.kind)
1286            {
1287                reorder_component(
1288                    p1_map,
1289                    new_p1,
1290                    Some(var_data.kind),
1291                    inserted,
1292                    doc,
1293                    var_types,
1294                )?;
1295            }
1296
1297            new_p1.push(v.to_owned());
1298            inserted.push(k.to_string());
1299        }
1300        Ok(())
1301    }
1302
1303    let mut p1_map: ftd::Map<ftd::p1::Section> = Default::default();
1304    let mut inserted_p1 = vec![];
1305    let mut new_p1 = vec![];
1306    let mut list_or_var = vec![];
1307    let mut var_types = vec![];
1308    for (idx, p1) in p1.iter().enumerate() {
1309        let var_data =
1310            ftd::variable::VariableData::get_name_kind(&p1.name, doc, p1.line_number, &var_types);
1311        if p1.name == "import"
1312            || p1.name.starts_with("record ")
1313            || p1.name.starts_with("or-type ")
1314            || p1.name.starts_with("map ")
1315        {
1316            inserted_p1.push(idx);
1317            new_p1.push(p1.to_owned());
1318        }
1319        if let Ok(ftd::variable::VariableData {
1320            type_: ftd::variable::Type::Variable,
1321            ref name,
1322            ..
1323        }) = var_data
1324        {
1325            inserted_p1.push(idx);
1326            new_p1.push(p1.to_owned());
1327            list_or_var.push(name.to_string());
1328        }
1329
1330        if p1.name.starts_with("record ") {
1331            let name = ftd::p2::utils::get_name("record", &p1.name, "")?;
1332            var_types.push(name.to_string());
1333        }
1334
1335        if p1.name.starts_with("or-type ") {
1336            let name = ftd::p2::utils::get_name("or-type", &p1.name, "")?;
1337            var_types.push(name.to_string());
1338            for s in &p1.sub_sections.0 {
1339                var_types.push(format!("{}.{}", name, s.name));
1340            }
1341        }
1342
1343        if list_or_var.contains(&p1.name) {
1344            inserted_p1.push(idx);
1345            new_p1.push(p1.to_owned());
1346        }
1347
1348        if let Ok(ftd::variable::VariableData {
1349            type_: ftd::variable::Type::Component,
1350            ref name,
1351            ..
1352        }) = var_data
1353        {
1354            if p1_map.contains_key(name) {
1355                return ftd::p2::utils::e2(
1356                    format!("{} is already declared", name),
1357                    doc.name,
1358                    p1.line_number,
1359                );
1360            }
1361            p1_map.insert(name.to_string(), p1.to_owned());
1362            inserted_p1.push(idx);
1363        }
1364    }
1365    let mut new_p1_component = vec![];
1366    reorder_component(
1367        &p1_map,
1368        &mut new_p1_component,
1369        None,
1370        &mut vec![],
1371        doc,
1372        &var_types,
1373    )?;
1374    new_p1.extend(new_p1_component);
1375
1376    for (idx, p1) in p1.iter().enumerate() {
1377        if inserted_p1.contains(&idx) {
1378            continue;
1379        }
1380        new_p1.push(p1.to_owned());
1381    }
1382
1383    Ok((new_p1, var_types))
1384}
1385
1386pub(crate) fn get_root_component_name(
1387    doc: &ftd::p2::TDoc,
1388    name: &str,
1389    line_number: usize,
1390) -> ftd::p1::Result<String> {
1391    let mut name = name.to_string();
1392    let mut root_name = name.to_string();
1393    while name != "ftd.kernel" {
1394        let component = doc.get_component(line_number, name.as_str())?;
1395        name = component.root;
1396        root_name = component.full_name;
1397    }
1398    Ok(root_name)
1399}
1400
1401pub(crate) fn get_markup_child(
1402    sub: &ftd::p1::SubSection,
1403    doc: &ftd::p2::TDoc,
1404    arguments: &ftd::Map<ftd::p2::Kind>,
1405) -> ftd::p1::Result<ftd::ChildComponent> {
1406    let (sub_name, ref_name) = match sub.name.split_once(' ') {
1407        Some((sub_name, ref_name)) => (sub_name.trim(), ref_name.trim()),
1408        _ => {
1409            return ftd::p2::utils::e2("the component should have name", doc.name, sub.line_number)
1410        }
1411    };
1412    let sub_caption = if sub.caption.is_none() && sub.body.is_none() {
1413        Some(ref_name.to_string())
1414    } else {
1415        sub.caption.clone()
1416    };
1417    let mut child = ftd::ChildComponent::from_p1(
1418        sub.line_number,
1419        sub_name,
1420        &sub.header,
1421        &sub_caption,
1422        &sub.body,
1423        doc,
1424        arguments,
1425    )?;
1426    child.root = format!("{} {}", child.root, ref_name);
1427    Ok(child)
1428}
1429
1430pub fn structure_header_to_properties(
1431    s: &str,
1432    arguments: &ftd::Map<crate::p2::Kind>,
1433    doc: &ftd::p2::TDoc,
1434    line_number: usize,
1435    p1: &ftd::p1::Header,
1436) -> ftd::p1::Result<ftd::Map<ftd::component::Property>> {
1437    let (name, caption) = ftd::p2::utils::split(s.to_string(), ":")?;
1438    match doc.get_thing(line_number, &name) {
1439        Ok(ftd::p2::Thing::Component(c)) => ftd::component::read_properties(
1440            line_number,
1441            p1,
1442            &if caption.is_empty() {
1443                None
1444            } else {
1445                Some(caption)
1446            },
1447            &None,
1448            "",
1449            "",
1450            &c.arguments,
1451            arguments,
1452            doc,
1453            &Default::default(),
1454            false,
1455        ),
1456        t => ftd::p2::utils::e2(
1457            format!("expected component, found: {:?}", t),
1458            doc.name,
1459            line_number,
1460        ),
1461    }
1462}
1463
1464pub fn arguments_on_condition(
1465    condition: &ftd::p2::Boolean,
1466    line_number: usize,
1467    doc: &ftd::p2::TDoc,
1468) -> ftd::p1::Result<(ftd::Map<ftd::Value>, bool)> {
1469    let mut arguments: ftd::Map<ftd::Value> = Default::default();
1470    let mut is_visible = true;
1471    if let ftd::p2::Boolean::IsNotNull { ref value } = condition {
1472        match value {
1473            ftd::PropertyValue::Value { .. } => {}
1474            ftd::PropertyValue::Reference { name, kind }
1475            | ftd::PropertyValue::Variable { name, kind } => {
1476                if let ftd::p2::Kind::Optional { kind, .. } = kind {
1477                    if doc.get_value(line_number, name).is_err() {
1478                        is_visible = false;
1479                        arguments.insert(
1480                            name.to_string(),
1481                            kind_to_value(kind, line_number, doc.name)?,
1482                        );
1483                    }
1484                }
1485                // TODO: Check if it's parent variable then don't throw error else throw error
1486                /* else {
1487                    return ftd::p2::utils::e2(
1488                        format!("expected optional kind, found: {} {:?}", name, kind),
1489                        doc.name,
1490                        line_number,
1491                    );
1492                }*/
1493            }
1494        }
1495    }
1496    return Ok((arguments, is_visible));
1497
1498    fn kind_to_value(
1499        kind: &ftd::p2::Kind,
1500        line_number: usize,
1501        doc_id: &str,
1502    ) -> ftd::p1::Result<ftd::Value> {
1503        if let Ok(value) = kind.to_value(line_number, doc_id) {
1504            return Ok(value);
1505        }
1506        // todo implement for all the kind
1507        Ok(match kind {
1508            ftd::p2::Kind::String { .. } => ftd::Value::String {
1509                text: "".to_string(),
1510                source: ftd::TextSource::Header,
1511            },
1512            ftd::p2::Kind::Integer { .. } => ftd::Value::Integer { value: 0 },
1513            ftd::p2::Kind::Decimal { .. } => ftd::Value::Decimal { value: 0.0 },
1514            ftd::p2::Kind::Boolean { .. } => ftd::Value::Boolean { value: false },
1515            _ => {
1516                return ftd::p2::utils::e2(
1517                    format!(
1518                        "implemented for string, integer, decimal and boolean, found: {:?}",
1519                        kind
1520                    ),
1521                    doc_id,
1522                    line_number,
1523                )
1524            }
1525        })
1526    }
1527}
1528
1529pub fn split_module<'a>(
1530    id: &'a str,
1531    _doc_id: &str,
1532    _line_number: usize,
1533) -> ftd::p1::Result<(Option<&'a str>, &'a str, Option<&'a str>)> {
1534    match id.split_once('.') {
1535        Some((p1, p2)) => match p2.split_once('.') {
1536            Some((p21, p22)) => Ok((Some(p1), p21, Some(p22))),
1537            None => Ok((Some(p1), p2, None)),
1538        },
1539        None => Ok((None, id, None)),
1540    }
1541}
1542
1543pub fn e2<T, S1>(m: S1, doc_id: &str, line_number: usize) -> ftd::p1::Result<T>
1544where
1545    S1: Into<String>,
1546{
1547    Err(ftd::p1::Error::ParseError {
1548        message: m.into(),
1549        doc_id: doc_id.to_string(),
1550        line_number,
1551    })
1552}
1553
1554pub fn unknown_processor_error<T, S>(m: S, doc_id: String, line_number: usize) -> ftd::p1::Result<T>
1555where
1556    S: Into<String>,
1557{
1558    Err(ftd::p1::Error::ParseError {
1559        message: m.into(),
1560        doc_id,
1561        line_number,
1562    })
1563}