1#[derive(serde::Deserialize, Clone)]
2#[cfg_attr(not(feature = "wasm"), derive(Debug, PartialEq, serde::Serialize))]
3#[serde(tag = "type")]
4pub enum Element {
5 Text(Text),
6 TextBlock(TextBlock),
7 Code(Code),
8 Image(Image),
9 Row(Row),
10 Column(Column),
11 IFrame(IFrame),
12 Input(Input),
13 Integer(Text),
14 Boolean(Text),
15 Decimal(Text),
16 Scene(Scene),
17 Null,
18}
19
20impl Element {
21 pub fn set_id(
22 children: &mut [ftd_rt::Element],
23 index_vec: &[usize],
24 external_id: Option<String>,
25 ) {
26 for (idx, child) in children.iter_mut().enumerate() {
27 let index_string: String = {
28 let mut index_vec = index_vec.to_vec();
29 index_vec.push(idx);
30 index_vec
31 .iter()
32 .map(|v| v.to_string())
33 .collect::<Vec<String>>()
34 .join(",")
35 };
36 let mut id = match child {
37 Self::Text(ftd_rt::Text {
38 common: ftd_rt::Common { data_id: id, .. },
39 ..
40 }) => id,
41 Self::TextBlock(ftd_rt::TextBlock {
42 common: ftd_rt::Common { data_id: id, .. },
43 ..
44 }) => id,
45 Self::Code(ftd_rt::Code {
46 common: ftd_rt::Common { data_id: id, .. },
47 ..
48 }) => id,
49 Self::Image(ftd_rt::Image {
50 common: ftd_rt::Common { data_id: id, .. },
51 ..
52 }) => id,
53 Self::Row(ftd_rt::Row {
54 common: ftd_rt::Common { data_id: id, .. },
55 container,
56 })
57 | Self::Column(ftd_rt::Column {
58 common: ftd_rt::Common { data_id: id, .. },
59 container,
60 })
61 | Self::Scene(ftd_rt::Scene {
62 common: ftd_rt::Common { data_id: id, .. },
63 container,
64 ..
65 }) => {
66 let mut index_vec = index_vec.to_vec();
67 index_vec.push(idx);
68 Self::set_id(&mut container.children, &index_vec, external_id.clone());
69 if let Some((id, container, external_children)) =
70 &mut container.external_children
71 {
72 if let Some(ftd_rt::Element::Column(col)) = external_children.first_mut() {
73 let index_string: String = index_vec
74 .iter()
75 .map(|v| v.to_string())
76 .collect::<Vec<String>>()
77 .join(",");
78
79 let external_id = Some({
80 if let Some(ref ext_id) = external_id {
81 format!("{}.{}-external:{}", ext_id, id, index_string)
82 } else {
83 format!("{}-external:{}", id, index_string)
84 }
85 });
86 col.common.data_id = external_id.clone();
87 if let Some(val) = container.first_mut() {
88 index_vec.append(&mut val.to_vec());
89 Self::set_id(&mut col.container.children, &index_vec, external_id);
90 }
91 }
92 }
93 id
94 }
95 Self::IFrame(ftd_rt::IFrame {
96 common: ftd_rt::Common { data_id: id, .. },
97 ..
98 }) => id,
99 Self::Input(ftd_rt::Input {
100 common: ftd_rt::Common { data_id: id, .. },
101 ..
102 }) => id,
103 Self::Integer(ftd_rt::Text {
104 common: ftd_rt::Common { data_id: id, .. },
105 ..
106 }) => id,
107 Self::Boolean(ftd_rt::Text {
108 common: ftd_rt::Common { data_id: id, .. },
109 ..
110 }) => id,
111 Self::Decimal(ftd_rt::Text {
112 common: ftd_rt::Common { data_id: id, .. },
113 ..
114 }) => id,
115 Self::Null => continue,
116 };
117
118 let external_id = {
119 if let Some(ref external_id) = external_id {
120 format!(":{}", external_id)
121 } else {
122 "".to_string()
123 }
124 };
125
126 if let Some(id) = &mut id {
127 *id = format!("{}:{}{}", id, index_string, external_id);
128 } else {
129 *id = Some(format!("{}{}", index_string, external_id));
130 }
131 }
132 }
133
134 pub fn get_external_children_condition(
135 &self,
136 external_open_id: &Option<String>,
137 external_children_container: &[Vec<usize>],
138 ) -> Vec<ftd_rt::ExternalChildrenCondition> {
139 let mut d: Vec<ftd_rt::ExternalChildrenCondition> = vec![];
140 let mut ext_child_condition = None;
141 let (id, open_id, children_container, children) = match self {
142 Self::Row(ftd_rt::Row {
143 common: ftd_rt::Common { data_id: id, .. },
144 container:
145 ftd_rt::Container {
146 external_children,
147 children,
148 ..
149 },
150 })
151 | Self::Column(ftd_rt::Column {
152 common: ftd_rt::Common { data_id: id, .. },
153 container:
154 ftd_rt::Container {
155 external_children,
156 children,
157 ..
158 },
159 })
160 | Self::Scene(ftd_rt::Scene {
161 common: ftd_rt::Common { data_id: id, .. },
162 container:
163 ftd_rt::Container {
164 external_children,
165 children,
166 ..
167 },
168 ..
169 }) => (
170 id,
171 external_children
172 .as_ref()
173 .map(|(open_id, _, _)| open_id.to_string()),
174 external_children
175 .as_ref()
176 .map(|(_, children_container, _)| children_container.to_vec()),
177 children,
178 ),
179 _ => return d,
180 };
181
182 #[allow(clippy::blocks_in_if_conditions)]
183 if *external_open_id
184 == id.as_ref().map(|v| {
185 if v.contains(':') {
186 let mut part = v.splitn(2, ':');
187 part.next().unwrap().trim().to_string()
188 } else {
189 v.to_string()
190 }
191 })
192 && external_children_container.is_empty()
193 {
194 ext_child_condition = id.clone();
195 if open_id.is_none() {
196 let id = ext_child_condition.expect("");
197 d.push(ftd_rt::ExternalChildrenCondition {
198 condition: vec![id.to_string()],
199 set_at: id,
200 });
201 return d;
202 }
203 }
204
205 let (open_id, external_children_container) =
206 if open_id.is_some() && external_children_container.is_empty() {
207 (open_id, {
208 if let Some(c) = children_container {
209 c
210 } else {
211 vec![]
212 }
213 })
214 } else {
215 (
216 external_open_id.clone(),
217 external_children_container.to_vec(),
218 )
219 };
220
221 let mut index = 0;
222 for (i, v) in children.iter().enumerate() {
223 let external_container = {
224 let mut external_container = vec![];
225 while index < external_children_container.len() {
226 if let Some(container) = external_children_container[index].get(0) {
227 if container < &i {
228 index += 1;
229 continue;
230 }
231 let external_child_container =
232 external_children_container[index][1..].to_vec();
233 if container == &i && !external_child_container.is_empty() {
234 external_container.push(external_child_container)
235 } else {
236 break;
237 }
238 }
239 index += 1;
240 }
241 external_container
242 };
243 let conditions =
244 v.get_external_children_condition(&open_id, external_container.as_slice());
245 for mut condition in conditions {
246 if let Some(e) = &ext_child_condition {
247 condition.condition.push(e.to_string());
248 }
249 d.push(condition);
250 }
251 }
252 d
253 }
254
255 pub fn get_external_children_dependencies(
256 children: &[ftd_rt::Element],
257 ) -> ftd_rt::ExternalChildrenDependenciesMap {
258 let mut d: ftd_rt::ExternalChildrenDependenciesMap = Default::default();
259 for child in children {
260 let container = match child {
261 ftd_rt::Element::Row(ftd_rt::Row { container, .. }) => container,
262 ftd_rt::Element::Column(ftd_rt::Column { container, .. }) => container,
263 ftd_rt::Element::Scene(ftd_rt::Scene { container, .. }) => container,
264 _ => continue,
265 };
266 let all_locals =
267 ftd_rt::Element::get_external_children_dependencies(&container.children);
268 for (k, v) in all_locals {
269 d.insert(k.to_string(), v);
270 }
271 if let Some((external_open_id, external_children_container, external_children)) =
272 &container.external_children
273 {
274 if let Some(ftd_rt::Element::Column(col)) = external_children.first() {
275 let external_children_condition: Vec<ftd_rt::ExternalChildrenCondition> = child
276 .get_external_children_condition(
277 &Some(external_open_id.to_string()),
278 external_children_container,
279 );
280 d.insert(
281 col.common.data_id.as_ref().expect("").to_string(),
282 external_children_condition,
283 );
284 let all_locals = ftd_rt::Element::get_external_children_dependencies(
285 &col.container.children,
286 );
287 for (k, v) in all_locals {
288 d.insert(k.to_string(), v);
289 }
290 }
291 }
292 }
293 d
294 }
295
296 pub fn get_style_event_dependencies(
297 children: &[ftd_rt::Element],
298 data: &mut ftd_rt::DataDependenciesMap,
299 ) {
300 for child in children {
301 let (conditional_attributes, id) = match child {
302 ftd_rt::Element::Column(ftd_rt::Column {
303 common:
304 ftd_rt::Common {
305 conditional_attribute,
306 data_id: id,
307 ..
308 },
309 container,
310 })
311 | ftd_rt::Element::Row(ftd_rt::Row {
312 common:
313 ftd_rt::Common {
314 conditional_attribute,
315 data_id: id,
316 ..
317 },
318 container,
319 })
320 | ftd_rt::Element::Scene(ftd_rt::Scene {
321 common:
322 ftd_rt::Common {
323 conditional_attribute,
324 data_id: id,
325 ..
326 },
327 container,
328 }) => {
329 ftd_rt::Element::get_style_event_dependencies(&container.children, data);
330 if let Some((_, _, external_children)) = &container.external_children {
331 ftd_rt::Element::get_style_event_dependencies(external_children, data);
332 }
333 (conditional_attribute, id)
334 }
335 ftd_rt::Element::Image(ftd_rt::Image { common, .. })
336 | ftd_rt::Element::Text(ftd_rt::Text { common, .. })
337 | ftd_rt::Element::TextBlock(ftd_rt::TextBlock { common, .. })
338 | ftd_rt::Element::Code(ftd_rt::Code { common, .. })
339 | ftd_rt::Element::IFrame(ftd_rt::IFrame { common, .. })
340 | ftd_rt::Element::Input(ftd_rt::Input { common, .. })
341 | ftd_rt::Element::Integer(ftd_rt::Text { common, .. })
342 | ftd_rt::Element::Boolean(ftd_rt::Text { common, .. })
343 | ftd_rt::Element::Decimal(ftd_rt::Text { common, .. }) => {
344 (&common.conditional_attribute, &common.data_id)
345 }
346 ftd_rt::Element::Null => continue,
347 };
348 for (k, v) in conditional_attributes {
349 if let ftd_rt::ConditionalAttribute {
350 attribute_type: ftd_rt::AttributeType::Style,
351 conditions_with_value,
352 default,
353 } = v
354 {
355 for (condition, value) in conditions_with_value {
356 let id = id.clone().expect("universal id should be present");
357 if let Some(ftd_rt::Data { dependencies, .. }) =
358 data.get_mut(&condition.variable)
359 {
360 let json = ftd_rt::Dependencies {
361 dependency_type: ftd_rt::DependencyType::Style,
362 condition: Some(condition.value.to_string()),
363 parameters: std::array::IntoIter::new([(
364 k.to_string(),
365 ftd_rt::ValueWithDefault {
366 value: value.clone(),
367 default: default.clone(),
368 },
369 )])
370 .collect(),
371 };
372 if let Some(dependencies) = dependencies.get_mut(&id) {
373 let mut d =
374 serde_json::from_str::<Vec<ftd_rt::Dependencies>>(dependencies)
375 .unwrap();
376 d.push(json);
377 *dependencies = serde_json::to_string(&d).unwrap();
378 } else {
379 dependencies
380 .insert(id, serde_json::to_string(&vec![json]).unwrap());
381 }
382 } else {
383 panic!("{} should be declared", condition.variable)
384 }
385 }
386 }
387 }
388 }
389 }
390
391 pub fn get_value_event_dependencies(
392 children: &[ftd_rt::Element],
393 data: &mut ftd_rt::DataDependenciesMap,
394 ) {
395 for child in children {
396 let (reference, id) = match child {
397 ftd_rt::Element::Column(ftd_rt::Column {
398 common:
399 ftd_rt::Common {
400 reference,
401 data_id: id,
402 ..
403 },
404 container,
405 })
406 | ftd_rt::Element::Row(ftd_rt::Row {
407 common:
408 ftd_rt::Common {
409 reference,
410 data_id: id,
411 ..
412 },
413 container,
414 })
415 | ftd_rt::Element::Scene(ftd_rt::Scene {
416 common:
417 ftd_rt::Common {
418 reference,
419 data_id: id,
420 ..
421 },
422 container,
423 }) => {
424 ftd_rt::Element::get_value_event_dependencies(&container.children, data);
425 if let Some((_, _, external_children)) = &container.external_children {
426 ftd_rt::Element::get_value_event_dependencies(external_children, data);
427 }
428 (reference, id)
429 }
430 ftd_rt::Element::Image(ftd_rt::Image { common, .. })
431 | ftd_rt::Element::Text(ftd_rt::Text { common, .. })
432 | ftd_rt::Element::TextBlock(ftd_rt::TextBlock { common, .. })
433 | ftd_rt::Element::Code(ftd_rt::Code { common, .. })
434 | ftd_rt::Element::IFrame(ftd_rt::IFrame { common, .. })
435 | ftd_rt::Element::Input(ftd_rt::Input { common, .. })
436 | ftd_rt::Element::Integer(ftd_rt::Text { common, .. })
437 | ftd_rt::Element::Boolean(ftd_rt::Text { common, .. })
438 | ftd_rt::Element::Decimal(ftd_rt::Text { common, .. }) => {
439 (&common.reference, &common.data_id)
440 }
441 ftd_rt::Element::Null => continue,
442 };
443 if let Some(reference) = reference {
444 let id = id.clone().expect("universal id should be present");
445
446 if let Some(ftd_rt::Data { dependencies, .. }) = data.get_mut(reference) {
447 let json = ftd_rt::Dependencies {
448 dependency_type: ftd_rt::DependencyType::Value,
449 condition: None,
450 parameters: Default::default(),
451 };
452 if let Some(dependencies) = dependencies.get_mut(&id) {
453 let mut d = serde_json::from_str::<Vec<ftd_rt::Dependencies>>(dependencies)
454 .unwrap();
455 d.push(json);
456 *dependencies = serde_json::to_string(&d).unwrap();
457 } else {
458 dependencies.insert(id, serde_json::to_string(&vec![json]).unwrap());
459 }
460 }
461 }
462 }
463 }
464
465 pub fn get_visible_event_dependencies(
466 children: &[ftd_rt::Element],
467 data: &mut ftd_rt::DataDependenciesMap,
468 ) {
469 for child in children {
470 let (condition, id) = match child {
471 ftd_rt::Element::Column(ftd_rt::Column {
472 common:
473 ftd_rt::Common {
474 condition,
475 data_id: id,
476 ..
477 },
478 container,
479 })
480 | ftd_rt::Element::Row(ftd_rt::Row {
481 common:
482 ftd_rt::Common {
483 condition,
484 data_id: id,
485 ..
486 },
487 container,
488 })
489 | ftd_rt::Element::Scene(ftd_rt::Scene {
490 common:
491 ftd_rt::Common {
492 condition,
493 data_id: id,
494 ..
495 },
496 container,
497 }) => {
498 ftd_rt::Element::get_visible_event_dependencies(&container.children, data);
499 if let Some((_, _, external_children)) = &container.external_children {
500 ftd_rt::Element::get_visible_event_dependencies(external_children, data);
501 }
502 (condition, id)
503 }
504 ftd_rt::Element::Image(ftd_rt::Image { common, .. })
505 | ftd_rt::Element::Text(ftd_rt::Text { common, .. })
506 | ftd_rt::Element::TextBlock(ftd_rt::TextBlock { common, .. })
507 | ftd_rt::Element::Code(ftd_rt::Code { common, .. })
508 | ftd_rt::Element::IFrame(ftd_rt::IFrame { common, .. })
509 | ftd_rt::Element::Input(ftd_rt::Input { common, .. })
510 | ftd_rt::Element::Integer(ftd_rt::Text { common, .. })
511 | ftd_rt::Element::Boolean(ftd_rt::Text { common, .. })
512 | ftd_rt::Element::Decimal(ftd_rt::Text { common, .. }) => {
513 (&common.condition, &common.data_id)
514 }
515 ftd_rt::Element::Null => continue,
516 };
517 if let Some(condition) = condition {
518 let id = id.clone().expect("universal id should be present");
519
520 if let Some(ftd_rt::Data { dependencies, .. }) = data.get_mut(&condition.variable) {
521 let json = ftd_rt::Dependencies {
522 dependency_type: ftd_rt::DependencyType::Visible,
523 condition: Some(condition.value.to_string()),
524 parameters: Default::default(),
525 };
526 if let Some(dependencies) = dependencies.get_mut(&id) {
527 let mut d = serde_json::from_str::<Vec<ftd_rt::Dependencies>>(dependencies)
528 .unwrap();
529 d.push(json);
530 *dependencies = serde_json::to_string(&d).unwrap();
531 } else {
532 dependencies.insert(id, serde_json::to_string(&vec![json]).unwrap());
533 }
534 } else {
535 panic!("{} should be declared", condition.variable)
536 }
537 }
538 }
539 }
540
541 pub fn get_locals(children: &[ftd_rt::Element]) -> ftd_rt::Map {
542 let mut d: ftd_rt::Map = Default::default();
543 for child in children {
544 let locals = match child {
545 ftd_rt::Element::Row(ftd_rt::Row {
546 common: ftd_rt::Common { locals, .. },
547 container,
548 })
549 | ftd_rt::Element::Column(ftd_rt::Column {
550 common: ftd_rt::Common { locals, .. },
551 container,
552 })
553 | ftd_rt::Element::Scene(ftd_rt::Scene {
554 common: ftd_rt::Common { locals, .. },
555 container,
556 }) => {
557 let mut all_locals = ftd_rt::Element::get_locals(&container.children);
558 for (k, v) in locals {
559 all_locals.insert(k.to_string(), v.to_string());
560 }
561 if let Some((_, _, ref c)) = container.external_children {
562 for (k, v) in ftd_rt::Element::get_locals(c) {
563 all_locals.insert(k.to_string(), v.to_string());
564 }
565 }
566 all_locals
567 }
568 ftd_rt::Element::Decimal(ftd_rt::Text { common, .. })
569 | ftd_rt::Element::Text(ftd_rt::Text { common, .. })
570 | ftd_rt::Element::TextBlock(ftd_rt::TextBlock { common, .. })
571 | ftd_rt::Element::Code(ftd_rt::Code { common, .. })
572 | ftd_rt::Element::Image(ftd_rt::Image { common, .. })
573 | ftd_rt::Element::IFrame(ftd_rt::IFrame { common, .. })
574 | ftd_rt::Element::Input(ftd_rt::Input { common, .. })
575 | ftd_rt::Element::Integer(ftd_rt::Text { common, .. })
576 | ftd_rt::Element::Boolean(ftd_rt::Text { common, .. }) => common.locals.clone(),
577 ftd_rt::Element::Null => Default::default(),
578 };
579
580 for (k, v) in locals {
581 d.insert(k.to_string(), v.to_string());
582 }
583 }
584 d
585 }
586
587 pub fn is_open_container(&self, is_container_children_empty: bool) -> (bool, Option<String>) {
588 match self {
589 ftd_rt::Element::Column(e) => e.container.is_open(is_container_children_empty),
590 ftd_rt::Element::Row(e) => e.container.is_open(is_container_children_empty),
591 ftd_rt::Element::Scene(e) => e.container.is_open(is_container_children_empty),
592 _ => (false, None),
593 }
594 }
595
596 pub fn container_id(&self) -> Option<String> {
597 match self {
598 ftd_rt::Element::Column(e) => e.common.data_id.clone(),
599 ftd_rt::Element::Row(e) => e.common.data_id.clone(),
600 ftd_rt::Element::Scene(e) => e.common.data_id.clone(),
601 _ => None,
602 }
603 }
604
605 pub fn set_container_id(&mut self, name: Option<String>) {
606 match self {
607 ftd_rt::Element::Column(e) => e.common.data_id = name,
608 ftd_rt::Element::Row(e) => e.common.data_id = name,
609 ftd_rt::Element::Scene(e) => e.common.data_id = name,
610 _ => {}
611 }
612 }
613
614 pub fn set_element_id(&mut self, name: Option<String>) {
615 match self {
616 ftd_rt::Element::Column(ftd_rt::Column { common, .. })
617 | ftd_rt::Element::Row(ftd_rt::Row { common, .. })
618 | ftd_rt::Element::Text(ftd_rt::Text { common, .. })
619 | ftd_rt::Element::TextBlock(ftd_rt::TextBlock { common, .. })
620 | ftd_rt::Element::Code(ftd_rt::Code { common, .. })
621 | ftd_rt::Element::Image(ftd_rt::Image { common, .. })
622 | ftd_rt::Element::IFrame(ftd_rt::IFrame { common, .. })
623 | ftd_rt::Element::Input(ftd_rt::Input { common, .. })
624 | ftd_rt::Element::Integer(ftd_rt::Text { common, .. })
625 | ftd_rt::Element::Boolean(ftd_rt::Text { common, .. })
626 | ftd_rt::Element::Decimal(ftd_rt::Text { common, .. })
627 | ftd_rt::Element::Scene(ftd_rt::Scene { common, .. }) => common.id = name,
628 ftd_rt::Element::Null => {}
629 }
630 }
631
632 pub fn set_condition(&mut self, condition: Option<ftd_rt::Condition>) {
633 match self {
634 ftd_rt::Element::Column(ftd_rt::Column { common, .. })
635 | ftd_rt::Element::Row(ftd_rt::Row { common, .. })
636 | ftd_rt::Element::Text(ftd_rt::Text { common, .. })
637 | ftd_rt::Element::TextBlock(ftd_rt::TextBlock { common, .. })
638 | ftd_rt::Element::Code(ftd_rt::Code { common, .. })
639 | ftd_rt::Element::Image(ftd_rt::Image { common, .. })
640 | ftd_rt::Element::IFrame(ftd_rt::IFrame { common, .. })
641 | ftd_rt::Element::Input(ftd_rt::Input { common, .. })
642 | ftd_rt::Element::Integer(ftd_rt::Text { common, .. })
643 | ftd_rt::Element::Boolean(ftd_rt::Text { common, .. })
644 | ftd_rt::Element::Decimal(ftd_rt::Text { common, .. })
645 | ftd_rt::Element::Scene(ftd_rt::Scene { common, .. }) => common,
646 ftd_rt::Element::Null => return,
647 }
648 .condition = condition;
649 }
650
651 pub fn set_non_visibility(&mut self, is_not_visible: bool) {
652 match self {
653 ftd_rt::Element::Column(ftd_rt::Column { common, .. })
654 | ftd_rt::Element::Row(ftd_rt::Row { common, .. })
655 | ftd_rt::Element::Text(ftd_rt::Text { common, .. })
656 | ftd_rt::Element::TextBlock(ftd_rt::TextBlock { common, .. })
657 | ftd_rt::Element::Code(ftd_rt::Code { common, .. })
658 | ftd_rt::Element::Image(ftd_rt::Image { common, .. })
659 | ftd_rt::Element::IFrame(ftd_rt::IFrame { common, .. })
660 | ftd_rt::Element::Input(ftd_rt::Input { common, .. })
661 | ftd_rt::Element::Integer(ftd_rt::Text { common, .. })
662 | ftd_rt::Element::Boolean(ftd_rt::Text { common, .. })
663 | ftd_rt::Element::Decimal(ftd_rt::Text { common, .. })
664 | ftd_rt::Element::Scene(ftd_rt::Scene { common, .. }) => common,
665 ftd_rt::Element::Null => return,
666 }
667 .is_not_visible = is_not_visible;
668 }
669
670 pub fn set_locals(&mut self, locals: ftd_rt::Map) {
671 match self {
672 ftd_rt::Element::Column(ftd_rt::Column { common, .. })
673 | ftd_rt::Element::Row(ftd_rt::Row { common, .. })
674 | ftd_rt::Element::Text(ftd_rt::Text { common, .. })
675 | ftd_rt::Element::TextBlock(ftd_rt::TextBlock { common, .. })
676 | ftd_rt::Element::Code(ftd_rt::Code { common, .. })
677 | ftd_rt::Element::Image(ftd_rt::Image { common, .. })
678 | ftd_rt::Element::IFrame(ftd_rt::IFrame { common, .. })
679 | ftd_rt::Element::Input(ftd_rt::Input { common, .. })
680 | ftd_rt::Element::Integer(ftd_rt::Text { common, .. })
681 | ftd_rt::Element::Boolean(ftd_rt::Text { common, .. })
682 | ftd_rt::Element::Decimal(ftd_rt::Text { common, .. })
683 | ftd_rt::Element::Scene(ftd_rt::Scene { common, .. }) => common,
684 ftd_rt::Element::Null => return,
685 }
686 .locals = locals;
687 }
688
689 pub fn set_events(&mut self, events: &mut Vec<ftd_rt::Event>) {
690 match self {
691 ftd_rt::Element::Column(ftd_rt::Column { common, .. })
692 | ftd_rt::Element::Row(ftd_rt::Row { common, .. })
693 | ftd_rt::Element::Text(ftd_rt::Text { common, .. })
694 | ftd_rt::Element::TextBlock(ftd_rt::TextBlock { common, .. })
695 | ftd_rt::Element::Code(ftd_rt::Code { common, .. })
696 | ftd_rt::Element::Image(ftd_rt::Image { common, .. })
697 | ftd_rt::Element::IFrame(ftd_rt::IFrame { common, .. })
698 | ftd_rt::Element::Input(ftd_rt::Input { common, .. })
699 | ftd_rt::Element::Integer(ftd_rt::Text { common, .. })
700 | ftd_rt::Element::Boolean(ftd_rt::Text { common, .. })
701 | ftd_rt::Element::Decimal(ftd_rt::Text { common, .. })
702 | ftd_rt::Element::Scene(ftd_rt::Scene { common, .. }) => common,
703 ftd_rt::Element::Null => return,
704 }
705 .events
706 .append(events)
707 }
708
709 pub fn get_heading_region(&self) -> Option<&ftd_rt::Region> {
710 match self {
711 ftd_rt::Element::Column(e) => e.common.region.as_ref().filter(|v| v.is_heading()),
712 ftd_rt::Element::Row(e) => e.common.region.as_ref().filter(|v| v.is_heading()),
713 _ => None,
714 }
715 }
716
717 pub fn get_mut_common(&mut self) -> Option<&mut ftd_rt::Common> {
718 match self {
719 ftd_rt::Element::Column(e) => Some(&mut e.common),
720 ftd_rt::Element::Row(e) => Some(&mut e.common),
721 ftd_rt::Element::Text(e) => Some(&mut e.common),
722 ftd_rt::Element::TextBlock(e) => Some(&mut e.common),
723 ftd_rt::Element::Code(e) => Some(&mut e.common),
724 ftd_rt::Element::Image(e) => Some(&mut e.common),
725 ftd_rt::Element::IFrame(e) => Some(&mut e.common),
726 ftd_rt::Element::Input(e) => Some(&mut e.common),
727 ftd_rt::Element::Integer(e) => Some(&mut e.common),
728 ftd_rt::Element::Boolean(e) => Some(&mut e.common),
729 ftd_rt::Element::Decimal(e) => Some(&mut e.common),
730 ftd_rt::Element::Scene(e) => Some(&mut e.common),
731 _ => None,
732 }
733 }
734
735 pub fn get_common(&self) -> Option<&ftd_rt::Common> {
736 match self {
737 ftd_rt::Element::Column(e) => Some(&e.common),
738 ftd_rt::Element::Row(e) => Some(&e.common),
739 ftd_rt::Element::Text(e) => Some(&e.common),
740 ftd_rt::Element::TextBlock(e) => Some(&e.common),
741 ftd_rt::Element::Code(e) => Some(&e.common),
742 ftd_rt::Element::Image(e) => Some(&e.common),
743 ftd_rt::Element::IFrame(e) => Some(&e.common),
744 ftd_rt::Element::Input(e) => Some(&e.common),
745 ftd_rt::Element::Integer(e) => Some(&e.common),
746 ftd_rt::Element::Boolean(e) => Some(&e.common),
747 ftd_rt::Element::Decimal(e) => Some(&e.common),
748 ftd_rt::Element::Scene(e) => Some(&e.common),
749 _ => None,
750 }
751 }
752
753 pub fn renesting_on_region(elements: &mut Vec<ftd_rt::Element>) {
754 let mut region: Option<(usize, &Region)> = None;
755 let mut insert: Vec<(usize, usize)> = Default::default();
756 for (idx, element) in elements.iter().enumerate() {
757 match element {
758 ftd_rt::Element::Column(ftd_rt::Column { common, .. })
759 | ftd_rt::Element::Row(ftd_rt::Row { common, .. }) => {
760 let r = common.region.as_ref().filter(|v| v.is_heading());
761 if let Some(r) = r {
762 if let Some((place_at, r1)) = region {
763 if r.get_lower_priority_heading().contains(r1) || r == r1 {
764 insert.push((place_at, idx));
765 region = Some((idx, r));
766 }
767 } else {
768 region = Some((idx, r));
769 }
770 }
771 }
772 _ => continue,
773 }
774 }
775 if let Some((place_at, _)) = region {
776 insert.push((place_at, elements.len()));
777 }
778
779 for (place_at, end) in insert.iter().rev() {
780 let mut children = elements[place_at + 1..*end].to_vec();
781 match elements[*place_at] {
782 ftd_rt::Element::Column(ftd_rt::Column {
783 ref mut container, ..
784 })
785 | ftd_rt::Element::Row(ftd_rt::Row {
786 ref mut container, ..
787 }) => {
788 container.children.append(&mut children);
789 }
790 _ => continue,
791 }
792 for idx in (place_at + 1..*end).rev() {
793 elements.remove(idx);
794 }
795 }
796
797 for element in &mut *elements {
798 match element {
799 ftd_rt::Element::Column(ftd_rt::Column {
800 ref mut container, ..
801 })
802 | ftd_rt::Element::Row(ftd_rt::Row {
803 ref mut container, ..
804 }) => {
805 if let Some((_, _, ref mut e)) = container.external_children {
806 ftd_rt::Element::renesting_on_region(e);
807 }
808 ftd_rt::Element::renesting_on_region(&mut container.children);
809 }
810 _ => continue,
811 }
812 }
813 }
814}
815
816#[derive(serde::Deserialize, PartialEq)]
817#[cfg_attr(not(feature = "wasm"), derive(Debug, Clone, serde::Serialize))]
818#[serde(tag = "type")]
819pub enum Length {
820 Fill,
821 Shrink,
822 Auto,
823 FitContent,
824 Px { value: i64 },
825 Portion { value: i64 },
826 Percent { value: i64 },
827 Calc { value: String },
828}
829
830impl Length {
831 pub fn from(l: Option<String>, doc_id: &str) -> ftd_rt::Result<Option<ftd_rt::Length>> {
832 let l = match l {
833 Some(l) => l,
834 None => return Ok(None),
835 };
836
837 if l == "fill" {
838 return Ok(Some(Length::Fill));
839 }
840
841 if l == "shrink" {
842 return Ok(Some(Length::Shrink));
843 }
844 if l == "auto" {
845 return Ok(Some(Length::Auto));
846 }
847
848 if l.starts_with("calc ") {
849 let v = crate::get_name("calc", l.as_str(), doc_id)?;
850 return match v.parse() {
851 Ok(v) => Ok(Some(Length::Calc { value: v })),
852 Err(_) => crate::e(format!("{} is not a valid integer", v), doc_id),
853 };
854 }
855
856 if l == "fit-content" {
857 return Ok(Some(Length::FitContent));
858 }
859
860 if l.starts_with("portion ") {
861 let v = crate::get_name("portion", l.as_str(), doc_id)?;
862 return match v.parse() {
863 Ok(v) => Ok(Some(Length::Portion { value: v })),
864 Err(_) => crate::e(format!("{} is not a valid integer", v), doc_id),
865 };
866 }
867 if l.starts_with("percent ") {
868 let v = crate::get_name("percent", l.as_str(), doc_id)?;
869 return match v.parse() {
870 Ok(v) => Ok(Some(Length::Percent { value: v })),
871 Err(_) => crate::e(format!("{} is not a valid integer", v), doc_id),
872 };
873 }
874
875 match l.parse() {
876 Ok(v) => Ok(Some(Length::Px { value: v })),
877 Err(_) => crate::e(format!("{} is not a valid integer", l), doc_id),
878 }
879 }
880}
881
882#[derive(serde::Deserialize)]
883#[cfg_attr(
884 not(feature = "wasm"),
885 derive(Debug, PartialEq, Clone, serde::Serialize)
886)]
887#[serde(tag = "type")]
888pub enum Position {
889 Center,
890 Top,
891 Bottom,
892 Left,
893 Right,
894 TopLeft,
895 TopRight,
896 BottomLeft,
897 BottomRight,
898}
899
900impl Default for Position {
901 fn default() -> ftd_rt::Position {
902 Self::TopLeft
903 }
904}
905
906impl Position {
907 pub fn from(l: Option<String>, doc_id: &str) -> ftd_rt::Result<ftd_rt::Position> {
908 Ok(match l.as_deref() {
909 Some("center") => Self::Center,
910 Some("top") => Self::Top,
911 Some("bottom") => Self::Bottom,
912 Some("left") => Self::Left,
913 Some("right") => Self::Right,
914 Some("top-left") => Self::TopLeft,
915 Some("top-right") => Self::TopRight,
916 Some("bottom-left") => Self::BottomLeft,
917 Some("bottom-right") => Self::BottomRight,
918 Some(t) => return crate::e(format!("{} is not a valid alignment", t), doc_id),
919 None => return Ok(Self::TopLeft),
920 })
921 }
922}
923
924#[derive(serde::Deserialize)]
926#[cfg_attr(
927 not(feature = "wasm"),
928 derive(Debug, PartialEq, Clone, serde::Serialize)
929)]
930pub enum Region {
931 H0,
932 H1,
933 H2,
934 H3,
935 H4,
936 H5,
937 H6,
938 H7,
939 Title,
940 MainContent,
941 Navigation,
942 Aside,
943 Footer,
944 Description,
945 Announce,
946 AnnounceUrgently,
947}
948
949impl ToString for Region {
950 fn to_string(&self) -> String {
951 match self {
952 Self::H0 => "h0",
953 Self::H1 => "h1",
954 Self::H2 => "h2",
955 Self::H3 => "h3",
956 Self::H4 => "h4",
957 Self::H5 => "h5",
958 Self::H6 => "h6",
959 Self::H7 => "h7",
960 Self::Title => "title",
961 Self::MainContent => "main",
962 Self::Navigation => "navigation",
963 Self::Aside => "aside",
964 Self::Footer => "footer",
965 Self::Description => "description",
966 Self::Announce => "announce",
967 Self::AnnounceUrgently => "announce-urgently",
968 }
969 .to_string()
970 }
971}
972
973impl Region {
974 pub fn from(l: Option<String>, doc_id: &str) -> ftd_rt::Result<Option<ftd_rt::Region>> {
975 Ok(Some(match l.as_deref() {
976 Some("h0") => Self::H0,
977 Some("h1") => Self::H1,
978 Some("h2") => Self::H2,
979 Some("h3") => Self::H3,
980 Some("h4") => Self::H4,
981 Some("h5") => Self::H5,
982 Some("h6") => Self::H6,
983 Some("h7") => Self::H7,
984 Some("title") => Self::Title,
985 Some("main") => Self::MainContent,
986 Some("navigation") => Self::Navigation,
987 Some("aside") => Self::Aside,
988 Some("footer") => Self::Footer,
989 Some("description") => Self::Description,
990 Some("announce") => Self::Announce,
991 Some("announce-urgently") => Self::AnnounceUrgently,
992 Some(t) => return crate::e(format!("{} is not a valid alignment", t), doc_id),
993 None => return Ok(None),
994 }))
995 }
996
997 pub fn is_heading(&self) -> bool {
998 matches!(
999 self,
1000 ftd_rt::Region::H0
1001 | ftd_rt::Region::H1
1002 | ftd_rt::Region::H2
1003 | ftd_rt::Region::H3
1004 | ftd_rt::Region::H4
1005 | ftd_rt::Region::H5
1006 | ftd_rt::Region::H6
1007 | ftd_rt::Region::H7
1008 )
1009 }
1010
1011 pub fn is_primary_heading(&self) -> bool {
1012 matches!(self, ftd_rt::Region::H0 | ftd_rt::Region::H1)
1013 }
1014
1015 pub fn is_title(&self) -> bool {
1016 matches!(self, ftd_rt::Region::Title)
1017 }
1018
1019 pub fn get_lower_priority_heading(&self) -> Vec<ftd_rt::Region> {
1020 let mut list = vec![];
1021 if matches!(
1022 self,
1023 ftd_rt::Region::Title
1024 | ftd_rt::Region::MainContent
1025 | ftd_rt::Region::Navigation
1026 | ftd_rt::Region::Aside
1027 | ftd_rt::Region::Footer
1028 | ftd_rt::Region::Description
1029 | ftd_rt::Region::Announce
1030 | ftd_rt::Region::AnnounceUrgently
1031 ) {
1032 return list;
1033 }
1034 for region in [
1035 ftd_rt::Region::H7,
1036 ftd_rt::Region::H6,
1037 ftd_rt::Region::H5,
1038 ftd_rt::Region::H4,
1039 ftd_rt::Region::H3,
1040 ftd_rt::Region::H2,
1041 ftd_rt::Region::H1,
1042 ] {
1043 if self.to_string() == region.to_string() {
1044 return list;
1045 }
1046 list.push(region);
1047 }
1048 list
1049 }
1050}
1051
1052#[derive(serde::Deserialize)]
1053#[cfg_attr(
1054 not(feature = "wasm"),
1055 derive(Debug, PartialEq, Clone, serde::Serialize)
1056)]
1057#[serde(tag = "type")]
1058pub enum Overflow {
1059 Hidden,
1060 Visible,
1061 Auto,
1062 Scroll,
1063}
1064
1065impl Overflow {
1066 pub fn from(l: Option<String>, doc_id: &str) -> ftd_rt::Result<Option<ftd_rt::Overflow>> {
1067 Ok(Option::from(match l.as_deref() {
1068 Some("hidden") => Self::Hidden,
1069 Some("visible") => Self::Visible,
1070 Some("auto") => Self::Auto,
1071 Some("scroll") => Self::Scroll,
1072 Some(t) => return crate::e(format!("{} is not a valid property", t), doc_id),
1073 None => return Ok(None),
1074 }))
1075 }
1076}
1077
1078#[derive(serde::Deserialize)]
1079#[cfg_attr(
1080 not(feature = "wasm"),
1081 derive(Debug, PartialEq, Clone, serde::Serialize)
1082)]
1083#[serde(tag = "type")]
1084pub enum Anchor {
1085 Window,
1086 Parent,
1087}
1088
1089impl Anchor {
1090 pub fn from(l: Option<String>, doc_id: &str) -> ftd_rt::Result<Option<ftd_rt::Anchor>> {
1091 let l = match l {
1092 Some(l) => l,
1093 None => return Ok(None),
1094 };
1095
1096 Ok(Some(match l.as_str() {
1097 "window" => ftd_rt::Anchor::Window,
1098 "parent" => ftd_rt::Anchor::Parent,
1099 t => {
1100 return ftd_rt::e(
1101 format!(
1102 "invalid value for `absolute` expected `window` or `parent` found: {}",
1103 t
1104 ),
1105 doc_id,
1106 )
1107 }
1108 }))
1109 }
1110
1111 pub fn to_postion(&self) -> String {
1112 match self {
1113 ftd_rt::Anchor::Window => "fixed",
1114 ftd_rt::Anchor::Parent => "absolute",
1115 }
1116 .to_string()
1117 }
1118}
1119
1120#[derive(serde::Deserialize)]
1121#[cfg_attr(
1122 not(feature = "wasm"),
1123 derive(Debug, PartialEq, Clone, serde::Serialize)
1124)]
1125#[serde(tag = "type")]
1126pub enum GradientDirection {
1127 BottomToTop,
1128 TopToBottom,
1129 RightToLeft,
1130 LeftToRight,
1131 BottomRightToTopLeft,
1132 BottomLeftToTopRight,
1133 TopRightToBottomLeft,
1134 TopLeftBottomRight,
1135 Center,
1136 Angle { value: i64 },
1137}
1138
1139impl GradientDirection {
1140 pub fn from(
1141 l: Option<String>,
1142 doc_id: &str,
1143 ) -> ftd_rt::Result<Option<ftd_rt::GradientDirection>> {
1144 let l = match l {
1145 Some(l) => l,
1146 None => return Ok(None),
1147 };
1148
1149 if l == "bottom to top" {
1150 return Ok(Some(GradientDirection::BottomToTop));
1151 }
1152 if l == "top to bottom" {
1153 return Ok(Some(GradientDirection::TopToBottom));
1154 }
1155 if l == "right to left" {
1156 return Ok(Some(GradientDirection::RightToLeft));
1157 }
1158 if l == "left to right" {
1159 return Ok(Some(GradientDirection::LeftToRight));
1160 }
1161 if l == "bottom-left to top-right" {
1162 return Ok(Some(GradientDirection::BottomLeftToTopRight));
1163 }
1164 if l == "bottom-right to top-left" {
1165 return Ok(Some(GradientDirection::BottomRightToTopLeft));
1166 }
1167 if l == "top-right to bottom-left" {
1168 return Ok(Some(GradientDirection::TopRightToBottomLeft));
1169 }
1170 if l == "top-left to bottom-right" {
1171 return Ok(Some(GradientDirection::TopLeftBottomRight));
1172 }
1173 if l == "center" {
1174 return Ok(Some(GradientDirection::Center));
1175 }
1176 if l.starts_with("angle ") {
1177 let v = crate::get_name("angle", l.as_str(), doc_id)?;
1178 return match v.parse() {
1179 Ok(v) => Ok(Some(GradientDirection::Angle { value: v })),
1180 Err(_) => crate::e(format!("{} is not a valid integer", v), doc_id),
1181 };
1182 }
1183 Ok(None)
1184 }
1185}
1186
1187#[derive(serde::Deserialize)]
1188#[cfg_attr(
1189 not(feature = "wasm"),
1190 derive(Debug, PartialEq, Clone, serde::Serialize)
1191)]
1192pub enum AttributeType {
1193 Style,
1194 Attribute,
1195}
1196
1197#[derive(serde::Deserialize)]
1198#[cfg_attr(
1199 not(feature = "wasm"),
1200 derive(Debug, PartialEq, Clone, serde::Serialize)
1201)]
1202pub struct ConditionalAttribute {
1203 pub attribute_type: AttributeType,
1204 pub conditions_with_value: Vec<(ftd_rt::Condition, ConditionalValue)>,
1205 pub default: Option<ConditionalValue>,
1206}
1207
1208#[derive(serde::Deserialize, Clone)]
1209#[cfg_attr(not(feature = "wasm"), derive(Debug, PartialEq, serde::Serialize))]
1210pub struct ConditionalValue {
1211 pub value: String,
1212 pub important: bool,
1213}
1214
1215#[derive(serde::Deserialize)]
1216#[cfg_attr(
1217 not(feature = "wasm"),
1218 derive(Debug, PartialEq, Clone, serde::Serialize, Default)
1219)]
1220pub struct Common {
1221 pub conditional_attribute: std::collections::BTreeMap<String, ConditionalAttribute>,
1222 pub locals: ftd_rt::Map,
1223 pub condition: Option<ftd_rt::Condition>,
1224 pub is_not_visible: bool,
1225 pub events: Vec<ftd_rt::Event>,
1226 pub reference: Option<String>,
1227 pub region: Option<Region>,
1228 pub padding: Option<i64>,
1229 pub padding_vertical: Option<i64>,
1230 pub padding_horizontal: Option<i64>,
1231 pub padding_left: Option<i64>,
1232 pub padding_right: Option<i64>,
1233 pub padding_top: Option<i64>,
1234 pub padding_bottom: Option<i64>,
1235 pub border_top_radius: Option<i64>,
1236 pub border_bottom_radius: Option<i64>,
1237 pub border_left_radius: Option<i64>,
1238 pub border_right_radius: Option<i64>,
1239 pub width: Option<Length>,
1240 pub max_width: Option<Length>,
1241 pub min_width: Option<Length>,
1242 pub height: Option<Length>,
1243 pub min_height: Option<Length>,
1244 pub max_height: Option<Length>,
1245 pub color: Option<Color>,
1246 pub background_color: Option<Color>,
1247 pub border_color: Option<Color>,
1248 pub border_width: i64,
1249 pub border_radius: i64,
1250 pub id: Option<String>,
1251 pub data_id: Option<String>,
1252 pub overflow_x: Option<Overflow>,
1253 pub overflow_y: Option<Overflow>,
1254 pub border_top: Option<i64>,
1255 pub border_left: Option<i64>,
1256 pub border_right: Option<i64>,
1257 pub border_bottom: Option<i64>,
1258 pub margin_top: Option<i64>,
1259 pub margin_left: Option<i64>,
1260 pub margin_right: Option<i64>,
1261 pub margin_bottom: Option<i64>,
1262 pub link: Option<String>,
1263 pub open_in_new_tab: bool,
1264 pub sticky: bool,
1265 pub top: Option<i64>,
1266 pub bottom: Option<i64>,
1267 pub left: Option<i64>,
1268 pub right: Option<i64>,
1269 pub submit: Option<String>,
1270 pub cursor: Option<String>,
1271 pub shadow_offset_x: Option<i64>,
1272 pub shadow_offset_y: Option<i64>,
1273 pub shadow_size: Option<i64>,
1274 pub shadow_blur: Option<i64>,
1275 pub shadow_color: Option<Color>,
1276 pub anchor: Option<ftd_rt::Anchor>,
1277 pub gradient_direction: Option<GradientDirection>,
1278 pub gradient_colors: Vec<Color>,
1279 pub background_image: Option<String>,
1280 pub background_repeat: bool,
1281 pub background_parallax: bool,
1282 pub scale: Option<f64>,
1283 pub scale_x: Option<f64>,
1284 pub scale_y: Option<f64>,
1285 pub rotate: Option<i64>,
1286 pub move_up: Option<i64>,
1287 pub move_down: Option<i64>,
1288 pub move_left: Option<i64>,
1289 pub move_right: Option<i64>,
1290 pub position: Position,
1291 pub inner: bool,
1292 }
1296
1297#[derive(serde::Deserialize)]
1298#[cfg_attr(
1299 not(feature = "wasm"),
1300 derive(Debug, PartialEq, Clone, serde::Serialize, Default)
1301)]
1302pub struct Container {
1303 pub children: Vec<ftd_rt::Element>,
1304 pub external_children: Option<(String, Vec<Vec<usize>>, Vec<ftd_rt::Element>)>,
1305 pub open: (Option<bool>, Option<String>),
1306 pub spacing: Option<i64>,
1307 pub wrap: bool,
1308}
1309
1310impl Container {
1311 pub fn is_open(&self, is_container_children_empty: bool) -> (bool, Option<String>) {
1312 (
1313 self.open
1314 .0
1315 .unwrap_or_else(|| (self.children.is_empty() && is_container_children_empty)),
1316 self.open.1.clone(),
1317 )
1318 }
1319}
1320
1321#[derive(serde::Deserialize)]
1322#[cfg_attr(
1323 not(feature = "wasm"),
1324 derive(Debug, PartialEq, Clone, serde::Serialize, Default)
1325)]
1326pub struct Image {
1327 pub src: String,
1328 pub description: String,
1329 pub common: Common,
1330 pub crop: bool,
1331}
1332
1333#[derive(serde::Deserialize)]
1334#[cfg_attr(
1335 not(feature = "wasm"),
1336 derive(Debug, PartialEq, Clone, serde::Serialize, Default)
1337)]
1338pub struct Row {
1339 pub container: Container,
1340 pub common: Common,
1341}
1342
1343#[derive(serde::Deserialize)]
1344#[cfg_attr(
1345 not(feature = "wasm"),
1346 derive(Debug, PartialEq, Clone, serde::Serialize, Default)
1347)]
1348pub struct Scene {
1349 pub container: Container,
1350 pub common: Common,
1351}
1352
1353#[derive(serde::Deserialize)]
1354#[cfg_attr(
1355 not(feature = "wasm"),
1356 derive(Debug, PartialEq, Clone, serde::Serialize, Default)
1357)]
1358pub struct Column {
1359 pub container: Container,
1360 pub common: Common,
1361}
1362
1363#[derive(serde::Deserialize)]
1364#[cfg_attr(
1365 not(feature = "wasm"),
1366 derive(Debug, PartialEq, Clone, serde::Serialize)
1367)]
1368#[serde(tag = "type")]
1369pub enum TextAlign {
1370 Left,
1371 Right,
1372 Center,
1373 Justify,
1374}
1375
1376impl Default for TextAlign {
1377 fn default() -> Self {
1378 ftd_rt::TextAlign::Left
1379 }
1380}
1381
1382impl TextAlign {
1383 pub fn from(l: Option<String>, doc_id: &str) -> ftd_rt::Result<ftd_rt::TextAlign> {
1384 Ok(match l.as_deref() {
1385 Some("center") => ftd_rt::TextAlign::Center,
1386 Some("left") => ftd_rt::TextAlign::Left,
1387 Some("right") => ftd_rt::TextAlign::Right,
1388 Some("justify") => ftd_rt::TextAlign::Justify,
1389 Some(t) => return crate::e(format!("{} is not a valid alignment", t), doc_id),
1390 None => return Ok(ftd_rt::TextAlign::Left),
1391 })
1392 }
1393}
1394
1395#[derive(serde::Deserialize)]
1396#[cfg_attr(
1397 not(feature = "wasm"),
1398 derive(Debug, PartialEq, Clone, serde::Serialize)
1399)]
1400#[serde(tag = "type")]
1401pub enum FontDisplay {
1402 Swap,
1403 Block,
1404}
1405impl Default for ftd_rt::FontDisplay {
1406 fn default() -> Self {
1407 ftd_rt::FontDisplay::Block
1408 }
1409}
1410
1411impl FontDisplay {
1412 pub fn from(l: Option<String>, doc_id: &str) -> ftd_rt::Result<ftd_rt::FontDisplay> {
1413 Ok(match l.as_deref() {
1414 Some("swap") => ftd_rt::FontDisplay::Swap,
1415 Some("block") => ftd_rt::FontDisplay::Block,
1416 Some(t) => return crate::e(format!("{} is not a valid alignment", t), doc_id),
1417 None => return Ok(ftd_rt::FontDisplay::Block),
1418 })
1419 }
1420}
1421
1422#[derive(serde::Deserialize)]
1423#[cfg_attr(
1424 not(feature = "wasm"),
1425 derive(Debug, PartialEq, Clone, serde::Serialize)
1426)]
1427#[serde(tag = "type")]
1428pub enum NamedFont {
1429 Monospace,
1430 Serif,
1431 SansSerif,
1432 Named { value: String },
1433}
1434
1435impl NamedFont {
1436 pub fn from(l: Option<String>) -> ftd_rt::Result<ftd_rt::NamedFont> {
1437 Ok(match l.as_deref() {
1438 Some("monospace") => ftd_rt::NamedFont::Monospace,
1439 Some("serif") => ftd_rt::NamedFont::Serif,
1440 Some("sansSerif") => ftd_rt::NamedFont::SansSerif,
1441 Some(t) => ftd_rt::NamedFont::Named {
1442 value: t.to_string(),
1443 },
1444 None => return Ok(ftd_rt::NamedFont::Serif),
1445 })
1446 }
1447}
1448
1449#[derive(serde::Deserialize)]
1450#[cfg_attr(
1451 not(feature = "wasm"),
1452 derive(Debug, PartialEq, Clone, serde::Serialize, Default)
1453)]
1454pub struct ExternalFont {
1455 pub url: String,
1456 pub name: String,
1457 pub display: FontDisplay,
1458}
1459
1460#[derive(serde::Deserialize)]
1461#[cfg_attr(
1462 not(feature = "wasm"),
1463 derive(Debug, PartialEq, Clone, serde::Serialize)
1464)]
1465#[serde(tag = "type")]
1466pub enum Weight {
1467 Heavy,
1468 ExtraBold,
1469 Bold,
1470 SemiBold,
1471 Medium,
1472 Regular,
1473 Light,
1474 ExtraLight,
1475 HairLine,
1476}
1477
1478impl Default for Weight {
1479 fn default() -> Self {
1480 ftd_rt::Weight::Regular
1481 }
1482}
1483
1484#[derive(serde::Deserialize)]
1485#[cfg_attr(
1486 not(feature = "wasm"),
1487 derive(Debug, PartialEq, Clone, serde::Serialize, Default)
1488)]
1489pub struct Style {
1490 pub italic: bool,
1491 pub underline: bool,
1492 pub strike: bool,
1493 pub weight: ftd_rt::Weight,
1494}
1495
1496impl Style {
1497 pub fn from(l: Option<String>, doc_id: &str) -> ftd_rt::Result<ftd_rt::Style> {
1498 let mut s = Style {
1499 italic: false,
1500 underline: false,
1501 strike: false,
1502 weight: Default::default(),
1503 };
1504 let l = match l {
1505 Some(v) => v,
1506 None => return Ok(s),
1507 };
1508 for part in l.split_ascii_whitespace() {
1510 match part {
1511 "italic" => s.italic = true,
1512 "underline" => s.underline = true,
1513 "strike" => s.strike = true,
1514 "heavy" => s.weight = ftd_rt::Weight::Heavy,
1515 "extra-bold" => s.weight = ftd_rt::Weight::ExtraBold,
1516 "bold" => s.weight = ftd_rt::Weight::Bold,
1517 "semi-bold" => s.weight = ftd_rt::Weight::SemiBold,
1518 "medium" => s.weight = ftd_rt::Weight::Medium,
1519 "regular" => s.weight = ftd_rt::Weight::Regular,
1520 "light" => s.weight = ftd_rt::Weight::Light,
1521 "extra-light" => s.weight = ftd_rt::Weight::ExtraLight,
1522 "hairline" => s.weight = ftd_rt::Weight::HairLine,
1523 t => return crate::e(format!("{} is not a valid style", t), doc_id),
1524 }
1525 }
1526 Ok(s)
1527 }
1528}
1529
1530#[derive(serde::Deserialize)]
1531#[cfg_attr(
1532 not(feature = "wasm"),
1533 derive(Debug, PartialEq, Clone, serde::Serialize)
1534)]
1535#[serde(tag = "type")]
1536pub enum TextFormat {
1537 Markdown,
1539 Latex,
1540 Code { lang: String },
1541 Text,
1542}
1543
1544impl Default for ftd_rt::TextFormat {
1545 fn default() -> ftd_rt::TextFormat {
1546 ftd_rt::TextFormat::Markdown
1547 }
1548}
1549
1550impl TextFormat {
1551 pub fn from(
1552 l: Option<String>,
1553 lang: Option<String>,
1554 doc_id: &str,
1555 ) -> ftd_rt::Result<ftd_rt::TextFormat> {
1556 Ok(match l.as_deref() {
1557 Some("markdown") => ftd_rt::TextFormat::Markdown,
1558 Some("latex") => ftd_rt::TextFormat::Latex,
1559 Some("code") => ftd_rt::TextFormat::Code {
1560 lang: lang.unwrap_or_else(|| "txt".to_string()),
1561 },
1562 Some("text") => ftd_rt::TextFormat::Text,
1563 Some(t) => return ftd_rt::e(format!("{} is not a valid format", t), doc_id),
1564 None => return Ok(ftd_rt::TextFormat::Markdown),
1565 })
1566 }
1567}
1568
1569#[derive(serde::Deserialize)]
1570#[cfg_attr(
1571 not(feature = "wasm"),
1572 derive(Debug, PartialEq, Clone, serde::Serialize, Default)
1573)]
1574pub struct IFrame {
1575 pub src: String,
1576 pub common: Common,
1577}
1578
1579#[derive(serde::Deserialize)]
1580#[cfg_attr(
1581 not(feature = "wasm"),
1582 derive(Debug, PartialEq, Clone, serde::Serialize, Default)
1583)]
1584pub struct Text {
1585 pub text: ftd_rt::Rendered,
1586 pub line: bool,
1587 pub common: Common,
1588 pub text_align: TextAlign,
1589 pub style: Style,
1590 pub size: Option<i64>,
1591 pub font: Vec<NamedFont>,
1592 pub external_font: Option<ExternalFont>,
1593 pub line_height: Option<i64>,
1594 pub line_clamp: Option<i64>,
1595 }
1603
1604#[derive(serde::Deserialize)]
1605#[cfg_attr(
1606 not(feature = "wasm"),
1607 derive(Debug, PartialEq, Clone, serde::Serialize, Default)
1608)]
1609pub struct TextBlock {
1610 pub text: ftd_rt::Rendered,
1611 pub line: bool,
1612 pub common: Common,
1613 pub text_align: TextAlign,
1614 pub style: Style,
1615 pub size: Option<i64>,
1616 pub font: Vec<NamedFont>,
1617 pub external_font: Option<ExternalFont>,
1618 pub line_height: Option<i64>,
1619 pub line_clamp: Option<i64>,
1620}
1621
1622#[derive(serde::Deserialize)]
1623#[cfg_attr(
1624 not(feature = "wasm"),
1625 derive(Debug, PartialEq, Clone, serde::Serialize, Default)
1626)]
1627pub struct Code {
1628 pub text: ftd_rt::Rendered,
1629 pub common: Common,
1630 pub text_align: TextAlign,
1631 pub style: Style,
1632 pub size: Option<i64>,
1633 pub font: Vec<NamedFont>,
1634 pub external_font: Option<ExternalFont>,
1635 pub line_height: Option<i64>,
1636 pub line_clamp: Option<i64>,
1637}
1638
1639#[derive(serde::Deserialize)]
1640#[cfg_attr(
1641 not(feature = "wasm"),
1642 derive(Debug, PartialEq, Clone, serde::Serialize, Default)
1643)]
1644pub struct Color {
1645 pub r: u8,
1646 pub g: u8,
1647 pub b: u8,
1648 pub alpha: f32,
1649}
1650
1651#[derive(serde::Deserialize)]
1652#[cfg_attr(
1653 not(feature = "wasm"),
1654 derive(Debug, PartialEq, Clone, serde::Serialize, Default)
1655)]
1656pub struct Input {
1657 pub common: Common,
1658 pub placeholder: Option<String>,
1659}