diff --git a/src/bindings.rs b/src/bindings.rs new file mode 100644 index 0000000..546f4b1 --- /dev/null +++ b/src/bindings.rs @@ -0,0 +1,74 @@ +use kuchiki::{Attribute, ElementData, ExpandedName, NodeData}; +use lazy_static::lazy_static; +use regex::{Captures, Regex}; + +use crate::template::TemplateContext; + +pub struct BindingContext<'a> { + node: &'a NodeData, + ctx: &'a TemplateContext +} + +lazy_static! { + pub static ref BIND_REGEX: Regex = Regex::new(r"[^\!]?\{\{(?P.*?)\}\}").unwrap(); + pub static ref LITERAL_BIND_REGEX: Regex = Regex::new(r"\!\{\{").unwrap(); +} + +impl<'a> BindingContext<'a> { + pub fn new(node: &'a NodeData, ctx: &'a TemplateContext) -> Self { + Self { + node, ctx + } + } + + pub fn expand_attributes(&self) { + if let NodeData::Element(element) = self.node { + let mut attrs = element.attributes.borrow_mut(); + for (name, value) in attrs.map.clone() { + if name.local.starts_with('[') && name.local.ends_with(']') { + attrs.map.insert( + ExpandedName::new( + "", + name.local + .clone() + .strip_prefix('[') + .unwrap() + .strip_suffix(']') + .unwrap(), + ), + Attribute { + prefix: None, + value: self.ctx + .attrs + .get(&ExpandedName::new("", value.value)) + .map(|attr| attr.value.clone()) + .unwrap_or_default(), + }, + ); + } + } + } + } + + pub fn expand_text(&self) { + if let NodeData::Text(text_ref) = self.node { + let mut text = text_ref.borrow_mut(); + *text = BIND_REGEX + .replace_all(&text, |caps: &Captures| { + if let Some(name) = caps.get(1) { + self.ctx.attrs + .get(&ExpandedName::new("", name.as_str())) + .map(|attr| attr.value.clone()) + .unwrap_or_default() + } else { + "".to_string() + } + }) + .to_string(); + *text = LITERAL_BIND_REGEX + .replace_all(&text, "{{") + .to_string(); + + } + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 68b61db..786b926 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,6 +19,7 @@ mod config; mod markdown; mod server; mod template; +mod bindings; const BUILD_DIR: &str = "_build"; diff --git a/src/template.rs b/src/template.rs index 41ae4f2..58f37da 100644 --- a/src/template.rs +++ b/src/template.rs @@ -1,5 +1,6 @@ use html5ever::{local_name, ns, QualName}; use kuchiki::{traits::*, Attribute, ExpandedName, NodeData, NodeRef}; +use lazy_static::lazy_static; use regex::{Captures, Regex}; use std::{ cell::RefCell, @@ -10,7 +11,7 @@ use std::{ rc::Rc, }; -use crate::{config::SETTINGS, markdown}; +use crate::{bindings::BindingContext, config::SETTINGS, markdown}; #[derive(Clone, Debug)] pub struct Template { @@ -92,8 +93,6 @@ impl Template { ctx: &TemplateContext, ) -> Result<(), Box> { let scripts_ref_cloned = scripts_ref.clone(); - let bind_regex = Regex::new(r"[^\!]?\{\{(?P.*?)\}\}").unwrap(); - let literal_bind_regex = Regex::new(r"\!\{\{").unwrap(); let node = root.deref_mut(); let settings = SETTINGS.lock().unwrap(); @@ -152,33 +151,11 @@ impl Template { self.expand_tree_recursive(&mut child, scripts_ref, registrar, ctx)?; } + let binding = BindingContext::new(node.data(), ctx); match node.data() { NodeData::Element(el) => { - let mut attrs = el.attributes.borrow_mut(); - for (name, value) in attrs.map.clone() { - if name.local.starts_with('[') && name.local.ends_with(']') { - attrs.map.insert( - ExpandedName::new( - "", - name.local - .clone() - .strip_prefix('[') - .unwrap() - .strip_suffix(']') - .unwrap(), - ), - Attribute { - prefix: None, - value: ctx - .attrs - .get(&ExpandedName::new("", value.value)) - .map(|attr| attr.value.clone()) - .unwrap_or_default(), - }, - ); - } - } - drop(attrs); + binding.expand_attributes(); + if el.name.local.to_string().contains('-') { let (rendered_contents, new_scripts) = ctx .loader @@ -220,28 +197,13 @@ impl Template { } } NodeData::Text(text_ref) => { - let mut text = text_ref.borrow_mut(); - *text = bind_regex - .replace_all(&text, |caps: &Captures| { - if let Some(name) = caps.get(1) { - ctx.attrs - .get(&ExpandedName::new("", name.as_str())) - .map(|attr| attr.value.clone()) - .unwrap_or_default() - } else { - "".to_string() - } - }) - .to_string(); - *text = literal_bind_regex - .replace_all(&text, "{{") - .to_string(); + binding.expand_text(); if let Some(registrar) = registrar { registrar .borrow_mut() .connected_scripts - .push(text.to_string()); + .push(text_ref.borrow().to_string()); node.detach(); }; } @@ -296,6 +258,8 @@ impl Template { ) -> Result<(NodeRef, HashMap), Box> { match &self.extends { Some(tmpl) => { + BindingContext::new(tmpl.data(), ctx).expand_attributes(); + let attrs = tmpl.as_element().unwrap().attributes.borrow(); let (contents, scripts) = self.render_basic(ctx)?; let new_scripts = Rc::new(RefCell::new(HashMap::new()));