use std::fmt::{self, Write};
use crate::formatter::{
ErrorKind, Render, TemplateError,
condition::{Condition, ConditionalToken},
template::TemplateItem,
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct Block<'a> {
content: Vec<TemplateItem<'a>>,
prefix: Vec<TemplateItem<'a>>,
postfix: Vec<TemplateItem<'a>>,
fallback: Vec<TemplateItem<'a>>,
condition: Option<Condition<'a>>,
}
impl<'a> Render for Block<'a> {
fn render(&self, format_table: &super::FormatTable) -> String {
if let Some(condition) = &self.condition
&& !condition.solve(format_table)
{
return String::new();
}
let mut content = self.content.render(format_table);
if content.is_empty() {
let fallback = self.fallback.render(format_table);
if fallback.is_empty() {
return String::new();
}
content = fallback
}
let mut prefix = self.prefix.render(format_table);
prefix.push_str(&content);
prefix.push_str(&self.postfix.render(format_table));
prefix
}
}
#[derive(Debug, PartialEq, Eq)]
pub(super) enum BlockMode {
Content,
Prefix,
Suffix,
Fallback,
Condition,
}
impl BlockMode {
pub fn to_str(&self) -> &'static str {
match self {
BlockMode::Content => panic!(
"Attempted to turn BlockMode::Content into a &str. This mode should never be set, only defaulted to"
),
BlockMode::Prefix => "<",
BlockMode::Suffix => ">",
BlockMode::Fallback => "?",
BlockMode::Condition => "@",
}
}
}
pub(super) struct BlockBuilder<'a> {
content: Vec<TemplateItem<'a>>,
prefix: Vec<TemplateItem<'a>>,
postfix: Vec<TemplateItem<'a>>,
fallback: Vec<TemplateItem<'a>>,
condition: Option<Condition<'a>>,
mode: Vec<BlockMode>,
}
impl<'a> Default for BlockBuilder<'a> {
fn default() -> Self {
Self {
content: Default::default(),
prefix: Default::default(),
postfix: Default::default(),
fallback: Default::default(),
condition: Default::default(),
mode: vec![BlockMode::Content],
}
}
}
impl<'a> BlockBuilder<'a> {
pub(super) fn build(self) -> Block<'a> {
Block {
content: self.content,
prefix: self.prefix,
postfix: self.postfix,
fallback: self.fallback,
condition: self.condition,
}
}
pub(super) fn set_mode(&mut self, mode: BlockMode) -> Result<(), TemplateError> {
if self.mode.contains(&mode) {
Err(TemplateError::new(ErrorKind::DoubleDefinition(
mode.to_str(),
)))
} else {
self.mode.push(mode);
Ok(())
}
}
pub(super) fn push_arg(&mut self, arg: TemplateItem<'a>) {
let arg_list = match self.mode.last().unwrap() {
BlockMode::Content => &mut self.content,
BlockMode::Prefix => &mut self.prefix,
BlockMode::Suffix => &mut self.postfix,
BlockMode::Fallback => &mut self.fallback,
BlockMode::Condition => {
let condition = self.condition.get_or_insert(Condition::default());
condition.tokens.push(ConditionalToken::Arg(arg));
return;
}
};
arg_list.push(arg);
}
pub(super) fn push_conditional_token(&mut self, token: ConditionalToken<'a>) {
match self.mode.last().unwrap() {
BlockMode::Condition => {
let condition = self.condition.get_or_insert(Condition::default());
condition.tokens.push(token);
}
_ => self.push_arg(TemplateItem::Text(token.to_str())),
}
}
}
impl fmt::Display for Block<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_char('{')?;
for arg in &self.content {
arg.fmt(f)?;
}
if let Some(condition) = &self.condition {
f.write_char('@')?;
condition.fmt(f)?;
}
if !self.prefix.is_empty() {
f.write_char('<')?;
for arg in &self.prefix {
arg.fmt(f)?;
}
}
if !self.postfix.is_empty() {
f.write_char('>')?;
for arg in &self.postfix {
arg.fmt(f)?;
}
}
if !self.fallback.is_empty() {
f.write_char('?')?;
for arg in &self.fallback {
arg.fmt(f)?;
}
}
f.write_char('}')
}
}