ocpi_tariffs/
enumeration.rs1use crate::{json, warning};
4
5#[derive(Clone, Debug)]
10pub(crate) enum Enum<T> {
11 Known(T),
12 Unknown(String),
13}
14
15pub(crate) trait IntoEnum: Sized {
19 fn enum_from_str(s: &str) -> Enum<Self>;
20}
21
22#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
24pub struct Warning {
25 type_name: &'static str,
27
28 kind: WarningKind,
30}
31
32#[cfg(test)]
33impl Warning {
34 pub(crate) fn type_name(&self) -> &'static str {
35 self.type_name
36 }
37
38 pub(crate) fn kind(&self) -> &WarningKind {
39 &self.kind
40 }
41}
42
43impl std::fmt::Display for Warning {
44 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45 match &self.kind {
46 WarningKind::ContainsEscapeCodes => write!(
47 f,
48 "The {} value contains escape codes but it does not need them.",
49 self.type_name
50 ),
51 WarningKind::Decode(warning) => std::fmt::Display::fmt(&warning, f),
52 WarningKind::PreferUpperCase => {
53 write!(f, "The `{}` should be uppercase.", self.type_name)
54 }
55 WarningKind::InvalidVariant => {
56 write!(f, "The value is not a valid `{}`.", self.type_name)
57 }
58 WarningKind::InvalidType { type_found } => {
59 write!(
60 f,
61 "A {} value should be a string but `{}` was found.",
62 self.type_name, type_found
63 )
64 }
65 }
66 }
67}
68
69impl crate::Warning for Warning {
70 fn id(&self) -> warning::Id {
71 match &self.kind {
72 WarningKind::ContainsEscapeCodes => warning::Id::from_static("contains_escape_codes"),
73 WarningKind::Decode(warning) => warning.id(),
74 WarningKind::PreferUpperCase => warning::Id::from_static("prefer_upper_case"),
75 WarningKind::InvalidVariant => warning::Id::from_static("invalid_variant"),
76
77 WarningKind::InvalidType { type_found } => {
78 warning::Id::from_string(format!("invalid_type({type_found})"))
79 }
80 }
81 }
82}
83
84#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
85pub enum WarningKind {
86 ContainsEscapeCodes,
88
89 Decode(json::decode::Warning),
91
92 PreferUpperCase,
94
95 InvalidVariant,
97
98 InvalidType { type_found: json::ValueKind },
100}
101
102impl WarningKind {
103 pub(crate) fn invalid_type(elem: &json::Element<'_>) -> Self {
104 Self::InvalidType {
105 type_found: elem.value().kind(),
106 }
107 }
108
109 pub(crate) fn into_warning(self, type_name: &'static str) -> Warning {
111 Warning {
112 type_name,
113 kind: self,
114 }
115 }
116}
117
118#[macro_export]
122macro_rules! define_enum_from_json {
123 ($kind:ident, display_name: $name:literal, warning_id: $warning:literal) => {
124 impl $crate::json::FromJson<'_> for $kind {
125 type Warning = $crate::enumeration::Warning;
126
127 fn from_json(elem: &$crate::json::Element<'_>) -> $crate::Verdict<Self, Self::Warning> {
128 use $crate::warning::IntoCaveat as _;
129
130 let value = Enum::<$kind>::from_json(elem)?;
131 let (value, warnings) = value.into_parts();
132
133 let value = match value {
134 $crate::Enum::Known(v) => v,
135 $crate::Enum::Unknown(_) => {
136 return warnings.bail(
137 $crate::enumeration::WarningKind::InvalidVariant
138 .into_warning(stringify!($kind)),
139 elem,
140 );
141 }
142 };
143
144 Ok(value.into_caveat(warnings))
145 }
146 }
147
148 impl $crate::json::FromJson<'_> for $crate::Enum<$kind> {
149 type Warning = $crate::enumeration::Warning;
150
151 fn from_json(elem: &$crate::json::Element<'_>) -> $crate::Verdict<Self, Self::Warning> {
152 use $crate::warning::IntoCaveat as _;
153
154 let mut warnings = $crate::warning::Set::new();
155 let value = elem.as_value();
156
157 let Some(s) = value.to_raw_str() else {
158 return warnings.bail(
159 $crate::enumeration::WarningKind::invalid_type(elem)
160 .into_warning(stringify!($kind)),
161 elem,
162 );
163 };
164
165 let (s, inner_warnings) = s.has_escapes(elem).into_parts();
169 let inner_warnings =
170 inner_warnings
171 .into_inner()
172 .into_iter()
173 .map(|(elem_id, group)| {
174 (
175 elem_id,
176 group.map(|w| {
177 $crate::enumeration::WarningKind::Decode(w)
178 .into_warning(stringify!($kind))
179 }),
180 )
181 });
182
183 warnings.extend(inner_warnings);
184
185 let s = match s {
186 $crate::json::decode::PendingStr::NoEscapes(s) => s,
187 $crate::json::decode::PendingStr::HasEscapes(_) => {
188 return warnings.bail(
189 $crate::enumeration::WarningKind::ContainsEscapeCodes
190 .into_warning(stringify!($kind)),
191 elem,
192 );
193 }
194 };
195
196 if !s.chars().all(|c| c.is_uppercase() || c == '_') {
197 warnings.insert(
198 $crate::enumeration::WarningKind::PreferUpperCase
199 .into_warning(stringify!($kind)),
200 elem,
201 );
202 }
203
204 let value = $kind::enum_from_str(s);
205
206 Ok(value.into_caveat(warnings))
207 }
208 }
209 };
210}