apollo_smith/
lib.rs

1#![doc = include_str!("../README.md")]
2
3pub(crate) mod argument;
4pub(crate) mod description;
5pub(crate) mod directive;
6pub(crate) mod document;
7pub(crate) mod enum_;
8pub(crate) mod field;
9pub(crate) mod fragment;
10pub(crate) mod input_object;
11pub(crate) mod input_value;
12pub(crate) mod interface;
13pub(crate) mod name;
14pub(crate) mod object;
15pub(crate) mod operation;
16pub(crate) mod response;
17pub(crate) mod scalar;
18pub(crate) mod schema;
19pub(crate) mod selection_set;
20#[cfg(test)]
21pub(crate) mod snapshot_tests;
22pub(crate) mod ty;
23pub(crate) mod union;
24pub(crate) mod variable;
25
26use indexmap::IndexMap;
27use std::fmt::Debug;
28
29#[derive(Debug, Clone, thiserror::Error)]
30pub enum FromError {
31    #[error("parse tree is missing a node")]
32    MissingNode,
33    #[error("invalid i32")]
34    ParseIntError(#[from] std::num::ParseIntError),
35    #[error("invalid f64")]
36    ParseFloatError(#[from] std::num::ParseFloatError),
37    #[error("invalid boolean")]
38    ParseBoolError(#[from] std::str::ParseBoolError),
39}
40
41pub use arbitrary::Result;
42pub use arbitrary::Unstructured;
43use argument::Argument;
44pub use directive::DirectiveDef;
45pub use document::Document;
46pub use enum_::EnumTypeDef;
47use field::FieldDef;
48pub use fragment::FragmentDef;
49pub use input_object::InputObjectTypeDef;
50pub use interface::InterfaceTypeDef;
51use name::Name;
52pub use object::ObjectTypeDef;
53pub use operation::OperationDef;
54pub use response::Generator;
55pub use response::ResponseBuilder;
56pub use scalar::ScalarTypeDef;
57pub use schema::SchemaDef;
58pub use serde_json_bytes::Value;
59use ty::Ty;
60pub use union::UnionTypeDef;
61
62/// DocumentBuilder is a struct to build an arbitrary valid GraphQL document
63///
64/// ```compile_fail
65/// // fuzz/fuzz_targets/my_apollo_smith_fuzz_target.rs
66/// #![no_main]
67///
68/// use libfuzzer_sys::fuzz_target;
69/// use arbitrary::Unstructured;
70/// use apollo_smith::DocumentBuilder;
71///
72/// fuzz_target!(|input: &[u8]| {
73///     let mut u = Unstructured::new(input);
74///     let gql_doc = DocumentBuilder::new(&mut u)?;
75///     let document = gql_doc.finish();
76///     let document_str = String::from(document);
77///
78///     // Your code here...
79/// });
80/// ```
81pub struct DocumentBuilder<'a> {
82    pub(crate) u: &'a mut Unstructured<'a>,
83    pub(crate) input_object_type_defs: Vec<InputObjectTypeDef>,
84    pub(crate) object_type_defs: Vec<ObjectTypeDef>,
85    pub(crate) interface_type_defs: Vec<InterfaceTypeDef>,
86    pub(crate) union_type_defs: Vec<UnionTypeDef>,
87    pub(crate) enum_type_defs: Vec<EnumTypeDef>,
88    pub(crate) scalar_type_defs: Vec<ScalarTypeDef>,
89    pub(crate) schema_def: Option<SchemaDef>,
90    pub(crate) directive_defs: Vec<DirectiveDef>,
91    pub(crate) operation_defs: Vec<OperationDef>,
92    pub(crate) fragment_defs: Vec<FragmentDef>,
93    // A stack to set current ObjectTypeDef
94    pub(crate) stack: Vec<Box<dyn StackedEntity>>,
95    // Useful to keep the same arguments for a specific field
96    pub(crate) chosen_arguments: IndexMap<Name, Vec<Argument>>,
97    // Useful to keep the same aliases for a specific field name
98    pub(crate) chosen_aliases: IndexMap<Name, Name>,
99}
100
101impl Debug for DocumentBuilder<'_> {
102    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103        f.debug_struct("DocumentBuilder")
104            .field("input_object_type_defs", &self.input_object_type_defs)
105            .field("object_type_defs", &self.object_type_defs)
106            .field("interface_type_defs", &self.interface_type_defs)
107            .field("union_type_defs", &self.union_type_defs)
108            .field("enum_type_defs", &self.enum_type_defs)
109            .field("scalar_type_defs", &self.scalar_type_defs)
110            .field("schema_def", &self.schema_def)
111            .field("directive_defs", &self.directive_defs)
112            .field("operation_defs", &self.operation_defs)
113            .field("fragment_defs", &self.fragment_defs)
114            .finish()
115    }
116}
117
118impl<'a> DocumentBuilder<'a> {
119    /// Create an instance of `DocumentBuilder`
120    pub fn new(u: &'a mut Unstructured<'a>) -> Result<Self> {
121        let mut builder = Self {
122            u,
123            object_type_defs: Vec::new(),
124            interface_type_defs: Vec::new(),
125            enum_type_defs: Vec::new(),
126            schema_def: None,
127            directive_defs: Vec::new(),
128            operation_defs: Vec::new(),
129            fragment_defs: Vec::new(),
130            scalar_type_defs: Vec::new(),
131            union_type_defs: Vec::new(),
132            input_object_type_defs: Vec::new(),
133            stack: Vec::new(),
134            chosen_arguments: IndexMap::new(),
135            chosen_aliases: IndexMap::new(),
136        };
137
138        for _ in 0..builder.u.int_in_range(1..=50)? {
139            let scalar_type_def = builder.scalar_type_definition()?;
140            builder.scalar_type_defs.push(scalar_type_def);
141        }
142
143        for _ in 0..builder.u.int_in_range(1..=50)? {
144            let enum_type_def = builder.enum_type_definition()?;
145            builder.enum_type_defs.push(enum_type_def);
146        }
147
148        for _ in 0..builder.u.int_in_range(1..=50)? {
149            let interface_type_def = builder.interface_type_definition()?;
150            builder.interface_type_defs.push(interface_type_def);
151        }
152
153        for _ in 0..builder.u.int_in_range(1..=50)? {
154            let object_type_def = builder.object_type_definition()?;
155            builder.object_type_defs.push(object_type_def);
156        }
157
158        for _ in 0..builder.u.int_in_range(1..=50)? {
159            let union_type_def = builder.union_type_definition()?;
160            builder.union_type_defs.push(union_type_def);
161        }
162
163        for _ in 0..builder.u.int_in_range(1..=50)? {
164            let input_object_type_def = builder.input_object_type_definition()?;
165            builder.input_object_type_defs.push(input_object_type_def);
166        }
167
168        for _ in 0..builder.u.int_in_range(1..=50)? {
169            let fragment_def = builder.fragment_definition()?;
170            builder.fragment_defs.push(fragment_def);
171        }
172
173        for _ in 0..builder.u.int_in_range(1..=50)? {
174            let directive_def = builder.directive_def()?;
175            builder.directive_defs.push(directive_def);
176        }
177
178        let schema_def = builder.schema_definition()?;
179        builder.schema_def = Some(schema_def);
180
181        for _ in 0..builder.u.int_in_range(1..=50)? {
182            let operation_def = builder.operation_definition()?;
183            // Could be None if there is no schema definition (in this case it never happens)
184            if let Some(operation_def) = operation_def {
185                builder.operation_defs.push(operation_def);
186            }
187        }
188
189        Ok(builder)
190    }
191
192    /// Create an instance of `DocumentBuilder` given a `Document` to be able to call
193    /// methods on DocumentBuilder and generate valid entities like for example an operation
194    pub fn with_document(u: &'a mut Unstructured<'a>, document: Document) -> Result<Self> {
195        let builder = Self {
196            u,
197            object_type_defs: document.object_type_definitions,
198            interface_type_defs: document.interface_type_definitions,
199            enum_type_defs: document.enum_type_definitions,
200            schema_def: document.schema_definition,
201            directive_defs: document.directive_definitions,
202            operation_defs: document.operation_definitions,
203            fragment_defs: document.fragment_definitions,
204            scalar_type_defs: document.scalar_type_definitions,
205            union_type_defs: document.union_type_definitions,
206            input_object_type_defs: document.input_object_type_definitions,
207            stack: Vec::new(),
208            chosen_arguments: IndexMap::new(),
209            chosen_aliases: IndexMap::new(),
210        };
211
212        Ok(builder)
213    }
214
215    /// Returns whether the provided `Unstructured` is now empty
216    pub fn input_exhausted(&self) -> bool {
217        self.u.is_empty()
218    }
219
220    /// Convert a `DocumentBuilder` into a GraphQL `Document`
221    pub fn finish(self) -> Document {
222        Document {
223            schema_definition: self.schema_def,
224            object_type_definitions: self.object_type_defs,
225            interface_type_definitions: self.interface_type_defs,
226            enum_type_definitions: self.enum_type_defs,
227            directive_definitions: self.directive_defs,
228            operation_definitions: self.operation_defs,
229            fragment_definitions: self.fragment_defs,
230            scalar_type_definitions: self.scalar_type_defs,
231            union_type_definitions: self.union_type_defs,
232            input_object_type_definitions: self.input_object_type_defs,
233        }
234    }
235
236    pub(crate) fn stack_ty(&mut self, ty: &Ty) -> bool {
237        if ty.is_builtin() {
238            return false;
239        }
240        let type_name = ty.name();
241
242        if let Some(object_ty) = self
243            .object_type_defs
244            .iter()
245            .find(|object_ty_def| &object_ty_def.name == type_name)
246            .cloned()
247        {
248            self.stack.push(Box::new(object_ty));
249            true
250        } else if let Some(itf_type) = self
251            .interface_type_defs
252            .iter()
253            .find(|itf_type_def| &itf_type_def.name == type_name)
254            .cloned()
255        {
256            self.stack.push(Box::new(itf_type));
257            true
258        } else if let Some(_enum_ty) = self
259            .enum_type_defs
260            .iter()
261            .find(|object_ty_def| &object_ty_def.name == type_name)
262            .cloned()
263        {
264            false
265        } else {
266            todo!("'{:?}' need to implement for union, scalar, ...", type_name);
267        }
268    }
269}
270
271pub(crate) trait StackedEntity {
272    fn name(&self) -> &Name;
273    fn fields_def(&self) -> &[FieldDef];
274}