use crate::utils::trad::{t, tf};
use std::fmt;
#[derive(Debug, Clone)]
pub struct BuildError {
pub kind: BuildErrorKind,
pub context: Option<String>,
}
#[derive(Debug, Clone)]
pub enum BuildErrorKind {
ValidationFailed(String),
CheckFailed(CheckReport),
TemplateLoadFailed(String),
DatabaseMissing,
ComponentNotReady(String),
}
#[derive(Debug, Clone)]
pub struct CheckReport {
pub errors: Vec<CheckError>,
}
#[derive(Debug, Clone)]
pub struct CheckError {
pub component: String,
pub message: String,
pub suggestion: Option<String>,
}
impl BuildError {
pub fn validation(msg: impl Into<String>) -> Self {
Self {
kind: BuildErrorKind::ValidationFailed(msg.into()),
context: None,
}
}
pub fn check(report: CheckReport) -> Self {
Self {
kind: BuildErrorKind::CheckFailed(report),
context: None,
}
}
pub fn template(err: impl Into<String>) -> Self {
Self {
kind: BuildErrorKind::TemplateLoadFailed(err.into()),
context: None,
}
}
pub fn database_missing() -> Self {
Self {
kind: BuildErrorKind::DatabaseMissing,
context: None,
}
}
pub fn with_context(mut self, context: impl Into<String>) -> Self {
self.context = Some(context.into());
self
}
}
impl CheckReport {
pub fn new() -> Self {
Self { errors: Vec::new() }
}
pub fn add(&mut self, error: CheckError) {
self.errors.push(error);
}
pub fn has_errors(&self) -> bool {
!self.errors.is_empty()
}
pub fn count(&self) -> usize {
self.errors.len()
}
}
impl Default for CheckReport {
fn default() -> Self {
Self::new()
}
}
impl CheckError {
pub fn new(component: impl Into<String>, message: impl Into<String>) -> Self {
Self {
component: component.into(),
message: message.into(),
suggestion: None,
}
}
pub fn with_suggestion(mut self, suggestion: impl Into<String>) -> Self {
self.suggestion = Some(suggestion.into());
self
}
}
impl fmt::Display for BuildError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.kind {
BuildErrorKind::ValidationFailed(msg) => {
write!(f, "{}", tf("build.validation_failed", &[msg]))?;
}
BuildErrorKind::CheckFailed(report) => {
writeln!(f, "{}", tf("build.check_failed", &[report.count()]))?;
writeln!(f)?;
for (i, err) in report.errors.iter().enumerate() {
write!(
f,
" {}. {} - {}",
i.saturating_add(1),
err.component,
err.message
)?;
if let Some(suggestion) = &err.suggestion {
write!(f, "\n → {}", suggestion)?;
}
if i < report.errors.len().saturating_sub(1) {
writeln!(f)?;
}
}
}
BuildErrorKind::TemplateLoadFailed(msg) => {
write!(f, "{}", tf("build.template_failed", &[msg]))?;
}
BuildErrorKind::DatabaseMissing => {
write!(f, "{}", t("build.db_missing"))?;
}
BuildErrorKind::ComponentNotReady(component) => {
write!(f, "{}", tf("build.component_not_ready", &[component]))?;
}
}
if let Some(ctx) = &self.context {
write!(f, "\n{}: {}", t("build.check.context"), ctx)?;
}
Ok(())
}
}
impl fmt::Display for CheckError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "[{}] {}", self.component, self.message)?;
if let Some(suggestion) = &self.suggestion {
write!(f, " ({}: {})", t("build.check.suggestion"), suggestion)?;
}
Ok(())
}
}
impl std::error::Error for BuildError {}
impl std::error::Error for CheckError {}
impl From<tera::Error> for BuildError {
fn from(err: tera::Error) -> Self {
Self::template(err.to_string())
}
}