use std::fmt;
use crate::formatter::lexer::lex_str;
#[derive(Debug, Clone, Copy)]
pub enum ErrorKind {
BadClosure(&'static str),
DoubleDefinition(&'static str),
}
#[derive(Debug)]
pub struct FormatError {
kind: ErrorKind,
}
impl fmt::Display for FormatError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.kind() {
ErrorKind::BadClosure(str) => write!(f, "Bad closure: {}", str)?,
ErrorKind::DoubleDefinition(str) => {
write!(
f,
"Tried to set the mode of a block to {}, but that mode has already been defined in this block (Maybe you forgot an escape?)",
str
)?;
}
}
Ok(())
}
}
impl FormatError {
pub fn new(kind: ErrorKind) -> Self {
Self { kind }
}
pub fn kind(&self) -> ErrorKind {
self.kind
}
}
mod block;
mod condition;
mod lexer;
mod tag;
mod format_args;
pub use format_args::Arguments;
mod format_table;
pub use format_table::*;
pub(crate) trait Render {
fn render(&self, format_table: &FormatTable) -> String;
}
pub fn format_args<'a>(format_string: &'a str) -> Result<Arguments<'a>, FormatError> {
let lexes = lex_str(format_string);
Arguments::from_lex(lexes)
}
#[inline(always)]
pub fn format(args: &Arguments, format_table: &FormatTable) -> String {
args.render(format_table)
}
pub fn format_str(
format_string: impl AsRef<str>,
format_table: &FormatTable,
) -> Result<String, FormatError> {
Ok(format(&format_args(format_string.as_ref())?, format_table))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn format_equality() {
let table = FormatTable::new();
let result = format_str("Hello, world!", &table).unwrap();
assert_eq!(result, "Hello, world!");
}
#[test]
fn format_simple_tag() {
let mut table = FormatTable::new();
table.add_entry("tag", "Hello, world!");
let result = format_str("$tag", &table).unwrap();
assert_eq!(result, "Hello, world!");
}
#[test]
fn format_block_tag() {
let mut table = FormatTable::new();
table.add_entry("tag", "Hello, world!");
let result = format_str("${tag}", &table).unwrap();
assert_eq!(result, "Hello, world!");
}
#[test]
fn format_double_tag() {
let mut table = FormatTable::new();
table.add_entry("foo", "foo");
table.add_entry("bar", "bar");
let result = format_str("$foo$bar", &table).unwrap();
assert_eq!(result, "foobar");
}
#[test]
fn format_tag_ends_at_spaces() {
let mut table = FormatTable::new();
table.add_entry("foo", "foo");
table.add_entry("bar", "bar");
let result = format_str("$foo $bar", &table).unwrap();
assert_eq!(result, "foo bar");
}
#[test]
fn format_recursive_block_tag() {
let mut table = FormatTable::new();
table.add_entry("foo", "bar");
table.add_entry("bar", "Hello, world!");
let result = format_str("${$foo}", &table).unwrap();
assert_eq!(result, "Hello, world!");
}
#[test]
fn format_conditional_block() {
let table = FormatTable::new();
let result = format_str("{Hello, world!@$invalid}", &table).unwrap();
assert_eq!(result, "");
}
#[test]
fn format_inverted_conditional_block() {
let table = FormatTable::new();
let result = format_str("{Hello, world!@!$invalid}", &table).unwrap();
assert_eq!(result, "Hello, world!");
}
#[test]
fn format_double_invert() {
let table = FormatTable::new();
let result = format_str("{Hello, world!@!!$invalid}", &table).unwrap();
assert_eq!(result, "");
}
#[test]
fn format_double_definition_fails() {
let table = FormatTable::new();
let result = format_str("{content<prefix>suffix?fallback<prefix_again}", &table);
match result {
Err(err) if matches!(err.kind(), ErrorKind::DoubleDefinition(_)) => {}
_ => panic!("Unexpected result: {result:?}"),
};
}
#[test]
fn format_prefix_and_suffix() {
let table = FormatTable::new();
let result = format_str("{content<prefix > suffix}", &table).unwrap();
assert_eq!(result, "prefix content suffix");
}
#[test]
fn format_fallback() {
let table = FormatTable::new();
let result = format_str("{$invalid?fallback}", &table).unwrap();
assert_eq!(result, "fallback");
}
#[test]
fn format_fallback_uses_prefix_and_suffix() {
let table = FormatTable::new();
let result = format_str("{$empty<prefix > suffix?fallback}", &table).unwrap();
assert_eq!(result, "prefix fallback suffix");
}
#[test]
fn format_fallback_condition() {
let table = FormatTable::new();
let result = format_str("{$invalid?fallback@$invalid}", &table).unwrap();
assert_eq!(result, "");
}
#[test]
fn format_or_condition() {
let mut table = FormatTable::new();
table.add_entry("a", "foo");
let result = format_str("{Hello, world!@$invalid||$a}", &table).unwrap();
assert_eq!(result, "Hello, world!");
}
#[test]
fn format_and_condition() {
let mut table = FormatTable::new();
table.add_entry("a", "foo");
table.add_entry("b", "bar");
let result = format_str("{Hello, world!@$a&&$b}", &table).unwrap();
assert_eq!(result, "Hello, world!");
}
#[test]
fn format_nand_condition() {
let mut table = FormatTable::new();
table.add_entry("a", "foo");
let result = format_str("{Hello, world!@$a!&$invalid}", &table).unwrap();
assert_eq!(result, "");
}
#[test]
fn format_nor_condition() {
let table = FormatTable::new();
let result = format_str("{Hello, world!@$invalid!|$invalid}", &table).unwrap();
assert_eq!(result, "");
}
#[test]
fn format_verify_conditional_left_to_right() {
let mut table = FormatTable::new();
table.add_entry("a", "foo");
let result = format_str("{Hello, world!@$invalid&&$invalid||$a&&$a}", &table).unwrap();
assert_eq!(result, "Hello, world!");
}
}