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 {
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 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), };
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), };
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), };
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), };
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), };
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 } None => None,
1945 })
1946 }
1947}
1948
1949#[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 } 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 } 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, );
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 }
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#[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 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 } 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 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 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 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 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 } 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 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 }
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}