apollo_smith/
ty.rs

1use crate::input_value::InputValue;
2use crate::name::Name;
3use crate::DocumentBuilder;
4use apollo_compiler::ast;
5use arbitrary::Result as ArbitraryResult;
6use once_cell::sync::Lazy;
7
8static BUILTIN_SCALAR_NAMES: Lazy<[Ty; 5]> = Lazy::new(|| {
9    [
10        Ty::Named(Name::new(String::from("Int"))),
11        Ty::Named(Name::new(String::from("Float"))),
12        Ty::Named(Name::new(String::from("String"))),
13        Ty::Named(Name::new(String::from("Boolean"))),
14        Ty::Named(Name::new(String::from("ID"))),
15    ]
16});
17
18/// Convenience Type_ implementation used when creating a Field.
19/// Can be a `NamedType`, a `NonNull` or a `List`.
20///
21/// This enum is resposible for encoding creating values such as `String!`, `[[[[String]!]!]!]!`, etc.
22#[derive(Debug, Clone, PartialEq, Eq, Hash)]
23pub enum Ty {
24    /// The Non-Null field type.
25    Named(Name),
26    /// The List field type.
27    List(Box<Ty>),
28    /// The Named field type.
29    NonNull(Box<Ty>),
30}
31
32impl From<Ty> for ast::Type {
33    fn from(val: Ty) -> Self {
34        match val {
35            Ty::Named(name) => Self::Named(name.into()),
36            Ty::List(ty) => Self::from(*ty).list(),
37            Ty::NonNull(ty) => Self::from(*ty).non_null(),
38        }
39    }
40}
41
42impl From<apollo_parser::cst::Type> for Ty {
43    fn from(ty: apollo_parser::cst::Type) -> Self {
44        match ty {
45            apollo_parser::cst::Type::NamedType(named_ty) => named_ty.into(),
46            apollo_parser::cst::Type::ListType(list_type) => {
47                Self::List(Box::new(list_type.ty().unwrap().into()))
48            }
49            apollo_parser::cst::Type::NonNullType(non_null) => {
50                if let Some(named_ty) = non_null.named_type() {
51                    Self::NonNull(Box::new(named_ty.into()))
52                } else if let Some(list_type) = non_null.list_type() {
53                    Self::NonNull(Box::new(Self::List(Box::new(
54                        list_type.ty().unwrap().into(),
55                    ))))
56                } else {
57                    panic!("a non null type must have a type")
58                }
59            }
60        }
61    }
62}
63
64impl From<apollo_parser::cst::NamedType> for Ty {
65    fn from(ty: apollo_parser::cst::NamedType) -> Self {
66        Self::Named(ty.name().unwrap().into())
67    }
68}
69
70impl Ty {
71    pub(crate) fn name(&self) -> &Name {
72        match self {
73            Ty::Named(name) => name,
74            Ty::List(list) => list.name(),
75            Ty::NonNull(non_null) => non_null.name(),
76        }
77    }
78
79    /// Returns `true` if the ty is [`Named`].
80    ///
81    /// [`Named`]: Ty::Named
82    pub fn is_named(&self) -> bool {
83        matches!(self, Self::Named(..))
84    }
85
86    pub(crate) fn is_builtin(&self) -> bool {
87        BUILTIN_SCALAR_NAMES.contains(&Ty::Named(self.name().clone()))
88    }
89}
90
91impl DocumentBuilder<'_> {
92    /// Create an arbitrary `Ty`
93    pub fn ty(&mut self) -> ArbitraryResult<Ty> {
94        self.generate_ty(true)
95    }
96
97    /// Choose an arbitrary existing `Ty` given a slice of existing types
98    pub fn choose_ty(&mut self, existing_types: &[Ty]) -> ArbitraryResult<Ty> {
99        self.choose_ty_given_nullable(existing_types, true)
100    }
101
102    /// Choose an arbitrary existing named `Ty` given a slice of existing types
103    pub fn choose_named_ty(&mut self, existing_types: &[Ty]) -> ArbitraryResult<Ty> {
104        let used_type_names: Vec<&Ty> = existing_types
105            .iter()
106            .chain(BUILTIN_SCALAR_NAMES.iter())
107            .collect();
108
109        Ok(self.u.choose(&used_type_names)?.to_owned().clone())
110    }
111
112    fn choose_ty_given_nullable(
113        &mut self,
114        existing_types: &[Ty],
115        is_nullable: bool,
116    ) -> ArbitraryResult<Ty> {
117        let ty: Ty = match self.u.int_in_range(0..=2usize)? {
118            // Named type
119            0 => {
120                let used_type_names: Vec<&Ty> = existing_types
121                    .iter()
122                    .chain(BUILTIN_SCALAR_NAMES.iter())
123                    .collect();
124
125                self.u.choose(&used_type_names)?.to_owned().clone()
126            }
127            // List type
128            1 => Ty::List(Box::new(
129                self.choose_ty_given_nullable(existing_types, true)?,
130            )),
131            // Non Null type
132            2 => {
133                if is_nullable {
134                    Ty::NonNull(Box::new(
135                        self.choose_ty_given_nullable(existing_types, false)?,
136                    ))
137                } else {
138                    self.choose_ty_given_nullable(existing_types, is_nullable)?
139                }
140            }
141            _ => unreachable!(),
142        };
143
144        Ok(ty)
145    }
146
147    fn generate_ty(&mut self, is_nullable: bool) -> ArbitraryResult<Ty> {
148        let ty = match self.u.int_in_range(0..=2usize)? {
149            // Named type
150            0 => Ty::Named(self.name()?),
151            // List type
152            1 => Ty::List(Box::new(self.generate_ty(true)?)),
153            // Non Null type
154            2 => {
155                if is_nullable {
156                    Ty::NonNull(Box::new(self.generate_ty(false)?))
157                } else {
158                    self.generate_ty(is_nullable)?
159                }
160            }
161            _ => unreachable!(),
162        };
163
164        Ok(ty)
165    }
166
167    /// List all existing (already created) `Ty`
168    pub(crate) fn list_existing_types(&self) -> Vec<Ty> {
169        self.object_type_defs
170            .iter()
171            .map(|o| Ty::Named(o.name.clone()))
172            .chain(
173                self.enum_type_defs
174                    .iter()
175                    .map(|e| Ty::Named(e.name.clone())),
176            )
177            .collect()
178    }
179
180    /// List all existing object (already created) `Ty`
181    pub(crate) fn list_existing_object_types(&self) -> Vec<Ty> {
182        self.object_type_defs
183            .iter()
184            .map(|o| Ty::Named(o.name.clone()))
185            .collect()
186    }
187
188    #[allow(dead_code)]
189    pub(crate) fn generate_value_for_type(&mut self, _ty: &Ty) -> InputValue {
190        todo!()
191    }
192}