Skip to main content

armour_typ/
schema.rs

1use crate::scalar::ScalarTyp;
2use crate::typ::{Fields, Typ};
3
4/// Owned runtime mirror of `Typ`. Reuses `ScalarTyp`. Deserializable
5/// (`impl Rapira` — in the rapira crate, Plan 2).
6#[derive(PartialEq, Eq, Clone, Debug)]
7pub enum SchemaTyp {
8    Scalar(ScalarTyp),
9    Array(u32, Box<SchemaTyp>),
10    Vec(Box<SchemaTyp>),
11    Optional(Box<SchemaTyp>),
12    SimpleEnum {
13        name: String,
14        variants: Vec<(u8, String)>,
15    },
16    Struct {
17        name: String,
18        fields: SchemaFields,
19    },
20    Enum {
21        name: String,
22        variants: Vec<(u8, String, SchemaTyp)>,
23    },
24    Custom(String, Vec<SchemaTyp>),
25}
26
27#[derive(PartialEq, Eq, Clone, Debug)]
28pub enum SchemaFields {
29    Named(Vec<(String, SchemaTyp)>),
30    Unnamed(Vec<SchemaTyp>),
31}
32
33impl From<&Typ> for SchemaTyp {
34    fn from(t: &Typ) -> Self {
35        match t {
36            Typ::Scalar(s) => SchemaTyp::Scalar(*s),
37            Typ::Array(n, inner) => SchemaTyp::Array(*n, Box::new(SchemaTyp::from(*inner))),
38            Typ::Vec(inner) => SchemaTyp::Vec(Box::new(SchemaTyp::from(*inner))),
39            Typ::Optional(inner) => SchemaTyp::Optional(Box::new(SchemaTyp::from(*inner))),
40            Typ::SimpleEnum(e) => SchemaTyp::SimpleEnum {
41                name: e.name.to_string(),
42                variants: e
43                    .variants
44                    .iter()
45                    .map(|(i, n)| (*i, n.to_string()))
46                    .collect(),
47            },
48            Typ::Struct(s) => SchemaTyp::Struct {
49                name: s.name.to_string(),
50                fields: match s.fields {
51                    Fields::Named(fs) => SchemaFields::Named(
52                        fs.iter()
53                            .map(|(n, ty)| (n.to_string(), SchemaTyp::from(ty)))
54                            .collect(),
55                    ),
56                    Fields::Unnamed(fs) => {
57                        SchemaFields::Unnamed(fs.iter().map(SchemaTyp::from).collect())
58                    }
59                },
60            },
61            Typ::Enum(e) => SchemaTyp::Enum {
62                name: e.name.to_string(),
63                variants: e
64                    .variants
65                    .iter()
66                    .map(|(i, (n, ty))| (*i, n.to_string(), SchemaTyp::from(ty)))
67                    .collect(),
68            },
69            Typ::Custom(name, items) => SchemaTyp::Custom(
70                name.to_string(),
71                items.iter().map(SchemaTyp::from).collect(),
72            ),
73        }
74    }
75}
76
77/// Exhaustiveness guard (spec §11): a new composite variant in `Typ` must be
78/// mirrored here in `SchemaTyp`, otherwise this match fails to compile. Pairs
79/// with the compiler-enforced `From<&Typ>` match above (which guards the
80/// `Typ → SchemaTyp` direction) to keep both enums in lockstep.
81#[cfg(test)]
82#[allow(dead_code)]
83fn _schema_guard(s: &SchemaTyp) {
84    match s {
85        SchemaTyp::Scalar(_) => {}
86        SchemaTyp::Array(_, _) => {}
87        SchemaTyp::Vec(_) => {}
88        SchemaTyp::Optional(_) => {}
89        SchemaTyp::SimpleEnum { .. } => {}
90        SchemaTyp::Struct { .. } => {}
91        SchemaTyp::Enum { .. } => {}
92        SchemaTyp::Custom(_, _) => {}
93    }
94}
95
96#[cfg(test)]
97mod tests {
98    use super::*;
99    use crate::scalar::ScalarTyp;
100    use crate::typ::{Fields, StructType, Typ};
101
102    #[test]
103    fn from_typ_struct_roundtrips_shape() {
104        static T: Typ = Typ::Struct(StructType {
105            name: "S",
106            fields: Fields::Named(&[
107                ("a", Typ::Scalar(ScalarTyp::U64)),
108                ("b", Typ::Vec(&Typ::Scalar(ScalarTyp::Str))),
109            ]),
110        });
111        let s = SchemaTyp::from(&T);
112        let expected = SchemaTyp::Struct {
113            name: "S".to_string(),
114            fields: SchemaFields::Named(vec![
115                ("a".to_string(), SchemaTyp::Scalar(ScalarTyp::U64)),
116                (
117                    "b".to_string(),
118                    SchemaTyp::Vec(Box::new(SchemaTyp::Scalar(ScalarTyp::Str))),
119                ),
120            ]),
121        };
122        assert_eq!(s, expected);
123    }
124
125    #[test]
126    fn from_typ_covers_scalars_and_composites() {
127        assert_eq!(
128            SchemaTyp::from(&Typ::Scalar(ScalarTyp::Fuid)),
129            SchemaTyp::Scalar(ScalarTyp::Fuid)
130        );
131        assert_eq!(
132            SchemaTyp::from(&Typ::Optional(&Typ::Scalar(ScalarTyp::Id64))),
133            SchemaTyp::Optional(Box::new(SchemaTyp::Scalar(ScalarTyp::Id64)))
134        );
135        assert_eq!(
136            SchemaTyp::from(&Typ::Custom("c", &[Typ::Scalar(ScalarTyp::U8)])),
137            SchemaTyp::Custom("c".to_string(), vec![SchemaTyp::Scalar(ScalarTyp::U8)])
138        );
139    }
140}