use crate::config::path::{Path, PathSegment};
use miette::Diagnostic;
use starbase_styles::color;
use starbase_styles::{Style, Stylize};
use std::fmt::{self, Display};
use thiserror::Error;
#[derive(Clone, Debug, Diagnostic, Error)]
#[error("{}{} {message}", .path.to_string().style(Style::Id), ":".style(Style::MutedLight))]
pub struct ValidateError {
pub message: String,
pub path: Path,
}
impl ValidateError {
pub fn new<T: AsRef<str>>(message: T) -> Self {
ValidateError {
message: message.as_ref().to_owned(),
path: Path::default(),
}
}
pub fn required() -> Self {
ValidateError {
message: "this setting is required".into(),
path: Path::default(),
}
}
pub fn with_path<T: AsRef<str>>(message: T, path: Path) -> Self {
ValidateError {
message: message.as_ref().to_owned(),
path,
}
}
pub fn with_segment<T: AsRef<str>>(message: T, segment: PathSegment) -> Self {
Self::with_segments(message, [segment])
}
pub fn with_segments<T: AsRef<str>, I>(message: T, segments: I) -> Self
where
I: IntoIterator<Item = PathSegment>,
{
ValidateError {
message: message.as_ref().to_owned(),
path: Path::new(segments.into_iter().collect()),
}
}
}
#[derive(Clone, Debug)]
pub enum ValidateErrorType {
Setting { path: Path, error: ValidateError },
Nested { error: ValidatorError },
}
impl ValidateErrorType {
pub fn setting(path: Path, error: ValidateError) -> Self {
ValidateErrorType::Setting { path, error }
}
pub fn setting_required(path: Path) -> Self {
ValidateErrorType::Setting {
path,
error: ValidateError::required(),
}
}
pub fn nested(error: ValidatorError) -> Self {
ValidateErrorType::Nested { error }
}
pub fn is_empty(&self) -> bool {
match self {
ValidateErrorType::Setting { .. } => false,
ValidateErrorType::Nested { error } => error.is_empty(),
}
}
pub fn len(&self) -> usize {
match self {
ValidateErrorType::Setting { .. } => 1,
ValidateErrorType::Nested { error } => error.len(),
}
}
pub fn to_error_list(&self) -> Vec<String> {
let mut list = vec![];
match self {
ValidateErrorType::Setting { path, error } => {
let mut error = error.clone();
error.path = path.join_path(&error.path);
list.push(error.to_string());
}
ValidateErrorType::Nested {
error: nested_error,
} => {
for error in &nested_error.errors {
list.extend(error.to_error_list());
}
}
}
list
}
}
#[derive(Clone, Debug, Diagnostic, Error)]
pub struct ValidatorError {
pub path: Path,
pub errors: Vec<ValidateErrorType>,
}
impl ValidatorError {
pub fn is_empty(&self) -> bool {
self.errors.is_empty()
}
pub fn len(&self) -> usize {
self.errors.iter().map(|e| e.len()).sum()
}
pub fn to_full_string(&self) -> String {
let mut message = String::new();
for error_type in &self.errors {
for error in error_type.to_error_list() {
message.push_str(format!("\n {error}").as_str());
}
}
message
}
}
impl Display for ValidatorError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut first = true;
let dot = color::failure("ยท");
for error_type in &self.errors {
for error in error_type.to_error_list() {
if first {
write!(f, "{} {}", dot, error)?;
first = false;
} else {
write!(f, "\n{} {}", dot, error)?;
}
}
}
writeln!(f)?;
Ok(())
}
}