1use thiserror::Error;
2
3use super::{ContainerSerdeMetadata, Enum, LinkingScheme, Struct, Tuple, Ty};
4use crate::schema::{IndexLinking, Primitive, Schema};
5
6pub trait TypeVisitor<L: LinkingScheme, M> {
7 type Arg;
8 type ReturnType;
9 fn visit_enum(
10 &mut self,
11 e: &Enum<L>,
12 schema: &impl TypeResolver<LinkingScheme = L, Metadata = M>,
13 context: Self::Arg,
14 ) -> Self::ReturnType;
15 fn visit_struct(
16 &mut self,
17 s: &Struct<L>,
18 schema: &impl TypeResolver<LinkingScheme = L, Metadata = M>,
19 context: Self::Arg,
20 ) -> Self::ReturnType;
21 fn visit_tuple(
22 &mut self,
23 t: &Tuple<L>,
24 schema: &impl TypeResolver<LinkingScheme = L, Metadata = M>,
25 context: Self::Arg,
26 ) -> Self::ReturnType;
27 fn visit_option(
28 &mut self,
29 value: &L::TypeLink,
30 schema: &impl TypeResolver<LinkingScheme = L, Metadata = M>,
31 context: Self::Arg,
32 ) -> Self::ReturnType;
33 fn visit_primitive(
34 &mut self,
35 p: Primitive,
36 schema: &impl TypeResolver<LinkingScheme = L, Metadata = M>,
37 context: Self::Arg,
38 ) -> Self::ReturnType;
39 fn visit_vec(
40 &mut self,
41 value: &L::TypeLink,
42 schema: &impl TypeResolver<LinkingScheme = L, Metadata = M>,
43 context: Self::Arg,
44 ) -> Self::ReturnType;
45 fn visit_array(
46 &mut self,
47 len: &usize,
48 value: &L::TypeLink,
49 schema: &impl TypeResolver<LinkingScheme = L, Metadata = M>,
50 context: Self::Arg,
51 ) -> Self::ReturnType;
52 fn visit_map(
53 &mut self,
54 key: &L::TypeLink,
55 value: &L::TypeLink,
56 schema: &impl TypeResolver<LinkingScheme = L, Metadata = M>,
57 context: Self::Arg,
58 ) -> Self::ReturnType;
59}
60
61pub trait TypeResolver {
62 type LinkingScheme: LinkingScheme;
63 type Metadata;
64
65 fn resolve_or_err(
66 &self,
67 maybe_resolved: &<Self::LinkingScheme as LinkingScheme>::TypeLink,
68 ) -> Result<Ty<Self::LinkingScheme>, ResolutionError>;
69
70 fn maybe_resolve_metadata(
71 &self,
72 _maybe_resolved: &<Self::LinkingScheme as LinkingScheme>::TypeLink,
73 ) -> Result<Option<Self::Metadata>, ResolutionError> {
74 Ok(None)
75 }
76}
77
78#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
79pub enum ResolutionError {
80 #[error("Could not resolve type with index `{index}` (max: `{max}`)")]
81 IndexOutOfBounds { index: usize, max: usize },
82 #[error("The schema contained an unresolved placholder")]
83 ErrContainsPlaceholder,
84}
85
86impl TypeResolver for Schema {
87 type LinkingScheme = IndexLinking;
88 type Metadata = ContainerSerdeMetadata;
89
90 fn resolve_or_err(
91 &self,
92 maybe_resolved: &<Self::LinkingScheme as LinkingScheme>::TypeLink,
93 ) -> Result<Ty<Self::LinkingScheme>, ResolutionError> {
94 match maybe_resolved {
95 crate::schema::Link::ByIndex(idx) => {
96 self.types()
97 .get(*idx)
98 .cloned()
99 .ok_or(ResolutionError::IndexOutOfBounds {
100 index: *idx,
101 max: self.types().len(),
102 })
103 }
104 crate::schema::Link::Immediate(primitive) => Ok(primitive.clone().into()),
105 crate::schema::Link::Placeholder | crate::schema::Link::IndexedPlaceholder(_) => {
106 Err(ResolutionError::ErrContainsPlaceholder)
107 }
108 }
109 }
110
111 fn maybe_resolve_metadata(
112 &self,
113 maybe_resolved: &<Self::LinkingScheme as LinkingScheme>::TypeLink,
114 ) -> Result<Option<Self::Metadata>, ResolutionError> {
115 match maybe_resolved {
116 crate::schema::Link::ByIndex(idx) => {
117 Some(self.serde_metadata().get(*idx).cloned().ok_or(
118 ResolutionError::IndexOutOfBounds {
119 index: *idx,
120 max: self.types().len(),
121 },
122 ))
123 .transpose()
124 }
125 crate::schema::Link::Immediate(_) => Ok(None),
126 crate::schema::Link::Placeholder | crate::schema::Link::IndexedPlaceholder(_) => {
127 Err(ResolutionError::ErrContainsPlaceholder)
128 }
129 }
130 }
131}
132
133impl<L: LinkingScheme> Ty<L> {
134 pub fn visit<V: TypeVisitor<L, M>, M>(
135 &self,
136 schema: &impl TypeResolver<LinkingScheme = L, Metadata = M>,
137 visitor: &mut V,
138 arg: V::Arg,
139 ) -> V::ReturnType {
140 match self {
141 Ty::Boolean => visitor.visit_primitive(Primitive::Boolean, schema, arg),
142 Ty::Enum(e) => visitor.visit_enum(e, schema, arg),
143 Ty::Struct(s) => visitor.visit_struct(s, schema, arg),
144 Ty::Tuple(t) => visitor.visit_tuple(t, schema, arg),
145 Ty::Option { value } => visitor.visit_option(value, schema, arg),
146 Ty::Integer(kind, display) => {
147 visitor.visit_primitive(Primitive::Integer(*kind, *display), schema, arg)
148 }
149 Ty::ByteArray { len, display } => visitor.visit_primitive(
150 Primitive::ByteArray {
151 len: *len,
152 display: *display,
153 },
154 schema,
155 arg,
156 ),
157 Ty::ByteVec { display } => {
158 visitor.visit_primitive(Primitive::ByteVec { display: *display }, schema, arg)
159 }
160 Ty::Array { len, value } => visitor.visit_array(len, value, schema, arg),
161 Ty::Float32 => visitor.visit_primitive(Primitive::Float32, schema, arg),
162 Ty::Float64 => visitor.visit_primitive(Primitive::Float64, schema, arg),
163 Ty::Map { key, value } => visitor.visit_map(key, value, schema, arg),
164 Ty::Vec { value } => visitor.visit_vec(value, schema, arg),
165 Ty::String => visitor.visit_primitive(Primitive::String, schema, arg),
166 Ty::Skip { len } => visitor.visit_primitive(Primitive::Skip { len: *len }, schema, arg),
167 }
168 }
169}