use alloc::format;
use alloc::string::String;
use thiserror::Error;
fn fmt_src_err(name: &Option<String>, line: usize, col: usize, message: &str) -> String {
match name {
Some(n) => format!("template: {n}:{line}:{col}: {message}"),
None => format!("template: {line}:{col}: {message}"),
}
}
#[cfg(feature = "html")]
fn fmt_escape_err(name: &Option<String>, line: usize, description: &str) -> String {
match (name, line) {
(Some(n), l) if l != 0 => format!("html/template:{n}:{l}: {description}"),
(Some(n), _) => format!("html/template:{n}: {description}"),
(None, l) if l != 0 => format!("html/template::{l}: {description}"),
(None, _) => format!("html/template: {description}"),
}
}
#[cfg(feature = "html")]
#[cfg_attr(docsrs, doc(cfg(feature = "html")))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EscapeErrorCode {
AmbigContext,
BadHtml,
BranchEnd,
EndContext,
NoSuchTemplate,
OutputContext,
PartialCharset,
PartialEscape,
RangeLoopReentry,
SlashAmbig,
PredefinedEscaper,
JsTemplate,
}
#[derive(Debug, Error)]
pub enum TemplateError {
#[error("{}", fmt_src_err(name, *line, *col, message))]
Parse {
name: Option<String>,
line: usize,
col: usize,
message: String,
},
#[error("{}", fmt_src_err(name, *line, *col, message))]
Lex {
name: Option<String>,
line: usize,
col: usize,
message: String,
},
#[error("execution error: {0}")]
Exec(String),
#[error("index out of range: {index}")]
IndexOutOfRange {
index: i64,
},
#[error("type mismatch: expected {expected}, got {got}")]
TypeMismatch {
expected: &'static str,
got: &'static str,
},
#[error("map has no entry for key: {key}")]
MissingKey {
key: String,
},
#[error("recursion limit exceeded")]
RecursionLimit,
#[error("range iteration budget exhausted")]
RangeIterLimit,
#[cfg(feature = "std")]
#[error("function {name} panicked: {message}")]
FuncPanic {
name: String,
message: String,
},
#[error("undefined template: {0}")]
UndefinedTemplate(String),
#[error("undefined function: {0}")]
UndefinedFunction(String),
#[error("undefined variable: {0}")]
UndefinedVariable(String),
#[error("wrong number of arguments: {name} expects {expected}, got {got}")]
ArgCount {
name: String,
expected: usize,
got: usize,
},
#[error("cannot range over {0}")]
NotIterable(String),
#[cfg(feature = "std")]
#[error("failed to read template file {path}: {source}")]
ReadFile {
path: String,
#[source]
source: std::io::Error,
},
#[cfg(feature = "std")]
#[error("no files named in call to parse_files")]
NoFiles,
#[cfg(feature = "glob")]
#[error("invalid pattern {pattern:?} at position {pos}: {msg}")]
BadPattern {
pattern: String,
pos: usize,
msg: &'static str,
},
#[cfg(feature = "std")]
#[error("io error: {0}")]
Io(#[from] std::io::Error),
#[error("write error")]
Write,
#[cfg(feature = "html")]
#[cfg_attr(docsrs, doc(cfg(feature = "html")))]
#[error("{}", fmt_escape_err(name, *line, description))]
Escape {
code: EscapeErrorCode,
name: Option<String>,
line: usize,
description: String,
},
}
impl From<core::fmt::Error> for TemplateError {
fn from(_: core::fmt::Error) -> Self {
TemplateError::Write
}
}
#[cfg(feature = "serde")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
impl serde::ser::Error for TemplateError {
fn custom<T: core::fmt::Display>(msg: T) -> Self {
TemplateError::Exec(alloc::string::ToString::to_string(&msg))
}
}
pub type Result<T> = core::result::Result<T, TemplateError>;