ftd/
ui.rs

1#[derive(serde::Deserialize, Clone, Debug, PartialEq, serde::Serialize)]
2#[serde(tag = "type")]
3pub enum Element {
4    TextBlock(TextBlock),
5    Code(Code),
6    Image(Image),
7    Row(Row),
8    Column(Column),
9    IFrame(IFrame),
10    Input(Input),
11    Integer(Text),
12    Boolean(Text),
13    Decimal(Text),
14    Scene(Scene),
15    Grid(Grid),
16    Markup(Markups),
17    Null,
18}
19
20#[derive(serde::Deserialize, Debug, PartialEq, Default, Clone, serde::Serialize)]
21pub struct Markups {
22    pub text: ftd::Rendered,
23    pub common: ftd::Common,
24    pub text_align: TextAlign,
25    pub line: bool,
26    pub style: Style,
27    pub font: Option<Type>,
28    pub line_clamp: Option<i64>,
29    pub text_indent: Option<Length>,
30    pub children: Vec<Markup>,
31}
32
33impl Markups {
34    pub(crate) fn to_text(&self) -> Text {
35        Text {
36            text: self.text.to_owned(),
37            line: self.line,
38            common: self.common.to_owned(),
39            text_align: self.text_align.to_owned(),
40            style: self.style.to_owned(),
41            font: self.font.to_owned(),
42            line_clamp: self.line_clamp,
43            text_indent: self.text_indent.to_owned(),
44        }
45    }
46}
47
48#[derive(serde::Deserialize, Debug, PartialEq, Clone, serde::Serialize)]
49pub struct Markup {
50    pub itext: IText,
51    pub children: Vec<Markup>,
52}
53
54#[derive(serde::Deserialize, Debug, PartialEq, Clone, serde::Serialize)]
55pub enum IText {
56    Text(Text),
57    TextBlock(TextBlock),
58    Integer(Text),
59    Boolean(Text),
60    Decimal(Text),
61    Markup(Markups),
62}
63
64impl Element {
65    pub(crate) fn set_children_count_variable(
66        elements: &mut [ftd::Element],
67        local_variables: &ftd::Map<ftd::p2::Thing>,
68    ) {
69        for child in elements.iter_mut() {
70            let (text, common) = match child {
71                Element::Integer(ftd::Text { text, common, .. })
72                | Element::Boolean(ftd::Text { text, common, .. })
73                | Element::Decimal(ftd::Text { text, common, .. }) => (Some(text), common),
74                Self::Markup(ftd::Markups {
75                    text,
76                    common,
77                    children,
78                    ..
79                }) => {
80                    set_markup_children_count_variable(children, local_variables);
81                    (Some(text), common)
82                }
83                Element::Row(ftd::Row {
84                    container, common, ..
85                })
86                | Element::Column(ftd::Column {
87                    container, common, ..
88                })
89                | Element::Scene(ftd::Scene {
90                    container, common, ..
91                })
92                | Element::Grid(ftd::Grid {
93                    container, common, ..
94                }) => {
95                    ftd::Element::set_children_count_variable(
96                        &mut container.children,
97                        local_variables,
98                    );
99                    if let Some((_, _, external_children)) = &mut container.external_children {
100                        ftd::Element::set_children_count_variable(
101                            external_children,
102                            local_variables,
103                        );
104                    }
105                    (None, common)
106                }
107                _ => continue,
108            };
109
110            match &common.reference {
111                Some(reference) if reference.contains("CHILDREN-COUNT") => {
112                    if let Some(ftd::p2::Thing::Variable(ftd::Variable {
113                        value:
114                            ftd::PropertyValue::Value {
115                                value: ftd::Value::Integer { value },
116                            },
117                        ..
118                    })) = local_variables.get(reference)
119                    {
120                        if let Some(text) = text {
121                            *text = ftd::rendered::markup_line(value.to_string().as_str());
122                        }
123                    }
124                }
125                _ => {}
126            }
127
128            for event in common.events.iter_mut() {
129                for action_value in event.action.parameters.values_mut() {
130                    for parameter_data in action_value.iter_mut() {
131                        let mut remove_reference = false;
132                        match parameter_data.reference {
133                            Some(ref reference) if reference.contains("CHILDREN-COUNT") => {
134                                if let Some(ftd::p2::Thing::Variable(ftd::Variable {
135                                    value:
136                                        ftd::PropertyValue::Value {
137                                            value: ftd::Value::Integer { value },
138                                        },
139                                    ..
140                                })) = local_variables.get(reference)
141                                {
142                                    parameter_data.value = serde_json::json!(value);
143                                    remove_reference = true;
144                                }
145                            }
146                            _ => {}
147                        }
148                        if remove_reference {
149                            parameter_data.reference = None;
150                        }
151                    }
152                }
153            }
154        }
155
156        fn set_markup_children_count_variable(
157            elements: &mut [ftd::Markup],
158            local_variables: &ftd::Map<ftd::p2::Thing>,
159        ) {
160            for child in elements.iter_mut() {
161                let (common, children, text) = match &mut child.itext {
162                    IText::Text(t) | IText::Integer(t) | IText::Boolean(t) | IText::Decimal(t) => {
163                        (&mut t.common, None, &mut t.text)
164                    }
165                    IText::TextBlock(t) => (&mut t.common, None, &mut t.text),
166                    IText::Markup(t) => (&mut t.common, Some(&mut t.children), &mut t.text),
167                };
168
169                match &common.reference {
170                    Some(reference) if reference.contains("CHILDREN-COUNT") => {
171                        if let Some(ftd::p2::Thing::Variable(ftd::Variable {
172                            value:
173                                ftd::PropertyValue::Value {
174                                    value: ftd::Value::Integer { value },
175                                },
176                            ..
177                        })) = local_variables.get(reference)
178                        {
179                            *text = ftd::rendered::markup_line(value.to_string().as_str());
180                        }
181                    }
182                    _ => {}
183                }
184
185                for event in common.events.iter_mut() {
186                    for action_value in event.action.parameters.values_mut() {
187                        for parameter_data in action_value.iter_mut() {
188                            let mut remove_reference = false;
189                            match parameter_data.reference {
190                                Some(ref reference) if reference.contains("CHILDREN-COUNT") => {
191                                    if let Some(ftd::p2::Thing::Variable(ftd::Variable {
192                                        value:
193                                            ftd::PropertyValue::Value {
194                                                value: ftd::Value::Integer { value },
195                                            },
196                                        ..
197                                    })) = local_variables.get(reference)
198                                    {
199                                        parameter_data.value = serde_json::json!(value);
200                                        remove_reference = true;
201                                    }
202                                }
203                                _ => {}
204                            }
205                            if remove_reference {
206                                parameter_data.reference = None;
207                            }
208                        }
209                    }
210                }
211
212                if let Some(children) = children {
213                    set_markup_children_count_variable(children, local_variables);
214                }
215            }
216        }
217    }
218
219    pub(crate) fn set_default_locals(elements: &mut [ftd::Element]) {
220        return set_default_locals_(elements);
221        fn set_default_locals_(children: &mut [ftd::Element]) {
222            for child in children.iter_mut() {
223                let common = match child {
224                    Element::TextBlock(ftd::TextBlock { common, .. })
225                    | Element::Code(ftd::Code { common, .. })
226                    | Element::Image(ftd::Image { common, .. })
227                    | Element::IFrame(ftd::IFrame { common, .. })
228                    | Element::Input(ftd::Input { common, .. })
229                    | Element::Integer(ftd::Text { common, .. })
230                    | Element::Boolean(ftd::Text { common, .. })
231                    | Element::Decimal(ftd::Text { common, .. })
232                    | Element::Markup(ftd::Markups { common, .. }) => common,
233                    Element::Row(ftd::Row {
234                        common, container, ..
235                    })
236                    | Element::Column(ftd::Column {
237                        common, container, ..
238                    })
239                    | Element::Scene(ftd::Scene {
240                        common, container, ..
241                    })
242                    | Element::Grid(ftd::Grid {
243                        common, container, ..
244                    }) => {
245                        set_default_locals_(&mut container.children);
246                        if let Some((_, _, external_children)) = &mut container.external_children {
247                            set_default_locals_(external_children);
248                        }
249                        common
250                    }
251                    Element::Null => continue,
252                };
253
254                if let Some(index) = check(common) {
255                    common.events.extend(ftd::p2::Event::mouse_event(&index));
256                }
257            }
258
259            fn check(common: &mut ftd::Common) -> Option<String> {
260                if let Some(ref mut condition) = common.condition {
261                    if condition.variable.contains("MOUSE-IN") {
262                        return Some(condition.variable.clone());
263                    }
264                }
265                if let Some(ref mut reference) = common.reference {
266                    if reference.contains("MOUSE-IN") {
267                        return Some(reference.to_string());
268                    }
269                }
270                for (_, v) in common.conditional_attribute.iter_mut() {
271                    for (condition, _) in &mut v.conditions_with_value {
272                        if condition.variable.contains("MOUSE-IN") {
273                            return Some(condition.variable.to_string());
274                        }
275                    }
276                }
277                None
278            }
279        }
280    }
281
282    pub fn set_id(children: &mut [ftd::Element], index_vec: &[usize], external_id: Option<String>) {
283        for (idx, child) in children.iter_mut().enumerate() {
284            let (id, is_dummy) = match child {
285                Self::TextBlock(ftd::TextBlock {
286                    common:
287                        ftd::Common {
288                            data_id: id,
289                            is_dummy,
290                            ..
291                        },
292                    ..
293                })
294                | Self::Code(ftd::Code {
295                    common:
296                        ftd::Common {
297                            data_id: id,
298                            is_dummy,
299                            ..
300                        },
301                    ..
302                })
303                | Self::Image(ftd::Image {
304                    common:
305                        ftd::Common {
306                            data_id: id,
307                            is_dummy,
308                            ..
309                        },
310                    ..
311                })
312                | Self::IFrame(ftd::IFrame {
313                    common:
314                        ftd::Common {
315                            data_id: id,
316                            is_dummy,
317                            ..
318                        },
319                    ..
320                })
321                | Self::Input(ftd::Input {
322                    common:
323                        ftd::Common {
324                            data_id: id,
325                            is_dummy,
326                            ..
327                        },
328                    ..
329                })
330                | Self::Integer(ftd::Text {
331                    common:
332                        ftd::Common {
333                            data_id: id,
334                            is_dummy,
335                            ..
336                        },
337                    ..
338                })
339                | Self::Boolean(ftd::Text {
340                    common:
341                        ftd::Common {
342                            data_id: id,
343                            is_dummy,
344                            ..
345                        },
346                    ..
347                })
348                | Self::Decimal(ftd::Text {
349                    common:
350                        ftd::Common {
351                            data_id: id,
352                            is_dummy,
353                            ..
354                        },
355                    ..
356                }) => (id, is_dummy),
357                Self::Row(ftd::Row {
358                    common:
359                        ftd::Common {
360                            data_id: id,
361                            is_dummy,
362                            ..
363                        },
364                    container,
365                    ..
366                })
367                | Self::Column(ftd::Column {
368                    common:
369                        ftd::Common {
370                            data_id: id,
371                            is_dummy,
372                            ..
373                        },
374                    container,
375                    ..
376                })
377                | Self::Scene(ftd::Scene {
378                    common:
379                        ftd::Common {
380                            data_id: id,
381                            is_dummy,
382                            ..
383                        },
384                    container,
385                    ..
386                })
387                | Self::Grid(ftd::Grid {
388                    common:
389                        ftd::Common {
390                            data_id: id,
391                            is_dummy,
392                            ..
393                        },
394                    container,
395                    ..
396                }) => {
397                    let mut index_vec = index_vec.to_vec();
398                    index_vec.push(idx);
399                    Self::set_id(&mut container.children, &index_vec, external_id.clone());
400                    if let Some((id, container, external_children)) =
401                        &mut container.external_children
402                    {
403                        if let Some(ftd::Element::Column(col)) = external_children.first_mut() {
404                            let index_string: String = index_vec
405                                .iter()
406                                .map(|v| v.to_string())
407                                .collect::<Vec<String>>()
408                                .join(",");
409
410                            let external_id = Some({
411                                if let Some(ref ext_id) = external_id {
412                                    format!("{}.{}-external:{}", ext_id, id, index_string)
413                                } else {
414                                    format!("{}-external:{}", id, index_string)
415                                }
416                            });
417                            col.common.data_id = external_id.clone();
418                            if let Some(val) = container.first_mut() {
419                                index_vec.append(&mut val.to_vec());
420                                Self::set_id(&mut col.container.children, &index_vec, external_id);
421                            }
422                        }
423                    }
424                    (id, is_dummy)
425                }
426                Self::Markup(ftd::Markups {
427                    common:
428                        ftd::Common {
429                            data_id: id,
430                            is_dummy,
431                            ..
432                        },
433                    children,
434                    ..
435                }) => {
436                    let mut index_vec = index_vec.to_vec();
437                    index_vec.push(idx);
438                    set_markup_id(children, &index_vec, external_id.clone());
439                    (id, is_dummy)
440                }
441                Self::Null => continue,
442            };
443            let index_string = if *is_dummy {
444                get_index_string(index_vec, None)
445            } else {
446                get_index_string(index_vec, Some(idx))
447            };
448            set_id(id, &external_id, index_string.as_str(), *is_dummy);
449        }
450
451        fn set_markup_id(
452            children: &mut [ftd::Markup],
453            index_vec: &[usize],
454            external_id: Option<String>,
455        ) {
456            return set_markup_id_(children, index_vec, external_id, 0);
457
458            fn set_markup_id_(
459                children: &mut [ftd::Markup],
460                index_vec: &[usize],
461                external_id: Option<String>,
462                start_index: usize,
463            ) {
464                for (idx, child) in children.iter_mut().enumerate() {
465                    let (id, children, is_dummy) = match &mut child.itext {
466                        IText::Text(t)
467                        | IText::Integer(t)
468                        | IText::Boolean(t)
469                        | IText::Decimal(t) => (&mut t.common.data_id, None, t.common.is_dummy),
470                        IText::TextBlock(t) => (&mut t.common.data_id, None, t.common.is_dummy),
471                        IText::Markup(t) => (
472                            &mut t.common.data_id,
473                            Some(&mut t.children),
474                            t.common.is_dummy,
475                        ),
476                    };
477                    let index_string = if is_dummy {
478                        get_index_string(index_vec, None)
479                    } else {
480                        get_index_string(index_vec, Some(idx + start_index))
481                    };
482
483                    let mut index_vec = index_vec.to_vec();
484                    index_vec.push(idx);
485                    set_markup_id_(&mut child.children, &index_vec, external_id.clone(), 0);
486                    if let Some(children) = children {
487                        set_markup_id_(
488                            children,
489                            &index_vec,
490                            external_id.clone(),
491                            child.children.len(),
492                        );
493                    }
494
495                    set_id(id, &external_id, index_string.as_str(), is_dummy)
496                }
497            }
498        }
499
500        fn set_id(
501            id: &mut Option<String>,
502            external_id: &Option<String>,
503            index_string: &str,
504            is_dummy: bool,
505        ) {
506            let external_id = {
507                if let Some(ref external_id) = external_id {
508                    format!(":{}", external_id)
509                } else {
510                    "".to_string()
511                }
512            };
513            let dummy_str = if is_dummy {
514                ":dummy".to_string()
515            } else {
516                "".to_string()
517            };
518
519            if let Some(id) = id {
520                *id = format!("{}:{}{}{}", id, index_string, external_id, dummy_str);
521            } else {
522                *id = Some(format!("{}{}{}", index_string, external_id, dummy_str));
523            }
524        }
525
526        fn get_index_string(index_vec: &[usize], idx: Option<usize>) -> String {
527            let index_string: String = {
528                let mut index_vec = index_vec.to_vec();
529                if let Some(idx) = idx {
530                    index_vec.push(idx);
531                }
532                index_vec
533                    .iter()
534                    .map(|v| v.to_string())
535                    .collect::<Vec<String>>()
536                    .join(",")
537            };
538            index_string
539        }
540    }
541
542    pub fn get_external_children_condition(
543        &self,
544        external_open_id: &Option<String>,
545        external_children_container: &[Vec<usize>],
546    ) -> Vec<ftd::ExternalChildrenCondition> {
547        let mut d: Vec<ftd::ExternalChildrenCondition> = vec![];
548        let mut ext_child_condition = None;
549        let (id, open_id, children_container, children) = match self {
550            Self::Row(ftd::Row {
551                common: ftd::Common { data_id: id, .. },
552                container:
553                    ftd::Container {
554                        external_children,
555                        children,
556                        ..
557                    },
558                ..
559            })
560            | Self::Column(ftd::Column {
561                common: ftd::Common { data_id: id, .. },
562                container:
563                    ftd::Container {
564                        external_children,
565                        children,
566                        ..
567                    },
568                ..
569            })
570            | Self::Scene(ftd::Scene {
571                common: ftd::Common { data_id: id, .. },
572                container:
573                    ftd::Container {
574                        external_children,
575                        children,
576                        ..
577                    },
578                ..
579            })
580            | Self::Grid(ftd::Grid {
581                common: ftd::Common { data_id: id, .. },
582                container:
583                    ftd::Container {
584                        external_children,
585                        children,
586                        ..
587                    },
588                ..
589            }) => (
590                id,
591                external_children
592                    .as_ref()
593                    .map(|(open_id, _, _)| open_id.to_string()),
594                external_children
595                    .as_ref()
596                    .map(|(_, children_container, _)| children_container.to_vec()),
597                children,
598            ),
599            _ => return d,
600        };
601
602        #[allow(clippy::blocks_in_if_conditions)]
603        if *external_open_id
604            == id.as_ref().map(|v| {
605                if v.contains(':') {
606                    let mut part = v.splitn(2, ':');
607                    part.next().unwrap().trim().to_string()
608                } else {
609                    v.to_string()
610                }
611            })
612            && external_children_container.is_empty()
613        {
614            ext_child_condition = id.clone();
615            if open_id.is_none() {
616                let id = ext_child_condition.expect("");
617                d.push(ftd::ExternalChildrenCondition {
618                    condition: vec![id.to_string()],
619                    set_at: id,
620                });
621                return d;
622            }
623        }
624
625        let (open_id, external_children_container) =
626            if open_id.is_some() && external_children_container.is_empty() {
627                (open_id, {
628                    if let Some(c) = children_container {
629                        c
630                    } else {
631                        vec![]
632                    }
633                })
634            } else {
635                (
636                    external_open_id.clone(),
637                    external_children_container.to_vec(),
638                )
639            };
640
641        let mut index = 0;
642        for (i, v) in children.iter().enumerate() {
643            let external_container = {
644                let mut external_container = vec![];
645                while index < external_children_container.len() {
646                    if let Some(container) = external_children_container[index].get(0) {
647                        if container < &i {
648                            index += 1;
649                            continue;
650                        }
651                        let external_child_container =
652                            external_children_container[index][1..].to_vec();
653                        if container == &i && !external_child_container.is_empty() {
654                            external_container.push(external_child_container)
655                        } else {
656                            break;
657                        }
658                    }
659                    index += 1;
660                }
661                external_container
662            };
663            let conditions =
664                v.get_external_children_condition(&open_id, external_container.as_slice());
665            for mut condition in conditions {
666                if let Some(e) = &ext_child_condition {
667                    condition.condition.push(e.to_string());
668                }
669                d.push(condition);
670            }
671        }
672        d
673    }
674
675    pub fn get_external_children_dependencies(
676        children: &[ftd::Element],
677    ) -> ftd::ExternalChildrenDependenciesMap {
678        let mut d: ftd::ExternalChildrenDependenciesMap = Default::default();
679        for child in children {
680            let container = match child {
681                ftd::Element::Row(ftd::Row { container, .. }) => container,
682                ftd::Element::Column(ftd::Column { container, .. }) => container,
683                ftd::Element::Scene(ftd::Scene { container, .. }) => container,
684                ftd::Element::Grid(ftd::Grid { container, .. }) => container,
685                _ => continue,
686            };
687            let all_locals = ftd::Element::get_external_children_dependencies(&container.children);
688            for (k, v) in all_locals {
689                d.insert(k.to_string(), v);
690            }
691            if let Some((external_open_id, external_children_container, external_children)) =
692                &container.external_children
693            {
694                if let Some(ftd::Element::Column(col)) = external_children.first() {
695                    let external_children_condition: Vec<ftd::ExternalChildrenCondition> = child
696                        .get_external_children_condition(
697                            &Some(external_open_id.to_string()),
698                            external_children_container,
699                        );
700                    d.insert(
701                        col.common.data_id.as_ref().expect("").to_string(),
702                        external_children_condition,
703                    );
704                    let all_locals =
705                        ftd::Element::get_external_children_dependencies(&col.container.children);
706                    for (k, v) in all_locals {
707                        d.insert(k.to_string(), v);
708                    }
709                }
710            }
711        }
712        d
713    }
714
715    pub fn get_event_dependencies(children: &[ftd::Element], data: &mut ftd::DataDependenciesMap) {
716        for child in children {
717            let (font, common) = match child {
718                ftd::Element::Column(ftd::Column {
719                    common, container, ..
720                })
721                | ftd::Element::Row(ftd::Row {
722                    common, container, ..
723                })
724                | ftd::Element::Scene(ftd::Scene {
725                    common, container, ..
726                })
727                | ftd::Element::Grid(ftd::Grid {
728                    common, container, ..
729                }) => {
730                    ftd::Element::get_event_dependencies(&container.children, data);
731                    if let Some((_, _, external_children)) = &container.external_children {
732                        ftd::Element::get_event_dependencies(external_children, data);
733                    }
734                    (&None, common)
735                }
736                ftd::Element::Markup(ftd::Markups {
737                    font,
738                    common,
739                    children,
740                    ..
741                }) => {
742                    markup_get_event_dependencies(children, data);
743                    (font, common)
744                }
745                ftd::Element::Code(ftd::Code { font, common, .. })
746                | ftd::Element::Integer(ftd::Text { font, common, .. })
747                | ftd::Element::Boolean(ftd::Text { font, common, .. })
748                | ftd::Element::Decimal(ftd::Text { font, common, .. })
749                | ftd::Element::Input(ftd::Input { font, common, .. }) => (font, common),
750                ftd::Element::IFrame(ftd::IFrame { common, .. })
751                | ftd::Element::TextBlock(ftd::TextBlock { common, .. })
752                | ftd::Element::Image(ftd::Image { common, .. }) => (&None, common),
753                ftd::Element::Null => continue,
754            };
755            value_condition(&common.reference, &common.data_id, data);
756            color_condition(common, &common.data_id, data);
757            font_condition(&common.data_id, data, font, &common.conditional_attribute);
758            image_condition(&common.data_id, data, &common.background_image);
759            style_condition(&common.conditional_attribute, &common.data_id, data);
760            visibility_condition(&common.condition, &common.data_id, data);
761        }
762
763        fn markup_get_event_dependencies(
764            children: &[ftd::Markup],
765            data: &mut ftd::DataDependenciesMap,
766        ) {
767            for child in children {
768                let (font, common) = match child.itext {
769                    IText::Text(ref t)
770                    | IText::Integer(ref t)
771                    | IText::Boolean(ref t)
772                    | IText::Decimal(ref t) => (&t.font, &t.common),
773                    IText::TextBlock(ref t) => (&None, &t.common),
774                    IText::Markup(ref t) => {
775                        markup_get_event_dependencies(&t.children, data);
776                        (&t.font, &t.common)
777                    }
778                };
779                markup_get_event_dependencies(&child.children, data);
780                value_condition(&common.reference, &common.data_id, data);
781                color_condition(common, &common.data_id, data);
782                font_condition(&common.data_id, data, font, &common.conditional_attribute);
783                image_condition(&common.data_id, data, &common.background_image);
784                style_condition(&common.conditional_attribute, &common.data_id, data);
785                visibility_condition(&common.condition, &common.data_id, data);
786            }
787        }
788
789        fn value_condition(
790            reference: &Option<String>,
791            id: &Option<String>,
792            data: &mut ftd::DataDependenciesMap,
793        ) {
794            if let Some(reference) = reference {
795                let id = id.clone().expect("universal id should be present");
796
797                let (variable, remaining) =
798                    ftd::p2::utils::get_doc_name_and_remaining(reference).unwrap();
799                if let Some(ftd::Data { dependencies, .. }) = data.get_mut(&variable) {
800                    let json = ftd::Dependencies {
801                        dependency_type: ftd::DependencyType::Value,
802                        condition: None,
803                        parameters: Default::default(),
804                        remaining,
805                    };
806                    if let Some(dependencies) = dependencies.get_mut(&id) {
807                        let mut d = serde_json::from_value::<Vec<ftd::Dependencies>>(
808                            dependencies.to_owned(),
809                        )
810                        .unwrap();
811                        d.push(json);
812                        *dependencies = serde_json::to_value(&d).unwrap();
813                    } else {
814                        dependencies.insert(id, serde_json::to_value(&vec![json]).unwrap());
815                    }
816                }
817            }
818        }
819
820        fn color_condition(
821            common: &ftd::Common,
822            id: &Option<String>,
823            data: &mut ftd::DataDependenciesMap,
824        ) {
825            let id = id.clone().expect("universal id should be present");
826            if let Some(ref color) = common.color {
827                color_condition(
828                    color,
829                    id.as_str(),
830                    data,
831                    "color",
832                    &common.conditional_attribute,
833                );
834            }
835            if let Some(ref color) = common.background_color {
836                color_condition(
837                    color,
838                    id.as_str(),
839                    data,
840                    "background-color",
841                    &common.conditional_attribute,
842                );
843            }
844            if let Some(ref color) = common.border_top_color {
845                color_condition(
846                    color,
847                    id.as_str(),
848                    data,
849                    "background-color",
850                    &common.conditional_attribute,
851                );
852            }
853            if let Some(ref color) = common.border_right_color {
854                color_condition(
855                    color,
856                    id.as_str(),
857                    data,
858                    "background-color",
859                    &common.conditional_attribute,
860                );
861            }
862            if let Some(ref color) = common.border_bottom_color {
863                color_condition(
864                    color,
865                    id.as_str(),
866                    data,
867                    "background-color",
868                    &common.conditional_attribute,
869                );
870            }
871            if let Some(ref color) = common.border_left_color {
872                color_condition(
873                    color,
874                    id.as_str(),
875                    data,
876                    "background-color",
877                    &common.conditional_attribute,
878                );
879            }
880            if let Some(ref color) = common.border_color {
881                color_condition(
882                    color,
883                    id.as_str(),
884                    data,
885                    "border-color",
886                    &common.conditional_attribute,
887                );
888            }
889
890            fn color_condition(
891                _color: &ftd::Color,
892                id: &str,
893                data: &mut ftd::DataDependenciesMap,
894                style: &str,
895                conditional_attribute: &ftd::Map<ftd::ConditionalAttribute>,
896            ) {
897                let (reference, value) = if let Some(ftd::ConditionalAttribute {
898                    default:
899                        Some(ConditionalValue {
900                            reference: Some(reference),
901                            value,
902                            ..
903                        }),
904                    ..
905                }) = conditional_attribute.get(style)
906                {
907                    (reference.to_string(), value.to_owned())
908                // } else if let Some(ref reference) = color.reference {
909                //     (
910                //         reference.to_string(),
911                //         serde_json::json!({ "light": ftd::html::color(&color.light), "dark": ftd::html::color(&color.dark), "$kind$": "light" }),
912                //     )
913                } else {
914                    return;
915                };
916                let parameters = {
917                    let mut parameters = ftd::Map::new();
918                    parameters.insert(
919                        style.to_string(),
920                        ftd::ConditionalValueWithDefault {
921                            value: ConditionalValue {
922                                value,
923                                important: false,
924                                reference: None,
925                            },
926                            default: None,
927                        },
928                    );
929                    let dependents = conditional_attribute
930                        .get(style)
931                        .unwrap_or(&ConditionalAttribute {
932                            attribute_type: AttributeType::Style,
933                            conditions_with_value: vec![],
934                            default: None,
935                        })
936                        .conditions_with_value
937                        .iter()
938                        .map(|(v, _)| v.variable.to_string())
939                        .collect::<Vec<String>>();
940                    parameters.insert(
941                        "dependents".to_string(),
942                        ftd::ConditionalValueWithDefault {
943                            value: ConditionalValue {
944                                value: serde_json::to_value(dependents).unwrap(),
945                                important: false,
946                                reference: None,
947                            },
948                            default: None,
949                        },
950                    );
951                    parameters
952                };
953                let (variable, remaining) =
954                    ftd::p2::utils::get_doc_name_and_remaining(&reference).unwrap();
955                if let Some(ftd::Data { dependencies, .. }) = data.get_mut(&variable) {
956                    let json = ftd::Dependencies {
957                        dependency_type: ftd::DependencyType::Style,
958                        condition: None,
959                        parameters,
960                        remaining,
961                    };
962                    if let Some(dependencies) = dependencies.get_mut(id) {
963                        let mut d = serde_json::from_value::<Vec<ftd::Dependencies>>(
964                            dependencies.to_owned(),
965                        )
966                        .unwrap();
967                        d.push(json);
968                        *dependencies = serde_json::to_value(&d).unwrap();
969                    } else {
970                        dependencies
971                            .insert(id.to_string(), serde_json::to_value(&vec![json]).unwrap());
972                    }
973                }
974            }
975        }
976
977        fn font_condition(
978            id: &Option<String>,
979            data: &mut ftd::DataDependenciesMap,
980            font: &Option<Type>,
981            conditions: &ftd::Map<ConditionalAttribute>,
982        ) {
983            let id = id.clone().expect("universal id should be present");
984            if !conditions
985                .keys()
986                .any(|x| ["line-height", "font-size"].contains(&x.as_str()))
987            {
988                //mention all font attributes.
989                // since font is not conditional attribute yet so this will always pass
990                return;
991            }
992            if let Some(ref type_) = font {
993                font_condition(type_, id.as_str(), data);
994            }
995
996            fn font_condition(type_: &ftd::Type, id: &str, data: &mut ftd::DataDependenciesMap) {
997                let (reference, value) = if let Some(ref reference) = type_.reference {
998                    let desktop = serde_json::to_value(&type_.desktop).unwrap();
999                    let mobile = serde_json::to_value(&type_.mobile).unwrap();
1000                    let xl = serde_json::to_value(&type_.xl).unwrap();
1001                    (
1002                        reference.to_string(),
1003                        serde_json::json!({ "desktop": desktop,
1004                            "mobile": mobile,
1005                            "xl": xl,
1006                            "$kind$": "desktop"
1007                        }),
1008                    )
1009                } else {
1010                    return;
1011                };
1012                let parameters = {
1013                    let mut parameters = ftd::Map::new();
1014                    parameters.insert(
1015                        "font".to_string(),
1016                        ftd::ConditionalValueWithDefault {
1017                            value: ConditionalValue {
1018                                value,
1019                                important: false,
1020                                reference: None,
1021                            },
1022                            default: None,
1023                        },
1024                    );
1025                    parameters
1026                };
1027
1028                let (variable, remaining) =
1029                    ftd::p2::utils::get_doc_name_and_remaining(&reference).unwrap();
1030
1031                if let Some(ftd::Data { dependencies, .. }) = data.get_mut(&variable) {
1032                    let json = ftd::Dependencies {
1033                        dependency_type: ftd::DependencyType::Style,
1034                        condition: None,
1035                        parameters,
1036                        remaining,
1037                    };
1038                    if let Some(dependencies) = dependencies.get_mut(id) {
1039                        let mut d = serde_json::from_value::<Vec<ftd::Dependencies>>(
1040                            dependencies.to_owned(),
1041                        )
1042                        .unwrap();
1043                        d.push(json);
1044                        *dependencies = serde_json::to_value(&d).unwrap();
1045                    } else {
1046                        dependencies
1047                            .insert(id.to_string(), serde_json::to_value(&vec![json]).unwrap());
1048                    }
1049                }
1050            }
1051        }
1052
1053        fn image_condition(
1054            id: &Option<String>,
1055            data: &mut ftd::DataDependenciesMap,
1056            background_image: &Option<ImageSrc>,
1057        ) {
1058            let id = id.clone().expect("universal id should be present");
1059            if let Some(ref image_src) = background_image {
1060                image_condition(image_src, id.as_str(), data);
1061            }
1062
1063            fn image_condition(
1064                image_src: &ftd::ImageSrc,
1065                id: &str,
1066                data: &mut ftd::DataDependenciesMap,
1067            ) {
1068                let (reference, value) = if let Some(ref reference) = image_src.reference {
1069                    (
1070                        reference.to_string(),
1071                        serde_json::json!({ "light": format!("url({})", image_src.light),
1072                            "dark": format!("url({})", image_src.light),
1073                            "$kind$": "light"
1074                        }),
1075                    )
1076                } else {
1077                    return;
1078                };
1079
1080                let parameters = {
1081                    let mut parameters = ftd::Map::new();
1082                    parameters.insert(
1083                        "background-image".to_string(),
1084                        ftd::ConditionalValueWithDefault {
1085                            value: ConditionalValue {
1086                                value,
1087                                important: false,
1088                                reference: None,
1089                            },
1090                            default: None,
1091                        },
1092                    );
1093                    parameters
1094                };
1095
1096                let (variable, remaining) =
1097                    ftd::p2::utils::get_doc_name_and_remaining(&reference).unwrap();
1098
1099                if let Some(ftd::Data { dependencies, .. }) = data.get_mut(&variable) {
1100                    let json = ftd::Dependencies {
1101                        dependency_type: ftd::DependencyType::Style,
1102                        condition: None,
1103                        parameters,
1104                        remaining,
1105                    };
1106                    if let Some(dependencies) = dependencies.get_mut(id) {
1107                        let mut d = serde_json::from_value::<Vec<ftd::Dependencies>>(
1108                            dependencies.to_owned(),
1109                        )
1110                        .unwrap();
1111                        d.push(json);
1112                        *dependencies = serde_json::to_value(&d).unwrap();
1113                    } else {
1114                        dependencies
1115                            .insert(id.to_string(), serde_json::to_value(&vec![json]).unwrap());
1116                    }
1117                }
1118            }
1119        }
1120
1121        fn style_condition(
1122            conditional_attributes: &ftd::Map<ConditionalAttribute>,
1123            id: &Option<String>,
1124            data: &mut ftd::DataDependenciesMap,
1125        ) {
1126            for (k, v) in conditional_attributes {
1127                if let ftd::ConditionalAttribute {
1128                    attribute_type: ftd::AttributeType::Style,
1129                    conditions_with_value,
1130                    default,
1131                } = v
1132                {
1133                    for (condition, value) in conditions_with_value {
1134                        let id = id.clone().expect("universal id should be present");
1135                        let (variable, remaining) =
1136                            ftd::p2::utils::get_doc_name_and_remaining(&condition.variable)
1137                                .unwrap();
1138                        if let Some(ftd::Data { dependencies, .. }) = data.get_mut(&variable) {
1139                            let json = ftd::Dependencies {
1140                                dependency_type: ftd::DependencyType::Style,
1141                                condition: Some(condition.value.to_owned()),
1142                                parameters: std::iter::IntoIterator::into_iter([(
1143                                    k.to_string(),
1144                                    ftd::ConditionalValueWithDefault {
1145                                        value: value.clone(),
1146                                        default: default.clone(),
1147                                    },
1148                                )])
1149                                .collect(),
1150                                remaining,
1151                            };
1152                            if let Some(dependencies) = dependencies.get_mut(&id) {
1153                                let mut d = serde_json::from_value::<Vec<ftd::Dependencies>>(
1154                                    dependencies.to_owned(),
1155                                )
1156                                .unwrap();
1157                                d.push(json);
1158                                *dependencies = serde_json::to_value(&d).unwrap();
1159                            } else {
1160                                dependencies.insert(
1161                                    id.to_string(),
1162                                    serde_json::to_value(&vec![json]).unwrap(),
1163                                );
1164                            }
1165                        } else {
1166                            panic!("{} should be declared", condition.variable)
1167                        }
1168                        if let Some(ref reference) = value.reference {
1169                            let (variable, remaining) =
1170                                ftd::p2::utils::get_doc_name_and_remaining(reference).unwrap();
1171                            if let Some(ftd::Data { dependencies, .. }) = data.get_mut(&variable) {
1172                                let json = ftd::Dependencies {
1173                                    dependency_type: ftd::DependencyType::Variable,
1174                                    condition: None,
1175                                    remaining,
1176                                    parameters: std::iter::IntoIterator::into_iter([(
1177                                        k.to_string(),
1178                                        ftd::ConditionalValueWithDefault {
1179                                            value: ftd::ConditionalValue {
1180                                                value: serde_json::json!({ "$variable$": condition.variable, "$node$": id}),
1181                                                important: false,
1182                                                reference: None,
1183                                            },
1184                                            default: None,
1185                                        },
1186                                    )])
1187                                        .collect(),
1188                                };
1189                                if let Some(dependencies) = dependencies.get_mut("$style$") {
1190                                    let mut d = serde_json::from_value::<Vec<ftd::Dependencies>>(
1191                                        dependencies.to_owned(),
1192                                    )
1193                                    .unwrap();
1194                                    d.push(json);
1195                                    *dependencies = serde_json::to_value(&d).unwrap();
1196                                } else {
1197                                    dependencies.insert(
1198                                        "$style$".to_string(),
1199                                        serde_json::to_value(&vec![json]).unwrap(),
1200                                    );
1201                                }
1202                            }
1203                        }
1204                    }
1205                }
1206            }
1207        }
1208
1209        fn visibility_condition(
1210            condition: &Option<ftd::Condition>,
1211            id: &Option<String>,
1212            data: &mut ftd::DataDependenciesMap,
1213        ) {
1214            if let Some(condition) = condition {
1215                let id = id.clone().expect("universal id should be present");
1216                let (variable, remaining) =
1217                    ftd::p2::utils::get_doc_name_and_remaining(&condition.variable).unwrap();
1218                if let Some(ftd::Data { dependencies, .. }) = data.get_mut(&variable) {
1219                    let json = ftd::Dependencies {
1220                        dependency_type: ftd::DependencyType::Visible,
1221                        condition: Some(condition.value.to_owned()),
1222                        parameters: Default::default(),
1223                        remaining,
1224                    };
1225                    if let Some(dependencies) = dependencies.get_mut(&id) {
1226                        let mut d = serde_json::from_value::<Vec<ftd::Dependencies>>(
1227                            dependencies.to_owned(),
1228                        )
1229                        .unwrap();
1230                        d.push(json);
1231                        *dependencies = serde_json::to_value(&d).unwrap();
1232                    } else {
1233                        dependencies.insert(id, serde_json::to_value(&vec![json]).unwrap());
1234                    }
1235                } else {
1236                    panic!("{} should be declared 2", condition.variable)
1237                }
1238            }
1239        }
1240    }
1241
1242    pub fn get_device_dependencies(
1243        document: &ftd::p2::Document,
1244        data: &mut ftd::DataDependenciesMap,
1245    ) {
1246        let doc = ftd::p2::TDoc {
1247            name: document.name.as_str(),
1248            aliases: &document.aliases,
1249            bag: &document.data,
1250            local_variables: &mut Default::default(),
1251            referenced_local_variables: &mut Default::default(),
1252        };
1253        for (k, v) in document.data.iter() {
1254            if !data.contains_key(k) {
1255                continue;
1256            }
1257            let keys = if let ftd::p2::Thing::Variable(ftd::Variable { value, .. }) = v {
1258                get_ftd_type_variables(value, &doc, k)
1259            } else {
1260                continue;
1261            };
1262            let dependencies =
1263                if let Some(ftd::Data { dependencies, .. }) = data.get_mut("ftd#device") {
1264                    dependencies
1265                } else {
1266                    continue;
1267                };
1268            let mut device_json = vec![];
1269            for k in keys {
1270                let mobile_json = ftd::Dependencies {
1271                    dependency_type: ftd::DependencyType::Variable,
1272                    condition: Some(serde_json::Value::String("mobile".to_string())),
1273                    remaining: None,
1274                    parameters: std::iter::IntoIterator::into_iter([(
1275                        k.to_string(),
1276                        ftd::ConditionalValueWithDefault {
1277                            value: ConditionalValue {
1278                                value: serde_json::Value::String("mobile".to_string()),
1279                                important: false,
1280                                reference: None,
1281                            },
1282                            default: None,
1283                        },
1284                    )])
1285                    .collect(),
1286                };
1287
1288                let xl_json = ftd::Dependencies {
1289                    dependency_type: ftd::DependencyType::Variable,
1290                    condition: Some(serde_json::Value::String("xl".to_string())),
1291                    remaining: None,
1292                    parameters: std::iter::IntoIterator::into_iter([(
1293                        k.to_string(),
1294                        ftd::ConditionalValueWithDefault {
1295                            value: ConditionalValue {
1296                                value: serde_json::Value::String("xl".to_string()),
1297                                important: false,
1298                                reference: None,
1299                            },
1300                            default: None,
1301                        },
1302                    )])
1303                    .collect(),
1304                };
1305
1306                let desktop_json = ftd::Dependencies {
1307                    dependency_type: ftd::DependencyType::Variable,
1308                    condition: Some(serde_json::Value::String("desktop".to_string())),
1309                    remaining: None,
1310                    parameters: std::iter::IntoIterator::into_iter([(
1311                        k.to_string(),
1312                        ftd::ConditionalValueWithDefault {
1313                            value: ConditionalValue {
1314                                value: serde_json::Value::String("desktop".to_string()),
1315                                important: false,
1316                                reference: None,
1317                            },
1318                            default: None,
1319                        },
1320                    )])
1321                    .collect(),
1322                };
1323
1324                device_json.push(mobile_json);
1325                device_json.push(xl_json);
1326                device_json.push(desktop_json);
1327            }
1328
1329            if let Some(dependencies) = dependencies.get_mut("$value#kind$") {
1330                let mut d =
1331                    serde_json::from_value::<Vec<ftd::Dependencies>>(dependencies.to_owned())
1332                        .unwrap();
1333                d.extend(device_json);
1334                *dependencies = serde_json::to_value(&d).unwrap();
1335            } else {
1336                dependencies.insert(
1337                    "$value#kind$".to_string(),
1338                    serde_json::to_value(&device_json).unwrap(),
1339                );
1340            }
1341        }
1342
1343        fn get_ftd_type_variables(
1344            property_value: &ftd::PropertyValue,
1345            doc: &ftd::p2::TDoc,
1346            key: &str,
1347        ) -> Vec<String> {
1348            match property_value.kind() {
1349                ftd::p2::Kind::Record { name, .. } if ["ftd#type"].contains(&name.as_str()) => {
1350                    return vec![key.to_string()];
1351                }
1352                ftd::p2::Kind::Record { .. } => {
1353                    if let Ok(ftd::Value::Record { fields, .. }) = property_value.resolve(0, doc) {
1354                        let mut reference = vec![];
1355                        for (k, field) in fields.iter() {
1356                            reference.extend(get_ftd_type_variables(field, doc, k));
1357                        }
1358                        return reference
1359                            .into_iter()
1360                            .map(|v| format!("{}.{}", key, v))
1361                            .collect();
1362                    }
1363                }
1364                _ => {}
1365            }
1366            vec![]
1367        }
1368    }
1369
1370    pub fn get_dark_mode_dependencies(
1371        document: &ftd::p2::Document,
1372        data: &mut ftd::DataDependenciesMap,
1373    ) {
1374        let doc = ftd::p2::TDoc {
1375            name: document.name.as_str(),
1376            aliases: &document.aliases,
1377            bag: &document.data,
1378            local_variables: &mut Default::default(),
1379            referenced_local_variables: &mut Default::default(),
1380        };
1381        for (k, v) in document.data.iter() {
1382            if !data.contains_key(k) {
1383                continue;
1384            }
1385            let keys = if let ftd::p2::Thing::Variable(ftd::Variable { value, .. }) = v {
1386                get_ftd_type_variables(value, &doc, k)
1387            } else {
1388                continue;
1389            };
1390            let dependencies =
1391                if let Some(ftd::Data { dependencies, .. }) = data.get_mut("ftd#dark-mode") {
1392                    dependencies
1393                } else {
1394                    continue;
1395                };
1396            let dark_mode_json = keys
1397                .iter()
1398                .map(|k| ftd::Dependencies {
1399                    dependency_type: ftd::DependencyType::Variable,
1400                    condition: Some(serde_json::Value::Bool(true)),
1401                    remaining: None,
1402                    parameters: std::iter::IntoIterator::into_iter([(
1403                        k.to_string(),
1404                        ftd::ConditionalValueWithDefault {
1405                            value: ConditionalValue {
1406                                value: serde_json::Value::String("dark".to_string()),
1407                                important: false,
1408                                reference: None,
1409                            },
1410                            default: Some(ConditionalValue {
1411                                value: serde_json::Value::String("light".to_string()),
1412                                important: false,
1413                                reference: None,
1414                            }),
1415                        },
1416                    )])
1417                    .collect(),
1418                })
1419                .collect::<Vec<ftd::Dependencies>>();
1420
1421            if let Some(dependencies) = dependencies.get_mut("$value#kind$") {
1422                let mut d =
1423                    serde_json::from_value::<Vec<ftd::Dependencies>>(dependencies.to_owned())
1424                        .unwrap();
1425                d.extend(dark_mode_json);
1426                *dependencies = serde_json::to_value(&d).unwrap();
1427            } else {
1428                dependencies.insert(
1429                    "$value#kind$".to_string(),
1430                    serde_json::to_value(&dark_mode_json).unwrap(),
1431                );
1432            }
1433        }
1434
1435        fn get_ftd_type_variables(
1436            property_value: &ftd::PropertyValue,
1437            doc: &ftd::p2::TDoc,
1438            key: &str,
1439        ) -> Vec<String> {
1440            match property_value.kind() {
1441                ftd::p2::Kind::Record { name, .. }
1442                    if ["ftd#image-src", "ftd#color"].contains(&name.as_str()) =>
1443                {
1444                    return vec![key.to_string()];
1445                }
1446                ftd::p2::Kind::Record { .. } => {
1447                    if let Ok(ftd::Value::Record { fields, .. }) = property_value.resolve(0, doc) {
1448                        let mut reference = vec![];
1449                        for (k, field) in fields.iter() {
1450                            reference.extend(get_ftd_type_variables(field, doc, k));
1451                        }
1452                        return reference
1453                            .into_iter()
1454                            .map(|v| format!("{}.{}", key, v))
1455                            .collect();
1456                    }
1457                }
1458                _ => {}
1459            }
1460            vec![]
1461        }
1462    }
1463
1464    pub fn get_variable_dependencies(
1465        document: &ftd::p2::Document,
1466        data: &mut ftd::DataDependenciesMap,
1467    ) {
1468        let doc = ftd::p2::TDoc {
1469            name: document.name.as_str(),
1470            aliases: &document.aliases,
1471            bag: &document.data,
1472            local_variables: &mut Default::default(),
1473            referenced_local_variables: &mut Default::default(),
1474        };
1475        for (k, v) in document.data.iter() {
1476            if !data.contains_key(k) {
1477                continue;
1478            }
1479            let (conditions, default) = if let ftd::p2::Thing::Variable(ftd::Variable {
1480                conditions,
1481                value: default,
1482                ..
1483            }) = v
1484            {
1485                (conditions, default)
1486            } else {
1487                continue;
1488            };
1489            let default = match default.resolve(0, &doc) {
1490                Ok(v) => v,
1491                _ => continue,
1492            };
1493            for (condition, value) in conditions {
1494                let condition = if let Ok(condition) = condition.to_condition(
1495                    0,
1496                    &ftd::p2::TDoc {
1497                        name: document.name.as_str(),
1498                        aliases: &document.aliases,
1499                        bag: &document.data,
1500                        local_variables: &mut Default::default(),
1501                        referenced_local_variables: &mut Default::default(),
1502                    },
1503                ) {
1504                    condition
1505                } else {
1506                    continue;
1507                };
1508                let value = match value.resolve(0, &doc) {
1509                    Ok(value) => match value.to_serde_value() {
1510                        Some(v) => v,
1511                        None => continue,
1512                    },
1513                    _ => continue,
1514                };
1515
1516                let (variable, remaining) =
1517                    ftd::p2::utils::get_doc_name_and_remaining(&condition.variable).unwrap();
1518                let dependencies =
1519                    if let Some(ftd::Data { dependencies, .. }) = data.get_mut(&variable) {
1520                        dependencies
1521                    } else {
1522                        continue;
1523                    };
1524                let json = ftd::Dependencies {
1525                    dependency_type: ftd::DependencyType::Variable,
1526                    condition: Some(condition.value.to_owned()),
1527                    remaining,
1528                    parameters: std::iter::IntoIterator::into_iter([(
1529                        k.to_string(),
1530                        ftd::ConditionalValueWithDefault {
1531                            value: ConditionalValue {
1532                                value,
1533                                important: false,
1534                                reference: None,
1535                            },
1536                            default: default.to_serde_value().map(|value| ConditionalValue {
1537                                value,
1538                                important: false,
1539                                reference: None,
1540                            }),
1541                        },
1542                    )])
1543                    .collect(),
1544                };
1545                if let Some(dependencies) = dependencies.get_mut("$value$") {
1546                    let mut d =
1547                        serde_json::from_value::<Vec<ftd::Dependencies>>(dependencies.to_owned())
1548                            .unwrap();
1549                    d.push(json);
1550                    *dependencies = serde_json::to_value(&d).unwrap();
1551                } else {
1552                    dependencies.insert(
1553                        "$value$".to_string(),
1554                        serde_json::to_value(&vec![json]).unwrap(),
1555                    );
1556                }
1557            }
1558        }
1559    }
1560
1561    pub fn is_open_container(&self, is_container_children_empty: bool) -> bool {
1562        match self {
1563            ftd::Element::Column(e) => e.container.is_open(is_container_children_empty),
1564            ftd::Element::Row(e) => e.container.is_open(is_container_children_empty),
1565            ftd::Element::Scene(e) => e.container.is_open(is_container_children_empty),
1566            ftd::Element::Grid(e) => e.container.is_open(is_container_children_empty),
1567            _ => false,
1568        }
1569    }
1570
1571    pub fn append_at(&self) -> Option<String> {
1572        match self {
1573            ftd::Element::Column(e) => e.container.append_at.to_owned(),
1574            ftd::Element::Row(e) => e.container.append_at.to_owned(),
1575            ftd::Element::Scene(e) => e.container.append_at.to_owned(),
1576            ftd::Element::Grid(e) => e.container.append_at.to_owned(),
1577            _ => None,
1578        }
1579    }
1580
1581    pub fn number_of_children(&self) -> usize {
1582        match self {
1583            ftd::Element::Column(e) => e.container.children.len(),
1584            ftd::Element::Row(e) => e.container.children.len(),
1585            ftd::Element::Scene(e) => e.container.children.len(),
1586            ftd::Element::Grid(e) => e.container.children.len(),
1587            _ => 0,
1588        }
1589    }
1590
1591    pub fn container_id(&self) -> Option<String> {
1592        match self {
1593            ftd::Element::Column(e) => e.common.data_id.clone(),
1594            ftd::Element::Row(e) => e.common.data_id.clone(),
1595            ftd::Element::Scene(e) => e.common.data_id.clone(),
1596            ftd::Element::Grid(e) => e.common.data_id.clone(),
1597            _ => None,
1598        }
1599    }
1600
1601    pub fn set_container_id(&mut self, name: Option<String>) {
1602        match self {
1603            ftd::Element::Column(e) => e.common.data_id = name,
1604            ftd::Element::Row(e) => e.common.data_id = name,
1605            ftd::Element::Scene(e) => e.common.data_id = name,
1606            ftd::Element::Grid(e) => e.common.data_id = name,
1607            _ => {}
1608        }
1609    }
1610
1611    pub fn set_element_id(&mut self, name: Option<String>) {
1612        match self {
1613            ftd::Element::Column(ftd::Column { common, .. })
1614            | ftd::Element::Row(ftd::Row { common, .. })
1615            | ftd::Element::TextBlock(ftd::TextBlock { common, .. })
1616            | ftd::Element::Code(ftd::Code { common, .. })
1617            | ftd::Element::Image(ftd::Image { common, .. })
1618            | ftd::Element::IFrame(ftd::IFrame { common, .. })
1619            | ftd::Element::Markup(ftd::Markups { common, .. })
1620            | ftd::Element::Input(ftd::Input { common, .. })
1621            | ftd::Element::Integer(ftd::Text { common, .. })
1622            | ftd::Element::Boolean(ftd::Text { common, .. })
1623            | ftd::Element::Decimal(ftd::Text { common, .. })
1624            | ftd::Element::Scene(ftd::Scene { common, .. })
1625            | ftd::Element::Grid(ftd::Grid { common, .. }) => common.id = name,
1626            ftd::Element::Null => {}
1627        }
1628    }
1629
1630    pub fn set_condition(&mut self, condition: Option<ftd::Condition>) {
1631        match self {
1632            ftd::Element::Column(ftd::Column { common, .. })
1633            | ftd::Element::Row(ftd::Row { common, .. })
1634            | ftd::Element::TextBlock(ftd::TextBlock { common, .. })
1635            | ftd::Element::Code(ftd::Code { common, .. })
1636            | ftd::Element::Image(ftd::Image { common, .. })
1637            | ftd::Element::IFrame(ftd::IFrame { common, .. })
1638            | ftd::Element::Markup(ftd::Markups { common, .. })
1639            | ftd::Element::Input(ftd::Input { common, .. })
1640            | ftd::Element::Integer(ftd::Text { common, .. })
1641            | ftd::Element::Boolean(ftd::Text { common, .. })
1642            | ftd::Element::Decimal(ftd::Text { common, .. })
1643            | ftd::Element::Scene(ftd::Scene { common, .. })
1644            | ftd::Element::Grid(ftd::Grid { common, .. }) => common,
1645            ftd::Element::Null => return,
1646        }
1647        .condition = condition;
1648    }
1649
1650    pub fn set_non_visibility(&mut self, is_not_visible: bool) {
1651        match self {
1652            ftd::Element::Column(ftd::Column { common, .. })
1653            | ftd::Element::Row(ftd::Row { common, .. })
1654            | ftd::Element::TextBlock(ftd::TextBlock { common, .. })
1655            | ftd::Element::Code(ftd::Code { common, .. })
1656            | ftd::Element::Image(ftd::Image { common, .. })
1657            | ftd::Element::IFrame(ftd::IFrame { common, .. })
1658            | ftd::Element::Markup(ftd::Markups { common, .. })
1659            | ftd::Element::Input(ftd::Input { common, .. })
1660            | ftd::Element::Integer(ftd::Text { common, .. })
1661            | ftd::Element::Boolean(ftd::Text { common, .. })
1662            | ftd::Element::Decimal(ftd::Text { common, .. })
1663            | ftd::Element::Scene(ftd::Scene { common, .. })
1664            | ftd::Element::Grid(ftd::Grid { common, .. }) => common,
1665            ftd::Element::Null => return,
1666        }
1667        .is_not_visible = is_not_visible;
1668    }
1669
1670    pub fn set_events(&mut self, events: &mut Vec<ftd::Event>) {
1671        match self {
1672            ftd::Element::Column(ftd::Column { common, .. })
1673            | ftd::Element::Row(ftd::Row { common, .. })
1674            | ftd::Element::TextBlock(ftd::TextBlock { common, .. })
1675            | ftd::Element::Code(ftd::Code { common, .. })
1676            | ftd::Element::Image(ftd::Image { common, .. })
1677            | ftd::Element::IFrame(ftd::IFrame { common, .. })
1678            | ftd::Element::Markup(ftd::Markups { common, .. })
1679            | ftd::Element::Input(ftd::Input { common, .. })
1680            | ftd::Element::Integer(ftd::Text { common, .. })
1681            | ftd::Element::Boolean(ftd::Text { common, .. })
1682            | ftd::Element::Decimal(ftd::Text { common, .. })
1683            | ftd::Element::Scene(ftd::Scene { common, .. })
1684            | ftd::Element::Grid(ftd::Grid { common, .. }) => common,
1685            ftd::Element::Null => return,
1686        }
1687        .events
1688        .append(events)
1689    }
1690
1691    pub fn get_heading_region(&self) -> Option<&ftd::Region> {
1692        match self {
1693            ftd::Element::Column(e) => e.common.region.as_ref().filter(|v| v.is_heading()),
1694            ftd::Element::Row(e) => e.common.region.as_ref().filter(|v| v.is_heading()),
1695            _ => None,
1696        }
1697    }
1698
1699    pub fn get_mut_common(&mut self) -> Option<&mut ftd::Common> {
1700        match self {
1701            ftd::Element::Column(e) => Some(&mut e.common),
1702            ftd::Element::Row(e) => Some(&mut e.common),
1703            ftd::Element::Markup(e) => Some(&mut e.common),
1704            ftd::Element::TextBlock(e) => Some(&mut e.common),
1705            ftd::Element::Code(e) => Some(&mut e.common),
1706            ftd::Element::Image(e) => Some(&mut e.common),
1707            ftd::Element::IFrame(e) => Some(&mut e.common),
1708            ftd::Element::Input(e) => Some(&mut e.common),
1709            ftd::Element::Integer(e) => Some(&mut e.common),
1710            ftd::Element::Boolean(e) => Some(&mut e.common),
1711            ftd::Element::Decimal(e) => Some(&mut e.common),
1712            ftd::Element::Scene(e) => Some(&mut e.common),
1713            ftd::Element::Grid(e) => Some(&mut e.common),
1714            ftd::Element::Null => None,
1715        }
1716    }
1717
1718    pub fn get_common(&self) -> Option<&ftd::Common> {
1719        match self {
1720            ftd::Element::Column(e) => Some(&e.common),
1721            ftd::Element::Row(e) => Some(&e.common),
1722            ftd::Element::Markup(e) => Some(&e.common),
1723            ftd::Element::TextBlock(e) => Some(&e.common),
1724            ftd::Element::Code(e) => Some(&e.common),
1725            ftd::Element::Image(e) => Some(&e.common),
1726            ftd::Element::IFrame(e) => Some(&e.common),
1727            ftd::Element::Input(e) => Some(&e.common),
1728            ftd::Element::Integer(e) => Some(&e.common),
1729            ftd::Element::Boolean(e) => Some(&e.common),
1730            ftd::Element::Decimal(e) => Some(&e.common),
1731            ftd::Element::Scene(e) => Some(&e.common),
1732            ftd::Element::Grid(e) => Some(&e.common),
1733            ftd::Element::Null => None,
1734        }
1735    }
1736
1737    pub fn get_container(&self) -> Option<&ftd::Container> {
1738        match self {
1739            ftd::Element::Column(e) => Some(&e.container),
1740            ftd::Element::Row(e) => Some(&e.container),
1741            ftd::Element::Scene(e) => Some(&e.container),
1742            ftd::Element::Grid(e) => Some(&e.container),
1743            _ => None,
1744        }
1745    }
1746
1747    pub fn renest_on_region(elements: &mut Vec<ftd::Element>) {
1748        let mut region: Option<(usize, &Region)> = None;
1749        let mut insert: Vec<(usize, usize)> = Default::default();
1750        for (idx, element) in elements.iter().enumerate() {
1751            match element {
1752                ftd::Element::Column(ftd::Column { common, .. })
1753                | ftd::Element::Row(ftd::Row { common, .. }) => {
1754                    let r = common.region.as_ref().filter(|v| v.is_heading());
1755                    if let Some(r) = r {
1756                        if let Some((place_at, r1)) = region {
1757                            if r.get_lower_priority_heading().contains(r1) || r == r1 {
1758                                insert.push((place_at, idx));
1759                                region = Some((idx, r));
1760                            }
1761                        } else {
1762                            region = Some((idx, r));
1763                        }
1764                    }
1765                }
1766                _ => continue,
1767            }
1768        }
1769        if let Some((place_at, _)) = region {
1770            insert.push((place_at, elements.len()));
1771        }
1772
1773        for (place_at, end) in insert.iter().rev() {
1774            let mut children = elements[place_at + 1..*end].to_vec();
1775            if children.is_empty() {
1776                continue;
1777            }
1778            match elements[*place_at] {
1779                ftd::Element::Column(ftd::Column {
1780                    ref mut container, ..
1781                })
1782                | ftd::Element::Row(ftd::Row {
1783                    ref mut container, ..
1784                }) => {
1785                    if let Some(ref id) = container.append_at {
1786                        match &mut container.external_children {
1787                            Some((_, _, e)) => {
1788                                if let Some(ftd::Element::Column(col)) = e.first_mut() {
1789                                    col.container.children.extend(children);
1790                                } else {
1791                                    let mut main = ftd::p2::interpreter::default_column();
1792                                    main.container.children.extend(children);
1793                                    e.push(ftd::Element::Column(main))
1794                                }
1795                            }
1796                            _ => panic!("{} has no external_children data", id),
1797                        }
1798                    } else {
1799                        container.children.append(&mut children);
1800                    }
1801                }
1802                _ => continue,
1803            }
1804            for idx in (place_at + 1..*end).rev() {
1805                elements.remove(idx);
1806            }
1807        }
1808
1809        for element in &mut *elements {
1810            match element {
1811                ftd::Element::Column(ftd::Column {
1812                    ref mut container, ..
1813                })
1814                | ftd::Element::Row(ftd::Row {
1815                    ref mut container, ..
1816                }) => {
1817                    if let Some((_, _, ref mut e)) = container.external_children {
1818                        ftd::Element::renest_on_region(e);
1819                    }
1820                    ftd::Element::renest_on_region(&mut container.children);
1821                }
1822                _ => continue,
1823            }
1824        }
1825    }
1826}
1827
1828#[derive(serde::Deserialize, PartialEq, Debug, Clone, serde::Serialize)]
1829#[serde(tag = "type")]
1830pub enum Length {
1831    Fill,
1832    Shrink,
1833    Auto,
1834    FitContent,
1835    Px { value: i64 },
1836    Portion { value: i64 },
1837    Percent { value: i64 },
1838    Calc { value: String },
1839    VH { value: i64 },
1840    VW { value: i64 },
1841}
1842
1843impl Length {
1844    pub fn from(l: Option<String>, doc_id: &str) -> ftd::p1::Result<Option<ftd::Length>> {
1845        let l = match l {
1846            Some(l) => l,
1847            None => return Ok(None),
1848        };
1849
1850        if l == "fill" {
1851            return Ok(Some(Length::Fill));
1852        }
1853
1854        if l == "shrink" {
1855            return Ok(Some(Length::Shrink));
1856        }
1857        if l == "auto" {
1858            return Ok(Some(Length::Auto));
1859        }
1860
1861        if l.starts_with("calc ") {
1862            let v = ftd::p2::utils::get_name("calc", l.as_str(), doc_id)?;
1863            return match v.parse() {
1864                Ok(v) => Ok(Some(Length::Calc { value: v })),
1865                Err(_) => ftd::p2::utils::e2(format!("{} is not a valid integer", v), doc_id, 0), // TODO
1866            };
1867        }
1868
1869        if l == "fit-content" {
1870            return Ok(Some(Length::FitContent));
1871        }
1872
1873        if l.starts_with("portion ") {
1874            let v = ftd::p2::utils::get_name("portion", l.as_str(), doc_id)?;
1875            return match v.parse() {
1876                Ok(v) => Ok(Some(Length::Portion { value: v })),
1877                Err(_) => ftd::p2::utils::e2(format!("{} is not a valid integer", v), doc_id, 0), // TODO
1878            };
1879        }
1880        if l.starts_with("percent ") {
1881            let v = ftd::p2::utils::get_name("percent", l.as_str(), doc_id)?;
1882            return match v.parse() {
1883                Ok(v) => Ok(Some(Length::Percent { value: v })),
1884                Err(_) => ftd::p2::utils::e2(format!("{} is not a valid integer", v), doc_id, 0), // TODO
1885            };
1886        }
1887        if l.starts_with("vh ") {
1888            let v = ftd::p2::utils::get_name("vh", l.as_str(), doc_id)?;
1889            return match v.parse() {
1890                Ok(v) => Ok(Some(Length::VH { value: v })),
1891                Err(_) => ftd::p2::utils::e2(format!("{} is not a valid integer", v), doc_id, 0), // TODO
1892            };
1893        }
1894        if l.starts_with("vw ") {
1895            let v = ftd::p2::utils::get_name("vw", l.as_str(), doc_id)?;
1896            return match v.parse() {
1897                Ok(v) => Ok(Some(Length::VW { value: v })),
1898                Err(_) => ftd::p2::utils::e2(format!("{} is not a valid integer", v), doc_id, 0), // TODO
1899            };
1900        }
1901
1902        match l.parse() {
1903            Ok(v) => Ok(Some(Length::Px { value: v })),
1904            Err(_) => ftd::p2::utils::e2(format!("{} is not a valid integer", l), doc_id, 0),
1905        }
1906    }
1907}
1908
1909#[derive(serde::Deserialize, Debug, PartialEq, Clone, serde::Serialize)]
1910#[serde(tag = "type")]
1911pub enum Position {
1912    Center,
1913    Top,
1914    Bottom,
1915    Left,
1916    Right,
1917    TopLeft,
1918    TopRight,
1919    BottomLeft,
1920    BottomRight,
1921}
1922
1923impl Default for Position {
1924    fn default() -> ftd::Position {
1925        Self::TopLeft
1926    }
1927}
1928
1929impl Position {
1930    pub fn from(l: Option<String>, doc_id: &str) -> ftd::p1::Result<Option<ftd::Position>> {
1931        Ok(match l.as_deref() {
1932            Some("center") => Some(Self::Center),
1933            Some("top") => Some(Self::Top),
1934            Some("bottom") => Some(Self::Bottom),
1935            Some("left") => Some(Self::Left),
1936            Some("right") => Some(Self::Right),
1937            Some("top-left") => Some(Self::TopLeft),
1938            Some("top-right") => Some(Self::TopRight),
1939            Some("bottom-left") => Some(Self::BottomLeft),
1940            Some("bottom-right") => Some(Self::BottomRight),
1941            Some(t) => {
1942                return ftd::p2::utils::e2(format!("{} is not a valid alignment", t), doc_id, 0)
1943            } // TODO
1944            None => None,
1945        })
1946    }
1947}
1948
1949// https://package.elm-lang.org/packages/mdgriffith/elm-ui/latest/Element-Region
1950#[derive(serde::Deserialize, Debug, PartialEq, Clone, serde::Serialize)]
1951pub enum Region {
1952    H0,
1953    H1,
1954    H2,
1955    H3,
1956    H4,
1957    H5,
1958    H6,
1959    H7,
1960    Title,
1961    MainContent,
1962    Navigation,
1963    Aside,
1964    Footer,
1965    Description,
1966    Announce,
1967    AnnounceUrgently,
1968}
1969
1970impl ToString for Region {
1971    fn to_string(&self) -> String {
1972        match self {
1973            Self::H0 => "h0",
1974            Self::H1 => "h1",
1975            Self::H2 => "h2",
1976            Self::H3 => "h3",
1977            Self::H4 => "h4",
1978            Self::H5 => "h5",
1979            Self::H6 => "h6",
1980            Self::H7 => "h7",
1981            Self::Title => "title",
1982            Self::MainContent => "main",
1983            Self::Navigation => "navigation",
1984            Self::Aside => "aside",
1985            Self::Footer => "footer",
1986            Self::Description => "description",
1987            Self::Announce => "announce",
1988            Self::AnnounceUrgently => "announce-urgently",
1989        }
1990        .to_string()
1991    }
1992}
1993
1994impl Region {
1995    pub fn from(l: Option<String>, doc_id: &str) -> ftd::p1::Result<Option<ftd::Region>> {
1996        Ok(Some(match l.as_deref() {
1997            Some("h0") => Self::H0,
1998            Some("h1") => Self::H1,
1999            Some("h2") => Self::H2,
2000            Some("h3") => Self::H3,
2001            Some("h4") => Self::H4,
2002            Some("h5") => Self::H5,
2003            Some("h6") => Self::H6,
2004            Some("h7") => Self::H7,
2005            Some("title") => Self::Title,
2006            Some("main") => Self::MainContent,
2007            Some("navigation") => Self::Navigation,
2008            Some("aside") => Self::Aside,
2009            Some("footer") => Self::Footer,
2010            Some("description") => Self::Description,
2011            Some("announce") => Self::Announce,
2012            Some("announce-urgently") => Self::AnnounceUrgently,
2013            Some(t) => {
2014                return ftd::p2::utils::e2(format!("{} is not a valid alignment", t), doc_id, 0)
2015            } // TODO
2016            None => return Ok(None),
2017        }))
2018    }
2019
2020    pub fn is_heading(&self) -> bool {
2021        matches!(
2022            self,
2023            ftd::Region::H0
2024                | ftd::Region::H1
2025                | ftd::Region::H2
2026                | ftd::Region::H3
2027                | ftd::Region::H4
2028                | ftd::Region::H5
2029                | ftd::Region::H6
2030                | ftd::Region::H7
2031        )
2032    }
2033
2034    pub fn is_primary_heading(&self) -> bool {
2035        matches!(self, ftd::Region::H0 | ftd::Region::H1)
2036    }
2037
2038    pub fn is_title(&self) -> bool {
2039        matches!(self, ftd::Region::Title)
2040    }
2041
2042    pub fn get_lower_priority_heading(&self) -> Vec<ftd::Region> {
2043        let mut list = vec![];
2044        if matches!(
2045            self,
2046            ftd::Region::Title
2047                | ftd::Region::MainContent
2048                | ftd::Region::Navigation
2049                | ftd::Region::Aside
2050                | ftd::Region::Footer
2051                | ftd::Region::Description
2052                | ftd::Region::Announce
2053                | ftd::Region::AnnounceUrgently
2054        ) {
2055            return list;
2056        }
2057        for region in [
2058            ftd::Region::H7,
2059            ftd::Region::H6,
2060            ftd::Region::H5,
2061            ftd::Region::H4,
2062            ftd::Region::H3,
2063            ftd::Region::H2,
2064            ftd::Region::H1,
2065        ] {
2066            if self.to_string() == region.to_string() {
2067                return list;
2068            }
2069            list.push(region);
2070        }
2071        list
2072    }
2073}
2074
2075#[derive(serde::Deserialize, Debug, PartialEq, Clone, serde::Serialize)]
2076#[serde(tag = "type")]
2077pub enum Overflow {
2078    Hidden,
2079    Visible,
2080    Auto,
2081    Scroll,
2082}
2083
2084impl Overflow {
2085    pub fn from(l: Option<String>, doc_id: &str) -> ftd::p1::Result<Option<ftd::Overflow>> {
2086        Ok(Option::from(match l.as_deref() {
2087            Some("hidden") => Self::Hidden,
2088            Some("visible") => Self::Visible,
2089            Some("auto") => Self::Auto,
2090            Some("scroll") => Self::Scroll,
2091            Some(t) => {
2092                return ftd::p2::utils::e2(format!("{} is not a valid property", t), doc_id, 0)
2093            } // TODO
2094            None => return Ok(None),
2095        }))
2096    }
2097}
2098
2099#[derive(serde::Deserialize, Debug, PartialEq, Clone, serde::Serialize)]
2100#[serde(tag = "type")]
2101pub enum Anchor {
2102    Window,
2103    Parent,
2104}
2105
2106impl Anchor {
2107    pub fn from(l: Option<String>, doc_id: &str) -> ftd::p1::Result<Option<ftd::Anchor>> {
2108        let l = match l {
2109            Some(l) => l,
2110            None => return Ok(None),
2111        };
2112
2113        Ok(Some(match l.as_str() {
2114            "window" => ftd::Anchor::Window,
2115            "parent" => ftd::Anchor::Parent,
2116            t => {
2117                return ftd::p2::utils::e2(
2118                    format!(
2119                        "invalid value for `absolute` expected `window` or `parent` found: {}",
2120                        t
2121                    ),
2122                    doc_id,
2123                    0, // TODO
2124                );
2125            }
2126        }))
2127    }
2128
2129    pub fn to_position(&self) -> String {
2130        match self {
2131            ftd::Anchor::Window => "fixed",
2132            ftd::Anchor::Parent => "absolute",
2133        }
2134        .to_string()
2135    }
2136}
2137
2138#[derive(serde::Deserialize, Debug, PartialEq, Clone, serde::Serialize)]
2139#[serde(tag = "type")]
2140pub enum GradientDirection {
2141    BottomToTop,
2142    TopToBottom,
2143    RightToLeft,
2144    LeftToRight,
2145    BottomRightToTopLeft,
2146    BottomLeftToTopRight,
2147    TopRightToBottomLeft,
2148    TopLeftBottomRight,
2149    Center,
2150    Angle { value: i64 },
2151}
2152
2153impl GradientDirection {
2154    pub fn from(
2155        l: Option<String>,
2156        doc_id: &str,
2157    ) -> ftd::p1::Result<Option<ftd::GradientDirection>> {
2158        let l = match l {
2159            Some(l) => l,
2160            None => return Ok(None),
2161        };
2162
2163        if l == "bottom to top" {
2164            return Ok(Some(GradientDirection::BottomToTop));
2165        }
2166        if l == "top to bottom" {
2167            return Ok(Some(GradientDirection::TopToBottom));
2168        }
2169        if l == "right to left" {
2170            return Ok(Some(GradientDirection::RightToLeft));
2171        }
2172        if l == "left to right" {
2173            return Ok(Some(GradientDirection::LeftToRight));
2174        }
2175        if l == "bottom-left to top-right" {
2176            return Ok(Some(GradientDirection::BottomLeftToTopRight));
2177        }
2178        if l == "bottom-right to top-left" {
2179            return Ok(Some(GradientDirection::BottomRightToTopLeft));
2180        }
2181        if l == "top-right to bottom-left" {
2182            return Ok(Some(GradientDirection::TopRightToBottomLeft));
2183        }
2184        if l == "top-left to bottom-right" {
2185            return Ok(Some(GradientDirection::TopLeftBottomRight));
2186        }
2187        if l == "center" {
2188            return Ok(Some(GradientDirection::Center));
2189        }
2190        if l.starts_with("angle ") {
2191            let v = ftd::p2::utils::get_name("angle", l.as_str(), doc_id)?;
2192            return match v.parse() {
2193                Ok(v) => Ok(Some(GradientDirection::Angle { value: v })),
2194                Err(_) => ftd::p2::utils::e2(format!("{} is not a valid integer", v), doc_id, 0),
2195            };
2196        }
2197        Ok(None)
2198    }
2199}
2200
2201#[derive(serde::Deserialize, Debug, PartialEq, Clone, serde::Serialize)]
2202pub enum AttributeType {
2203    Style,
2204    Attribute,
2205}
2206
2207#[derive(serde::Deserialize, Debug, PartialEq, Clone, serde::Serialize)]
2208pub struct ConditionalAttribute {
2209    pub attribute_type: AttributeType,
2210    pub conditions_with_value: Vec<(ftd::Condition, ConditionalValue)>,
2211    pub default: Option<ConditionalValue>,
2212}
2213
2214#[derive(serde::Deserialize, Debug, PartialEq, Clone, serde::Serialize, Default)]
2215pub struct ConditionalValue {
2216    pub value: serde_json::Value,
2217    pub important: bool,
2218    pub reference: Option<String>,
2219}
2220
2221#[derive(serde::Deserialize, Debug, PartialEq, Default, Clone, serde::Serialize)]
2222pub struct Common {
2223    pub conditional_attribute: ftd::Map<ConditionalAttribute>,
2224    pub condition: Option<ftd::Condition>,
2225    pub is_not_visible: bool,
2226    pub is_dummy: bool,
2227    pub events: Vec<ftd::Event>,
2228    pub reference: Option<String>,
2229    pub region: Option<Region>,
2230    pub padding: Option<i64>,
2231    pub padding_vertical: Option<i64>,
2232    pub padding_horizontal: Option<i64>,
2233    pub padding_left: Option<i64>,
2234    pub padding_right: Option<i64>,
2235    pub padding_top: Option<i64>,
2236    pub padding_bottom: Option<i64>,
2237    pub border_top_radius: Option<i64>,
2238    pub border_bottom_radius: Option<i64>,
2239    pub border_left_radius: Option<i64>,
2240    pub border_right_radius: Option<i64>,
2241    pub width: Option<Length>,
2242    pub max_width: Option<Length>,
2243    pub min_width: Option<Length>,
2244    pub height: Option<Length>,
2245    pub min_height: Option<Length>,
2246    pub max_height: Option<Length>,
2247    pub color: Option<Color>,
2248    pub background_color: Option<Color>,
2249    pub border_color: Option<Color>,
2250    pub border_width: i64,
2251    pub border_radius: i64,
2252    pub id: Option<String>,
2253    pub data_id: Option<String>,
2254    pub overflow_x: Option<Overflow>,
2255    pub overflow_y: Option<Overflow>,
2256    pub border_top: Option<i64>,
2257    pub border_left: Option<i64>,
2258    pub border_right: Option<i64>,
2259    pub border_bottom: Option<i64>,
2260    pub border_top_color: Option<Color>,
2261    pub border_bottom_color: Option<Color>,
2262    pub border_left_color: Option<Color>,
2263    pub border_right_color: Option<Color>,
2264    pub margin_top: Option<i64>,
2265    pub margin_left: Option<i64>,
2266    pub margin_right: Option<i64>,
2267    pub margin_bottom: Option<i64>,
2268    pub link: Option<String>,
2269    pub open_in_new_tab: bool,
2270    pub sticky: bool,
2271    pub top: Option<i64>,
2272    pub bottom: Option<i64>,
2273    pub left: Option<i64>,
2274    pub right: Option<i64>,
2275    pub submit: Option<String>,
2276    pub cursor: Option<String>,
2277    pub shadow_offset_x: Option<i64>,
2278    pub shadow_offset_y: Option<i64>,
2279    pub shadow_size: Option<i64>,
2280    pub shadow_blur: Option<i64>,
2281    pub shadow_color: Option<Color>,
2282    pub anchor: Option<ftd::Anchor>,
2283    pub gradient_direction: Option<GradientDirection>,
2284    pub gradient_colors: Vec<ColorValue>,
2285    pub background_image: Option<ImageSrc>,
2286    pub background_repeat: bool,
2287    pub background_parallax: bool,
2288    pub scale: Option<f64>,
2289    pub scale_x: Option<f64>,
2290    pub scale_y: Option<f64>,
2291    pub rotate: Option<i64>,
2292    pub move_up: Option<i64>,
2293    pub move_down: Option<i64>,
2294    pub move_left: Option<i64>,
2295    pub move_right: Option<i64>,
2296    pub position: Option<Position>,
2297    pub inner: bool,
2298    pub z_index: Option<i64>,
2299    pub slot: Option<String>,
2300    pub grid_column: Option<String>,
2301    pub grid_row: Option<String>,
2302    pub white_space: Option<String>,
2303    pub border_style: Option<String>,
2304    pub text_transform: Option<String>,
2305    pub title: Option<String>,
2306    // TODO: background-image, un-cropped, tiled, tiled{X, Y}
2307    // TODO: border-style: solid, dashed, dotted
2308    // TODO: border-{shadow, glow}
2309}
2310
2311#[derive(serde::Deserialize, Debug, PartialEq, Clone, serde::Serialize)]
2312#[serde(tag = "type")]
2313pub enum Spacing {
2314    SpaceEvenly,
2315    SpaceBetween,
2316    SpaceAround,
2317    Absolute { value: String },
2318}
2319
2320impl Spacing {
2321    pub fn from(l: Option<String>) -> ftd::p1::Result<Option<ftd::Spacing>> {
2322        Ok(match l.as_deref() {
2323            Some("space-evenly") => Some(ftd::Spacing::SpaceEvenly),
2324            Some("space-between") => Some(ftd::Spacing::SpaceBetween),
2325            Some("space-around") => Some(ftd::Spacing::SpaceAround),
2326            Some(t) => Some(ftd::Spacing::Absolute {
2327                value: t.to_string(),
2328            }),
2329            None => return Ok(None),
2330        })
2331    }
2332}
2333
2334#[derive(serde::Deserialize, Debug, PartialEq, Default, Clone, serde::Serialize)]
2335pub struct Container {
2336    pub children: Vec<ftd::Element>,
2337    pub external_children: Option<(String, Vec<Vec<usize>>, Vec<ftd::Element>)>,
2338    pub open: Option<bool>,
2339    pub append_at: Option<String>,
2340    pub wrap: bool,
2341}
2342
2343impl Container {
2344    pub fn is_open(&self, is_container_children_empty: bool) -> bool {
2345        self.open
2346            .unwrap_or(self.children.is_empty() && is_container_children_empty)
2347    }
2348}
2349
2350/// https://html.spec.whatwg.org/multipage/urls-and-fetching.html#lazy-loading-attributes
2351#[derive(serde::Deserialize, Debug, PartialEq, Clone, serde::Serialize)]
2352#[serde(tag = "type")]
2353pub enum Loading {
2354    Lazy,
2355    Eager,
2356}
2357
2358impl Default for Loading {
2359    fn default() -> Self {
2360        Loading::Lazy
2361    }
2362}
2363
2364impl Loading {
2365    pub fn from(s: &str, doc_id: &str) -> ftd::p1::Result<Loading> {
2366        match s {
2367            "lazy" => Ok(Loading::Lazy),
2368            "eager" => Ok(Loading::Eager),
2369            _ => ftd::p2::utils::e2(
2370                format!("{} is not a valid alignment, allowed: lazy, eager", s),
2371                doc_id,
2372                0,
2373            ),
2374        }
2375    }
2376
2377    pub fn to_html(&self) -> &'static str {
2378        match self {
2379            Loading::Lazy => "lazy",
2380            Loading::Eager => "eager",
2381        }
2382    }
2383}
2384
2385#[derive(serde::Deserialize, Debug, Default, PartialEq, Clone, serde::Serialize)]
2386pub struct Image {
2387    pub src: ImageSrc,
2388    pub description: Option<String>,
2389    pub common: Common,
2390    pub crop: bool,
2391    /// images can load lazily.
2392    pub loading: Loading,
2393}
2394
2395#[derive(serde::Deserialize, Debug, Default, PartialEq, Clone, serde::Serialize)]
2396pub struct Row {
2397    pub container: Container,
2398    pub spacing: Option<Spacing>,
2399    pub common: Common,
2400}
2401
2402#[derive(serde::Deserialize, Debug, Default, PartialEq, Clone, serde::Serialize)]
2403pub struct Scene {
2404    pub container: Container,
2405    pub spacing: Option<Spacing>,
2406    pub common: Common,
2407}
2408
2409#[derive(serde::Deserialize, Debug, Default, PartialEq, Clone, serde::Serialize)]
2410pub struct Grid {
2411    pub slots: String,
2412    pub slot_widths: Option<String>,
2413    pub slot_heights: Option<String>,
2414    pub spacing: Option<i64>,
2415    pub spacing_vertical: Option<i64>,
2416    pub spacing_horizontal: Option<i64>,
2417    pub inline: bool,
2418    pub auto_flow: Option<String>,
2419    pub container: Container,
2420    pub common: Common,
2421}
2422
2423#[derive(serde::Deserialize, Debug, PartialEq, Clone, Default, serde::Serialize)]
2424pub struct Column {
2425    pub container: Container,
2426    pub spacing: Option<Spacing>,
2427    pub common: Common,
2428}
2429
2430#[derive(serde::Deserialize, Debug, PartialEq, Clone, serde::Serialize)]
2431#[serde(tag = "type")]
2432pub enum TextAlign {
2433    Left,
2434    Right,
2435    Center,
2436    Justify,
2437}
2438
2439impl Default for TextAlign {
2440    fn default() -> Self {
2441        ftd::TextAlign::Left
2442    }
2443}
2444
2445impl TextAlign {
2446    pub fn from(l: Option<String>, doc_id: &str) -> ftd::p1::Result<ftd::TextAlign> {
2447        Ok(match l.as_deref() {
2448            Some("center") => ftd::TextAlign::Center,
2449            Some("left") => ftd::TextAlign::Left,
2450            Some("right") => ftd::TextAlign::Right,
2451            Some("justify") => ftd::TextAlign::Justify,
2452            Some(t) => {
2453                return ftd::p2::utils::e2(
2454                    format!(
2455                        "{} is not a valid alignment, allowed: center, left, right, justify",
2456                        t
2457                    ),
2458                    doc_id,
2459                    0,
2460                )
2461            }
2462            None => return Ok(ftd::TextAlign::Left),
2463        })
2464    }
2465}
2466
2467#[derive(serde::Deserialize, Debug, PartialEq, Clone, serde::Serialize)]
2468#[serde(tag = "type")]
2469pub enum FontDisplay {
2470    Swap,
2471    Block,
2472}
2473impl Default for ftd::FontDisplay {
2474    fn default() -> Self {
2475        ftd::FontDisplay::Block
2476    }
2477}
2478
2479impl FontDisplay {
2480    pub fn from(l: Option<String>, doc_id: &str) -> ftd::p1::Result<ftd::FontDisplay> {
2481        Ok(match l.as_deref() {
2482            Some("swap") => ftd::FontDisplay::Swap,
2483            Some("block") => ftd::FontDisplay::Block,
2484            Some(t) => {
2485                return ftd::p2::utils::e2(
2486                    format!("{} is not a valid alignment, allowed: swap, block", t),
2487                    doc_id,
2488                    0,
2489                )
2490            } // TODO
2491            None => return Ok(ftd::FontDisplay::Block),
2492        })
2493    }
2494}
2495
2496#[derive(serde::Deserialize, Debug, Default, PartialEq, Clone, serde::Serialize)]
2497pub struct ImageSrc {
2498    pub light: String,
2499    pub dark: String,
2500    pub reference: Option<String>,
2501}
2502
2503impl ImageSrc {
2504    pub fn from(
2505        l: &ftd::Map<ftd::PropertyValue>,
2506        doc: &ftd::p2::TDoc,
2507        line_number: usize,
2508        reference: Option<String>,
2509    ) -> ftd::p1::Result<ImageSrc> {
2510        let properties = l
2511            .iter()
2512            .map(|(k, v)| v.resolve(line_number, doc).map(|v| (k.to_string(), v)))
2513            .collect::<ftd::p1::Result<ftd::Map<ftd::Value>>>()?;
2514        Ok(ImageSrc {
2515            light: ftd::p2::utils::string_optional("light", &properties, doc.name, 0)?
2516                .unwrap_or_else(|| "".to_string()),
2517            dark: ftd::p2::utils::string_optional("dark", &properties, doc.name, 0)?
2518                .unwrap_or_else(|| "".to_string()),
2519            reference,
2520        })
2521    }
2522}
2523
2524#[derive(serde::Deserialize, Debug, PartialEq, Clone, serde::Serialize)]
2525pub struct FontSize {
2526    pub line_height: i64,
2527    #[serde(rename = "font-size")]
2528    pub size: i64,
2529    pub letter_spacing: i64,
2530    pub reference: Option<String>,
2531}
2532
2533impl FontSize {
2534    pub fn from(
2535        l: &ftd::Map<ftd::PropertyValue>,
2536        doc: &ftd::p2::TDoc,
2537        line_number: usize,
2538        reference: Option<String>,
2539    ) -> ftd::p1::Result<FontSize> {
2540        let properties = l
2541            .iter()
2542            .map(|(k, v)| v.resolve(line_number, doc).map(|v| (k.to_string(), v)))
2543            .collect::<ftd::p1::Result<ftd::Map<ftd::Value>>>()?;
2544        Ok(FontSize {
2545            line_height: ftd::p2::utils::int("line-height", &properties, doc.name, 0)?,
2546            size: ftd::p2::utils::int("size", &properties, doc.name, 0)?,
2547            letter_spacing: ftd::p2::utils::int("letter-spacing", &properties, doc.name, 0)?,
2548            reference,
2549        })
2550    }
2551}
2552
2553#[derive(serde::Deserialize, Debug, PartialEq, Clone, serde::Serialize)]
2554pub struct Type {
2555    pub font: String,
2556    pub desktop: FontSize,
2557    pub mobile: FontSize,
2558    pub xl: FontSize,
2559    pub weight: i64,
2560    pub style: Style,
2561    pub reference: Option<String>,
2562}
2563
2564impl Type {
2565    pub fn from(
2566        l: &ftd::Map<ftd::PropertyValue>,
2567        doc: &ftd::p2::TDoc,
2568        line_number: usize,
2569        reference: Option<String>,
2570    ) -> ftd::p1::Result<Type> {
2571        let properties = l
2572            .iter()
2573            .map(|(k, v)| v.resolve(line_number, doc).map(|v| (k.to_string(), v)))
2574            .collect::<ftd::p1::Result<ftd::Map<ftd::Value>>>()?;
2575        return Ok(Type {
2576            font: ftd::p2::utils::string("font", &properties, doc.name, 0)?,
2577            desktop: get_font_size(l, doc, line_number, "desktop")?,
2578            mobile: get_font_size(l, doc, line_number, "mobile")?,
2579            xl: get_font_size(l, doc, line_number, "xl")?,
2580            weight: ftd::p2::utils::int("weight", &properties, doc.name, 0)?,
2581            style: ftd::Style::from(
2582                ftd::p2::utils::string_optional("style", &properties, doc.name, 0)?,
2583                doc.name,
2584            )?,
2585            reference,
2586        });
2587
2588        fn get_font_size(
2589            l: &ftd::Map<ftd::PropertyValue>,
2590            doc: &ftd::p2::TDoc,
2591            line_number: usize,
2592            name: &str,
2593        ) -> ftd::p1::Result<FontSize> {
2594            let properties = l
2595                .iter()
2596                .map(|(k, v)| v.resolve(line_number, doc).map(|v| (k.to_string(), v)))
2597                .collect::<ftd::p1::Result<ftd::Map<ftd::Value>>>()?;
2598
2599            let property_value = ftd::p2::utils::record_optional(name, &properties, doc.name, 0)?
2600                .ok_or_else(|| ftd::p1::Error::ParseError {
2601                message: format!("expected record, for: `{}` found: `None`", name),
2602                doc_id: doc.name.to_string(),
2603                line_number,
2604            })?;
2605
2606            let reference = {
2607                let mut reference = None;
2608                if let Some(val) = l.get(name) {
2609                    reference = val.get_reference();
2610                }
2611                reference
2612            };
2613            FontSize::from(&property_value, doc, line_number, reference)
2614        }
2615    }
2616}
2617
2618#[derive(serde::Deserialize, Debug, PartialEq, Clone, serde::Serialize)]
2619#[serde(tag = "type")]
2620pub enum NamedFont {
2621    Monospace,
2622    Serif,
2623    SansSerif,
2624    Named { value: String },
2625}
2626
2627impl NamedFont {
2628    pub fn from(l: Option<String>) -> ftd::p1::Result<ftd::NamedFont> {
2629        Ok(match l.as_deref() {
2630            Some("monospace") => ftd::NamedFont::Monospace,
2631            Some("serif") => ftd::NamedFont::Serif,
2632            Some("sansSerif") => ftd::NamedFont::SansSerif,
2633            Some(t) => ftd::NamedFont::Named {
2634                value: t.to_string(),
2635            },
2636            None => return Ok(ftd::NamedFont::Serif),
2637        })
2638    }
2639}
2640
2641impl ToString for NamedFont {
2642    fn to_string(&self) -> String {
2643        match self {
2644            ftd::NamedFont::Monospace => "monospace",
2645            ftd::NamedFont::Serif => "serif",
2646            ftd::NamedFont::SansSerif => "sansSerif",
2647            ftd::NamedFont::Named { value } => value,
2648        }
2649        .to_string()
2650    }
2651}
2652
2653#[derive(serde::Deserialize, Debug, PartialEq, Default, Clone, serde::Serialize)]
2654pub struct ExternalFont {
2655    pub url: String,
2656    pub name: String,
2657    pub display: FontDisplay,
2658}
2659
2660#[derive(serde::Deserialize, Debug, PartialEq, Clone, serde::Serialize)]
2661#[serde(tag = "type")]
2662pub enum Weight {
2663    Heavy,
2664    ExtraBold,
2665    Bold,
2666    SemiBold,
2667    Medium,
2668    Regular,
2669    Light,
2670    ExtraLight,
2671    HairLine,
2672}
2673
2674#[derive(serde::Deserialize, Debug, PartialEq, Default, Clone, serde::Serialize)]
2675pub struct Style {
2676    pub italic: bool,
2677    pub underline: bool,
2678    pub strike: bool,
2679    pub weight: Option<ftd::Weight>,
2680}
2681
2682impl Style {
2683    pub fn from(l: Option<String>, doc_id: &str) -> ftd::p1::Result<ftd::Style> {
2684        fn add_in_map(style: &str, map: &mut ftd::Map<i32>) {
2685            if !map.contains_key(style) {
2686                map.insert(style.to_string(), 1);
2687                return;
2688            }
2689            map.insert(style.to_string(), map[style] + 1);
2690        }
2691
2692        let mut s = Style {
2693            italic: false,
2694            underline: false,
2695            strike: false,
2696            weight: Default::default(),
2697        };
2698        let l = match l {
2699            Some(v) => v,
2700            None => return Ok(s),
2701        };
2702        let mut booleans: ftd::Map<i32> = Default::default();
2703        let mut weights: ftd::Map<i32> = Default::default();
2704
2705        for part in l.split_ascii_whitespace() {
2706            match part {
2707                "italic" => {
2708                    s.italic = true;
2709                    add_in_map("italic", &mut booleans);
2710                }
2711                "underline" => {
2712                    s.underline = true;
2713                    add_in_map("underline", &mut booleans);
2714                }
2715                "strike" => {
2716                    s.strike = true;
2717                    add_in_map("strike", &mut booleans);
2718                }
2719                "heavy" => {
2720                    s.weight = Some(ftd::Weight::Heavy);
2721                    add_in_map("heavy", &mut weights);
2722                }
2723                "extra-bold" => {
2724                    s.weight = Some(ftd::Weight::ExtraBold);
2725                    add_in_map("extra-bold", &mut weights);
2726                }
2727                "bold" => {
2728                    s.weight = Some(ftd::Weight::Bold);
2729                    add_in_map("bold", &mut weights);
2730                }
2731                "semi-bold" => {
2732                    s.weight = Some(ftd::Weight::SemiBold);
2733                    add_in_map("semi-bold", &mut weights);
2734                }
2735                "medium" => {
2736                    s.weight = Some(ftd::Weight::Medium);
2737                    add_in_map("medium", &mut weights);
2738                }
2739                "regular" => {
2740                    s.weight = Some(ftd::Weight::Regular);
2741                    add_in_map("regular", &mut weights);
2742                }
2743                "light" => {
2744                    s.weight = Some(ftd::Weight::Light);
2745                    add_in_map("light", &mut weights);
2746                }
2747                "extra-light" => {
2748                    s.weight = Some(ftd::Weight::ExtraLight);
2749                    add_in_map("extra-light", &mut weights);
2750                }
2751                "hairline" => {
2752                    s.weight = Some(ftd::Weight::HairLine);
2753                    add_in_map("hairline", &mut weights);
2754                }
2755                t => return ftd::p2::utils::e2(format!("{} is not a valid style", t), doc_id, 0),
2756            }
2757        }
2758
2759        // Checks if there is repeatation in Underline,italic,strike
2760        for (style, count) in booleans.iter() {
2761            if count > &1 {
2762                return Err(ftd::p1::Error::ForbiddenUsage {
2763                    message: format!("\'{}\' repeated {} times in \'{}\'", style, count, &l),
2764                    doc_id: doc_id.to_string(),
2765                    line_number: 0,
2766                });
2767            }
2768        }
2769
2770        // Checks if there is conflict in font weights
2771        if weights.len() > 1 {
2772            return Err(ftd::p1::Error::ForbiddenUsage {
2773                message: format!("Conflicting weights {:?} in \'{}\'", weights.keys(), &l),
2774                doc_id: doc_id.to_string(),
2775                line_number: 0,
2776            });
2777        }
2778
2779        // Checks if there is repeatation in font weights
2780        for (weight, count) in weights.iter() {
2781            if count > &1 {
2782                return Err(ftd::p1::Error::ForbiddenUsage {
2783                    message: format!("\'{}\' repeated {} times in \'{}\'", weight, count, &l),
2784                    doc_id: doc_id.to_string(),
2785                    line_number: 0,
2786                });
2787            }
2788        }
2789
2790        Ok(s)
2791    }
2792}
2793
2794#[derive(serde::Deserialize, Debug, PartialEq, Clone, serde::Serialize)]
2795#[serde(tag = "type")]
2796pub enum TextFormat {
2797    // FTD, // TODO
2798    Markdown,
2799    Code { lang: String },
2800    Text,
2801}
2802
2803impl Default for ftd::TextFormat {
2804    fn default() -> ftd::TextFormat {
2805        ftd::TextFormat::Markdown
2806    }
2807}
2808
2809impl TextFormat {
2810    pub fn from(
2811        l: Option<String>,
2812        lang: Option<String>,
2813        doc_id: &str,
2814    ) -> ftd::p1::Result<ftd::TextFormat> {
2815        Ok(match l.as_deref() {
2816            Some("markup") => ftd::TextFormat::Markdown,
2817            Some("code") => ftd::TextFormat::Code {
2818                lang: lang.unwrap_or_else(|| "txt".to_string()),
2819            },
2820            Some("text") => ftd::TextFormat::Text,
2821            Some(t) => {
2822                return ftd::p2::utils::e2(format!("{} is not a valid format", t), doc_id, 0)
2823            } // TODO
2824            None => return Ok(ftd::TextFormat::Markdown),
2825        })
2826    }
2827}
2828
2829#[derive(serde::Deserialize, Debug, PartialEq, Default, Clone, serde::Serialize)]
2830pub struct IFrame {
2831    pub src: String,
2832    /// iframe can load lazily.
2833    pub loading: Loading,
2834    pub common: Common,
2835}
2836
2837#[derive(serde::Deserialize, Debug, PartialEq, Default, Clone, serde::Serialize)]
2838pub struct Text {
2839    pub text: ftd::Rendered,
2840    pub line: bool,
2841    pub common: Common,
2842    pub text_align: TextAlign,
2843    pub text_indent: Option<Length>,
2844    pub style: Style,
2845    pub font: Option<Type>,
2846    pub line_clamp: Option<i64>,
2847    // TODO: line-height
2848    // TODO: region (https://package.elm-lang.org/packages/mdgriffith/elm-ui/latest/Element-Region)
2849    // TODO: family (maybe we need a type to represent font-family?)
2850    // TODO: letter-spacing
2851    // TODO: word-spacing
2852    // TODO: font-variants [small-caps, slashed-zero, feature/index etc]
2853    // TODO: shadow, glow
2854}
2855
2856#[derive(serde::Deserialize, Debug, PartialEq, Default, Clone, serde::Serialize)]
2857pub struct TextBlock {
2858    pub text: ftd::Rendered,
2859    pub line: bool,
2860    pub common: Common,
2861    pub text_align: TextAlign,
2862    pub style: Style,
2863    pub size: Option<i64>,
2864    pub font: Vec<NamedFont>,
2865    pub line_height: Option<i64>,
2866    pub line_clamp: Option<i64>,
2867    pub text_indent: Option<Length>,
2868}
2869
2870#[derive(serde::Deserialize, Debug, PartialEq, Default, Clone, serde::Serialize)]
2871pub struct Code {
2872    pub text: ftd::Rendered,
2873    pub common: Common,
2874    pub text_align: TextAlign,
2875    pub style: Style,
2876    pub font: Option<Type>,
2877    pub line_clamp: Option<i64>,
2878    pub text_indent: Option<Length>,
2879}
2880
2881#[derive(serde::Deserialize, Debug, PartialEq, Default, Clone, serde::Serialize)]
2882pub struct Color {
2883    pub light: ColorValue,
2884    pub dark: ColorValue,
2885    pub reference: Option<String>,
2886}
2887
2888impl Color {
2889    pub fn from(
2890        l: (Option<ftd::Map<ftd::PropertyValue>>, Option<String>),
2891        doc: &ftd::p2::TDoc,
2892        line_number: usize,
2893    ) -> ftd::p1::Result<Option<Color>> {
2894        let reference = l.1;
2895        let l = if let Some(l) = l.0 {
2896            l
2897        } else {
2898            return Ok(None);
2899        };
2900
2901        let properties = l
2902            .iter()
2903            .map(|(k, v)| v.resolve(line_number, doc).map(|v| (k.to_string(), v)))
2904            .collect::<ftd::p1::Result<ftd::Map<ftd::Value>>>()?;
2905        Ok(Some(Color {
2906            light: ftd::p2::element::color_from(
2907                ftd::p2::utils::string_optional("light", &properties, doc.name, 0)?,
2908                doc.name,
2909            )?
2910            .unwrap(),
2911            dark: ftd::p2::element::color_from(
2912                ftd::p2::utils::string_optional("dark", &properties, doc.name, 0)?,
2913                doc.name,
2914            )?
2915            .unwrap(),
2916            reference,
2917        }))
2918    }
2919}
2920
2921#[derive(serde::Deserialize, Debug, PartialEq, Default, Clone, serde::Serialize)]
2922pub struct ColorValue {
2923    pub r: u8,
2924    pub g: u8,
2925    pub b: u8,
2926    pub alpha: f32,
2927}
2928
2929#[derive(serde::Deserialize, Debug, PartialEq, Default, Clone, serde::Serialize)]
2930pub struct Input {
2931    pub common: Common,
2932    pub placeholder: Option<String>,
2933    pub value: Option<String>,
2934    pub type_: Option<String>,
2935    pub multiline: bool,
2936    pub font: Option<Type>,
2937    pub default_value: Option<String>,
2938}