use crate::de::FromStrVisitor;
use crate::expr::{Expression, TemplateExpr};
use crate::{format, parser, Error, Identifier, Result};
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display};
use std::str::FromStr;
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Template {
elements: Vec<Element>,
}
impl Template {
pub fn new() -> Template {
Template {
elements: Vec::new(),
}
}
pub fn from_expr(expr: &TemplateExpr) -> Result<Self> {
Template::from_str(expr.as_str())
}
pub fn elements(&self) -> &[Element] {
&self.elements
}
pub fn elements_mut(&mut self) -> &mut [Element] {
&mut self.elements
}
}
impl Template {
pub fn add_element<T>(mut self, element: T) -> Template
where
T: Into<Element>,
{
self.elements.push(element.into());
self
}
pub fn add_literal<T>(self, literal: T) -> Template
where
T: Into<String>,
{
self.add_element(literal.into())
}
pub fn add_interpolation<T>(self, interpolation: T) -> Template
where
T: Into<Interpolation>,
{
self.add_element(interpolation.into())
}
pub fn add_directive<T>(self, directive: T) -> Template
where
T: Into<Directive>,
{
self.add_element(directive.into())
}
}
impl FromStr for Template {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
parser::parse_template(s)
}
}
impl<T> FromIterator<T> for Template
where
T: Into<Element>,
{
fn from_iter<I>(iter: I) -> Self
where
I: IntoIterator<Item = T>,
{
Template {
elements: iter.into_iter().map(Into::into).collect(),
}
}
}
impl Display for Template {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let formatted = format::to_string(self).expect("a Template failed to format unexpectedly");
f.write_str(&formatted)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Element {
Literal(String),
Interpolation(Interpolation),
Directive(Directive),
}
impl Element {
pub(crate) fn strip_start(&self) -> bool {
match self {
Element::Literal(_) => false,
Element::Interpolation(interp) => interp.strip.strip_start(),
Element::Directive(dir) => dir.strip_start(),
}
}
pub(crate) fn strip_end(&self) -> bool {
match self {
Element::Literal(_) => false,
Element::Interpolation(interp) => interp.strip.strip_end(),
Element::Directive(dir) => dir.strip_end(),
}
}
}
impl From<&str> for Element {
fn from(literal: &str) -> Self {
Element::Literal(literal.to_owned())
}
}
impl From<String> for Element {
fn from(literal: String) -> Self {
Element::Literal(literal)
}
}
impl From<Interpolation> for Element {
fn from(interpolation: Interpolation) -> Self {
Element::Interpolation(interpolation)
}
}
impl From<Directive> for Element {
fn from(directive: Directive) -> Self {
Element::Directive(directive)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Interpolation {
pub expr: Expression,
pub strip: StripMode,
}
impl Interpolation {
pub fn new<T>(expr: T) -> Interpolation
where
T: Into<Expression>,
{
Interpolation {
expr: expr.into(),
strip: StripMode::None,
}
}
pub fn with_strip(mut self, strip: StripMode) -> Interpolation {
self.strip = strip;
self
}
}
impl<T> From<T> for Interpolation
where
T: Into<Expression>,
{
fn from(expr: T) -> Self {
Interpolation {
expr: expr.into(),
strip: StripMode::default(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Directive {
If(IfDirective),
For(ForDirective),
}
impl Directive {
fn strip_start(&self) -> bool {
match self {
Directive::If(dir) => dir.if_strip.strip_start(),
Directive::For(dir) => dir.for_strip.strip_start(),
}
}
fn strip_end(&self) -> bool {
match self {
Directive::If(dir) => dir.endif_strip.strip_end(),
Directive::For(dir) => dir.endfor_strip.strip_end(),
}
}
}
impl From<IfDirective> for Directive {
fn from(directive: IfDirective) -> Self {
Directive::If(directive)
}
}
impl From<ForDirective> for Directive {
fn from(directive: ForDirective) -> Self {
Directive::For(directive)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IfDirective {
pub cond_expr: Expression,
pub true_template: Template,
pub false_template: Option<Template>,
pub if_strip: StripMode,
pub else_strip: StripMode,
pub endif_strip: StripMode,
}
impl IfDirective {
pub fn new<T>(cond_expr: T, true_template: Template) -> IfDirective
where
T: Into<Expression>,
{
IfDirective {
cond_expr: cond_expr.into(),
true_template,
false_template: None,
if_strip: StripMode::default(),
else_strip: StripMode::default(),
endif_strip: StripMode::default(),
}
}
pub fn with_false_template<T>(mut self, else_template: T) -> IfDirective
where
T: Into<Template>,
{
self.false_template = Some(else_template.into());
self
}
pub fn with_if_strip(mut self, strip: StripMode) -> IfDirective {
self.if_strip = strip;
self
}
pub fn with_else_strip(mut self, strip: StripMode) -> IfDirective {
self.else_strip = strip;
self
}
pub fn with_endif_strip(mut self, strip: StripMode) -> IfDirective {
self.endif_strip = strip;
self
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ForDirective {
pub key_var: Option<Identifier>,
pub value_var: Identifier,
pub collection_expr: Expression,
pub template: Template,
pub for_strip: StripMode,
pub endfor_strip: StripMode,
}
impl ForDirective {
pub fn new<T>(value: Identifier, collection_expr: T, template: Template) -> ForDirective
where
T: Into<Expression>,
{
ForDirective {
key_var: None,
value_var: value,
collection_expr: collection_expr.into(),
template,
for_strip: StripMode::default(),
endfor_strip: StripMode::default(),
}
}
pub fn with_key_var(mut self, key_var: Identifier) -> ForDirective {
self.key_var = Some(key_var);
self
}
pub fn with_for_strip(mut self, strip: StripMode) -> ForDirective {
self.for_strip = strip;
self
}
pub fn with_endfor_strip(mut self, strip: StripMode) -> ForDirective {
self.endfor_strip = strip;
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StripMode {
None,
Start,
End,
Both,
}
impl StripMode {
pub(crate) fn from_adjacent(prev: StripMode, next: StripMode) -> Self {
StripMode::from((prev.strip_end(), next.strip_start()))
}
pub(crate) fn strip_start(self) -> bool {
matches!(self, StripMode::Start | StripMode::Both)
}
pub(crate) fn strip_end(self) -> bool {
matches!(self, StripMode::End | StripMode::Both)
}
}
impl Default for StripMode {
fn default() -> StripMode {
StripMode::None
}
}
impl From<(bool, bool)> for StripMode {
fn from((start, end): (bool, bool)) -> Self {
match (start, end) {
(true, true) => StripMode::Both,
(true, false) => StripMode::Start,
(false, true) => StripMode::End,
(false, false) => StripMode::None,
}
}
}
impl Serialize for Template {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl<'de> Deserialize<'de> for Template {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_any(FromStrVisitor::<Self>::new("a template"))
}
}