Layout XML text nodes are now parsed template strings instead of raw text containing {{template tags}}

This commit is contained in:
Keavon Chambers 2020-08-02 23:53:56 -07:00
parent 214c1c09ac
commit 3205e8d274
3 changed files with 31 additions and 22 deletions

View File

@ -1,6 +1,7 @@
use crate::layout_abstract_types::*; use crate::layout_abstract_types::*;
// AST for a component with info on its definition (from the root element of the XML layout) and a vector of direct child component tags // AST for a component with info on its definition (from the root element of the XML layout) and a vector of direct child component tags
#[derive(Debug, Clone, PartialEq)]
pub struct FlatComponent { pub struct FlatComponent {
// The abstract definition of the root node of the component with attribute parameters // The abstract definition of the root node of the component with attribute parameters
pub own_info: LayoutComponentDefinition, pub own_info: LayoutComponentDefinition,
@ -48,7 +49,7 @@ pub type NodeOrDefTree = rctree::Node<LayoutComponentNodeOrDefinition>;
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum LayoutComponentNode { pub enum LayoutComponentNode {
Tag(LayoutComponentTag), Tag(LayoutComponentTag),
Text(String), Text(Vec<TemplateStringSegment>),
} }
impl LayoutComponentNode { impl LayoutComponentNode {
@ -58,7 +59,7 @@ impl LayoutComponentNode {
} }
/// Given some text hanging out in the XML between tags, construct a `LayoutComponentNode` with that text which simply stores the provided `String` /// Given some text hanging out in the XML between tags, construct a `LayoutComponentNode` with that text which simply stores the provided `String`
pub fn new_text(text: String) -> Self { pub fn new_text(text: Vec<TemplateStringSegment>) -> Self {
Self::Text(text) Self::Text(text)
} }
@ -67,7 +68,7 @@ impl LayoutComponentNode {
pub fn debug_print(&self) { pub fn debug_print(&self) {
match self { match self {
LayoutComponentNode::Tag(tag) => tag.debug_print(), LayoutComponentNode::Tag(tag) => tag.debug_print(),
LayoutComponentNode::Text(text) => println!("================> Text Node: {}", text), LayoutComponentNode::Text(text) => println!("================> Text Node: {:#?}", text),
} }
} }
} }

View File

@ -157,24 +157,7 @@ impl AttributeParser {
Some([height]) if height.eq_ignore_ascii_case("height") => TypeValueOrArgument::TypeValue(TypeValue::Dimension(Dimension::Height)), Some([height]) if height.eq_ignore_ascii_case("height") => TypeValueOrArgument::TypeValue(TypeValue::Dimension(Dimension::Height)),
// TemplateString: `? ... {{?}} ...` // TemplateString: `? ... {{?}} ...`
Some(["`", string, "`"]) => { Some(["`", string, "`"]) => {
let mut segments = Vec::<TemplateStringSegment>::new(); let segments = self.parse_text_template_sequence(string);
let mut is_template = false;
// Alternate between string and handlebars, always starting wtih string even if empty, and push abstract tokens of non-empty ones to the TemplateString sequence
for part in self.split_by_string_templates_regex.split(string) {
// Push only non-empty template string segments (a String or Argument)
if !part.is_empty() {
// Based on whether we are alternating to a string or template, push the appropriate abstract token
let segment = match is_template {
false => TemplateStringSegment::String(String::from(part)),
true => TemplateStringSegment::Argument(TypeValueOrArgument::VariableArgument(String::from(part))),
};
segments.push(segment);
}
// The next iteration will switch from a template to a string or vice versa
is_template = !is_template;
}
TypeValueOrArgument::TypeValue(TypeValue::TemplateString(segments)) TypeValueOrArgument::TypeValue(TypeValue::TemplateString(segments))
}, },
@ -299,6 +282,30 @@ impl AttributeParser {
} }
} }
/// Extract {{template tags}} from surrounding text and return a vector alternating between text and the argument
pub fn parse_text_template_sequence(&self, input_text: &str) -> Vec<TemplateStringSegment> {
let mut segments = Vec::<TemplateStringSegment>::new();
let mut is_template = false;
// Alternate between string and handlebars, always starting wtih string even if empty, and push abstract tokens of non-empty ones to the TemplateString sequence
for part in self.split_by_string_templates_regex.split(input_text) {
// Push only non-empty template string segments (a String or Argument)
if !part.is_empty() {
// Based on whether we are alternating to a string or template, push the appropriate abstract token
let segment = match is_template {
false => TemplateStringSegment::String(String::from(part)),
true => TemplateStringSegment::Argument(TypeValueOrArgument::VariableArgument(String::from(part))),
};
segments.push(segment);
}
// The next iteration will switch from a template to a string or vice versa
is_template = !is_template;
}
segments
}
/// Replace escape characters in an XML string, only supports `&, <, >, ", '` /// Replace escape characters in an XML string, only supports `&, <, >, ", '`
fn unescape_xml(xml: &str) -> String { fn unescape_xml(xml: &str) -> String {
// Find and replace each escape character, starting with `&` to avoid unescaping other escape sequences // Find and replace each escape character, starting with `&` to avoid unescaping other escape sequences

View File

@ -317,7 +317,8 @@ impl LayoutSystem {
.expect(&format!("Encountered text outside the root tag when parsing XML layout in component: {}", path)[..]); .expect(&format!("Encountered text outside the root tag when parsing XML layout in component: {}", path)[..]);
// Construct a text node with the provided text // Construct a text node with the provided text
let abstract_text_node = LayoutComponentNodeOrDefinition::LayoutComponentNode(LayoutComponentNode::new_text(text_string)); let text_template_sequence = attribute_parser.parse_text_template_sequence(&text_string[..]);
let abstract_text_node = LayoutComponentNodeOrDefinition::LayoutComponentNode(LayoutComponentNode::new_text(text_template_sequence));
// Put the text node in a new tree node // Put the text node in a new tree node
let tree_node = rctree::Node::new(abstract_text_node); let tree_node = rctree::Node::new(abstract_text_node);