Skip to main content

sov_universal_wallet/ty/
visitor.rs

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}