use super::path::{Path, PathSegment};
use miette::Diagnostic;
use starbase_styles::{Style, Stylize};
use std::borrow::Borrow;
use thiserror::Error;
pub type ValidateResult = std::result::Result<(), ValidateError>;
pub type Validator<Val, Data, Ctx> = Box<dyn FnOnce(&Val, &Data, &Ctx, bool) -> ValidateResult>;
#[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()),
}
}
#[doc(hidden)]
pub fn prepend_path(self, path: Path) -> Self {
Self {
message: self.message,
path: path.join_path(&self.path),
}
}
}
#[derive(Debug, Diagnostic, Error)]
#[error("{}", self.render_errors())]
pub struct ValidatorError {
pub errors: Vec<ValidateError>,
}
impl ValidatorError {
fn render_errors(&self) -> String {
self.errors
.iter()
.map(|error| error.to_string())
.collect::<Vec<_>>()
.join("\n")
}
}
impl Borrow<dyn Diagnostic> for Box<ValidatorError> {
fn borrow(&self) -> &(dyn Diagnostic + 'static) {
self.as_ref()
}
}