1use serde::Deserialize;
17use serde::de::Error as _;
18use serde_json::Value;
19use std::collections::BTreeMap;
20
21use crate::schema::{FieldDef, FieldKind, ForeignKey, Schema, TableDef, TextFormat};
22
23#[derive(Debug, Clone, Copy, Deserialize)]
25#[serde(rename_all = "snake_case")]
26enum KindName {
27 Text,
28 Integer,
29 Real,
30 Boolean,
31 Timestamp,
32 Uuid,
33 Json,
34 Enum,
35}
36
37impl KindName {
38 fn as_str(self) -> &'static str {
39 match self {
40 KindName::Text => "text",
41 KindName::Integer => "integer",
42 KindName::Real => "real",
43 KindName::Boolean => "boolean",
44 KindName::Timestamp => "timestamp",
45 KindName::Uuid => "uuid",
46 KindName::Json => "json",
47 KindName::Enum => "enum",
48 }
49 }
50}
51
52impl<'de> Deserialize<'de> for TextFormat {
53 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
54 let name = String::deserialize(deserializer)?;
55 match name.as_str() {
56 "email" => Ok(TextFormat::Email),
57 "url" => Ok(TextFormat::Url),
58 other => Err(D::Error::custom(format!(
59 "unknown format `{other}`; the formats are email and url"
60 ))),
61 }
62 }
63}
64
65#[derive(Debug, Deserialize)]
68#[serde(untagged)]
69enum PrimaryKeyWire {
70 One(String),
71 Many(Vec<String>),
72}
73
74impl PrimaryKeyWire {
75 fn into_vec(self) -> Vec<String> {
76 match self {
77 PrimaryKeyWire::One(one) => vec![one],
78 PrimaryKeyWire::Many(many) => many,
79 }
80 }
81}
82
83fn default_primary_key() -> PrimaryKeyWire {
84 PrimaryKeyWire::One("id".to_owned())
85}
86
87#[derive(Debug, Deserialize)]
88#[serde(deny_unknown_fields)]
89struct FieldWire {
90 name: String,
91 kind: KindName,
92 #[serde(default)]
93 required: bool,
94 #[serde(default)]
95 unique: bool,
96 #[serde(default)]
97 indexed: bool,
98 #[serde(default)]
99 default: Option<Value>,
100 #[serde(default)]
101 min_len: Option<u32>,
102 #[serde(default)]
103 max_len: Option<u32>,
104 #[serde(default)]
105 format: Option<TextFormat>,
106 #[serde(default)]
107 min: Option<Value>,
108 #[serde(default)]
109 max: Option<Value>,
110 #[serde(default)]
111 values: Option<Vec<String>>,
112}
113
114impl FieldWire {
115 fn reject_unless<E: serde::de::Error>(&self, accepted: &[&str]) -> Result<(), E> {
118 let present: [(&str, bool); 6] = [
119 ("min_len", self.min_len.is_some()),
120 ("max_len", self.max_len.is_some()),
121 ("format", self.format.is_some()),
122 ("min", self.min.is_some()),
123 ("max", self.max.is_some()),
124 ("values", self.values.is_some()),
125 ];
126 for (key, is_set) in present {
127 if is_set && !accepted.contains(&key) {
128 return Err(E::custom(format!(
129 "field `{}`: `{key}` does not apply to a {} field",
130 self.name,
131 self.kind.as_str()
132 )));
133 }
134 }
135 Ok(())
136 }
137
138 fn bound_i64<E: serde::de::Error>(
139 &self,
140 key: &str,
141 value: Option<&Value>,
142 ) -> Result<Option<i64>, E> {
143 match value {
144 None => Ok(None),
145 Some(value) => value.as_i64().map(Some).ok_or_else(|| {
146 E::custom(format!(
147 "field `{}`: `{key}` must be a whole number, not {value}",
148 self.name
149 ))
150 }),
151 }
152 }
153
154 fn bound_f64<E: serde::de::Error>(
155 &self,
156 key: &str,
157 value: Option<&Value>,
158 ) -> Result<Option<f64>, E> {
159 match value {
160 None => Ok(None),
161 Some(value) => value
162 .as_f64()
163 .filter(|number| number.is_finite())
164 .map(Some)
165 .ok_or_else(|| {
166 E::custom(format!(
167 "field `{}`: `{key}` must be a finite number, not {value}",
168 self.name
169 ))
170 }),
171 }
172 }
173
174 fn into_field<E: serde::de::Error>(self) -> Result<FieldDef, E> {
175 let kind = match self.kind {
176 KindName::Text => {
177 self.reject_unless(&["min_len", "max_len", "format"])?;
178 FieldKind::Text {
179 min_len: self.min_len,
180 max_len: self.max_len,
181 format: self.format,
182 }
183 }
184 KindName::Integer => {
185 self.reject_unless(&["min", "max"])?;
186 FieldKind::Integer {
187 min: self.bound_i64("min", self.min.as_ref())?,
188 max: self.bound_i64("max", self.max.as_ref())?,
189 }
190 }
191 KindName::Real => {
192 self.reject_unless(&["min", "max"])?;
193 FieldKind::Real {
194 min: self.bound_f64("min", self.min.as_ref())?,
195 max: self.bound_f64("max", self.max.as_ref())?,
196 }
197 }
198 KindName::Boolean => {
199 self.reject_unless(&[])?;
200 FieldKind::Boolean
201 }
202 KindName::Timestamp => {
203 self.reject_unless(&[])?;
204 FieldKind::Timestamp
205 }
206 KindName::Uuid => {
207 self.reject_unless(&[])?;
208 FieldKind::Uuid
209 }
210 KindName::Json => {
211 self.reject_unless(&[])?;
212 FieldKind::Json
213 }
214 KindName::Enum => {
215 self.reject_unless(&["values"])?;
216 let values = self.values.clone().ok_or_else(|| {
217 E::custom(format!(
218 "field `{}`: an enum field needs a `values` list",
219 self.name
220 ))
221 })?;
222 FieldKind::Enum { values }
223 }
224 };
225
226 Ok(FieldDef {
227 name: self.name,
228 kind,
229 required: self.required,
230 unique: self.unique,
231 indexed: self.indexed,
232 default: self.default,
233 })
234 }
235}
236
237impl<'de> Deserialize<'de> for FieldDef {
238 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
239 FieldWire::deserialize(deserializer)?.into_field()
240 }
241}
242
243#[derive(Debug, Deserialize)]
244#[serde(deny_unknown_fields)]
245struct ForeignKeyWire {
246 field: String,
247 references: String,
248}
249
250impl<'de> Deserialize<'de> for ForeignKey {
251 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
252 let wire = ForeignKeyWire::deserialize(deserializer)?;
253 Ok(ForeignKey {
254 field: wire.field,
255 references: wire.references,
256 })
257 }
258}
259
260#[derive(Debug, Deserialize)]
261#[serde(deny_unknown_fields)]
262struct TableWire {
263 #[serde(default)]
267 name: Option<String>,
268 #[serde(default = "default_primary_key")]
269 primary_key: PrimaryKeyWire,
270 fields: Vec<FieldDef>,
271 #[serde(default)]
272 foreign_keys: Vec<ForeignKey>,
273}
274
275impl TableWire {
276 fn into_table<E: serde::de::Error>(self, name: String) -> Result<TableDef, E> {
277 if let Some(inner) = &self.name
278 && inner != &name
279 {
280 return Err(E::custom(format!(
281 "table `{name}`: the inner name `{inner}` does not match the section it is \
282 declared under"
283 )));
284 }
285 Ok(TableDef {
286 name,
287 fields: self.fields,
288 primary_key: self.primary_key.into_vec(),
289 foreign_keys: self.foreign_keys,
290 })
291 }
292}
293
294impl<'de> Deserialize<'de> for TableDef {
295 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
296 let wire = TableWire::deserialize(deserializer)?;
297 let name = wire
298 .name
299 .clone()
300 .ok_or_else(|| D::Error::custom("a table declared on its own needs a `name`"))?;
301 wire.into_table(name)
302 }
303}
304
305impl<'de> Deserialize<'de> for Schema {
306 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
307 let wires = BTreeMap::<String, TableWire>::deserialize(deserializer)?;
308 let mut tables = Vec::with_capacity(wires.len());
309 for (name, wire) in wires {
310 tables.push(wire.into_table(name)?);
311 }
312 Ok(Schema { tables })
315 }
316}
317
318use serde::Serialize;
334use serde::ser::SerializeMap as _;
335
336impl Serialize for TextFormat {
337 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
338 serializer.serialize_str(match self {
339 TextFormat::Email => "email",
340 TextFormat::Url => "url",
341 })
342 }
343}
344
345impl Serialize for FieldDef {
346 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
347 let mut map = serializer.serialize_map(None)?;
348 map.serialize_entry("name", &self.name)?;
349 map.serialize_entry("kind", self.kind.as_str())?;
350 match &self.kind {
354 FieldKind::Text {
355 min_len,
356 max_len,
357 format,
358 } => {
359 if let Some(min) = min_len {
360 map.serialize_entry("min_len", min)?;
361 }
362 if let Some(max) = max_len {
363 map.serialize_entry("max_len", max)?;
364 }
365 if let Some(format) = format {
366 map.serialize_entry("format", format)?;
367 }
368 }
369 FieldKind::Integer { min, max } => {
370 if let Some(min) = min {
371 map.serialize_entry("min", min)?;
372 }
373 if let Some(max) = max {
374 map.serialize_entry("max", max)?;
375 }
376 }
377 FieldKind::Real { min, max } => {
378 if let Some(min) = min {
379 map.serialize_entry("min", min)?;
380 }
381 if let Some(max) = max {
382 map.serialize_entry("max", max)?;
383 }
384 }
385 FieldKind::Enum { values } => map.serialize_entry("values", values)?,
386 FieldKind::Boolean | FieldKind::Timestamp | FieldKind::Uuid | FieldKind::Json => {}
387 }
388 if self.required {
392 map.serialize_entry("required", &true)?;
393 }
394 if self.unique {
395 map.serialize_entry("unique", &true)?;
396 }
397 if self.indexed {
398 map.serialize_entry("indexed", &true)?;
399 }
400 if let Some(default) = &self.default {
401 map.serialize_entry("default", default)?;
402 }
403 map.end()
404 }
405}
406
407impl Serialize for ForeignKey {
408 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
409 let mut map = serializer.serialize_map(Some(2))?;
410 map.serialize_entry("field", &self.field)?;
411 map.serialize_entry("references", &self.references)?;
412 map.end()
413 }
414}
415
416impl Serialize for TableDef {
417 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
418 let mut map = serializer.serialize_map(None)?;
419 map.serialize_entry("name", &self.name)?;
423 match self.primary_key.as_slice() {
427 [one] => map.serialize_entry("primary_key", one)?,
428 many => map.serialize_entry("primary_key", many)?,
429 }
430 map.serialize_entry("fields", &self.fields)?;
431 if !self.foreign_keys.is_empty() {
432 map.serialize_entry("foreign_keys", &self.foreign_keys)?;
433 }
434 map.end()
435 }
436}
437
438impl Serialize for Schema {
439 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
440 let mut map = serializer.serialize_map(Some(self.tables.len()))?;
443 for table in &self.tables {
444 map.serialize_entry(&table.name, table)?;
445 }
446 map.end()
447 }
448}