1use crate::description::Description;
2use crate::directive::Directive;
3use crate::directive::DirectiveLocation;
4use crate::name::Name;
5use crate::ty::Ty;
6use crate::DocumentBuilder;
7use apollo_compiler::ast;
8use apollo_compiler::Node;
9use arbitrary::Result as ArbitraryResult;
10use indexmap::IndexMap;
11
12#[derive(Debug, Clone)]
19pub struct SchemaDef {
20 pub(crate) description: Option<Description>,
21 pub(crate) directives: IndexMap<Name, Directive>,
22 pub(crate) query: Option<Ty>,
23 pub(crate) mutation: Option<Ty>,
24 pub(crate) subscription: Option<Ty>,
25 pub(crate) extend: bool,
26}
27
28impl From<SchemaDef> for ast::Definition {
29 fn from(x: SchemaDef) -> Self {
30 let root_operations = [
31 (ast::OperationType::Query, x.query),
32 (ast::OperationType::Mutation, x.mutation),
33 (ast::OperationType::Subscription, x.subscription),
34 ]
35 .into_iter()
36 .flat_map(|(ty, name)| name.map(|n| Node::new((ty, n.name().into()))))
37 .collect();
38 if x.extend {
39 ast::SchemaExtension {
40 directives: Directive::to_ast(x.directives),
41 root_operations,
42 }
43 .into()
44 } else {
45 ast::SchemaDefinition {
46 description: x.description.map(Into::into),
47 directives: Directive::to_ast(x.directives),
48 root_operations,
49 }
50 .into()
51 }
52 }
53}
54
55impl TryFrom<apollo_parser::cst::SchemaDefinition> for SchemaDef {
56 type Error = crate::FromError;
57
58 fn try_from(schema_def: apollo_parser::cst::SchemaDefinition) -> Result<Self, Self::Error> {
59 let mut query = None;
60 let mut mutation = None;
61 let mut subcription = None;
62 for root_op in schema_def.root_operation_type_definitions() {
63 let op_type = root_op.operation_type().unwrap();
64 let named_type = root_op.named_type().unwrap();
65 if op_type.query_token().is_some() {
66 query = named_type.into();
67 } else if op_type.mutation_token().is_some() {
68 mutation = named_type.into();
69 } else if op_type.subscription_token().is_some() {
70 subcription = named_type.into();
71 } else {
72 panic!("operation type must be one of query|mutation|subscription");
73 }
74 }
75 Ok(Self {
76 description: schema_def.description().map(Description::from),
77 directives: schema_def
78 .directives()
79 .map(Directive::convert_directives)
80 .transpose()?
81 .unwrap_or_default(),
82 query: query.map(Ty::from),
83 mutation: mutation.map(Ty::from),
84 subscription: subcription.map(Ty::from),
85 extend: false,
86 })
87 }
88}
89
90impl TryFrom<apollo_parser::cst::SchemaExtension> for SchemaDef {
91 type Error = crate::FromError;
92
93 fn try_from(schema_def: apollo_parser::cst::SchemaExtension) -> Result<Self, Self::Error> {
94 let mut query = None;
95 let mut mutation = None;
96 let mut subcription = None;
97 for root_op in schema_def.root_operation_type_definitions() {
98 let op_type = root_op.operation_type().unwrap();
99 let named_type = root_op.named_type().unwrap();
100 if op_type.query_token().is_some() {
101 query = named_type.into();
102 } else if op_type.mutation_token().is_some() {
103 mutation = named_type.into();
104 } else if op_type.subscription_token().is_some() {
105 subcription = named_type.into();
106 } else {
107 panic!("operation type must be one of query|mutation|subscription");
108 }
109 }
110 Ok(Self {
111 description: None,
112 directives: schema_def
113 .directives()
114 .map(Directive::convert_directives)
115 .transpose()?
116 .unwrap_or_default(),
117 query: query.map(Ty::from),
118 mutation: mutation.map(Ty::from),
119 subscription: subcription.map(Ty::from),
120 extend: true,
121 })
122 }
123}
124
125impl DocumentBuilder<'_> {
126 pub fn schema_definition(&mut self) -> ArbitraryResult<SchemaDef> {
128 let description = self
129 .u
130 .arbitrary()
131 .unwrap_or(false)
132 .then(|| self.description())
133 .transpose()?;
134 let directives = self.directives(DirectiveLocation::Schema)?;
135 let named_types: Vec<Ty> = self
136 .list_existing_object_types()
137 .into_iter()
138 .filter(|ty| ty.is_named() && !ty.is_builtin())
139 .collect();
140
141 let arbitrary_idx: usize = self.u.arbitrary::<usize>()?;
142
143 let mut query = arbitrary_idx
144 .is_multiple_of(2)
145 .then(|| self.u.choose(&named_types))
146 .transpose()?
147 .cloned();
148 let mut mutation = arbitrary_idx
149 .is_multiple_of(3)
150 .then(|| self.u.choose(&named_types))
151 .transpose()?
152 .cloned();
153 let mut subscription = arbitrary_idx
154 .is_multiple_of(5)
155 .then(|| self.u.choose(&named_types))
156 .transpose()?
157 .cloned();
158 if let (None, None, None) = (&query, &mutation, &subscription) {
160 let arbitrary_op_type_idx = self.u.int_in_range(0..=2usize)?;
161 match arbitrary_op_type_idx {
162 0 => query = Some(self.u.choose(&named_types)?.clone()),
163 1 => mutation = Some(self.u.choose(&named_types)?.clone()),
164 2 => subscription = Some(self.u.choose(&named_types)?.clone()),
165 _ => unreachable!(),
166 }
167 }
168
169 Ok(SchemaDef {
170 description,
171 directives,
172 query,
173 mutation,
174 subscription,
175 extend: self.u.arbitrary().unwrap_or(false),
176 })
177 }
178}