1#[cfg(test)]
4mod test;
5
6#[cfg(test)]
7mod test_reasonable_str;
8
9use std::{fmt, ops::Deref};
10
11use crate::{
12 json,
13 warning::{self, IntoCaveat as _, WithElement as _},
14 Verdict,
15};
16
17#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
19pub enum Warning {
20 ContainsEscapeCodes,
22
23 ContainsNonPrintableASCII,
25
26 InvalidType { type_found: json::ValueKind },
28
29 InvalidLengthMax { length: usize },
31
32 InvalidLengthExact { length: usize },
34
35 PreferUppercase,
40}
41
42impl Warning {
43 fn invalid_type(elem: &json::Element<'_>) -> Self {
45 Self::InvalidType {
46 type_found: elem.value().kind(),
47 }
48 }
49}
50
51impl fmt::Display for Warning {
52 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53 match self {
54 Self::ContainsEscapeCodes => f.write_str("The string contains escape codes."),
55 Self::ContainsNonPrintableASCII => {
56 f.write_str("The string contains non-printable bytes.")
57 }
58 Self::InvalidType { type_found } => {
59 write!(f, "The value should be a string but is `{type_found}`")
60 }
61 Self::InvalidLengthMax { length } => {
62 write!(
63 f,
64 "The string is longer than the max length `{length}` defined in the spec.",
65 )
66 }
67 Self::InvalidLengthExact { length } => {
68 write!(f, "The string should be length `{length}`.")
69 }
70 Self::PreferUppercase => {
71 write!(f, "Upper case is preferred")
72 }
73 }
74 }
75}
76
77impl crate::Warning for Warning {
78 fn id(&self) -> warning::Id {
79 match self {
80 Self::ContainsEscapeCodes => warning::Id::from_static("contains_escape_codes"),
81 Self::ContainsNonPrintableASCII => {
82 warning::Id::from_static("contains_non_printable_ascii")
83 }
84 Self::InvalidType { type_found } => {
85 warning::Id::from_string(format!("invalid_type({type_found})"))
86 }
87 Self::InvalidLengthMax { .. } => warning::Id::from_static("invalid_length_max"),
88 Self::InvalidLengthExact { .. } => warning::Id::from_static("invalid_length_exact"),
89 Self::PreferUppercase => warning::Id::from_static("prefer_upper_case"),
90 }
91 }
92}
93
94#[derive(Copy, Clone, Debug)]
102pub(crate) struct CiMaxLen<'buf, const MAX_LEN: usize>(&'buf str);
103
104impl<const MAX_LEN: usize> Deref for CiMaxLen<'_, MAX_LEN> {
105 type Target = str;
106
107 fn deref(&self) -> &Self::Target {
108 self.0
109 }
110}
111
112impl<const MAX_LEN: usize> fmt::Display for CiMaxLen<'_, MAX_LEN> {
113 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114 write!(f, "{}", self.0)
115 }
116}
117
118impl<'buf, const MAX_LEN: usize> json::FromJson<'buf> for CiMaxLen<'buf, MAX_LEN> {
119 type Warning = Warning;
120
121 fn from_json(elem: &json::Element<'buf>) -> Verdict<Self, Self::Warning> {
122 let (s, mut warnings) = Base::from_json(elem)?.into_parts();
123
124 if s.len() > MAX_LEN {
125 warnings.insert(elem, Warning::InvalidLengthMax { length: MAX_LEN });
126 }
127
128 Ok(Self(s.0).into_caveat(warnings))
129 }
130}
131
132#[derive(Copy, Clone, Debug)]
140pub(crate) struct CiExactLen<'buf, const LEN: usize>(&'buf str);
141
142impl<const LEN: usize> Deref for CiExactLen<'_, LEN> {
143 type Target = str;
144
145 fn deref(&self) -> &Self::Target {
146 self.0
147 }
148}
149
150impl<const LEN: usize> fmt::Display for CiExactLen<'_, LEN> {
151 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152 write!(f, "{}", self.0)
153 }
154}
155
156impl<'buf, const LEN: usize> json::FromJson<'buf> for CiExactLen<'buf, LEN> {
157 type Warning = Warning;
158
159 fn from_json(elem: &json::Element<'buf>) -> Verdict<Self, Self::Warning> {
160 let (s, mut warnings) = Base::from_json(elem)?.into_parts();
161
162 if s.len() != LEN {
163 warnings.insert(elem, Warning::InvalidLengthExact { length: LEN });
164 }
165
166 Ok(Self(s.0).into_caveat(warnings))
167 }
168}
169
170#[derive(Copy, Clone, Debug)]
175struct Base<'buf>(&'buf str);
176
177impl Deref for Base<'_> {
178 type Target = str;
179
180 fn deref(&self) -> &Self::Target {
181 self.0
182 }
183}
184
185impl fmt::Display for Base<'_> {
186 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
187 write!(f, "{}", self.0)
188 }
189}
190
191impl<'buf> json::FromJson<'buf> for Base<'buf> {
192 type Warning = Warning;
193
194 fn from_json(elem: &json::Element<'buf>) -> Verdict<Self, Self::Warning> {
195 let mut warnings = warning::Set::new();
196 let Some(id) = elem.to_raw_str() else {
197 return warnings.bail(elem, Warning::invalid_type(elem));
198 };
199
200 let s = id.has_escapes(elem).ignore_warnings();
203 let s = match s {
204 json::PendingStr::NoEscapes(s) => {
205 if check_printable(s) {
206 warnings.insert(elem, Warning::ContainsNonPrintableASCII);
207 }
208 s
209 }
210 json::PendingStr::HasEscapes(escape_str) => {
211 warnings.insert(elem, Warning::ContainsEscapeCodes);
212 let decoded = escape_str
214 .decode_escapes()
215 .with_element(elem)
216 .ignore_warnings();
217
218 if check_printable(&decoded) {
219 warnings.insert(elem, Warning::ContainsNonPrintableASCII);
220 }
221
222 escape_str.into_raw()
223 }
224 };
225
226 Ok(Self(s).into_caveat(warnings))
227 }
228}
229
230fn check_printable(s: &str) -> bool {
233 s.chars()
234 .any(|c| c.is_ascii_whitespace() || c.is_ascii_control())
235}
236
237pub(crate) struct SizeExceedsMax(());
239
240impl std::error::Error for SizeExceedsMax {}
241
242impl fmt::Debug for SizeExceedsMax {
243 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
244 f.debug_tuple("SizeExceedsMax").finish()
245 }
246}
247
248impl fmt::Display for SizeExceedsMax {
249 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
250 write!(
251 f,
252 "The size of the input string exceeds the maximum length of {} megabytes",
253 ReasonableLen::FACTOR
254 )
255 }
256}
257
258#[derive(Copy, Clone)]
260pub(crate) struct ReasonableLen<'buf>(&'buf str);
261
262impl<'buf> ReasonableLen<'buf> {
263 const MEGA: usize = 1_000_000;
265
266 pub(crate) const FACTOR: usize = 5;
268
269 pub(crate) const MAX_STR_INPUT_LEN: usize = Self::FACTOR * Self::MEGA;
279
280 pub(crate) fn new(s: &'buf str) -> Result<ReasonableLen<'buf>, SizeExceedsMax> {
282 if s.len() >= Self::MAX_STR_INPUT_LEN {
283 return Err(SizeExceedsMax(()));
284 }
285
286 Ok(Self(s))
287 }
288
289 pub(crate) fn into_inner(self) -> &'buf str {
291 self.0
292 }
293}