cynic_querygen/
lib.rs

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}