212 lines
7.7 KiB
Rust
212 lines
7.7 KiB
Rust
use crate::parser::Filter;
|
|
use crate::parser::KVPair;
|
|
use crate::parser::OwnedLiteral;
|
|
use crate::parser::RValue;
|
|
use crate::renderer::breadcrumb_tree::BreadcrumbTreeElement;
|
|
use crate::renderer::context_element::CompareContextElement;
|
|
use crate::renderer::context_element::ContextElement;
|
|
use crate::renderer::context_element::IceResult;
|
|
use crate::renderer::context_element::IntoContextElement;
|
|
use crate::renderer::tree_walking::walk_path;
|
|
use crate::renderer::DustRenderer;
|
|
use crate::renderer::Loopable;
|
|
use crate::renderer::RenderError;
|
|
use crate::renderer::Renderable;
|
|
use crate::renderer::Truthiness;
|
|
use crate::renderer::WalkError;
|
|
use crate::renderer::Walkable;
|
|
use std::borrow::Borrow;
|
|
use std::cmp::Ordering;
|
|
use std::collections::HashMap;
|
|
use std::rc::Rc;
|
|
|
|
#[derive(Debug)]
|
|
pub struct ParametersContext<'a> {
|
|
params: HashMap<&'a str, BreadcrumbTreeElement<'a>>,
|
|
}
|
|
|
|
impl<'a> ParametersContext<'a> {
|
|
pub fn new(
|
|
renderer: &DustRenderer,
|
|
breadcrumbs: &'a Vec<BreadcrumbTreeElement<'a>>,
|
|
params: &'a Vec<KVPair>,
|
|
) -> Self {
|
|
// If the parameter is a Path, then we resolve it immediately
|
|
// to a context element because those are resolved using the
|
|
// breadcrumbs at the time of assignment.
|
|
//
|
|
// If the parameter is a template (for example `foo="{bar}"`)
|
|
// then those are resolved at the time of access rather than
|
|
// the time of assignment, so we leave them into their
|
|
// original IntoContextElement state.
|
|
let rendered_params: HashMap<&'a str, BreadcrumbTreeElement<'a>> = params
|
|
.iter()
|
|
.map(|kvpair| {
|
|
let k = kvpair.key;
|
|
let v: Option<BreadcrumbTreeElement<'a>> = match &kvpair.value {
|
|
RValue::RVLiteral(owned_literal) => {
|
|
Some(BreadcrumbTreeElement::from_borrowed(&kvpair.value))
|
|
}
|
|
RValue::RVPath(path) => kvpair
|
|
.value
|
|
.into_context_element(renderer, breadcrumbs)
|
|
.map(std::convert::From::from),
|
|
RValue::RVTemplate(template) => {
|
|
Some(BreadcrumbTreeElement::from_borrowed(&kvpair.value))
|
|
}
|
|
};
|
|
v.map(|some_v| (k, some_v))
|
|
})
|
|
// TODO: Should a None value here be the same as a key not existing, or should we store the Nones?
|
|
.filter_map(|pair| pair)
|
|
.collect();
|
|
|
|
ParametersContext {
|
|
params: rendered_params,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> IntoContextElement for ParametersContext<'a> {
|
|
fn into_context_element<'b>(
|
|
&'b self,
|
|
renderer: &DustRenderer,
|
|
breadcrumbs: &'b Vec<BreadcrumbTreeElement<'b>>,
|
|
) -> Option<IceResult<'b>> {
|
|
panic!("into_context_element cannot be called on pseudo elements");
|
|
}
|
|
}
|
|
|
|
impl<'a> Walkable for ParametersContext<'a> {
|
|
fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> {
|
|
self.params
|
|
.get(segment)
|
|
.map(|bte| bte.borrow())
|
|
.ok_or(WalkError::CantWalk)
|
|
}
|
|
|
|
fn is_pseudo_element(&self) -> bool {
|
|
true
|
|
}
|
|
}
|
|
|
|
impl<'a> IntoContextElement for RValue<'a> {
|
|
fn into_context_element<'b>(
|
|
&'b self,
|
|
renderer: &DustRenderer,
|
|
breadcrumbs: &'b Vec<BreadcrumbTreeElement<'b>>,
|
|
) -> Option<IceResult<'b>> {
|
|
match self {
|
|
RValue::RVLiteral(owned_literal) => Some(IceResult::from_borrowed(owned_literal)),
|
|
RValue::RVPath(path) => walk_path(breadcrumbs, &path.keys)
|
|
.map(|ice| ice.into_context_element(renderer, breadcrumbs))
|
|
.ok()
|
|
.flatten(),
|
|
RValue::RVTemplate(template) => renderer
|
|
.render_partial_name(template, breadcrumbs)
|
|
.map(|rendered| OwnedLiteral::LString(rendered))
|
|
.ok()
|
|
.map(|owned_literal| IceResult::from_owned(owned_literal)),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> Walkable for RValue<'a> {
|
|
fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> {
|
|
Err(WalkError::CantWalk)
|
|
}
|
|
}
|
|
|
|
impl ContextElement for OwnedLiteral {}
|
|
|
|
impl Truthiness for OwnedLiteral {
|
|
fn is_truthy(&self) -> bool {
|
|
match self {
|
|
OwnedLiteral::LString(text) => !text.is_empty(),
|
|
OwnedLiteral::LPositiveInteger(num) => true,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Renderable for OwnedLiteral {
|
|
fn render(&self, _filters: &Vec<Filter>) -> Result<String, RenderError> {
|
|
match self {
|
|
OwnedLiteral::LString(text) => Ok(text.clone()),
|
|
OwnedLiteral::LPositiveInteger(num) => Ok(num.to_string()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Loopable for OwnedLiteral {
|
|
fn get_loop_elements(&self) -> Vec<&dyn ContextElement> {
|
|
Vec::new()
|
|
}
|
|
}
|
|
|
|
impl Walkable for OwnedLiteral {
|
|
fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> {
|
|
Err(WalkError::CantWalk)
|
|
}
|
|
}
|
|
|
|
impl CompareContextElement for OwnedLiteral {
|
|
fn equals(&self, other: &dyn ContextElement) -> bool {
|
|
// println!("equals literal {:?} | {:?}", self, other);
|
|
// If its an OwnedLiteral then compare them directly,
|
|
// otherwise defer to the other type's implementation of
|
|
// CompareContextElement since the end user could add any
|
|
// type.
|
|
match other.to_any().downcast_ref::<Self>() {
|
|
None => other.equals(self),
|
|
Some(other_literal) => match (self, other_literal) {
|
|
(OwnedLiteral::LString(self_text), OwnedLiteral::LString(other_text)) => {
|
|
self_text == other_text
|
|
}
|
|
(OwnedLiteral::LPositiveInteger(self_num), OwnedLiteral::LString(other_text)) => {
|
|
&self_num.to_string() == other_text
|
|
}
|
|
(OwnedLiteral::LString(self_text), OwnedLiteral::LPositiveInteger(other_num)) => {
|
|
self_text == &other_num.to_string()
|
|
}
|
|
(
|
|
OwnedLiteral::LPositiveInteger(self_num),
|
|
OwnedLiteral::LPositiveInteger(other_num),
|
|
) => self_num == other_num,
|
|
},
|
|
}
|
|
}
|
|
|
|
fn partial_compare(&self, other: &dyn ContextElement) -> Option<Ordering> {
|
|
// println!("partial_compare literal {:?} | {:?}", self, other);
|
|
// If its an OwnedLiteral then compare them directly,
|
|
// otherwise defer to the other type's implementation of
|
|
// CompareContextElement since the end user could add any
|
|
// type.
|
|
match other.to_any().downcast_ref::<Self>() {
|
|
None => match other.partial_compare(self) {
|
|
None => None,
|
|
Some(ord) => match ord {
|
|
Ordering::Equal => Some(Ordering::Equal),
|
|
Ordering::Greater => Some(Ordering::Less),
|
|
Ordering::Less => Some(Ordering::Greater),
|
|
},
|
|
},
|
|
Some(other_literal) => match (self, other_literal) {
|
|
(OwnedLiteral::LString(self_text), OwnedLiteral::LString(other_text)) => {
|
|
self_text.partial_cmp(other_text)
|
|
}
|
|
(OwnedLiteral::LPositiveInteger(self_num), OwnedLiteral::LString(other_text)) => {
|
|
self_num.to_string().partial_cmp(other_text)
|
|
}
|
|
(OwnedLiteral::LString(self_text), OwnedLiteral::LPositiveInteger(other_num)) => {
|
|
self_text.partial_cmp(&other_num.to_string())
|
|
}
|
|
(
|
|
OwnedLiteral::LPositiveInteger(self_num),
|
|
OwnedLiteral::LPositiveInteger(other_num),
|
|
) => self_num.partial_cmp(other_num),
|
|
},
|
|
}
|
|
}
|
|
}
|