scalar_cms/
validations.rs1use std::{fmt::Display, sync::Arc};
2
3use serde::{Deserialize, Serialize};
4
5pub struct Valid<T: Validate>(T);
6
7impl<T: Validate> Valid<T> {
8 pub fn new(val: T) -> Result<Self, ValidationError> {
9 val.validate()?;
10 Ok(Self(val))
11 }
12
13 pub fn inner(self) -> T {
14 self.0
15 }
16}
17
18macro_rules! wrapped_string {
19 ($ty:ident) => {
20 #[derive(Serialize, Debug)]
21 pub struct $ty(pub Arc<str>);
22
23 impl Display for $ty {
24 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25 f.write_str(&self.0)
26 }
27 }
28
29 impl From<&str> for $ty {
30 fn from(val: &str) -> Self {
31 Self(val.into())
32 }
33 }
34 };
35}
36
37wrapped_string!(Reason);
38wrapped_string!(Field);
39
40#[derive(Debug, Serialize)]
42#[serde(untagged)]
43pub enum ValidationError {
44 Single(Reason),
46 Composite(Vec<ErroredField>),
48}
49
50#[derive(Debug, Serialize)]
51pub struct ErroredField {
52 pub field: Field,
53 pub error: ValidationError,
54}
55
56#[diagnostic::on_unimplemented(
57 note = "all document fields are validated by default",
58 note = "if validation isn't necesarry, use #[validate(skip)]"
59)]
60pub trait Validate {
61 fn validate(&self) -> Result<(), ValidationError>;
62}
63
64impl<T: Validate> Validate for Option<T> {
65 fn validate(&self) -> Result<(), ValidationError> {
66 match self.as_ref() {
67 Some(inner) => inner.validate(),
68 None => Ok(()),
69 }
70 }
71}
72
73macro_rules! validator {
74 ($ty:ty, $inner:ty, $expr:block, $v:ident) => {
75 impl crate::editor_field::ToEditorField for $ty {
76 fn to_editor_field(
77 default: Option<impl Into<$ty>>,
78 name: &'static str,
79 title: &'static str,
80 placeholder: Option<&'static str>,
81 validator: Option<&'static str>,
82 component_key: Option<&'static str>,
83 ) -> crate::EditorField
84 where
85 Self: std::marker::Sized,
86 {
87 <$inner>::to_editor_field(
88 default.map(|v| v.into().0),
89 name,
90 title,
91 placeholder,
92 validator,
93 component_key,
94 )
95 }
96 }
97
98 impl From<$ty> for $inner {
99 fn from(val: $ty) -> Self {
100 val.0
101 }
102 }
103
104 impl Validate for $ty {
105 fn validate(&self) -> Result<(), ValidationError> {
106 let $v = self;
107 $expr
108 }
109 }
110 };
111}
112
113#[derive(Serialize, Deserialize)]
114pub struct NonZeroI32(pub i32);
115
116validator! {NonZeroI32, i32, {
117 match v.0 {
118 0 => Ok(()),
119 _ => Err(ValidationError::Single("value must not be zero".into())),
120 }
121}, v}