microsoft_fast_convert/syntax/
mod.rs1pub(crate) mod fast_v3_ts;
2pub(crate) mod webui;
3
4use crate::error::{template_context, ConvertError};
5use crate::html::parse_attributes;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub struct SyntaxMetadata {
10 pub name: &'static str,
12 pub extension: &'static str,
14 pub suffix: &'static str,
16}
17
18const SYNTAX_METADATA: &[SyntaxMetadata] = &[webui::METADATA, fast_v3_ts::METADATA];
19
20pub fn syntax_metadata() -> &'static [SyntaxMetadata] {
21 SYNTAX_METADATA
22}
23
24pub(crate) fn accepted_syntax_names() -> String {
25 syntax_metadata()
26 .iter()
27 .map(|metadata| metadata.name)
28 .collect::<Vec<_>>()
29 .join(", ")
30}
31
32pub fn syntax_metadata_json() -> String {
33 let mut out = String::from("[");
34 for (index, metadata) in syntax_metadata().iter().enumerate() {
35 if index > 0 {
36 out.push(',');
37 }
38 out.push_str("{\"syntax\":\"");
39 out.push_str(metadata.name);
40 out.push_str("\",\"extension\":\"");
41 out.push_str(metadata.extension);
42 out.push_str("\",\"suffix\":\"");
43 out.push_str(metadata.suffix);
44 out.push_str("\"}");
45 }
46 out.push(']');
47 out
48}
49
50pub(crate) fn required_binding_value(
51 open_tag: &str,
52 tag: &str,
53 template: &str,
54 at: usize,
55) -> Result<String, ConvertError> {
56 let value = parse_attributes(open_tag)
57 .into_iter()
58 .find(|attr| attr.name == "value")
59 .and_then(|attr| attr.value)
60 .ok_or_else(|| ConvertError::MissingValueAttribute {
61 tag: tag.to_string(),
62 context: template_context(template, at),
63 })?;
64
65 let trimmed = value.trim();
66 if !trimmed.starts_with("{{") || !trimmed.ends_with("}}") || trimmed.len() <= 4 {
67 return Err(ConvertError::InvalidDirectiveValue {
68 tag: tag.to_string(),
69 value: Some(value),
70 context: template_context(template, at),
71 });
72 }
73
74 let expr = trimmed[2..trimmed.len() - 2].trim();
75 if expr.is_empty() {
76 return Err(ConvertError::InvalidDirectiveValue {
77 tag: tag.to_string(),
78 value: Some(value),
79 context: template_context(template, at),
80 });
81 }
82
83 Ok(expr.to_string())
84}
85
86pub(crate) fn validate_directive_attrs(
87 open_tag: &str,
88 template: &str,
89 at: usize,
90) -> Result<(), ConvertError> {
91 for attr in parse_attributes(open_tag) {
92 if attr.name.starts_with("f-") {
93 return Err(ConvertError::UnsupportedFAttribute {
94 attribute: attr.name,
95 context: template_context(template, at),
96 });
97 }
98 }
99 Ok(())
100}
101
102pub(crate) fn is_supported_f_attribute(name: &str) -> bool {
103 matches!(name, "f-ref" | "f-children" | "f-slotted")
104}
105
106pub(crate) fn strip_single_brace(value: &str) -> Option<&str> {
107 let trimmed = value.trim();
108 if trimmed.starts_with('{')
109 && trimmed.ends_with('}')
110 && !trimmed.starts_with("{{")
111 && trimmed.len() >= 2
112 {
113 Some(trimmed[1..trimmed.len() - 1].trim())
114 } else {
115 None
116 }
117}