Created
September 24, 2020 17:13
-
-
Save joshuaclayton/a480fa2d931c558ca222248d5653c057 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/src/attributes.rs b/src/attributes.rs | |
index e1641b2..a03d803 100644 | |
--- a/src/attributes.rs | |
+++ b/src/attributes.rs | |
@@ -38,7 +38,9 @@ fn evaluate_attribute_value_components<'a>( | |
.iter() | |
.map(|v| match v { | |
AttributeValueComponent::RawValue(value) => value.to_string(), | |
- AttributeValueComponent::InterpolatedValue(values) => context.interpret(values), | |
+ AttributeValueComponent::InterpolatedValue(values) => { | |
+ context.interpret(values).unwrap_or("".to_string()) | |
+ } | |
}) | |
.collect() | |
} | |
diff --git a/src/context.rs b/src/context.rs | |
index e05703b..63becd7 100644 | |
--- a/src/context.rs | |
+++ b/src/context.rs | |
@@ -36,8 +36,8 @@ impl Context { | |
Ok(Context { payload }) | |
} | |
- pub fn interpret(&self, value: &[Selector]) -> String { | |
- self.at(value).map(value_to_string).unwrap_or("".into()) | |
+ pub fn interpret(&self, value: &[Selector]) -> Option<String> { | |
+ self.at(value).map(value_to_string) | |
} | |
pub fn at(&self, value: &[Selector]) -> Option<&Value> { | |
diff --git a/src/node.rs b/src/node.rs | |
index 2f61c5f..d779a99 100644 | |
--- a/src/node.rs | |
+++ b/src/node.rs | |
@@ -1,6 +1,6 @@ | |
use super::{ | |
context::{Context, Selector}, | |
- Blocks, Fragments, Nodes, Tag, | |
+ Blocks, Builder, Fragments, Nodes, Tag, | |
}; | |
use serde_json::Value; | |
use std::path::PathBuf; | |
@@ -27,67 +27,76 @@ pub enum Node<'a> { | |
}, | |
} | |
+#[derive(Debug)] | |
+pub enum NodeError<'a> { | |
+ InvalidFragmentPath(PathBuf), | |
+ JSONValueMissingAtSelector(Vec<Selector<'a>>), | |
+ JSONValueNotArrayAtSelector(Vec<Selector<'a>>), | |
+} | |
+ | |
impl<'a> Node<'a> { | |
pub fn to_html( | |
&self, | |
+ mut builder: Builder, | |
context: &Context, | |
fragments: &Fragments<'a>, | |
blocks: &Blocks<'a>, | |
styles: &'a Option<String>, | |
- ) -> String { | |
+ ) -> Builder { | |
match self { | |
- Node::Text(v) => v.to_string(), | |
- Node::InterpolatedText(v) => context.interpret(v), | |
+ Node::Text(v) => builder.append(v.to_string()), | |
+ Node::InterpolatedText(selectors) => match context.interpret(selectors) { | |
+ None => builder.warn(NodeError::JSONValueMissingAtSelector(selectors.to_vec())), | |
+ Some(value) => builder.append(value), | |
+ }, | |
Node::BlockValue(name) => { | |
if let Some(boxed_nodes) = blocks.get(name) { | |
- (*boxed_nodes.to_html(context, fragments, blocks, styles)).to_string() | |
+ builder = boxed_nodes.to_html(builder, context, fragments, blocks, styles); | |
} else { | |
- "".into() | |
+ builder.warn("Unable to find block value") | |
} | |
} | |
- Node::Element { tag, children } => format!( | |
- "{}{}{}{}", | |
- tag.open_tag_html(context), | |
- children.to_html(context, fragments, blocks, styles), | |
- tag.additional_markup(styles), | |
- tag.close_tag_html() | |
- ), | |
+ Node::Element { tag, children } => { | |
+ builder.append(tag.open_tag_html(context)); | |
+ builder = children.to_html(builder, context, fragments, blocks, styles); | |
+ builder.append(tag.additional_markup(styles)); | |
+ builder.append(tag.close_tag_html()); | |
+ } | |
Node::ForLoop { | |
local, | |
selectors, | |
children, | |
- } => { | |
- if let Some(Value::Array(loopable)) = context.at(selectors) { | |
- loopable | |
- .iter() | |
- .map(|looped_value| { | |
- children.to_html( | |
- &context.extend(local, looped_value), | |
- fragments, | |
- blocks, | |
- styles, | |
- ) | |
- }) | |
- .collect::<Vec<String>>() | |
- .join("") | |
- } else { | |
- "".into() | |
+ } => match context.at(selectors) { | |
+ None => builder.warn(NodeError::JSONValueMissingAtSelector(selectors.to_vec())), | |
+ Some(Value::Array(loopable)) => { | |
+ builder = loopable.iter().fold(builder, |acc, looped_value| { | |
+ children.to_html( | |
+ acc, | |
+ &context.extend(local, looped_value), | |
+ fragments, | |
+ blocks, | |
+ styles, | |
+ ) | |
+ }) | |
} | |
- } | |
+ Some(_) => builder.warn(NodeError::JSONValueNotArrayAtSelector(selectors.to_vec())), | |
+ }, | |
Node::Fragment { path } => { | |
if let Some(nodes) = fragments.get(path) { | |
- nodes.to_html(context, fragments, blocks, styles) | |
+ builder = nodes.to_html(builder, context, fragments, blocks, styles) | |
} else { | |
- "".into() | |
+ builder.warn(NodeError::InvalidFragmentPath(path.to_path_buf())) | |
} | |
} | |
Node::Block { name, children } => { | |
if let Some(boxed_nodes) = blocks.get(name) { | |
- (*boxed_nodes.to_html(context, fragments, blocks, styles)).to_string() | |
+ builder = boxed_nodes.to_html(builder, context, fragments, blocks, styles) | |
} else { | |
- children.to_html(context, fragments, blocks, styles) | |
+ builder = children.to_html(builder, context, fragments, blocks, styles) | |
} | |
} | |
- } | |
+ }; | |
+ | |
+ builder | |
} | |
} | |
diff --git a/src/nodes.rs b/src/nodes.rs | |
index 33b3166..b107712 100644 | |
--- a/src/nodes.rs | |
+++ b/src/nodes.rs | |
@@ -13,28 +13,30 @@ pub enum Nodes<'a> { | |
impl<'a> Nodes<'a> { | |
pub fn to_html( | |
&self, | |
+ mut builder: Builder, | |
context: &Context, | |
fragments: &Fragments<'a>, | |
blocks: &Blocks<'a>, | |
styles: &'a Option<String>, | |
- ) -> String { | |
+ ) -> Builder { | |
match self { | |
Nodes::Fragment { nodes } => { | |
- Self::nodes_to_html(nodes, context, fragments, blocks, styles) | |
+ builder = Self::nodes_to_html(builder, nodes, context, fragments, blocks, styles); | |
+ } | |
+ Nodes::Document { nodes } => { | |
+ builder.append("<!DOCTYPE html>".to_string()); | |
+ builder = Self::nodes_to_html(builder, nodes, context, fragments, blocks, styles); | |
} | |
- Nodes::Document { nodes } => format!( | |
- "{}{}", | |
- "<!DOCTYPE html>", | |
- Self::nodes_to_html(nodes, context, fragments, blocks, styles) | |
- ), | |
Nodes::FragmentSubclass { layout, blocks } => { | |
if let Some(nodes) = fragments.get(layout) { | |
- nodes.to_html(context, fragments, blocks, styles) | |
+ builder = nodes.to_html(builder, context, fragments, blocks, styles) | |
} else { | |
- "".into() | |
+ builder.warn("Bad fragment") | |
} | |
} | |
} | |
+ | |
+ builder | |
} | |
pub fn prepend(&mut self, node: Node<'a>) { | |
@@ -65,16 +67,39 @@ impl<'a> Nodes<'a> { | |
} | |
fn nodes_to_html( | |
+ builder: Builder, | |
nodes: &[Node], | |
context: &Context, | |
fragments: &Fragments<'a>, | |
blocks: &Blocks<'a>, | |
styles: &'a Option<String>, | |
- ) -> String { | |
- nodes | |
- .iter() | |
- .map(|n| n.to_html(context, fragments, blocks, styles)) | |
- .collect::<Vec<String>>() | |
- .join("") | |
+ ) -> Builder { | |
+ nodes.iter().fold(builder, |acc, n| { | |
+ n.to_html(acc, context, fragments, blocks, styles) | |
+ }) | |
+ } | |
+} | |
+ | |
+pub struct Builder { | |
+ values: Vec<String>, | |
+} | |
+ | |
+impl std::default::Default for Builder { | |
+ fn default() -> Self { | |
+ Builder { values: vec![] } | |
+ } | |
+} | |
+ | |
+impl Builder { | |
+ pub fn append(&mut self, value: String) { | |
+ self.values.push(value) | |
+ } | |
+ | |
+ pub fn warn<T: std::fmt::Debug>(&mut self, value: T) { | |
+ eprintln!("{:?}", value); | |
+ } | |
+ | |
+ pub fn result(&self) -> String { | |
+ self.values.join("") | |
} | |
} | |
diff --git a/src/socket.rs b/src/socket.rs | |
index 3fdd4b1..3479548 100644 | |
--- a/src/socket.rs | |
+++ b/src/socket.rs | |
@@ -1,6 +1,6 @@ | |
use super::{ | |
context::{Context, ContextError}, | |
- parser, styles, Nodes, | |
+ parser, styles, Builder, Nodes, | |
}; | |
use std::collections::HashMap; | |
use std::path::PathBuf; | |
@@ -109,11 +109,16 @@ impl<'a> Socket<'a> { | |
} | |
pub fn to_html(&self) -> String { | |
- self.nodes.to_html( | |
- &self.context, | |
- &self.fragments, | |
- &HashMap::new(), | |
- &self.styles, | |
- ) | |
+ let builder = Builder::default(); | |
+ | |
+ self.nodes | |
+ .to_html( | |
+ builder, | |
+ &self.context, | |
+ &self.fragments, | |
+ &HashMap::new(), | |
+ &self.styles, | |
+ ) | |
+ .result() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment