1use serde::{Deserialize, Serialize};
2use ts_rs::TS;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, TS)]
21#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
22pub enum SchemaVersion {
23 V1,
24}
25
26impl<'de> Deserialize<'de> for SchemaVersion {
27 fn deserialize<D>(de: D) -> Result<Self, D::Error>
28 where
29 D: serde::Deserializer<'de>,
30 {
31 if de.is_human_readable() {
36 #[derive(Deserialize)]
37 #[serde(untagged)]
38 enum Repr {
39 Num(u64),
40 Name(String),
41 }
42 match Repr::deserialize(de)? {
43 Repr::Num(1) => Ok(SchemaVersion::V1),
44 Repr::Num(n) => Err(serde::de::Error::custom(format!(
45 "unknown schema_version {n} (known versions: 1)"
46 ))),
47 Repr::Name(s) if s.eq_ignore_ascii_case("v1") => Ok(SchemaVersion::V1),
48 Repr::Name(s) => Err(serde::de::Error::custom(format!(
49 "unknown schema_version {s:?} (known versions: \"V1\")"
50 ))),
51 }
52 } else {
53 #[derive(Deserialize)]
54 enum Wire {
55 V1,
56 }
57 match Wire::deserialize(de)? {
58 Wire::V1 => Ok(SchemaVersion::V1),
59 }
60 }
61 }
62}
63
64impl Default for SchemaVersion {
65 fn default() -> Self {
66 Self::V1
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73
74 #[test]
76 fn accepts_integer_and_string_forms_and_writes_the_name() {
77 #[derive(Serialize, Deserialize, Debug, PartialEq)]
78 struct Doc {
79 schema_version: SchemaVersion,
80 }
81
82 for src in [
83 "schema_version = 1",
84 r#"schema_version = "V1""#,
85 r#"schema_version = "v1""#,
86 ] {
87 let doc: Doc = toml::from_str(src).unwrap_or_else(|e| panic!("{src}: {e}"));
88 assert_eq!(doc.schema_version, SchemaVersion::V1);
89 }
90
91 let out = toml::to_string(&Doc {
92 schema_version: SchemaVersion::V1,
93 })
94 .expect("serialize");
95 assert!(out.contains("\"V1\""), "canonical output, got {out}");
96 }
97
98 #[test]
99 fn rejects_unknown_versions() {
100 #[derive(Deserialize, Debug)]
101 struct Doc {
102 #[allow(dead_code)]
103 schema_version: SchemaVersion,
104 }
105 assert!(toml::from_str::<Doc>("schema_version = 2").is_err());
106 assert!(toml::from_str::<Doc>(r#"schema_version = "V2""#).is_err());
107 }
108
109 #[test]
112 fn round_trips_through_postcard() {
113 let bytes = postcard::to_allocvec(&SchemaVersion::V1).expect("encode");
114 assert_eq!(
115 postcard::from_bytes::<SchemaVersion>(&bytes).expect("decode"),
116 SchemaVersion::V1
117 );
118 }
119}