1use std::rc::Rc;
2
3mod casings;
4mod naming;
5mod output;
6mod query_parsing;
7mod schema;
8
9use output::Output;
10use schema::{add_builtins, GraphPath, TypeIndex};
11
12#[derive(thiserror::Error, Debug)]
13pub enum Error {
14 #[error("Query document not supported: {0}")]
15 UnsupportedQueryDocument(String),
16
17 #[error("could not parse query document: {0}")]
18 QueryParseError(cynic_parser::Error),
19
20 #[error("could not parse schema document: {0}")]
21 SchemaParseError(cynic_parser::Error),
22
23 #[error("could not find field `{0}` on `{1}`")]
24 UnknownField(String, String),
25
26 #[error("could not find enum `{0}`")]
27 UnknownEnum(String),
28
29 #[error("could not find type `{0}`")]
30 UnknownType(String),
31
32 #[error("could not find directive `{0}`")]
33 UnknownDirective(String),
34
35 #[error("expected type `{0}` to be an object")]
36 ExpectedObject(String),
37
38 #[error("expected type `{0}` to be an object or an interface")]
39 ExpectedObjectOrInterface(String),
40
41 #[error("expected type `{0}` to be an input object")]
42 ExpectedInputObject(String),
43
44 #[error("found a literal object but the argument is not an InputObject")]
45 ArgumentNotInputObject,
46
47 #[error("couldn't find an argument named `{0}`")]
48 UnknownArgument(String),
49
50 #[error("an enum-like value was provided to an argument that is not an enum")]
51 ArgumentNotEnum,
52
53 #[error("expected an input object, enum or scalar")]
54 ExpectedInputType,
55
56 #[error("expected an enum, scalar, object, union or interface")]
57 ExpectedOutputType,
58
59 #[error("expected an interface")]
60 ExpectedInterfaceType,
61
62 #[error("expected a homogeneous list of input values")]
63 ExpectedHomogenousList,
64
65 #[error("expected field to be a list")]
66 ExpectedListType,
67
68 #[error("expected a value that is or contains an input object")]
69 ExpectedInputObjectValue,
70
71 #[error("couldn't find a fragment named {0}")]
72 UnknownFragment(String),
73
74 #[error("Tried to apply a fragment for a {0} type on a {1} type")]
75 TypeConditionFailed(String, String),
76
77 #[error("{0} is not a member of the {1} union type")]
78 TypeNotUnionMember(String, String),
79
80 #[error("{0} does not implement the {1} interface")]
81 TypeDoesNotImplementInterface(String, String),
82
83 #[error("Could not find a type named {0}, which we expected to be the root type")]
84 CouldntFindRootType(String),
85
86 #[error("At least one field should be selected for `{0}`.")]
87 NoFieldSelected(String),
88
89 #[error("You tried to select some fields on the type {0} which is not a composite type")]
90 TriedToSelectFieldsOfNonComposite(String),
91
92 #[error("An inline fragment on a union or interface type must have a type condition")]
93 MissingTypeCondition,
94}
95
96#[derive(Debug)]
97pub struct QueryGenOptions {
98 pub schema_module_name: String,
99 pub schema_name: Option<String>,
100}
101
102impl Default for QueryGenOptions {
103 fn default() -> QueryGenOptions {
104 QueryGenOptions {
105 schema_module_name: "schema".into(),
106 schema_name: None,
107 }
108 }
109}
110
111pub fn document_to_fragment_structs(
112 query: impl AsRef<str>,
113 schema: impl AsRef<str>,
114 options: &QueryGenOptions,
115) -> Result<String, Error> {
116 use std::fmt::Write;
117
118 let schema = cynic_parser::parse_type_system_document(schema.as_ref())
119 .map_err(Error::SchemaParseError)?;
120
121 let query =
122 cynic_parser::parse_executable_document(query.as_ref()).map_err(Error::QueryParseError)?;
123
124 let (schema, typename_id) = add_builtins(schema);
125
126 let type_index = Rc::new(TypeIndex::from_schema(&schema, typename_id));
127 let mut parsed_output = query_parsing::parse_query_document(&query, &type_index)?;
128
129 add_schema_name(&mut parsed_output, options.schema_name.as_deref());
130
131 let mut output = String::new();
132
133 let input_objects_need_lifetime = parsed_output
134 .input_objects
135 .iter()
136 .map(|io| {
137 (
138 io.name.as_str(),
139 io.fields.iter().any(|f| f.type_spec.contains_lifetime_a),
140 )
141 })
142 .collect();
143 for variables_struct in parsed_output.variables_structs {
144 writeln!(
145 output,
146 "{}",
147 output::VariablesStructForDisplay {
148 variables_struct: &variables_struct,
149 input_objects_need_lifetime: &input_objects_need_lifetime
150 }
151 )
152 .unwrap();
153 }
154
155 for fragment in parsed_output.query_fragments {
156 writeln!(output, "{}", fragment).unwrap();
157 }
158
159 for fragment in parsed_output.inline_fragments {
160 writeln!(output, "{}", fragment).unwrap();
161 }
162
163 for en in parsed_output.enums {
164 writeln!(output, "{}", en).unwrap();
165 }
166
167 for input_object in parsed_output.input_objects {
168 writeln!(output, "{}", input_object).unwrap();
169 }
170
171 for scalar in parsed_output.scalars {
172 writeln!(output, "{}", scalar).unwrap();
173 }
174
175 Ok(output)
176}
177
178fn add_schema_name(output: &mut Output, schema_name: Option<&str>) {
179 let Some(schema_name) = schema_name else {
180 return;
181 };
182
183 for fragment in &mut output.query_fragments {
184 fragment.schema_name = Some(schema_name.to_string());
185 }
186
187 for fragment in &mut output.inline_fragments {
188 fragment.schema_name = Some(schema_name.to_string());
189 }
190
191 for en in &mut output.enums {
192 en.schema_name = Some(schema_name.to_string());
193 }
194
195 for input_object in &mut output.input_objects {
196 input_object.schema_name = Some(schema_name.to_string());
197 }
198
199 for scalar in &mut output.scalars {
200 scalar.schema_name = Some(schema_name.to_string());
201 }
202}