base_web_content/violation.rs
1// Released under the MIT License.
2// Copyright, 2026, by Samuel Williams.
3
4use crate::FieldPath;
5use std::fmt;
6
7/// A declared field that arrived but cannot be accepted, or a required one that did not arrive.
8///
9/// Violations do not stop parsing: every one in the body is collected and returned when it ends, in declaration order, so that an application can present them all. A violation never includes the submitted value, only the field's path and a code.
10#[derive(Clone, Debug, PartialEq, Eq, Hash)]
11pub struct Violation {
12 path: FieldPath,
13 code: ViolationCode,
14}
15
16impl Violation {
17 pub(crate) fn new(path: FieldPath, code: ViolationCode) -> Self {
18 Self { path, code }
19 }
20
21 /// The field's path: the declared names and array indices that lead to it.
22 pub fn path(&self) -> &FieldPath {
23 &self.path
24 }
25
26 /// What is wrong with the field.
27 pub fn code(&self) -> ViolationCode {
28 self.code
29 }
30}
31
32/// Writes the path and code, such as `items[1][count]: invalid_type`.
33impl fmt::Display for Violation {
34 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
35 write!(formatter, "{}: {}", self.path, self.code)
36 }
37}
38
39/// What is wrong with a field, as a [`Violation`] reports it.
40#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
41#[non_exhaustive]
42pub enum ViolationCode {
43 /// A required field did not arrive: a scalar or upload was absent, an array had no elements, or a nested group received none of its fields.
44 Required,
45 /// The value does not convert to the field's type, is not valid UTF-8, has an unsupported character set, or is a file where text is declared or the reverse.
46 InvalidType,
47 /// The upload's declared media type is not one its field accepts.
48 UnsupportedMediaType,
49}
50
51impl ViolationCode {
52 /// The code as it is written, such as `invalid_type`.
53 pub fn as_str(self) -> &'static str {
54 match self {
55 Self::Required => "required",
56 Self::InvalidType => "invalid_type",
57 Self::UnsupportedMediaType => "unsupported_media_type",
58 }
59 }
60}
61
62impl fmt::Display for ViolationCode {
63 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
64 formatter.write_str(self.as_str())
65 }
66}