1#[cfg(test)]
17pub(crate) mod test;
18
19#[cfg(test)]
20mod test_from_schema;
21
22use std::fmt;
23
24use chrono::{DateTime, NaiveDateTime, TimeZone as _, Utc};
25
26use crate::{
27 json,
28 schema::{self, HasElement as _},
29 warning::{self, GatherWarnings as _},
30 FromSchema, IntoCaveat as _, Verdict,
31};
32
33#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
35pub enum Warning {
36 ContainsEscapeCodes,
38
39 Decode(json::decode::Warning),
41
42 Invalid(String),
59}
60
61impl fmt::Display for Warning {
62 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63 match self {
64 Self::ContainsEscapeCodes => {
65 f.write_str("The value contains escape codes but it does not need them.")
66 }
67 Self::Decode(warning) => fmt::Display::fmt(warning, f),
68 Self::Invalid(err) => write!(f, "The value is not valid: {err}"),
69 }
70 }
71}
72
73impl crate::Warning for Warning {
74 fn id(&self) -> warning::Id {
75 match self {
76 Self::ContainsEscapeCodes => warning::Id::from_static("contains_escape_codes"),
77 Self::Decode(kind) => kind.id(),
78 Self::Invalid(_) => warning::Id::from_static("invalid"),
79 }
80 }
81}
82
83impl From<json::decode::Warning> for Warning {
84 fn from(warn_kind: json::decode::Warning) -> Self {
85 Self::Decode(warn_kind)
86 }
87}
88
89impl<'buf> FromSchema<'buf, schema::Str<'buf>> for DateTime<Utc> {
90 type Warning = Warning;
91
92 fn from_schema(source: &schema::Str<'buf>) -> Verdict<Self, Self::Warning> {
93 let mut warnings = warning::Set::new();
94 let elem = source.element();
95
96 let pending_str = source
97 .value()
98 .has_escapes(elem)
99 .gather_warnings_into(&mut warnings);
100
101 let s = match pending_str {
102 json::PendingStr::NoEscapes(s) => s,
103 json::PendingStr::HasEscapes(_) => {
104 return warnings.bail(elem, Warning::ContainsEscapeCodes);
105 }
106 };
107
108 let err = match s.parse::<DateTime<Utc>>() {
110 Ok(date) => return Ok(date.into_caveat(warnings)),
111 Err(err) => err,
112 };
113
114 let Ok(date) = s.parse::<NaiveDateTime>() else {
115 return warnings.bail(elem, Warning::Invalid(err.to_string()));
116 };
117
118 let datetime = Utc.from_utc_datetime(&date);
119 Ok(datetime.into_caveat(warnings))
120 }
121}
122
123impl<'buf> FromSchema<'buf, schema::Str<'buf>> for chrono::NaiveDate {
124 type Warning = Warning;
125
126 fn from_schema(source: &schema::Str<'buf>) -> Verdict<Self, Self::Warning> {
127 let mut warnings = warning::Set::new();
128 let elem = source.element();
129
130 let pending_str = source
133 .value()
134 .has_escapes(elem)
135 .gather_warnings_into(&mut warnings);
136
137 let s = match pending_str {
138 json::PendingStr::NoEscapes(s) => s,
139 json::PendingStr::HasEscapes(_) => {
140 return warnings.bail(elem, Warning::ContainsEscapeCodes);
141 }
142 };
143
144 let date = match s.parse::<chrono::NaiveDate>() {
145 Ok(v) => v,
146 Err(err) => {
147 return warnings.bail(elem, Warning::Invalid(err.to_string()));
148 }
149 };
150
151 Ok(date.into_caveat(warnings))
152 }
153}
154
155impl<'buf> FromSchema<'buf, schema::Str<'buf>> for chrono::NaiveTime {
156 type Warning = Warning;
157
158 fn from_schema(source: &schema::Str<'buf>) -> Verdict<Self, Self::Warning> {
159 let mut warnings = warning::Set::new();
160 let elem = source.element();
161
162 let pending_str = source
165 .value()
166 .has_escapes(elem)
167 .gather_warnings_into(&mut warnings);
168
169 let s = match pending_str {
170 json::PendingStr::NoEscapes(s) => s,
171 json::PendingStr::HasEscapes(_) => {
172 return warnings.bail(elem, Warning::ContainsEscapeCodes);
173 }
174 };
175
176 let date = match chrono::NaiveTime::parse_from_str(s, "%H:%M") {
177 Ok(v) => v,
178 Err(err) => {
179 return warnings.bail(elem, Warning::Invalid(err.to_string()));
180 }
181 };
182
183 Ok(date.into_caveat(warnings))
184 }
185}