pub type TemplateResult<T> = Result<T, TemplateError>;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TemplateErrorKind {
Code,
Internal,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TemplateErrorPhase {
Parse,
Resolve,
Render,
Io,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TemplateErrorLocation {
pub line: usize,
pub column: usize,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TemplateError {
pub kind: TemplateErrorKind,
pub phase: TemplateErrorPhase,
pub template_path: Option<String>,
pub directive: Option<String>,
pub location: Option<TemplateErrorLocation>,
pub message: String,
}
impl TemplateError {
fn new(kind: TemplateErrorKind, phase: TemplateErrorPhase, message: impl Into<String>) -> Self {
Self {
kind,
phase,
template_path: None,
directive: None,
location: None,
message: message.into(),
}
}
pub(crate) fn code(phase: TemplateErrorPhase, message: impl Into<String>) -> Self {
Self::new(TemplateErrorKind::Code, phase, message)
}
pub(crate) fn internal(phase: TemplateErrorPhase, message: impl Into<String>) -> Self {
Self::new(TemplateErrorKind::Internal, phase, message)
}
pub(crate) fn with_template_path(mut self, template_path: impl Into<String>) -> Self {
self.template_path = Some(template_path.into());
self
}
pub(crate) fn with_directive(mut self, directive: impl Into<String>) -> Self {
self.directive = Some(directive.into());
self
}
pub(crate) fn with_location(mut self, line: usize, column: usize) -> Self {
self.location = Some(TemplateErrorLocation { line, column });
self
}
}
impl std::fmt::Display for TemplateError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let label = match self.kind {
TemplateErrorKind::Code => "error",
TemplateErrorKind::Internal => "internal error",
};
let phase = match self.phase {
TemplateErrorPhase::Parse => "parse",
TemplateErrorPhase::Resolve => "resolve",
TemplateErrorPhase::Render => "render",
TemplateErrorPhase::Io => "io",
};
let path = self.template_path.as_deref().unwrap_or("<unknown>");
let loc = self
.location
.as_ref()
.map(|l| format!("{}:{}", l.line, l.column))
.unwrap_or_else(|| "?:?".to_string());
let directive_note = self
.directive
.as_ref()
.map(|d| format!(" in `<?{}?>`\n", d))
.unwrap_or_default();
write!(
f,
"\n \u{00d7} {label} [{phase}] in {path}:{loc}\n {}\n{dir} \u{2500}\u{2500} help: verify the template matches HRML directive syntax\n",
self.message,
dir = directive_note,
label = label,
phase = phase,
path = path,
loc = loc,
)
}
}
impl std::error::Error for TemplateError {}