1#[derive(Debug, Default)]
2pub struct InterpreterState {
3 pub id: String,
4 pub bag: ftd::Map<ftd::p2::Thing>,
5 pub document_stack: Vec<ParsedDocument>,
6 pub parsed_libs: ftd::Map<Vec<String>>,
7}
8
9impl InterpreterState {
10 fn new(id: String) -> InterpreterState {
11 InterpreterState {
12 id,
13 bag: ftd::p2::interpreter::default_bag(),
14 ..Default::default()
15 }
16 }
17
18 pub fn tdoc<'a>(
19 &'a self,
20 local_variables: &'a mut ftd::Map<ftd::p2::Thing>,
21 referenced_local_variables: &'a mut ftd::Map<String>,
22 ) -> ftd::p2::TDoc<'a> {
23 let l = self.document_stack.len() - 1;
24 ftd::p2::TDoc {
25 name: &self.document_stack[l].name,
26 aliases: &self.document_stack[l].doc_aliases,
27 bag: &self.bag,
28 local_variables,
29 referenced_local_variables,
30 }
31 }
32
33 fn library_in_the_bag(&self, name: &str) -> bool {
34 self.parsed_libs.contains_key(name)
35 }
36
37 fn add_library_to_bag(&mut self, name: &str) {
38 if !self.library_in_the_bag(name) {
39 self.parsed_libs.insert(name.to_string(), vec![]);
40 }
41 }
42
43 fn continue_(mut self) -> ftd::p1::Result<Interpreter> {
44 if self.document_stack.is_empty() {
45 panic!()
46 }
47
48 let l = self.document_stack.len() - 1; self.document_stack[l].ignore_comments();
52 if self.document_stack[l].processing_imports {
55 loop {
58 let top = &mut self.document_stack[l];
59 let module = Self::process_imports(top, &self.bag)?;
60 if let Some(module) = module {
61 if !self.library_in_the_bag(module.as_str()) {
62 self.add_library_to_bag(module.as_str());
63 return Ok(Interpreter::StuckOnImport {
64 state: self,
65 module,
66 });
67 }
68 if let Some(foreign_var_prefix) = self.parsed_libs.get(module.as_str()) {
69 self.document_stack[l]
70 .foreign_variable_prefix
71 .extend_from_slice(foreign_var_prefix.as_slice());
72 }
73 } else {
74 break;
75 }
76 }
77 self.document_stack[l].done_processing_imports();
78 self.document_stack[l].reorder(&self.bag)?;
79 }
80 let parsed_document = &mut self.document_stack[l];
81
82 while let Some(p1) = parsed_document.sections.last_mut() {
83 let doc = ftd::p2::TDoc {
85 name: &parsed_document.name,
86 aliases: &parsed_document.doc_aliases,
87 bag: &self.bag,
88 local_variables: &mut Default::default(),
89 referenced_local_variables: &mut Default::default(),
90 };
91
92 if let Some(variable) = Self::resolve_foreign_variable(
93 p1,
94 parsed_document.foreign_variable_prefix.as_slice(),
95 &doc,
96 )? {
97 return Ok(Interpreter::StuckOnForeignVariable {
98 variable,
99 state: self,
100 });
101 }
102
103 let p1 = parsed_document.sections.pop().unwrap();
106
107 let var_data = ftd::variable::VariableData::get_name_kind(
111 &p1.name,
112 &doc,
113 p1.line_number,
114 &parsed_document.var_types,
115 );
116
117 let mut thing = vec![];
118
119 if p1.name.starts_with("record ") {
120 let d =
122 ftd::p2::Record::from_p1(p1.name.as_str(), &p1.header, &doc, p1.line_number)?;
123 let name = doc.resolve_name(p1.line_number, &d.name.to_string())?;
124 if self.bag.contains_key(name.as_str()) {
125 return ftd::p2::utils::e2(
126 format!("{} is already declared", d.name),
127 doc.name,
128 p1.line_number,
129 );
130 }
131 thing.push((name, ftd::p2::Thing::Record(d)));
132 } else if p1.name.starts_with("or-type ") {
133 let d = ftd::OrType::from_p1(&p1, &doc)?;
135 let name = doc.resolve_name(p1.line_number, &d.name.to_string())?;
136 if self.bag.contains_key(name.as_str()) {
137 return ftd::p2::utils::e2(
138 format!("{} is already declared", d.name),
139 doc.name,
140 p1.line_number,
141 );
142 }
143 thing.push((name, ftd::p2::Thing::OrType(d)));
144 } else if p1.name.starts_with("map ") {
145 let d = ftd::Variable::map_from_p1(&p1, &doc)?;
146 let name = doc.resolve_name(p1.line_number, &d.name.to_string())?;
147 if self.bag.contains_key(name.as_str()) {
148 return ftd::p2::utils::e2(
149 format!("{} is already declared", d.name),
150 doc.name,
151 p1.line_number,
152 );
153 }
154 thing.push((name, ftd::p2::Thing::Variable(d)));
155 } else if p1.name == "container" {
160 parsed_document
161 .instructions
162 .push(ftd::Instruction::ChangeContainer {
163 name: doc.resolve_name_with_instruction(
164 p1.line_number,
165 p1.caption(p1.line_number, doc.name)?.as_str(),
166 &parsed_document.instructions,
167 )?,
168 });
169 } else if let Ok(ftd::variable::VariableData {
170 type_: ftd::variable::Type::Component,
171 ..
172 }) = var_data
173 {
174 let d = ftd::Component::from_p1(&p1, &doc)?;
176 let name = doc.resolve_name(p1.line_number, &d.full_name.to_string())?;
177 if self.bag.contains_key(name.as_str()) {
178 return ftd::p2::utils::e2(
179 format!("{} is already declared", d.full_name),
180 doc.name,
181 p1.line_number,
182 );
183 }
184 thing.push((name, ftd::p2::Thing::Component(d)));
185 } else if let Ok(ref var_data) = var_data {
187 let d = if p1
188 .header
189 .str(doc.name, p1.line_number, "$processor$")
190 .is_ok()
191 {
192 return Ok(Interpreter::StuckOnProcessor {
194 state: self,
195 section: p1,
196 });
197 } else if var_data.is_none() || var_data.is_optional() {
198 ftd::Variable::from_p1(&p1, &doc)?
200 } else {
201 ftd::Variable::list_from_p1(&p1, &doc)?
203 };
204 let name = doc.resolve_name(p1.line_number, &d.name)?;
205 if self.bag.contains_key(name.as_str()) {
206 return ftd::p2::utils::e2(
207 format!("{} is already declared", d.name),
208 doc.name,
209 p1.line_number,
210 );
211 }
212 thing.push((name, ftd::p2::Thing::Variable(d)));
213 } else if let ftd::p2::Thing::Variable(mut v) =
214 doc.get_thing(p1.line_number, p1.name.as_str())?
215 {
216 assert!(
217 !(p1.header
218 .str_optional(doc.name, p1.line_number, "if")?
219 .is_some()
220 && p1
221 .header
222 .str_optional(doc.name, p1.line_number, "$processor$")?
223 .is_some())
224 );
225 let (doc_name, remaining) = ftd::p2::utils::get_doc_name_and_remaining(
226 doc.resolve_name(p1.line_number, p1.name.as_str())?.as_str(),
227 )?;
228 if remaining.is_some()
229 && p1
230 .header
231 .str_optional(doc.name, p1.line_number, "if")?
232 .is_some()
233 {
234 return ftd::p2::utils::e2(
235 "Currently not supporting `if` for field value update.",
236 doc.name,
237 p1.line_number,
238 );
239 }
240 if let Some(expr) = p1.header.str_optional(doc.name, p1.line_number, "if")? {
241 let val = v.get_value(&p1, &doc)?;
242 v.conditions.push((
243 ftd::p2::Boolean::from_expression(
244 expr,
245 &doc,
246 &Default::default(),
247 (None, None),
248 p1.line_number,
249 )?,
250 val,
251 ));
252 } else if p1
253 .header
254 .str_optional(doc.name, p1.line_number, "$processor$")?
255 .is_some()
256 {
257 return Ok(Interpreter::StuckOnProcessor {
259 state: self,
260 section: p1.to_owned(),
261 });
262 } else {
267 v.update_from_p1(&p1, &doc)?;
268 }
269 thing.push((
270 doc.resolve_name(p1.line_number, doc_name.as_str())?,
271 ftd::p2::Thing::Variable(doc.set_value(p1.line_number, p1.name.as_str(), v)?),
272 ));
273 } else {
274 match (doc.get_thing(p1.line_number, p1.name.as_str())?).clone() {
276 ftd::p2::Thing::Variable(_) => {
277 return ftd::p2::utils::e2(
278 format!("variable should have prefix $, found: `{}`", p1.name),
279 doc.name,
280 p1.line_number,
281 );
282 }
283 ftd::p2::Thing::Component(_) => {
284 if p1
285 .header
286 .str_optional(doc.name, p1.line_number, "$processor$")?
287 .is_some()
288 {
289 return Ok(Interpreter::StuckOnProcessor {
291 state: self,
292 section: p1.to_owned(),
293 });
294 }
295 if let Ok(loop_data) = p1.header.str(doc.name, p1.line_number, "$loop$") {
296 let section_to_subsection = ftd::p1::SubSection {
297 name: p1.name.to_string(),
298 caption: p1.caption.to_owned(),
299 header: p1.header.to_owned(),
300 body: p1.body.to_owned(),
301 is_commented: p1.is_commented,
302 line_number: p1.line_number,
303 };
304 parsed_document.instructions.push(
305 ftd::Instruction::RecursiveChildComponent {
306 child: ftd::component::recursive_child_component(
307 loop_data,
308 §ion_to_subsection,
309 &doc,
310 &Default::default(),
311 None,
312 )?,
313 },
314 );
315 } else {
316 let parent = ftd::ChildComponent::from_p1(
317 p1.line_number,
318 p1.name.as_str(),
319 &p1.header,
320 &p1.caption,
321 &p1.body,
322 &doc,
323 &Default::default(),
324 )?;
325
326 let mut children = vec![];
327
328 for sub in p1.sub_sections.0.iter() {
329 if let Ok(loop_data) =
330 sub.header.str(doc.name, p1.line_number, "$loop$")
331 {
332 children.push(ftd::component::recursive_child_component(
333 loop_data,
334 sub,
335 &doc,
336 &parent.arguments,
337 None,
338 )?);
339 } else {
340 let root_name = ftd::p2::utils::get_root_component_name(
341 &doc,
342 parent.root.as_str(),
343 sub.line_number,
344 )?;
345 let child = if root_name.eq("ftd#text") {
346 ftd::p2::utils::get_markup_child(
347 sub,
348 &doc,
349 &parent.arguments,
350 )?
351 } else {
352 ftd::ChildComponent::from_p1(
353 sub.line_number,
354 sub.name.as_str(),
355 &sub.header,
356 &sub.caption,
357 &sub.body,
358 &doc,
359 &parent.arguments,
360 )?
361 };
362 children.push(child);
363 }
364 }
365
366 parsed_document
367 .instructions
368 .push(ftd::Instruction::Component { children, parent })
369 }
370 }
371 ftd::p2::Thing::Record(mut r) => {
372 r.add_instance(&p1, &doc)?;
373 thing.push((
374 doc.resolve_name(p1.line_number, &p1.name)?,
375 ftd::p2::Thing::Record(r),
376 ));
377 }
378 ftd::p2::Thing::OrType(_r) => {
379 return ftd::p2::utils::e2(
381 format!("'{}' is an or-type", p1.name.as_str()),
382 doc.name,
383 p1.line_number,
384 );
385 }
386 ftd::p2::Thing::OrTypeWithVariant { .. } => {
387 return ftd::p2::utils::e2(
389 format!("'{}' is an or-type variant", p1.name.as_str(),),
390 doc.name,
391 p1.line_number,
392 );
393 }
394 };
395 }
396 self.bag.extend(thing);
397 }
398
399 if self.document_stack.len() > 1 {
400 return self.continue_after_pop();
401 }
402
403 let mut rt = ftd::RT::from(
404 &self.id,
405 self.document_stack[0].get_doc_aliases(),
406 self.bag,
407 self.document_stack[0].instructions.clone(),
408 );
409
410 let main = if cfg!(test) {
411 rt.render_()?
412 } else {
413 rt.render()?
414 };
415
416 let d = ftd::p2::document::Document {
417 main,
418 name: rt.name,
419 data: rt.bag.clone(),
420 aliases: rt.aliases,
421 instructions: rt.instructions,
422 };
423
424 Ok(Interpreter::Done { document: d })
425 }
426
427 fn resolve_foreign_variable_name(name: &str) -> String {
428 name.replace('.', "-")
429 }
430
431 fn resolve_foreign_variable(
432 section: &mut ftd::p1::Section,
433 foreign_variables: &[String],
434 doc: &ftd::p2::TDoc,
435 ) -> ftd::p1::Result<Option<String>> {
436 if let Some(variable) = resolve_all_properties(
437 &mut section.caption,
438 &mut section.header,
439 &mut section.body,
440 section.line_number,
441 foreign_variables,
442 doc,
443 )? {
444 return Ok(Some(variable));
445 }
446
447 for subsection in section.sub_sections.0.iter_mut() {
448 if let Some(variable) = resolve_all_properties(
449 &mut subsection.caption,
450 &mut subsection.header,
451 &mut subsection.body,
452 subsection.line_number,
453 foreign_variables,
454 doc,
455 )? {
456 return Ok(Some(variable));
457 }
458 }
459
460 return Ok(None);
461
462 fn resolve_all_properties(
463 caption: &mut Option<String>,
464 header: &mut ftd::p1::Header,
465 body: &mut Option<(usize, String)>,
466 line_number: usize,
467 foreign_variables: &[String],
468 doc: &ftd::p2::TDoc,
469 ) -> ftd::p1::Result<Option<String>> {
470 if let Some(ref mut caption) = caption {
471 if let Some(cap) =
472 process_foreign_variables(caption, foreign_variables, doc, line_number)?
473 {
474 return Ok(Some(cap));
475 }
476 }
477
478 for (line_number, _, header) in header.0.iter_mut() {
479 if let Some(h) =
480 process_foreign_variables(header, foreign_variables, doc, *line_number)?
481 {
482 return Ok(Some(h));
483 }
484 }
485
486 if let Some((line_number, ref mut body)) = body {
487 if let Some(b) =
488 process_foreign_variables(body, foreign_variables, doc, *line_number)?
489 {
490 return Ok(Some(b));
491 }
492 }
493
494 Ok(None)
495 }
496
497 fn process_foreign_variables(
498 value: &mut String,
499 foreign_variables: &[String],
500 doc: &ftd::p2::TDoc,
501 line_number: usize,
502 ) -> ftd::p1::Result<Option<String>> {
503 if value.contains('#') {
504 return Ok(None);
505 }
506 if let Some(val) = value.clone().strip_prefix('$') {
507 if is_foreign_variable(val, foreign_variables, doc, line_number)? {
508 let val = doc.resolve_name(line_number, val)?;
509 *value = ftd::InterpreterState::resolve_foreign_variable_name(
510 format!("${}", val.as_str()).as_str(),
511 );
512 return Ok(Some(val));
513 }
514 }
515 Ok(None)
516 }
517
518 fn is_foreign_variable(
519 variable: &str,
520 foreign_variables: &[String],
521 doc: &ftd::p2::TDoc,
522 line_number: usize,
523 ) -> ftd::p1::Result<bool> {
524 let var_name = doc.resolve_name(line_number, variable)?;
525
526 if foreign_variables.iter().any(|v| var_name.starts_with(v)) {
527 return Ok(true);
528 }
529 Ok(false)
530 }
531 }
532
533 fn process_imports(
534 top: &mut ParsedDocument,
535 bag: &ftd::Map<ftd::p2::Thing>,
536 ) -> ftd::p1::Result<Option<String>> {
537 let mut iteration_index = 0;
538 while iteration_index < top.sections.len() {
539 if top.sections[iteration_index].name != "import" {
540 iteration_index += 1;
541 continue;
542 }
543 let (library_name, alias) = ftd::p2::utils::parse_import(
544 &top.sections[iteration_index].caption,
545 top.name.as_str(),
546 top.sections[iteration_index].line_number,
547 )?;
548
549 top.doc_aliases.insert(alias, library_name.clone());
550
551 if bag.contains_key(library_name.as_str()) {
552 iteration_index += 1;
553 continue;
554 }
555
556 top.sections.remove(iteration_index);
557 return Ok(Some(library_name));
558 }
559
560 Ok(None)
561 }
562
563 pub fn add_foreign_variable_prefix(&mut self, module: &str, prefix: Vec<String>) {
564 if let Some(document) = self.document_stack.last_mut() {
565 document
566 .foreign_variable_prefix
567 .extend_from_slice(prefix.as_slice());
568 }
569 self.parsed_libs.insert(module.to_string(), prefix);
570 }
571
572 pub fn continue_after_import(mut self, id: &str, source: &str) -> ftd::p1::Result<Interpreter> {
573 self.document_stack.push(ParsedDocument::parse(id, source)?);
574 self.continue_()
575 }
578
579 pub fn continue_after_variable(
580 mut self,
581 variable: &str,
582 value: ftd::Value,
583 ) -> ftd::p1::Result<Interpreter> {
584 let l = self.document_stack.len() - 1;
585 let doc = ftd::p2::TDoc {
586 name: &self.document_stack[l].name,
587 aliases: &self.document_stack[l].doc_aliases,
588 bag: &self.bag,
589 local_variables: &mut Default::default(),
590 referenced_local_variables: &mut Default::default(),
591 };
592 let var_name = ftd::InterpreterState::resolve_foreign_variable_name(
593 doc.resolve_name(0, variable)?.as_str(),
594 );
595 self.bag.insert(
596 var_name.clone(),
597 ftd::p2::Thing::Variable(ftd::Variable {
598 name: var_name,
599 value: ftd::PropertyValue::Value { value },
600 conditions: vec![],
601 flags: Default::default(),
602 }),
603 );
604 self.continue_()
605 }
606
607 pub fn continue_after_pop(mut self) -> ftd::p1::Result<Interpreter> {
608 self.document_stack.pop();
609 self.continue_()
610 }
613
614 pub fn continue_after_processor(
615 mut self,
616 p1: &ftd::p1::Section,
617 value: ftd::Value,
618 ) -> ftd::p1::Result<Interpreter> {
619 let l = self.document_stack.len() - 1;
620 let parsed_document = &mut self.document_stack[l];
621
622 let doc = ftd::p2::TDoc {
623 name: &parsed_document.name,
624 aliases: &parsed_document.doc_aliases,
625 bag: &self.bag,
626 local_variables: &mut Default::default(),
627 referenced_local_variables: &mut Default::default(),
628 };
629
630 let var_data = ftd::variable::VariableData::get_name_kind(
631 &p1.name,
632 &doc,
633 p1.line_number,
634 &parsed_document.var_types,
635 );
636
637 if let Ok(ftd::variable::VariableData {
638 type_: ftd::variable::Type::Variable,
639 name,
640 ..
641 }) = var_data
642 {
643 let name = doc.resolve_name(p1.line_number, &name)?;
644 let variable = ftd::p2::Thing::Variable(ftd::Variable {
645 name: name.clone(),
646 value: ftd::PropertyValue::Value { value },
647 conditions: vec![],
648 flags: ftd::variable::VariableFlags::from_p1(&p1.header, doc.name, p1.line_number)?,
649 });
650 self.bag.insert(name, variable);
651 return self.continue_();
652 }
653
654 match doc.get_thing(p1.line_number, p1.name.as_str())? {
655 ftd::p2::Thing::Variable(mut v) => {
656 let doc_name = ftd::p2::utils::get_doc_name_and_remaining(
658 doc.resolve_name(p1.line_number, p1.name.as_str())?.as_str(),
659 )?
660 .0;
661 v.value = ftd::PropertyValue::Value { value };
662 let key = doc.resolve_name(p1.line_number, doc_name.as_str())?;
663 let variable =
664 ftd::p2::Thing::Variable(doc.set_value(p1.line_number, p1.name.as_str(), v)?);
665 self.bag.insert(key, variable);
666 }
667 ftd::p2::Thing::Component(_) => {
668 let mut p1 = p1.clone();
670 Self::p1_from_processor(&mut p1, value);
671 parsed_document.sections.push(p1.to_owned());
672 }
673 _ => todo!(), }
675 self.continue_()
676 }
679
680 pub(crate) fn p1_from_processor(p1: &mut ftd::p1::Section, value: ftd::Value) {
681 for (idx, (_, k, _)) in p1.header.0.iter().enumerate() {
682 if k.eq("$processor$") {
683 p1.header.0.remove(idx);
684 break;
685 }
686 }
687 if let ftd::Value::Object { values } = value {
688 for (k, v) in values {
689 let v = if let ftd::PropertyValue::Value { value } = v {
690 if let Some(v) = value.to_string() {
691 v
692 } else {
693 continue;
694 }
695 } else {
696 continue;
697 };
698
699 if k.eq("$body$") {
700 p1.body = Some((p1.line_number, v));
701 } else if k.eq("$caption$") {
702 p1.caption = Some(v);
703 } else {
704 p1.header.0.push((p1.line_number, k, v));
705 }
706 }
707 }
708 }
709}
710
711#[derive(Debug, Clone)]
712pub struct ParsedDocument {
713 name: String,
714 sections: Vec<ftd::p1::Section>,
715 processing_imports: bool,
716 doc_aliases: ftd::Map<String>,
717 var_types: Vec<String>,
718 foreign_variable_prefix: Vec<String>,
719 instructions: Vec<ftd::Instruction>,
720}
721
722impl ParsedDocument {
723 fn parse(id: &str, source: &str) -> ftd::p1::Result<ParsedDocument> {
724 Ok(ParsedDocument {
725 name: id.to_string(),
726 sections: ftd::p1::parse(source, id)?,
727 processing_imports: true,
728 doc_aliases: ftd::p2::interpreter::default_aliases(),
729 var_types: Default::default(),
730 foreign_variable_prefix: vec![],
731 instructions: vec![],
732 })
733 }
734
735 fn done_processing_imports(&mut self) {
736 self.processing_imports = false;
737 }
738
739 fn ignore_comments(&mut self) {
764 self.sections = self
765 .sections
766 .iter()
767 .filter(|s| !s.is_commented)
768 .map(|s| s.remove_comments())
769 .collect::<Vec<ftd::p1::Section>>();
770 }
771
772 fn reorder(&mut self, bag: &ftd::Map<ftd::p2::Thing>) -> ftd::p1::Result<()> {
773 let (mut new_p1, var_types) = ftd::p2::utils::reorder(
774 &self.sections,
775 &ftd::p2::TDoc {
776 name: &self.name,
777 aliases: &self.doc_aliases,
778 bag,
779 local_variables: &mut Default::default(),
780 referenced_local_variables: &mut Default::default(),
781 },
782 )?;
783 new_p1.reverse();
784 self.sections = new_p1;
785 self.var_types = var_types;
786 Ok(())
787 }
788
789 pub fn get_doc_aliases(&self) -> ftd::Map<String> {
790 self.doc_aliases.clone()
791 }
792}
793
794#[allow(clippy::large_enum_variant)]
795#[derive(Debug)]
796pub enum Interpreter {
797 StuckOnImport {
798 module: String,
799 state: InterpreterState,
800 },
801 StuckOnProcessor {
802 state: InterpreterState,
803 section: ftd::p1::Section,
804 },
805 StuckOnForeignVariable {
806 variable: String,
807 state: InterpreterState,
808 },
809 Done {
810 document: ftd::p2::Document,
811 },
812}
813
814pub fn interpret(id: &str, source: &str) -> ftd::p1::Result<Interpreter> {
815 let mut s = InterpreterState::new(id.to_string());
816 s.document_stack.push(ParsedDocument::parse(id, source)?);
817 s.continue_()
818}
819
820#[allow(clippy::large_enum_variant)]
821#[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize)]
822#[serde(tag = "type")]
823pub enum Thing {
824 Component(ftd::Component),
825 Variable(ftd::Variable),
826 Record(ftd::p2::Record),
827 OrType(ftd::OrType),
828 OrTypeWithVariant { e: ftd::OrType, variant: String },
829 }
831
832pub fn default_bag() -> ftd::Map<ftd::p2::Thing> {
833 let record = |n: &str, r: &str| (n.to_string(), ftd::p2::Kind::record(r));
834 let color = |n: &str| record(n, "ftd#color");
835 std::iter::IntoIterator::into_iter([
836 (
837 "ftd#row".to_string(),
838 ftd::p2::Thing::Component(ftd::p2::element::row_function()),
839 ),
840 (
841 "ftd#column".to_string(),
842 ftd::p2::Thing::Component(ftd::p2::element::column_function()),
843 ),
844 (
845 "ftd#text-block".to_string(),
846 ftd::p2::Thing::Component(ftd::p2::element::text_function()),
847 ),
848 (
849 "ftd#code".to_string(),
850 ftd::p2::Thing::Component(ftd::p2::element::code_function()),
851 ),
852 (
853 "ftd#image".to_string(),
854 ftd::p2::Thing::Component(ftd::p2::element::image_function()),
855 ),
856 (
857 "ftd#iframe".to_string(),
858 ftd::p2::Thing::Component(ftd::p2::element::iframe_function()),
859 ),
860 (
861 "ftd#integer".to_string(),
862 ftd::p2::Thing::Component(ftd::p2::element::integer_function()),
863 ),
864 (
865 "ftd#decimal".to_string(),
866 ftd::p2::Thing::Component(ftd::p2::element::decimal_function()),
867 ),
868 (
869 "ftd#boolean".to_string(),
870 ftd::p2::Thing::Component(ftd::p2::element::boolean_function()),
871 ),
872 (
873 "ftd#scene".to_string(),
874 ftd::p2::Thing::Component(ftd::p2::element::scene_function()),
875 ),
876 (
877 "ftd#grid".to_string(),
878 ftd::p2::Thing::Component(ftd::p2::element::grid_function()),
879 ),
880 (
881 "ftd#text".to_string(),
882 ftd::p2::Thing::Component(ftd::p2::element::markup_function()),
883 ),
884 (
885 "ftd#input".to_string(),
886 ftd::p2::Thing::Component(ftd::p2::element::input_function()),
887 ),
888 (
889 "ftd#null".to_string(),
890 ftd::p2::Thing::Component(ftd::p2::element::null()),
891 ),
892 (
893 "ftd#dark-mode".to_string(),
894 ftd::p2::Thing::Variable(ftd::Variable {
895 name: "ftd#dark-mode".to_string(),
896 value: ftd::PropertyValue::Value {
897 value: ftd::Value::Boolean { value: false },
898 },
899 conditions: vec![],
900 flags: ftd::VariableFlags {
901 always_include: Some(true),
902 },
903 }),
904 ),
905 (
906 "ftd#system-dark-mode".to_string(),
907 ftd::p2::Thing::Variable(ftd::Variable {
908 name: "ftd#system-dark-mode".to_string(),
909 value: ftd::PropertyValue::Value {
910 value: ftd::Value::Boolean { value: false },
911 },
912 conditions: vec![],
913 flags: ftd::VariableFlags {
914 always_include: Some(true),
915 },
916 }),
917 ),
918 (
919 "ftd#follow-system-dark-mode".to_string(),
920 ftd::p2::Thing::Variable(ftd::Variable {
921 name: "ftd#follow-system-dark-mode".to_string(),
922 value: ftd::PropertyValue::Value {
923 value: ftd::Value::Boolean { value: true },
924 },
925 conditions: vec![],
926 flags: ftd::VariableFlags {
927 always_include: Some(true),
928 },
929 }),
930 ),
931 (
932 "ftd#device".to_string(),
933 ftd::p2::Thing::Variable(ftd::Variable {
934 name: "ftd#device".to_string(),
935 value: ftd::PropertyValue::Value {
936 value: ftd::Value::String {
937 text: "desktop".to_string(),
938 source: ftd::TextSource::Header,
939 },
940 },
941 conditions: vec![],
942 flags: ftd::VariableFlags {
943 always_include: Some(true),
944 },
945 }),
946 ),
947 (
948 "ftd#mobile-breakpoint".to_string(),
949 ftd::p2::Thing::Variable(ftd::Variable {
950 name: "ftd#mobile-breakpoint".to_string(),
951 value: ftd::PropertyValue::Value {
952 value: ftd::Value::Integer { value: 768 },
953 },
954 conditions: vec![],
955 flags: ftd::VariableFlags {
956 always_include: Some(true),
957 },
958 }),
959 ),
960 (
961 "ftd#desktop-breakpoint".to_string(),
962 ftd::p2::Thing::Variable(ftd::Variable {
963 name: "ftd#desktop-breakpoint".to_string(),
964 value: ftd::PropertyValue::Value {
965 value: ftd::Value::Integer { value: 1440 },
966 },
967 conditions: vec![],
968 flags: ftd::VariableFlags {
969 always_include: Some(true),
970 },
971 }),
972 ),
973 (
974 "ftd#markdown-color-data".to_string(),
975 ftd::p2::Thing::Record(ftd::p2::Record {
976 name: "ftd#markdown-color-data".to_string(),
977 fields: std::iter::IntoIterator::into_iter([
978 ("link".to_string(), ftd::p2::Kind::record("ftd#color")),
979 ("code".to_string(), ftd::p2::Kind::record("ftd#color")),
980 ("link-code".to_string(), ftd::p2::Kind::record("ftd#color")),
981 (
982 "link-visited".to_string(),
983 ftd::p2::Kind::record("ftd#color"),
984 ),
985 (
986 "link-visited-code".to_string(),
987 ftd::p2::Kind::record("ftd#color"),
988 ),
989 (
990 "ul-ol-li-before".to_string(),
991 ftd::p2::Kind::record("ftd#color"),
992 ),
993 ])
994 .collect(),
995 instances: Default::default(),
996 order: vec![
997 "link".to_string(),
998 "code".to_string(),
999 "link-code".to_string(),
1000 "link-visited".to_string(),
1001 "link-visited-code".to_string(),
1002 "ul-ol-li-before".to_string(),
1003 ],
1004 }),
1005 ),
1006 ("ftd#markdown-color".to_string(), markdown::color()),
1007 (
1008 "ftd#markdown-background-color-data".to_string(),
1009 ftd::p2::Thing::Record(ftd::p2::Record {
1010 name: "ftd#markdown-background-color-data".to_string(),
1011 fields: std::iter::IntoIterator::into_iter([
1012 ("link".to_string(), ftd::p2::Kind::record("ftd#color")),
1013 ("code".to_string(), ftd::p2::Kind::record("ftd#color")),
1014 ("link-code".to_string(), ftd::p2::Kind::record("ftd#color")),
1015 (
1016 "link-visited".to_string(),
1017 ftd::p2::Kind::record("ftd#color"),
1018 ),
1019 (
1020 "link-visited-code".to_string(),
1021 ftd::p2::Kind::record("ftd#color"),
1022 ),
1023 (
1024 "ul-ol-li-before".to_string(),
1025 ftd::p2::Kind::record("ftd#color"),
1026 ),
1027 ])
1028 .collect(),
1029 instances: Default::default(),
1030 order: vec![
1031 "link".to_string(),
1032 "code".to_string(),
1033 "link-code".to_string(),
1034 "link-visited".to_string(),
1035 "link-visited-code".to_string(),
1036 "ul-ol-li-before".to_string(),
1037 ],
1038 }),
1039 ),
1040 (
1041 "ftd#markdown-background-color".to_string(),
1042 markdown::background_color(),
1043 ),
1044 (
1045 "ftd#image-src".to_string(),
1046 ftd::p2::Thing::Record(ftd::p2::Record {
1047 name: "ftd#image-src".to_string(),
1048 fields: std::iter::IntoIterator::into_iter([
1049 ("light".to_string(), ftd::p2::Kind::caption()),
1050 ("dark".to_string(), ftd::p2::Kind::string()),
1051 ])
1052 .collect(),
1053 instances: Default::default(),
1054 order: vec!["light".to_string(), "dark".to_string()],
1055 }),
1056 ),
1057 (
1058 "ftd#color".to_string(),
1059 ftd::p2::Thing::Record(ftd::p2::Record {
1060 name: "ftd#color".to_string(),
1061 fields: std::iter::IntoIterator::into_iter([
1062 ("light".to_string(), ftd::p2::Kind::caption()),
1063 ("dark".to_string(), ftd::p2::Kind::string()),
1064 ])
1065 .collect(),
1066 instances: Default::default(),
1067 order: vec!["light".to_string(), "dark".to_string()],
1068 }),
1069 ),
1070 (
1071 "ftd#font-size".to_string(),
1072 ftd::p2::Thing::Record(ftd::p2::Record {
1073 name: "ftd#font-size".to_string(),
1074 fields: std::iter::IntoIterator::into_iter([
1075 ("line-height".to_string(), ftd::p2::Kind::integer()),
1076 ("size".to_string(), ftd::p2::Kind::integer()),
1077 (
1078 "letter-spacing".to_string(),
1079 ftd::p2::Kind::integer().set_default(Some("0".to_string())),
1080 ),
1081 ])
1082 .collect(),
1083 instances: Default::default(),
1084 order: vec![
1085 "line-height".to_string(),
1086 "size".to_string(),
1087 "letter-spacing".to_string(),
1088 ],
1089 }),
1090 ),
1091 (
1092 "ftd#type".to_string(),
1093 ftd::p2::Thing::Record(ftd::p2::Record {
1094 name: "ftd#type".to_string(),
1095 fields: std::iter::IntoIterator::into_iter([
1096 ("font".to_string(), ftd::p2::Kind::caption()),
1097 (
1098 "desktop".to_string(),
1099 ftd::p2::Kind::record("ftd#font-size"),
1100 ),
1101 ("mobile".to_string(), ftd::p2::Kind::record("ftd#font-size")),
1102 ("xl".to_string(), ftd::p2::Kind::record("ftd#font-size")),
1103 (
1104 "weight".to_string(),
1105 ftd::p2::Kind::integer().set_default(Some("400".to_string())),
1106 ),
1107 ("style".to_string(), ftd::p2::Kind::string().into_optional()),
1108 ])
1109 .collect(),
1110 instances: Default::default(),
1111 order: vec![
1112 "font".to_string(),
1113 "desktop".to_string(),
1114 "mobile".to_string(),
1115 "xl".to_string(),
1116 "weight".to_string(),
1117 "style".to_string(),
1118 ],
1119 }),
1120 ),
1121 (
1122 "ftd#btb".to_string(),
1123 ftd::p2::Thing::Record(ftd::p2::Record {
1124 name: "ftd#btb".to_string(),
1125 fields: std::iter::IntoIterator::into_iter([
1126 color("base"),
1127 color("text"),
1128 color("border"),
1129 ])
1130 .collect(),
1131 instances: Default::default(),
1132 order: vec!["base".to_string(), "text".to_string(), "border".to_string()],
1133 }),
1134 ),
1135 (
1136 "ftd#pst".to_string(),
1137 ftd::p2::Thing::Record(ftd::p2::Record {
1138 name: "ftd#pst".to_string(),
1139 fields: std::iter::IntoIterator::into_iter([
1140 color("primary"),
1141 color("secondary"),
1142 color("tertiary"),
1143 ])
1144 .collect(),
1145 instances: Default::default(),
1146 order: vec![
1147 "primary".to_string(),
1148 "secondary".to_string(),
1149 "tertiary".to_string(),
1150 ],
1151 }),
1152 ),
1153 (
1154 "ftd#background-colors".to_string(),
1155 ftd::p2::Thing::Record(ftd::p2::Record {
1156 name: "ftd#background-colors".to_string(),
1157 fields: std::iter::IntoIterator::into_iter([
1158 color("base"),
1159 color("step-1"),
1160 color("step-2"),
1161 color("overlay"),
1162 color("code"),
1163 ])
1164 .collect(),
1165 instances: Default::default(),
1166 order: vec![
1167 "base".to_string(),
1168 "step-1".to_string(),
1169 "step-2".to_string(),
1170 "overlay".to_string(),
1171 "code".to_string(),
1172 ],
1173 }),
1174 ),
1175 (
1176 "ftd#custom-colors".to_string(),
1177 ftd::p2::Thing::Record(ftd::p2::Record {
1178 name: "ftd#custom-colors".to_string(),
1179 fields: std::iter::IntoIterator::into_iter([
1180 color("one"),
1181 color("two"),
1182 color("three"),
1183 color("four"),
1184 color("five"),
1185 color("six"),
1186 color("seven"),
1187 color("eight"),
1188 color("nine"),
1189 color("ten"),
1190 ])
1191 .collect(),
1192 instances: Default::default(),
1193 order: vec![
1194 "one".to_string(),
1195 "two".to_string(),
1196 "three".to_string(),
1197 "four".to_string(),
1198 "five".to_string(),
1199 "six".to_string(),
1200 "seven".to_string(),
1201 "eight".to_string(),
1202 "nine".to_string(),
1203 "ten".to_string(),
1204 ],
1205 }),
1206 ),
1207 (
1208 "ftd#cta-colors".to_string(),
1209 ftd::p2::Thing::Record(ftd::p2::Record {
1210 name: "ftd#cta-colors".to_string(),
1211 fields: std::iter::IntoIterator::into_iter([
1212 color("base"),
1213 color("hover"),
1214 color("pressed"),
1215 color("disabled"),
1216 color("focused"),
1217 color("border"),
1218 color("text"),
1219 ])
1220 .collect(),
1221 instances: Default::default(),
1222 order: vec![
1223 "base".to_string(),
1224 "hover".to_string(),
1225 "pressed".to_string(),
1226 "disabled".to_string(),
1227 "focused".to_string(),
1228 "border".to_string(),
1229 "text".to_string(),
1230 ],
1231 }),
1232 ),
1233 (
1234 "ftd#color-scheme".to_string(),
1235 ftd::p2::Thing::Record(ftd::p2::Record {
1236 name: "ftd#color-scheme".to_string(),
1237 fields: std::iter::IntoIterator::into_iter([
1238 record("background", "ftd#background-colors"),
1239 color("border"),
1240 color("border-strong"),
1241 color("text"),
1242 color("text-strong"),
1243 color("shadow"),
1244 color("scrim"),
1245 record("cta-primary", "ftd#cta-colors"),
1246 record("cta-secondary", "ftd#cta-colors"),
1247 record("cta-tertiary", "ftd#cta-colors"),
1248 record("cta-danger", "ftd#cta-colors"),
1249 record("accent", "ftd#pst"),
1250 record("error", "ftd#btb"),
1251 record("success", "ftd#btb"),
1252 record("info", "ftd#btb"),
1253 record("warning", "ftd#btb"),
1254 record("custom", "ftd#custom-colors"),
1255 ])
1256 .collect(),
1257 instances: Default::default(),
1258 order: vec![
1259 "background".to_string(),
1260 "border".to_string(),
1261 "border-strong".to_string(),
1262 "text".to_string(),
1263 "text-strong".to_string(),
1264 "shadow".to_string(),
1265 "scrim".to_string(),
1266 "cta-primary".to_string(),
1267 "cta-secondary".to_string(),
1268 "cta-tertiary".to_string(),
1269 "cta-danger".to_string(),
1270 "accent".to_string(),
1271 "error".to_string(),
1272 "success".to_string(),
1273 "info".to_string(),
1274 "warning".to_string(),
1275 "custom".to_string(),
1276 ],
1277 }),
1278 ),
1279 ])
1280 .collect()
1281}
1282
1283pub fn default_aliases() -> ftd::Map<String> {
1284 std::iter::IntoIterator::into_iter([("ftd".to_string(), "ftd".to_string())]).collect()
1285}
1286
1287pub fn default_column() -> ftd::Column {
1288 ftd::Column {
1289 common: ftd::Common {
1290 width: Some(ftd::Length::Fill),
1291 height: Some(ftd::Length::Fill),
1292 position: Some(ftd::Position::Center),
1293 ..Default::default()
1294 },
1295 spacing: None,
1296 ..Default::default()
1297 }
1298}
1299
1300pub mod markdown {
1301 fn theme_color(light: &str, dark: &str) -> ftd::PropertyValue {
1302 ftd::PropertyValue::Value {
1303 value: ftd::Value::Record {
1304 name: "ftd#color".to_string(),
1305 fields: std::iter::IntoIterator::into_iter([
1306 (
1307 "light".to_string(),
1308 ftd::PropertyValue::Value {
1309 value: ftd::Value::String {
1310 text: light.to_string(),
1311 source: ftd::TextSource::Caption,
1312 },
1313 },
1314 ),
1315 (
1316 "dark".to_string(),
1317 ftd::PropertyValue::Value {
1318 value: ftd::Value::String {
1319 text: dark.to_string(),
1320 source: ftd::TextSource::Header,
1321 },
1322 },
1323 ),
1324 ])
1325 .collect(),
1326 },
1327 }
1328 }
1329
1330 fn link(light: &str, dark: &str) -> (String, ftd::PropertyValue) {
1331 ("link".to_string(), theme_color(light, dark))
1332 }
1333
1334 fn code(light: &str, dark: &str) -> (String, ftd::PropertyValue) {
1335 ("code".to_string(), theme_color(light, dark))
1336 }
1337
1338 fn link_visited(light: &str, dark: &str) -> (String, ftd::PropertyValue) {
1339 ("link-visited".to_string(), theme_color(light, dark))
1340 }
1341
1342 fn link_code(light: &str, dark: &str) -> (String, ftd::PropertyValue) {
1343 ("link-code".to_string(), theme_color(light, dark))
1344 }
1345
1346 fn link_visited_code(light: &str, dark: &str) -> (String, ftd::PropertyValue) {
1347 ("link-visited-code".to_string(), theme_color(light, dark))
1348 }
1349
1350 fn ul_ol_li_before(light: &str, dark: &str) -> (String, ftd::PropertyValue) {
1351 ("ul-ol-li-before".to_string(), theme_color(light, dark))
1352 }
1353
1354 fn blockquote(light: &str, dark: &str) -> (String, ftd::PropertyValue) {
1355 ("blockquote".to_string(), theme_color(light, dark))
1356 }
1357
1358 pub fn color() -> ftd::p2::Thing {
1359 ftd::p2::Thing::Variable(ftd::Variable {
1360 name: "ftd#markdown-color".to_string(),
1361 value: ftd::PropertyValue::Value {
1362 value: ftd::Value::Record {
1363 name: "ftd#markdown-color-data".to_string(),
1364 fields: std::iter::IntoIterator::into_iter([
1365 link("#136351", "#25c19f"),
1366 code("#000000", "#25c19f"),
1367 link_visited("#7b3ee8", "#0f5750"),
1368 link_code("#136351", "#25c19f"),
1369 link_visited_code("#136351", "#0f5750"),
1370 ul_ol_li_before("#000000", "#ffffff"),
1371 ])
1372 .collect(),
1373 },
1374 },
1375 conditions: vec![],
1376 flags: ftd::VariableFlags {
1377 always_include: Some(true),
1378 },
1379 })
1380 }
1381
1382 pub fn background_color() -> ftd::p2::Thing {
1383 ftd::p2::Thing::Variable(ftd::Variable {
1384 name: "ftd#markdown-background-color".to_string(),
1385 value: ftd::PropertyValue::Value {
1386 value: ftd::Value::Record {
1387 name: "ftd#markdown-background-color-data".to_string(),
1388 fields: std::iter::IntoIterator::into_iter([
1389 link("#136351", "#25c19f"),
1390 code("#f6f7f8", "#ffffff"),
1391 link_visited("#7b3ee8", "#0f5750"),
1392 link_code("#136351", "#25c19f"),
1393 link_visited_code("#136351", "#0f5750"),
1394 ul_ol_li_before("#000000", "#ffffff"),
1395 blockquote("#f6f7f8", "#f0f0f0"),
1396 ])
1397 .collect(),
1398 },
1399 },
1400 conditions: vec![],
1401 flags: ftd::VariableFlags {
1402 always_include: Some(true),
1403 },
1404 })
1405 }
1406}
1407
1408