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