schematic/config/
validator.rs1use super::path::{Path, PathSegment};
2use miette::Diagnostic;
3use starbase_styles::{Style, Stylize};
4use std::borrow::Borrow;
5use thiserror::Error;
6
7pub type ValidateResult = std::result::Result<(), ValidateError>;
8
9pub type Validator<Val, Data, Ctx> = Box<dyn FnOnce(&Val, &Data, &Ctx, bool) -> ValidateResult>;
13
14#[derive(Clone, Debug, Diagnostic, Error)]
16#[error("{}{} {message}", .path.to_string().style(Style::Id), ":".style(Style::MutedLight))]
17pub struct ValidateError {
18 pub message: String,
20
21 pub path: Path,
23}
24
25impl ValidateError {
26 pub fn new<T: AsRef<str>>(message: T) -> Self {
28 ValidateError {
29 message: message.as_ref().to_owned(),
30 path: Path::default(),
31 }
32 }
33
34 pub fn required() -> Self {
36 ValidateError {
37 message: "this setting is required".into(),
38 path: Path::default(),
39 }
40 }
41
42 pub fn with_path<T: AsRef<str>>(message: T, path: Path) -> Self {
44 ValidateError {
45 message: message.as_ref().to_owned(),
46 path,
47 }
48 }
49
50 pub fn with_segment<T: AsRef<str>>(message: T, segment: PathSegment) -> Self {
52 Self::with_segments(message, [segment])
53 }
54
55 pub fn with_segments<T: AsRef<str>, I>(message: T, segments: I) -> Self
57 where
58 I: IntoIterator<Item = PathSegment>,
59 {
60 ValidateError {
61 message: message.as_ref().to_owned(),
62 path: Path::new(segments.into_iter().collect()),
63 }
64 }
65
66 #[doc(hidden)]
67 pub fn prepend_path(self, path: Path) -> Self {
68 Self {
69 message: self.message,
70 path: path.join_path(&self.path),
71 }
72 }
73}
74
75#[derive(Debug, Diagnostic, Error)]
77#[error("{}", self.render_errors())]
78pub struct ValidatorError {
79 pub errors: Vec<ValidateError>,
81}
82
83impl ValidatorError {
84 fn render_errors(&self) -> String {
85 self.errors
86 .iter()
87 .map(|error| error.to_string())
88 .collect::<Vec<_>>()
89 .join("\n")
90 }
91}
92
93impl Borrow<dyn Diagnostic> for Box<ValidatorError> {
94 fn borrow(&self) -> &(dyn Diagnostic + 'static) {
95 self.as_ref()
96 }
97}