1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3use std::{collections::BTreeMap, fmt};
4
5#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
7#[serde(untagged)]
8pub enum PathSegment {
9 Field(String),
10 Index(usize),
11}
12
13impl From<&str> for PathSegment {
14 fn from(value: &str) -> Self {
15 Self::Field(value.to_owned())
16 }
17}
18impl From<String> for PathSegment {
19 fn from(value: String) -> Self {
20 Self::Field(value)
21 }
22}
23impl From<usize> for PathSegment {
24 fn from(value: usize) -> Self {
25 Self::Index(value)
26 }
27}
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
30#[serde(rename_all = "snake_case")]
31pub enum Stage {
32 Parse,
33 Validate,
34 Serialize,
35}
36
37#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
39pub struct ValidationError {
40 pub loc: Vec<PathSegment>,
41 pub code: String,
42 pub message: String,
43 pub stage: Stage,
44 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
45 pub params: BTreeMap<String, Value>,
46}
47
48impl ValidationError {
49 pub fn new(code: impl Into<String>, message: impl Into<String>) -> Self {
50 Self {
51 loc: vec![],
52 code: code.into(),
53 message: message.into(),
54 stage: Stage::Validate,
55 params: BTreeMap::new(),
56 }
57 }
58 pub fn custom(message: impl Into<String>) -> Self {
59 Self::new("custom", message)
60 }
61 pub fn at(mut self, segment: impl Into<PathSegment>) -> Self {
63 self.loc.push(segment.into());
64 self
65 }
66 pub fn with_param(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
67 self.params.insert(key.into(), value.into());
68 self
69 }
70 pub fn with_stage(mut self, stage: Stage) -> Self {
71 self.stage = stage;
72 self
73 }
74 pub fn pointer(&self) -> String {
75 self.loc
76 .iter()
77 .map(|part| match part {
78 PathSegment::Field(key) => {
79 format!("/{}", key.replace('~', "~0").replace('/', "~1"))
80 }
81 PathSegment::Index(index) => format!("/{index}"),
82 })
83 .collect()
84 }
85}
86
87impl fmt::Display for ValidationError {
88 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89 write!(f, "${}: {} [{}]", self.pointer(), self.message, self.code)
90 }
91}
92impl std::error::Error for ValidationError {}
93
94#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
95#[serde(transparent)]
96pub struct ValidationErrors(pub Vec<ValidationError>);
97
98impl ValidationErrors {
99 pub fn new(errors: Vec<ValidationError>) -> Self {
100 Self(errors)
101 }
102 pub fn errors(&self) -> &[ValidationError] {
103 &self.0
104 }
105 pub fn len(&self) -> usize {
106 self.0.len()
107 }
108 pub fn is_empty(&self) -> bool {
109 self.0.is_empty()
110 }
111 pub fn to_json(&self) -> Value {
112 serde_json::to_value(self).expect("error fields are JSON-compatible")
113 }
114}
115impl From<ValidationError> for ValidationErrors {
116 fn from(error: ValidationError) -> Self {
117 Self(vec![error])
118 }
119}
120impl fmt::Display for ValidationErrors {
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 writeln!(f, "{} validation error(s)", self.len())?;
123 for (index, error) in self.0.iter().enumerate() {
124 if index > 0 {
125 writeln!(f)?;
126 }
127 write!(f, "{error}")?;
128 }
129 Ok(())
130 }
131}
132impl std::error::Error for ValidationErrors {}
133
134#[derive(Debug, Default)]
136pub struct Context {
137 path: Vec<PathSegment>,
138 errors: Vec<ValidationError>,
139}
140impl Context {
141 pub fn new() -> Self {
142 Self::default()
143 }
144 pub fn error_count(&self) -> usize {
145 self.errors.len()
146 }
147 pub fn push(&mut self, mut error: ValidationError) {
148 let mut path = self.path.clone();
149 path.append(&mut error.loc);
150 error.loc = path;
151 self.errors.push(error);
152 }
153 pub fn extend(&mut self, errors: ValidationErrors) {
154 for error in errors.0 {
155 self.push(error);
156 }
157 }
158 pub fn at<T>(&mut self, segment: impl Into<PathSegment>, f: impl FnOnce(&mut Self) -> T) -> T {
159 self.path.push(segment.into());
160 let result = f(self);
161 self.path.pop();
162 result
163 }
164 pub fn child(&self, segment: Option<PathSegment>) -> Self {
165 let mut path = self.path.clone();
166 if let Some(segment) = segment {
167 path.push(segment);
168 }
169 Self {
170 path,
171 errors: vec![],
172 }
173 }
174 pub fn absorb(&mut self, child: Self) {
175 self.errors.extend(child.errors);
176 }
177 pub fn prepend_errors(
178 errors: ValidationErrors,
179 segment: impl Into<PathSegment>,
180 ) -> ValidationErrors {
181 let segment = segment.into();
182 ValidationErrors(
183 errors
184 .0
185 .into_iter()
186 .map(|mut error| {
187 error.loc.insert(0, segment.clone());
188 error
189 })
190 .collect(),
191 )
192 }
193 pub fn finish<T>(self, value: Option<T>) -> Result<T, ValidationErrors> {
194 if !self.errors.is_empty() {
195 return Err(ValidationErrors(self.errors));
196 }
197 value.ok_or_else(|| {
198 ValidationError::new("internal", "decoder returned no value without an error").into()
199 })
200 }
201 pub fn result(self) -> Result<(), ValidationErrors> {
202 self.finish(Some(()))
203 }
204}