diff --git a/graphql_client_codegen/Cargo.toml b/graphql_client_codegen/Cargo.toml index 0bcee1d86..61017a535 100644 --- a/graphql_client_codegen/Cargo.toml +++ b/graphql_client_codegen/Cargo.toml @@ -9,7 +9,7 @@ edition = "2018" [dependencies] graphql-introspection-query = { version = "0.2.0", path = "../graphql-introspection-query" } -graphql-parser = "^0.2" +graphql-parser = "0.3" heck = "0.3" lazy_static = "1.3" proc-macro2 = { version = "^1.0", features = [] } diff --git a/graphql_client_codegen/src/codegen.rs b/graphql_client_codegen/src/codegen.rs index e4e543638..83920a935 100644 --- a/graphql_client_codegen/src/codegen.rs +++ b/graphql_client_codegen/src/codegen.rs @@ -16,17 +16,20 @@ use selection::*; use std::collections::BTreeMap; /// The main code generation function. -pub(crate) fn response_for_query( +pub(crate) fn response_for_query<'a, 'b: 'a, 'schema: 'a, T>( operation_id: OperationId, - options: &GraphQLClientCodegenOptions, - query: BoundQuery<'_>, -) -> Result { + options: &'b GraphQLClientCodegenOptions, + query: BoundQuery<'a, 'a, 'schema, T>, +) -> Result +where + T: graphql_parser::query::Text<'a> + std::default::Default, +{ let all_used_types = all_used_types(operation_id, &query); let response_derives = render_derives(options.all_response_derives()); let variable_derives = render_derives(options.all_variable_derives()); - let scalar_definitions = generate_scalar_definitions(&all_used_types, options, query); - let enum_definitions = enums::generate_enum_definitions(&all_used_types, options, query); + let scalar_definitions = generate_scalar_definitions(&all_used_types, options, &query); + let enum_definitions = enums::generate_enum_definitions(&all_used_types, options, &query); let fragment_definitions = generate_fragment_definitions(&all_used_types, &response_derives, options, &query); let input_object_definitions = inputs::generate_input_object_definitions( @@ -71,12 +74,15 @@ pub(crate) fn response_for_query( Ok(q) } -fn generate_variables_struct( +fn generate_variables_struct<'a, 'schema, T>( operation_id: OperationId, variable_derives: &impl quote::ToTokens, options: &GraphQLClientCodegenOptions, - query: &BoundQuery<'_>, -) -> TokenStream { + query: &BoundQuery<'a, 'a, 'schema, T>, +) -> TokenStream +where + T: graphql_parser::query::Text<'a> + std::default::Default, +{ if operation_has_no_variables(operation_id, query.query) { return quote!( #variable_derives @@ -127,11 +133,14 @@ fn generate_variables_struct( variables_struct } -fn generate_variable_struct_field( - variable: &ResolvedVariable, +fn generate_variable_struct_field<'a, 'schema, T>( + variable: &ResolvedVariable<'a, T>, options: &GraphQLClientCodegenOptions, - query: &BoundQuery<'_>, -) -> TokenStream { + query: &BoundQuery<'a, '_, 'schema, T>, +) -> TokenStream +where + T: graphql_parser::query::Text<'a> + std::default::Default, +{ let snake_case_name = variable.name.to_snake_case(); let safe_name = shared::keyword_replace(&snake_case_name); let ident = Ident::new(&safe_name, Span::call_site()); @@ -141,11 +150,14 @@ fn generate_variable_struct_field( quote::quote!(#annotation pub #ident : #r#type) } -fn generate_scalar_definitions<'a, 'schema: 'a>( +fn generate_scalar_definitions<'a: 'c, 'b: 'a, 'c, 'schema: 'a, T>( all_used_types: &'a crate::query::UsedTypes, - options: &'a GraphQLClientCodegenOptions, - query: BoundQuery<'schema>, -) -> impl Iterator + 'a { + options: &'b GraphQLClientCodegenOptions, + query: &'c BoundQuery<'a, '_, 'schema, T>, +) -> impl Iterator + 'a +where + T: graphql_parser::query::Text<'a> + std::default::Default, +{ all_used_types .scalars(query.schema) .map(move |(_id, scalar)| { @@ -172,11 +184,14 @@ fn render_derives<'a>(derives: impl Iterator) -> impl quote::ToT quote!(#[derive(#(#idents),*)]) } -fn render_variable_field_type( - variable: &ResolvedVariable, +fn render_variable_field_type<'a, 'schema, T>( + variable: &ResolvedVariable<'a, T>, options: &GraphQLClientCodegenOptions, - query: &BoundQuery<'_>, -) -> TokenStream { + query: &BoundQuery<'a, '_, 'schema, T>, +) -> TokenStream +where + T: graphql_parser::query::Text<'a> + std::default::Default, +{ let normalized_name = options .normalization() .input_name(variable.type_name(query.schema)); @@ -224,24 +239,30 @@ fn decorate_type(ident: &Ident, qualifiers: &[GraphqlTypeQualifier]) -> TokenStr qualified } -fn generate_fragment_definitions<'a>( +fn generate_fragment_definitions<'a, 'schema, T>( all_used_types: &'a UsedTypes, response_derives: &'a impl quote::ToTokens, options: &'a GraphQLClientCodegenOptions, - query: &'a BoundQuery<'a>, -) -> impl Iterator + 'a { + query: &'a BoundQuery<'a, '_, 'schema, T>, +) -> impl Iterator + 'a +where + T: graphql_parser::query::Text<'a> + std::default::Default, +{ all_used_types.fragment_ids().map(move |fragment_id| { selection::render_fragment(fragment_id, options, query).render(&response_derives) }) } /// For default value constructors. -fn graphql_parser_value_to_literal( - value: &graphql_parser::query::Value, +fn graphql_parser_value_to_literal<'a, 'schema, T>( + value: &graphql_parser::query::Value<'a, T>, ty: TypeId, is_optional: bool, - query: &BoundQuery<'_>, -) -> TokenStream { + query: &BoundQuery<'a, '_, 'schema, T>, +) -> TokenStream +where + T: graphql_parser::query::Text<'a> + std::default::Default, +{ use graphql_parser::query::Value; let inner = match value { @@ -260,7 +281,10 @@ fn graphql_parser_value_to_literal( let i = i.as_i64(); quote!(#i) } - Value::Enum(en) => quote!(#en), + Value::Enum(en) => { + let en_string = en.as_ref().to_string(); + quote!(#en_string) + } Value::List(inner) => { let elements = inner .iter() @@ -289,11 +313,15 @@ fn graphql_parser_value_to_literal( } /// For default value constructors. -fn render_object_literal( - object_map: &BTreeMap, +// TODO object_map keys should be T::Value +fn render_object_literal<'a, 'schema, T>( + object_map: &BTreeMap>, input_id: InputId, - query: &BoundQuery<'_>, -) -> TokenStream { + query: &BoundQuery<'a, '_, 'schema, T>, +) -> TokenStream +where + T: graphql_parser::query::Text<'a> + std::default::Default, +{ let input = query.schema.get_input(input_id); let constructor = Ident::new(&input.name, Span::call_site()); let fields: Vec = input diff --git a/graphql_client_codegen/src/codegen/enums.rs b/graphql_client_codegen/src/codegen/enums.rs index c739f7a02..d7af0122e 100644 --- a/graphql_client_codegen/src/codegen/enums.rs +++ b/graphql_client_codegen/src/codegen/enums.rs @@ -11,11 +11,14 @@ use quote::quote; * Generated "variant_names" enum: pub enum AnEnum { where_, self_, Other(String), } * Generated serialize line: "AnEnum::where_ => "where"," */ -pub(super) fn generate_enum_definitions<'a, 'schema: 'a>( +pub(super) fn generate_enum_definitions<'a, 'b: 'a, 'c, 'schema: 'a, T>( all_used_types: &'a crate::query::UsedTypes, - options: &'a GraphQLClientCodegenOptions, - query: BoundQuery<'schema>, -) -> impl Iterator + 'a { + options: &'b GraphQLClientCodegenOptions, + query: &'c BoundQuery<'a, '_, 'schema, T>, +) -> impl Iterator + 'a +where + T: graphql_parser::query::Text<'a> + std::default::Default, +{ let derives = render_derives( options .all_response_derives() diff --git a/graphql_client_codegen/src/codegen/inputs.rs b/graphql_client_codegen/src/codegen/inputs.rs index f569710eb..5d6a29d50 100644 --- a/graphql_client_codegen/src/codegen/inputs.rs +++ b/graphql_client_codegen/src/codegen/inputs.rs @@ -8,12 +8,15 @@ use heck::SnakeCase; use proc_macro2::{Ident, Span, TokenStream}; use quote::quote; -pub(super) fn generate_input_object_definitions( +pub(super) fn generate_input_object_definitions<'a, 'schema, T>( all_used_types: &UsedTypes, options: &GraphQLClientCodegenOptions, variable_derives: &impl quote::ToTokens, - query: &BoundQuery<'_>, -) -> Vec { + query: &BoundQuery<'a, '_, 'schema, T>, +) -> Vec +where + T: graphql_parser::query::Text<'a> + std::default::Default, +{ all_used_types .inputs(query.schema) .map(|(_input_id, input)| { diff --git a/graphql_client_codegen/src/codegen/selection.rs b/graphql_client_codegen/src/codegen/selection.rs index 7e5b31161..0a8b86461 100644 --- a/graphql_client_codegen/src/codegen/selection.rs +++ b/graphql_client_codegen/src/codegen/selection.rs @@ -19,11 +19,14 @@ use proc_macro2::{Ident, Span, TokenStream}; use quote::quote; use std::borrow::Cow; -pub(crate) fn render_response_data_fields<'a>( +pub(crate) fn render_response_data_fields<'a, 'b: 'a, 'query, 'schema, T>( operation_id: OperationId, options: &'a GraphQLClientCodegenOptions, - query: &'a BoundQuery<'a>, -) -> ExpandedSelection<'a> { + query: &'b BoundQuery<'a, 'query, 'schema, T>, +) -> ExpandedSelection<'a, 'query, 'schema, T> +where + T: graphql_parser::query::Text<'a> + std::default::Default, +{ let operation = query.query.get_operation(operation_id); let mut expanded_selection = ExpandedSelection { query, @@ -49,11 +52,14 @@ pub(crate) fn render_response_data_fields<'a>( expanded_selection } -pub(super) fn render_fragment<'a>( +pub(super) fn render_fragment<'a, 'query, 'schema, T>( fragment_id: ResolvedFragmentId, options: &'a GraphQLClientCodegenOptions, - query: &'a BoundQuery<'a>, -) -> ExpandedSelection<'a> { + query: &'a BoundQuery<'a, 'query, 'schema, T>, +) -> ExpandedSelection<'a, 'query, 'schema, T> +where + T: graphql_parser::query::Text<'a> + std::default::Default, +{ let fragment = query.query.get_fragment(fragment_id); let mut expanded_selection = ExpandedSelection { query, @@ -87,11 +93,14 @@ enum VariantSelection<'a> { impl<'a> VariantSelection<'a> { /// The second argument is the parent type id, so it can be excluded. - fn from_selection( + fn from_selection( selection: &'a Selection, type_id: TypeId, - query: &BoundQuery<'a>, - ) -> Option> { + query: &BoundQuery<'a, 'a, '_, T>, + ) -> Option> + where + T: graphql_parser::query::Text<'a> + std::default::Default, + { match selection { Selection::InlineFragment(inline_fragment) => { Some(VariantSelection::InlineFragment(inline_fragment)) @@ -119,13 +128,15 @@ impl<'a> VariantSelection<'a> { } } -fn calculate_selection<'a>( - context: &mut ExpandedSelection<'a>, +fn calculate_selection<'a, 'schema, T>( + context: &mut ExpandedSelection<'a, '_, 'schema, T>, selection_set: &[SelectionId], struct_id: ResponseTypeId, type_id: TypeId, options: &'a GraphQLClientCodegenOptions, -) { +) where + T: graphql_parser::query::Text<'a> + std::default::Default, +{ // If the selection only contains a fragment, replace the selection with // that fragment. if selection_set.len() == 1 { @@ -467,8 +478,11 @@ pub(crate) struct ExpandedType<'a> { name: Cow<'a, str>, } -pub(crate) struct ExpandedSelection<'a> { - query: &'a BoundQuery<'a>, +pub(crate) struct ExpandedSelection<'a, 'query, 'schema, T> +where + T: graphql_parser::query::Text<'a> + std::default::Default, +{ + query: &'a BoundQuery<'a, 'query, 'schema, T>, types: Vec>, fields: Vec>, variants: Vec>, @@ -476,7 +490,10 @@ pub(crate) struct ExpandedSelection<'a> { options: &'a GraphQLClientCodegenOptions, } -impl<'a> ExpandedSelection<'a> { +impl<'a, 'query, 'schema, T> ExpandedSelection<'a, 'query, 'schema, T> +where + T: graphql_parser::query::Text<'a> + std::default::Default, +{ pub(crate) fn schema(&self) -> &'a Schema { self.query.schema } diff --git a/graphql_client_codegen/src/generated_module.rs b/graphql_client_codegen/src/generated_module.rs index 36c41a117..d9ca7e9ae 100644 --- a/graphql_client_codegen/src/generated_module.rs +++ b/graphql_client_codegen/src/generated_module.rs @@ -24,15 +24,21 @@ impl Display for OperationNotFound { impl Error for OperationNotFound {} /// This struct contains the parameters necessary to generate code for a given operation. -pub(crate) struct GeneratedModule<'a> { +pub(crate) struct GeneratedModule<'a, T> +where + T: graphql_parser::query::Text<'a> + std::default::Default, +{ pub operation: &'a str, pub query_string: &'a str, - pub resolved_query: &'a crate::query::Query, + pub resolved_query: &'a crate::query::Query<'a, T>, pub schema: &'a crate::schema::Schema, pub options: &'a crate::GraphQLClientCodegenOptions, } -impl<'a> GeneratedModule<'a> { +impl<'a, T> GeneratedModule<'a, T> +where + T: graphql_parser::query::Text<'a> + std::default::Default, +{ /// Generate the items for the variables and the response that will go inside the module. fn build_impls(&self) -> Result { Ok(crate::codegen::response_for_query( diff --git a/graphql_client_codegen/src/lib.rs b/graphql_client_codegen/src/lib.rs index 7037eaaba..93b624623 100644 --- a/graphql_client_codegen/src/lib.rs +++ b/graphql_client_codegen/src/lib.rs @@ -47,7 +47,7 @@ type CacheMap = std::sync::Mutex>; lazy_static! { static ref SCHEMA_CACHE: CacheMap = CacheMap::default(); - static ref QUERY_CACHE: CacheMap<(String, graphql_parser::query::Document)> = + static ref QUERY_CACHE: CacheMap<(String, graphql_parser::query::Document<'static, String>)> = CacheMap::default(); } @@ -73,7 +73,7 @@ pub fn generate_module_token_stream( let schema_string = read_file(v.key())?; let schema = match schema_extension { "graphql" | "gql" => { - let s = graphql_parser::schema::parse_schema(&schema_string).map_err(|parser_error| GeneralError(format!("Parser error: {}", parser_error)))?; + let s: graphql_parser::schema::Document<'_, String> = graphql_parser::schema::parse_schema(&schema_string).map_err(|parser_error| GeneralError(format!("Parser error: {}", parser_error)))?; schema::Schema::from(s) } "json" => { @@ -192,10 +192,13 @@ fn read_file(path: &std::path::Path) -> Result { } /// In derive mode, build an error when the operation with the same name as the struct is not found. -fn derive_operation_not_found_error( +fn derive_operation_not_found_error<'a, T>( ident: Option<&proc_macro2::Ident>, - query: &crate::query::Query, -) -> String { + query: &'a crate::query::Query<'a, T>, +) -> String +where + T: graphql_parser::query::Text<'a> + std::default::Default, +{ let operation_name = ident.map(ToString::to_string); let struct_ident = operation_name.as_deref().unwrap_or(""); diff --git a/graphql_client_codegen/src/query.rs b/graphql_client_codegen/src/query.rs index d8ff35bb3..b4236757c 100644 --- a/graphql_client_codegen/src/query.rs +++ b/graphql_client_codegen/src/query.rs @@ -59,11 +59,14 @@ pub(crate) struct ResolvedFragmentId(u32); #[derive(Debug, Clone, Copy)] pub(crate) struct VariableId(u32); -pub(crate) fn resolve( - schema: &Schema, - query: &graphql_parser::query::Document, -) -> Result { - let mut resolved_query: Query = Default::default(); +pub(crate) fn resolve<'doc, 'schema, T>( + schema: &'schema Schema, + query: &graphql_parser::query::Document<'doc, T>, +) -> Result, QueryValidationError> +where + T: Clone + graphql_parser::query::Text<'doc> + std::default::Default, +{ + let mut resolved_query: Query<'_, T> = Default::default(); create_roots(&mut resolved_query, query, schema)?; @@ -98,21 +101,24 @@ pub(crate) fn resolve( Ok(resolved_query) } -fn create_roots( - resolved_query: &mut Query, - query: &graphql_parser::query::Document, +fn create_roots<'a, T>( + resolved_query: &mut Query<'a, T>, + query: &graphql_parser::query::Document<'a, T>, schema: &Schema, -) -> Result<(), QueryValidationError> { +) -> Result<(), QueryValidationError> +where + T: graphql_parser::query::Text<'a> + std::default::Default, +{ // First, give ids to all fragments and operations. for definition in &query.definitions { match definition { graphql_parser::query::Definition::Fragment(fragment) => { let graphql_parser::query::TypeCondition::On(on) = &fragment.type_condition; resolved_query.fragments.push(ResolvedFragment { - name: fragment.name.clone(), - on: schema.find_type(on).ok_or_else(|| { + name: fragment.name.as_ref().to_string(), + on: schema.find_type(on.as_ref()).ok_or_else(|| { QueryValidationError::new(format!( - "Could not find type {} for fragment {} in schema.", + "Could not find type {:?} for fragment {:?} in schema.", on, fragment.name )) })?, @@ -130,7 +136,12 @@ fn create_roots( })?; let resolved_operation: ResolvedOperation = ResolvedOperation { object_id: on, - name: m.name.as_ref().expect("mutation without name").to_owned(), + name: m + .name + .as_ref() + .expect("mutation without name") + .as_ref() + .to_owned(), _operation_type: operations::OperationType::Mutation, selection_set: Vec::with_capacity(m.selection_set.items.len()), }; @@ -142,7 +153,12 @@ fn create_roots( ) => { let on = schema.query_type(); let resolved_operation: ResolvedOperation = ResolvedOperation { - name: q.name.as_ref().expect("query without name").to_owned(), + name: q + .name + .as_ref() + .expect("query without name") + .as_ref() + .to_owned(), _operation_type: operations::OperationType::Query, object_id: on, selection_set: Vec::with_capacity(q.selection_set.items.len()), @@ -170,6 +186,7 @@ fn create_roots( .name .as_ref() .expect("subscription without name") + .as_ref() .to_owned(), _operation_type: operations::OperationType::Subscription, object_id: on, @@ -191,24 +208,27 @@ fn create_roots( Ok(()) } -fn resolve_fragment( - query: &mut Query, - schema: &Schema, - fragment_definition: &graphql_parser::query::FragmentDefinition, -) -> Result<(), QueryValidationError> { +fn resolve_fragment<'doc, 'schema, T>( + query: &mut Query<'doc, T>, + schema: &'schema Schema, + fragment_definition: &graphql_parser::query::FragmentDefinition<'doc, T>, +) -> Result<(), QueryValidationError> +where + T: graphql_parser::query::Text<'doc> + std::default::Default, +{ let graphql_parser::query::TypeCondition::On(on) = &fragment_definition.type_condition; - let on = schema.find_type(on).ok_or_else(|| { + let on = schema.find_type(on.as_ref()).ok_or_else(|| { QueryValidationError::new(format!( - "Could not find type `{}` referenced by fragment `{}`", + "Could not find type `{:?}` referenced by fragment `{:?}`", on, fragment_definition.name )) })?; let (id, _) = query - .find_fragment(&fragment_definition.name) + .find_fragment(fragment_definition.name.as_ref()) .ok_or_else(|| { QueryValidationError::new(format!( - "Could not find fragment `{}`.", + "Could not find fragment `{:?}`.", fragment_definition.name )) })?; @@ -224,17 +244,20 @@ fn resolve_fragment( Ok(()) } -fn resolve_union_selection( - query: &mut Query, +fn resolve_union_selection<'doc, 'schema, T>( + query: &mut Query<'doc, T>, _union_id: UnionId, - selection_set: &graphql_parser::query::SelectionSet, + selection_set: &graphql_parser::query::SelectionSet<'doc, T>, parent: SelectionParent, - schema: &Schema, -) -> Result<(), QueryValidationError> { + schema: &'schema Schema, +) -> Result<(), QueryValidationError> +where + T: graphql_parser::query::Text<'doc> + std::default::Default, +{ for item in selection_set.items.iter() { match item { graphql_parser::query::Selection::Field(field) => { - if field.name == TYPENAME_FIELD { + if field.name.as_ref() == TYPENAME_FIELD { let id = query.push_selection(Selection::Typename, parent); parent.add_to_selection_set(query, id); } else { @@ -250,10 +273,10 @@ fn resolve_union_selection( } graphql_parser::query::Selection::FragmentSpread(fragment_spread) => { let (fragment_id, _fragment) = query - .find_fragment(&fragment_spread.fragment_name) + .find_fragment(fragment_spread.fragment_name.as_ref()) .ok_or_else(|| { QueryValidationError::new(format!( - "Could not find fragment `{}` referenced by fragment spread.", + "Could not find fragment `{:?}` referenced by fragment spread.", fragment_spread.fragment_name )) })?; @@ -268,27 +291,30 @@ fn resolve_union_selection( Ok(()) } -fn resolve_object_selection<'a>( - query: &mut Query, +fn resolve_object_selection<'doc, 'schema, T>( + query: &mut Query<'doc, T>, object: &dyn crate::schema::ObjectLike, - selection_set: &graphql_parser::query::SelectionSet, + selection_set: &graphql_parser::query::SelectionSet<'doc, T>, parent: SelectionParent, - schema: &'a Schema, -) -> Result<(), QueryValidationError> { + schema: &'schema Schema, +) -> Result<(), QueryValidationError> +where + T: graphql_parser::query::Text<'doc> + std::default::Default, +{ for item in selection_set.items.iter() { match item { graphql_parser::query::Selection::Field(field) => { - if field.name == TYPENAME_FIELD { + if field.name.as_ref() == TYPENAME_FIELD { let id = query.push_selection(Selection::Typename, parent); parent.add_to_selection_set(query, id); continue; } let (field_id, schema_field) = object - .get_field_by_name(&field.name, schema) + .get_field_by_name(field.name.as_ref(), schema) .ok_or_else(|| { QueryValidationError::new(format!( - "No field named {} on {}", + "No field named {:?} on {}", &field.name, object.name() )) @@ -296,7 +322,7 @@ fn resolve_object_selection<'a>( let id = query.push_selection( Selection::Field(SelectedField { - alias: field.alias.clone(), + alias: field.alias.as_ref().map(|x| x.as_ref().to_owned()), field_id, selection_set: Vec::with_capacity(selection_set.items.len()), }), @@ -320,10 +346,10 @@ fn resolve_object_selection<'a>( } graphql_parser::query::Selection::FragmentSpread(fragment_spread) => { let (fragment_id, _fragment) = query - .find_fragment(&fragment_spread.fragment_name) + .find_fragment(fragment_spread.fragment_name.as_ref()) .ok_or_else(|| { QueryValidationError::new(format!( - "Could not find fragment `{}` referenced by fragment spread.", + "Could not find fragment `{:?}` referenced by fragment spread.", fragment_spread.fragment_name )) })?; @@ -338,13 +364,16 @@ fn resolve_object_selection<'a>( Ok(()) } -fn resolve_selection( - ctx: &mut Query, +fn resolve_selection<'doc, 'schema, T>( + ctx: &mut Query<'doc, T>, on: TypeId, - selection_set: &graphql_parser::query::SelectionSet, + selection_set: &graphql_parser::query::SelectionSet<'doc, T>, parent: SelectionParent, - schema: &Schema, -) -> Result<(), QueryValidationError> { + schema: &'schema Schema, +) -> Result<(), QueryValidationError> +where + T: graphql_parser::query::Text<'doc> + std::default::Default, +{ match on { TypeId::Object(oid) => { let object = schema.get_object(oid); @@ -370,19 +399,22 @@ fn resolve_selection( Ok(()) } -fn resolve_inline_fragment( - query: &mut Query, - schema: &Schema, - inline_fragment: &graphql_parser::query::InlineFragment, +fn resolve_inline_fragment<'doc, 'schema, T>( + query: &mut Query<'doc, T>, + schema: &'schema Schema, + inline_fragment: &graphql_parser::query::InlineFragment<'doc, T>, parent: SelectionParent, -) -> Result { +) -> Result +where + T: graphql_parser::query::Text<'doc> + std::default::Default, +{ let graphql_parser::query::TypeCondition::On(on) = inline_fragment .type_condition .as_ref() .expect("missing type condition on inline fragment"); - let type_id = schema.find_type(on).ok_or_else(|| { + let type_id = schema.find_type(on.as_ref()).ok_or_else(|| { QueryValidationError::new(format!( - "Could not find type `{}` referenced by inline fragment.", + "Could not find type `{:?}` referenced by inline fragment.", on )) })?; @@ -406,11 +438,14 @@ fn resolve_inline_fragment( Ok(id) } -fn resolve_operation( - query: &mut Query, - schema: &Schema, - operation: &graphql_parser::query::OperationDefinition, -) -> Result<(), QueryValidationError> { +fn resolve_operation<'doc, 'schema, T>( + query: &mut Query<'doc, T>, + schema: &'schema Schema, + operation: &graphql_parser::query::OperationDefinition<'doc, T>, +) -> Result<(), QueryValidationError> +where + T: Clone + graphql_parser::query::Text<'doc> + std::default::Default, +{ match operation { graphql_parser::query::OperationDefinition::Mutation(m) => { let on = schema.mutation_type().ok_or_else(|| { @@ -421,7 +456,9 @@ fn resolve_operation( })?; let on = schema.get_object(on); - let (id, _) = query.find_operation(m.name.as_ref().unwrap()).unwrap(); + let (id, _) = query + .find_operation(m.name.as_ref().unwrap().as_ref()) + .unwrap(); resolve_variables(query, &m.variable_definitions, schema, id); resolve_object_selection( @@ -434,7 +471,9 @@ fn resolve_operation( } graphql_parser::query::OperationDefinition::Query(q) => { let on = schema.get_object(schema.query_type()); - let (id, _) = query.find_operation(q.name.as_ref().unwrap()).unwrap(); + let (id, _) = query + .find_operation(q.name.as_ref().unwrap().as_ref()) + .unwrap(); resolve_variables(query, &q.variable_definitions, schema, id); resolve_object_selection( @@ -448,7 +487,9 @@ fn resolve_operation( graphql_parser::query::OperationDefinition::Subscription(s) => { let on = schema.subscription_type().ok_or_else(|| QueryValidationError::new("Query contains a subscription operation, but the schema has no subscription type.".into()))?; let on = schema.get_object(on); - let (id, _) = query.find_operation(s.name.as_ref().unwrap()).unwrap(); + let (id, _) = query + .find_operation(s.name.as_ref().unwrap().as_ref()) + .unwrap(); resolve_variables(query, &s.variable_definitions, schema, id); resolve_object_selection( @@ -468,15 +509,21 @@ fn resolve_operation( } #[derive(Default)] -pub(crate) struct Query { +pub(crate) struct Query<'a, T> +where + T: graphql_parser::query::Text<'a> + std::default::Default, +{ fragments: Vec, operations: Vec, selection_parent_idx: HashMap, selections: Vec, - variables: Vec, + variables: Vec>, } -impl Query { +impl<'a, T> Query<'a, T> +where + T: graphql_parser::query::Text<'a> + std::default::Default, +{ fn push_selection(&mut self, node: Selection, parent: SelectionParent) -> SelectionId { let id = SelectionId(self.selections.len() as u32); self.selections.push(node); @@ -486,7 +533,7 @@ impl Query { id } - pub fn operations(&self) -> impl Iterator { + pub fn operations(&'a self) -> impl Iterator { walk_operations(self) } @@ -509,11 +556,11 @@ impl Query { } /// Selects the first operation matching `struct_name`. Returns `None` when the query document defines no operation, or when the selected operation does not match any defined operation. - pub(crate) fn select_operation<'a>( + pub(crate) fn select_operation( &'a self, name: &str, normalization: Normalization, - ) -> Option<(OperationId, &'a ResolvedOperation)> { + ) -> Option<(OperationId, &ResolvedOperation)> { walk_operations(self).find(|(_id, op)| normalization.operation(&op.name) == name) } @@ -540,7 +587,7 @@ impl Query { .map(|(idx, selection)| (SelectionId(idx as u32), selection)) } - fn walk_selection_set<'a>( + fn walk_selection_set( &'a self, selection_ids: &'a [SelectionId], ) -> impl Iterator + 'a { @@ -551,14 +598,22 @@ impl Query { } #[derive(Debug)] -pub(crate) struct ResolvedVariable { +pub(crate) struct ResolvedVariable<'a, T> +where + T: graphql_parser::query::Text<'a>, +{ pub(crate) operation_id: OperationId, pub(crate) name: String, - pub(crate) default: Option, + // Note that even if we wanted to simplify T = String here, we'd still be + // stuck with the lifetime parameter which is the real issue. + pub(crate) default: Option>, pub(crate) r#type: StoredFieldType, } -impl ResolvedVariable { +impl<'a, T> ResolvedVariable<'a, T> +where + T: graphql_parser::query::Text<'a>, +{ pub(crate) fn type_name<'schema>(&self, schema: &'schema Schema) -> &'schema str { self.r#type.id.name(schema) } @@ -622,25 +677,30 @@ impl UsedTypes { } } -fn resolve_variables( - query: &mut Query, - variables: &[graphql_parser::query::VariableDefinition], +fn resolve_variables<'a, T>( + query: &mut Query<'a, T>, + variables: &[graphql_parser::query::VariableDefinition<'a, T>], schema: &Schema, operation_id: OperationId, -) { +) where + T: Clone + graphql_parser::query::Text<'a> + std::default::Default, +{ for var in variables { query.variables.push(ResolvedVariable { operation_id, - name: var.name.clone(), + name: var.name.as_ref().to_owned(), default: var.default_value.clone(), r#type: resolve_field_type(schema, &var.var_type), }); } } -pub(crate) fn walk_operations( - query: &Query, -) -> impl Iterator { +pub(crate) fn walk_operations<'a, T>( + query: &'a Query<'a, T>, +) -> impl Iterator +where + T: graphql_parser::query::Text<'a> + std::default::Default, +{ query .operations .iter() @@ -648,16 +708,25 @@ pub(crate) fn walk_operations( .map(|(id, op)| (OperationId(id as u32), op)) } -pub(crate) fn operation_has_no_variables(operation_id: OperationId, query: &Query) -> bool { +pub(crate) fn operation_has_no_variables<'a, T>( + operation_id: OperationId, + query: &'a Query<'a, T>, +) -> bool +where + T: graphql_parser::query::Text<'a> + std::default::Default, +{ walk_operation_variables(operation_id, query) .next() .is_none() } -pub(crate) fn walk_operation_variables( +pub(crate) fn walk_operation_variables<'a, T>( operation_id: OperationId, - query: &Query, -) -> impl Iterator { + query: &'a Query<'a, T>, +) -> impl Iterator)> +where + T: graphql_parser::query::Text<'a> + std::default::Default, +{ query .variables .iter() @@ -666,7 +735,13 @@ pub(crate) fn walk_operation_variables( .filter(move |(_id, var)| var.operation_id == operation_id) } -pub(crate) fn all_used_types(operation_id: OperationId, query: &BoundQuery<'_>) -> UsedTypes { +pub(crate) fn all_used_types<'a, T>( + operation_id: OperationId, + query: &'a BoundQuery<'a, '_, '_, T>, +) -> UsedTypes +where + T: graphql_parser::query::Text<'a> + std::default::Default, +{ let mut used_types = UsedTypes::default(); let operation = query.query.get_operation(operation_id); @@ -682,7 +757,13 @@ pub(crate) fn all_used_types(operation_id: OperationId, query: &BoundQuery<'_>) used_types } -pub(crate) fn full_path_prefix(selection_id: SelectionId, query: &BoundQuery<'_>) -> String { +pub(crate) fn full_path_prefix<'doc, T>( + selection_id: SelectionId, + query: &BoundQuery<'doc, '_, '_, T>, +) -> String +where + T: graphql_parser::query::Text<'doc> + std::default::Default, +{ let mut path = match query.query.get_selection(selection_id) { Selection::FragmentSpread(_) | Selection::InlineFragment(_) => Vec::new(), selection => vec![selection.to_path_segment(query)], @@ -706,7 +787,10 @@ pub(crate) fn full_path_prefix(selection_id: SelectionId, query: &BoundQuery<'_> } #[derive(Clone, Copy)] -pub(crate) struct BoundQuery<'a> { - pub(crate) query: &'a Query, - pub(crate) schema: &'a Schema, +pub(crate) struct BoundQuery<'doc, 'query, 'schema, T> +where + T: graphql_parser::query::Text<'doc> + std::default::Default, +{ + pub(crate) query: &'query Query<'doc, T>, + pub(crate) schema: &'schema Schema, } diff --git a/graphql_client_codegen/src/query/fragments.rs b/graphql_client_codegen/src/query/fragments.rs index 2a667696d..29fab27fc 100644 --- a/graphql_client_codegen/src/query/fragments.rs +++ b/graphql_client_codegen/src/query/fragments.rs @@ -15,7 +15,13 @@ impl ResolvedFragment { } } -pub(crate) fn fragment_is_recursive(fragment_id: ResolvedFragmentId, query: &Query) -> bool { +pub(crate) fn fragment_is_recursive<'a, T>( + fragment_id: ResolvedFragmentId, + query: &'a Query<'a, T>, +) -> bool +where + T: graphql_parser::query::Text<'a> + std::default::Default, +{ let fragment = query.get_fragment(fragment_id); query diff --git a/graphql_client_codegen/src/query/selection.rs b/graphql_client_codegen/src/query/selection.rs index a241c1b85..e56dcfc18 100644 --- a/graphql_client_codegen/src/query/selection.rs +++ b/graphql_client_codegen/src/query/selection.rs @@ -7,10 +7,13 @@ use heck::CamelCase; /// This checks that the `on` clause on fragment spreads and inline fragments /// are valid in their context. -pub(super) fn validate_type_conditions( +pub(super) fn validate_type_conditions<'a, T>( selection_id: SelectionId, - query: &BoundQuery<'_>, -) -> Result<(), QueryValidationError> { + query: &BoundQuery<'a, '_, '_, T>, +) -> Result<(), QueryValidationError> +where + T: graphql_parser::query::Text<'a> + std::default::Default, +{ let selection = query.query.get_selection(selection_id); let selected_type = match selection { @@ -76,7 +79,10 @@ pub(super) enum SelectionParent { #[allow(clippy::trivially_copy_pass_by_ref)] impl SelectionParent { - fn schema_type_id(&self, query: &BoundQuery<'_>) -> TypeId { + fn schema_type_id<'a, T>(&self, query: &BoundQuery<'a, '_, '_, T>) -> TypeId + where + T: graphql_parser::query::Text<'a> + std::default::Default, + { match self { SelectionParent::Fragment(fragment_id) => query.query.get_fragment(*fragment_id).on, SelectionParent::Operation(operation_id) => { @@ -97,7 +103,13 @@ impl SelectionParent { } } - pub(super) fn add_to_selection_set(&self, q: &mut Query, selection_id: SelectionId) { + pub(super) fn add_to_selection_set<'a, T>( + &self, + q: &mut Query<'a, T>, + selection_id: SelectionId, + ) where + T: graphql_parser::query::Text<'a> + std::default::Default, + { match self { SelectionParent::Field(parent_selection_id) | SelectionParent::InlineFragment(parent_selection_id) => { @@ -131,7 +143,10 @@ impl SelectionParent { } } - pub(crate) fn to_path_segment(self, query: &BoundQuery<'_>) -> String { + pub(crate) fn to_path_segment<'a, T>(self, query: &BoundQuery<'a, '_, '_, T>) -> String + where + T: graphql_parser::query::Text<'a> + std::default::Default, + { match self { SelectionParent::Field(id) | SelectionParent::InlineFragment(id) => { query.query.get_selection(id).to_path_segment(query) @@ -165,7 +180,13 @@ impl Selection { } } - pub(crate) fn collect_used_types(&self, used_types: &mut UsedTypes, query: &BoundQuery<'_>) { + pub(crate) fn collect_used_types<'a, T>( + &self, + used_types: &mut UsedTypes, + query: &'a BoundQuery<'a, '_, '_, T>, + ) where + T: graphql_parser::query::Text<'a> + std::default::Default, + { match self { Selection::Field(field) => { let stored_field = query.schema.get_field(field.field_id); @@ -202,7 +223,14 @@ impl Selection { } } - pub(crate) fn contains_fragment(&self, fragment_id: ResolvedFragmentId, query: &Query) -> bool { + pub(crate) fn contains_fragment<'a, T>( + &self, + fragment_id: ResolvedFragmentId, + query: &Query<'a, T>, + ) -> bool + where + T: graphql_parser::query::Text<'a> + std::default::Default, + { match self { Selection::FragmentSpread(id) => *id == fragment_id, _ => self.subselection().iter().any(|selection_id| { @@ -221,7 +249,10 @@ impl Selection { } } - pub(super) fn to_path_segment(&self, query: &BoundQuery<'_>) -> String { + pub(super) fn to_path_segment<'a, T>(&self, query: &BoundQuery<'a, '_, '_, T>) -> String + where + T: graphql_parser::query::Text<'a> + std::default::Default, + { match self { Selection::Field(field) => field .alias @@ -247,6 +278,7 @@ pub(crate) struct InlineFragment { #[derive(Debug)] pub(crate) struct SelectedField { + // TODO make this Option? pub(crate) alias: Option, pub(crate) field_id: StoredFieldId, pub(crate) selection_set: Vec, diff --git a/graphql_client_codegen/src/query/validation.rs b/graphql_client_codegen/src/query/validation.rs index ee0a48f58..d2efec0ba 100644 --- a/graphql_client_codegen/src/query/validation.rs +++ b/graphql_client_codegen/src/query/validation.rs @@ -1,9 +1,12 @@ use super::{full_path_prefix, BoundQuery, Query, QueryValidationError, Selection, SelectionId}; use crate::schema::TypeId; -pub(super) fn validate_typename_presence( - query: &BoundQuery<'_>, -) -> Result<(), QueryValidationError> { +pub(super) fn validate_typename_presence<'a, T>( + query: &BoundQuery<'a, '_, '_, T>, +) -> Result<(), QueryValidationError> +where + T: graphql_parser::query::Text<'a> + std::default::Default, +{ for fragment in query.query.fragments.iter() { let type_id = match fragment.on { id @ TypeId::Interface(_) | id @ TypeId::Union(_) => id, @@ -46,11 +49,14 @@ pub(super) fn validate_typename_presence( Ok(()) } -fn selection_set_contains_type_name( +fn selection_set_contains_type_name<'a, T>( parent_type_id: TypeId, selection_set: &[SelectionId], - query: &Query, -) -> bool { + query: &Query<'a, T>, +) -> bool +where + T: graphql_parser::query::Text<'a> + std::default::Default, +{ for id in selection_set { let selection = query.get_selection(*id); diff --git a/graphql_client_codegen/src/schema.rs b/graphql_client_codegen/src/schema.rs index 89721c175..21e1b13e1 100644 --- a/graphql_client_codegen/src/schema.rs +++ b/graphql_client_codegen/src/schema.rs @@ -89,12 +89,14 @@ pub(crate) struct StoredFieldType { #[derive(Debug, Clone, PartialEq)] pub(crate) struct StoredUnion { + // TODO should this be a graphql_parser::query::Text type instead? pub(crate) name: String, pub(crate) variants: Vec, } #[derive(Debug, Clone, PartialEq)] pub(crate) struct StoredScalar { + // TODO should this be graphql_parser::query::Text? pub(crate) name: String, } @@ -443,8 +445,11 @@ pub(crate) fn input_is_recursive_without_indirection(input_id: InputId, schema: let mut visited_types = HashSet::<&str>::new(); input.contains_type_without_indirection(input_id, schema, &mut visited_types) } -impl std::convert::From for Schema { - fn from(ast: graphql_parser::schema::Document) -> Schema { +impl<'a, T> std::convert::From> for Schema +where + T: graphql_parser::query::Text<'a>, +{ + fn from(ast: graphql_parser::schema::Document<'a, T>) -> Schema { graphql_parser_conversion::build_schema(ast) } } @@ -459,10 +464,13 @@ impl std::convert::From( schema: &Schema, - inner: &graphql_parser::schema::Type, -) -> StoredFieldType { + inner: &graphql_parser::schema::Type<'a, T>, +) -> StoredFieldType +where + T: graphql_parser::query::Text<'a>, +{ use crate::type_qualifiers::graphql_parser_depth; use graphql_parser::schema::Type::*; @@ -483,7 +491,7 @@ pub(crate) fn resolve_field_type( } NamedType(name) => { return StoredFieldType { - id: schema.find_type_id(name), + id: schema.find_type_id(name.as_ref()), qualifiers, } } diff --git a/graphql_client_codegen/src/schema/graphql_parser_conversion.rs b/graphql_client_codegen/src/schema/graphql_parser_conversion.rs index a351aaeef..e7c45c2d1 100644 --- a/graphql_client_codegen/src/schema/graphql_parser_conversion.rs +++ b/graphql_client_codegen/src/schema/graphql_parser_conversion.rs @@ -2,13 +2,23 @@ use super::{Schema, StoredInputFieldType, TypeId}; use crate::schema::resolve_field_type; use graphql_parser::schema::{self as parser, Definition, Document, TypeDefinition, UnionType}; -pub(super) fn build_schema(mut src: graphql_parser::schema::Document) -> super::Schema { +pub(super) fn build_schema<'doc, T>( + mut src: graphql_parser::schema::Document<'doc, T>, +) -> super::Schema +where + T: graphql_parser::query::Text<'doc>, +{ let mut schema = Schema::new(); convert(&mut src, &mut schema); schema } -fn convert(src: &mut graphql_parser::schema::Document, schema: &mut Schema) { +fn convert<'a, 'doc: 'a, T>( + src: &'a mut graphql_parser::schema::Document<'doc, T>, + schema: &mut Schema, +) where + T: graphql_parser::query::Text<'doc>, +{ populate_names_map(schema, &src.definitions); src.definitions @@ -38,17 +48,17 @@ fn convert(src: &mut graphql_parser::schema::Document, schema: &mut Schema) { schema.query_type = schema_definition .query .as_mut() - .and_then(|n| schema.names.get(n)) + .and_then(|n| schema.names.get(n.as_ref())) .and_then(|id| id.as_object_id()); schema.mutation_type = schema_definition .mutation .as_mut() - .and_then(|n| schema.names.get(n)) + .and_then(|n| schema.names.get(n.as_ref())) .and_then(|id| id.as_object_id()); schema.subscription_type = schema_definition .subscription .as_mut() - .and_then(|n| schema.names.get(n)) + .and_then(|n| schema.names.get(n.as_ref())) .and_then(|id| id.as_object_id()); } else { schema.query_type = schema.names.get("Query").and_then(|id| id.as_object_id()); @@ -65,11 +75,14 @@ fn convert(src: &mut graphql_parser::schema::Document, schema: &mut Schema) { }; } -fn populate_names_map(schema: &mut Schema, definitions: &[Definition]) { +fn populate_names_map<'a, T>(schema: &mut Schema, definitions: &[Definition<'a, T>]) +where + T: graphql_parser::query::Text<'a>, +{ definitions .iter() .filter_map(|def| match def { - Definition::TypeDefinition(TypeDefinition::Enum(enm)) => Some(enm.name.as_str()), + Definition::TypeDefinition(TypeDefinition::Enum(enm)) => Some(enm.name.as_ref()), _ => None, }) .enumerate() @@ -81,7 +94,7 @@ fn populate_names_map(schema: &mut Schema, definitions: &[Definition]) { .iter() .filter_map(|def| match def { Definition::TypeDefinition(TypeDefinition::Object(object)) => { - Some(object.name.as_str()) + Some(object.name.as_ref()) } _ => None, }) @@ -96,7 +109,7 @@ fn populate_names_map(schema: &mut Schema, definitions: &[Definition]) { .iter() .filter_map(|def| match def { Definition::TypeDefinition(TypeDefinition::Interface(interface)) => { - Some(interface.name.as_str()) + Some(interface.name.as_ref()) } _ => None, }) @@ -110,7 +123,7 @@ fn populate_names_map(schema: &mut Schema, definitions: &[Definition]) { definitions .iter() .filter_map(|def| match def { - Definition::TypeDefinition(TypeDefinition::Union(union)) => Some(union.name.as_str()), + Definition::TypeDefinition(TypeDefinition::Union(union)) => Some(union.name.as_ref()), _ => None, }) .enumerate() @@ -122,7 +135,7 @@ fn populate_names_map(schema: &mut Schema, definitions: &[Definition]) { .iter() .filter_map(|def| match def { Definition::TypeDefinition(TypeDefinition::InputObject(input)) => { - Some(input.name.as_str()) + Some(input.name.as_ref()) } _ => None, }) @@ -134,26 +147,35 @@ fn populate_names_map(schema: &mut Schema, definitions: &[Definition]) { }); } -fn ingest_union(schema: &mut Schema, union: &mut UnionType) { +fn ingest_union<'doc, T>(schema: &mut Schema, union: &mut UnionType<'doc, T>) +where + T: graphql_parser::query::Text<'doc>, +{ let stored_union = super::StoredUnion { - name: std::mem::take(&mut union.name), + name: union.name.as_ref().to_string(), variants: union .types .iter() - .map(|name| schema.find_type_id(name)) + .map(|name| schema.find_type_id(name.as_ref())) .collect(), }; schema.stored_unions.push(stored_union); } -fn ingest_object(schema: &mut Schema, obj: &mut graphql_parser::schema::ObjectType) { - let object_id = schema.find_type_id(&obj.name).as_object_id().unwrap(); +fn ingest_object<'a, T>(schema: &mut Schema, obj: &mut graphql_parser::schema::ObjectType<'a, T>) +where + T: graphql_parser::query::Text<'a>, +{ + let object_id = schema + .find_type_id(obj.name.as_ref()) + .as_object_id() + .unwrap(); let mut field_ids = Vec::with_capacity(obj.fields.len()); for field in obj.fields.iter_mut() { let field = super::StoredField { - name: std::mem::take(&mut field.name), + name: field.name.as_ref().to_string(), r#type: resolve_field_type(schema, &field.field_type), parent: super::StoredFieldParent::Object(object_id), deprecation: find_deprecation(&field.directives), @@ -164,47 +186,57 @@ fn ingest_object(schema: &mut Schema, obj: &mut graphql_parser::schema::ObjectTy // Ingest the object itself let object = super::StoredObject { - name: std::mem::take(&mut obj.name), + name: obj.name.as_ref().to_string(), fields: field_ids, implements_interfaces: obj .implements_interfaces .iter() - .map(|iface_name| schema.find_interface(iface_name)) + .map(|iface_name| schema.find_interface(iface_name.as_ref())) .collect(), }; schema.push_object(object); } -fn ingest_scalar(schema: &mut Schema, scalar: &mut graphql_parser::schema::ScalarType) { - let name = std::mem::take(&mut scalar.name); - let name_for_names = name.clone(); +fn ingest_scalar<'a, T>(schema: &mut Schema, scalar: &mut graphql_parser::schema::ScalarType<'a, T>) +where + T: graphql_parser::query::Text<'a>, +{ + let name = scalar.name.as_ref().to_string(); - let scalar = super::StoredScalar { name }; + let scalar = super::StoredScalar { + name: name.to_string(), + }; let scalar_id = schema.push_scalar(scalar); - schema - .names - .insert(name_for_names, TypeId::Scalar(scalar_id)); + schema.names.insert(name, TypeId::Scalar(scalar_id)); } -fn ingest_enum(schema: &mut Schema, enm: &mut graphql_parser::schema::EnumType) { +fn ingest_enum<'a, T>(schema: &mut Schema, enm: &mut graphql_parser::schema::EnumType<'a, T>) +where + T: graphql_parser::query::Text<'a>, +{ let enm = super::StoredEnum { - name: std::mem::take(&mut enm.name), + name: enm.name.as_ref().to_string(), variants: enm .values .iter_mut() - .map(|value| std::mem::take(&mut value.name)) + .map(|value| value.name.as_ref().to_string()) .collect(), }; schema.push_enum(enm); } -fn ingest_interface(schema: &mut Schema, interface: &mut graphql_parser::schema::InterfaceType) { +fn ingest_interface<'a, T>( + schema: &mut Schema, + interface: &mut graphql_parser::schema::InterfaceType<'a, T>, +) where + T: graphql_parser::query::Text<'a>, +{ let interface_id = schema - .find_type_id(&interface.name) + .find_type_id(interface.name.as_ref()) .as_interface_id() .unwrap(); @@ -212,7 +244,7 @@ fn ingest_interface(schema: &mut Schema, interface: &mut graphql_parser::schema: for field in interface.fields.iter_mut() { let field = super::StoredField { - name: std::mem::take(&mut field.name), + name: field.name.as_ref().to_string(), r#type: resolve_field_type(schema, &field.field_type), parent: super::StoredFieldParent::Interface(interface_id), deprecation: find_deprecation(&field.directives), @@ -222,22 +254,25 @@ fn ingest_interface(schema: &mut Schema, interface: &mut graphql_parser::schema: } let new_interface = super::StoredInterface { - name: std::mem::take(&mut interface.name), + name: interface.name.as_ref().to_string(), fields: field_ids, }; schema.push_interface(new_interface); } -fn find_deprecation(directives: &[parser::Directive]) -> Option> { +fn find_deprecation<'a, T>(directives: &[parser::Directive<'a, T>]) -> Option> +where + T: graphql_parser::query::Text<'a>, +{ directives .iter() - .find(|directive| directive.name == "deprecated") + .find(|directive| directive.name.as_ref() == "deprecated") .map(|directive| { directive .arguments .iter() - .find(|(name, _)| name == "reason") + .find(|(name, _)| name.as_ref() == "reason") .and_then(|(_, value)| match value { graphql_parser::query::Value::String(s) => Some(s.clone()), _ => None, @@ -245,16 +280,19 @@ fn find_deprecation(directives: &[parser::Directive]) -> Option> }) } -fn ingest_input(schema: &mut Schema, input: &mut parser::InputObjectType) { +fn ingest_input<'a, T>(schema: &mut Schema, input: &mut parser::InputObjectType<'a, T>) +where + T: graphql_parser::query::Text<'a>, +{ let input = super::StoredInputType { - name: std::mem::take(&mut input.name), + name: input.name.as_ref().to_string(), fields: input .fields .iter_mut() .map(|val| { let field_type = super::resolve_field_type(schema, &val.value_type); ( - std::mem::take(&mut val.name), + val.name.as_ref().to_string(), StoredInputFieldType { qualifiers: field_type.qualifiers, id: field_type.id, @@ -267,35 +305,60 @@ fn ingest_input(schema: &mut Schema, input: &mut parser::InputObjectType) { schema.stored_inputs.push(input); } -fn objects_mut(doc: &mut Document) -> impl Iterator { +fn objects_mut<'a, 'doc: 'a, T>( + doc: &'a mut Document<'doc, T>, +) -> impl Iterator> +where + T: graphql_parser::query::Text<'doc>, +{ doc.definitions.iter_mut().filter_map(|def| match def { Definition::TypeDefinition(TypeDefinition::Object(obj)) => Some(obj), _ => None, }) } -fn interfaces_mut(doc: &mut Document) -> impl Iterator { +fn interfaces_mut<'a, 'doc: 'a, T>( + doc: &'a mut Document<'doc, T>, +) -> impl Iterator> +where + T: graphql_parser::query::Text<'doc>, +{ doc.definitions.iter_mut().filter_map(|def| match def { Definition::TypeDefinition(TypeDefinition::Interface(interface)) => Some(interface), _ => None, }) } -fn unions_mut(doc: &mut Document) -> impl Iterator { +fn unions_mut<'a, 'doc: 'a, T>( + doc: &'a mut Document<'doc, T>, +) -> impl Iterator> +where + T: graphql_parser::query::Text<'doc>, +{ doc.definitions.iter_mut().filter_map(|def| match def { Definition::TypeDefinition(TypeDefinition::Union(union)) => Some(union), _ => None, }) } -fn enums_mut(doc: &mut Document) -> impl Iterator { +fn enums_mut<'a, 'doc: 'a, T>( + doc: &'a mut Document<'doc, T>, +) -> impl Iterator> +where + T: graphql_parser::query::Text<'doc>, +{ doc.definitions.iter_mut().filter_map(|def| match def { Definition::TypeDefinition(TypeDefinition::Enum(r#enum)) => Some(r#enum), _ => None, }) } -fn inputs_mut(doc: &mut Document) -> impl Iterator { +fn inputs_mut<'a, 'doc: 'a, T>( + doc: &'a mut Document<'doc, T>, +) -> impl Iterator> +where + T: graphql_parser::query::Text<'doc>, +{ doc.definitions.iter_mut().filter_map(|def| match def { Definition::TypeDefinition(TypeDefinition::InputObject(input)) => Some(input), _ => None, diff --git a/graphql_client_codegen/src/schema/tests/github.rs b/graphql_client_codegen/src/schema/tests/github.rs index 8957b775c..5eb4877e7 100644 --- a/graphql_client_codegen/src/schema/tests/github.rs +++ b/graphql_client_codegen/src/schema/tests/github.rs @@ -7,7 +7,8 @@ const SCHEMA_GRAPHQL: &str = include_str!("github_schema.graphql"); fn ast_from_graphql_and_json_produce_the_same_schema() { let json: graphql_introspection_query::introspection_response::IntrospectionResponse = serde_json::from_str(SCHEMA_JSON).unwrap(); - let graphql_parser_schema = graphql_parser::parse_schema(SCHEMA_GRAPHQL).unwrap(); + let graphql_parser_schema: graphql_parser::schema::Document<'_, String> = + graphql_parser::parse_schema(SCHEMA_GRAPHQL).unwrap(); let mut json = Schema::from(json); let mut gql = Schema::from(graphql_parser_schema); diff --git a/graphql_client_codegen/src/tests/mod.rs b/graphql_client_codegen/src/tests/mod.rs index d72172957..7aa0bdffc 100644 --- a/graphql_client_codegen/src/tests/mod.rs +++ b/graphql_client_codegen/src/tests/mod.rs @@ -3,9 +3,11 @@ use crate::{generated_module, schema::Schema, CodegenMode, GraphQLClientCodegenO #[test] fn schema_with_keywords_works() { let query_string = include_str!("keywords_query.graphql"); - let query = graphql_parser::parse_query(query_string).expect("Parse keywords query"); - let schema = graphql_parser::parse_schema(include_str!("keywords_schema.graphql")) - .expect("Parse keywords schema"); + let query: graphql_parser::query::Document<'_, String> = + graphql_parser::parse_query(query_string).expect("Parse keywords query"); + let schema: graphql_parser::schema::Document<'_, String> = + graphql_parser::parse_schema(include_str!("keywords_schema.graphql")) + .expect("Parse keywords schema"); let schema = Schema::from(schema); let options = GraphQLClientCodegenOptions::new(CodegenMode::Cli); @@ -41,9 +43,11 @@ fn schema_with_keywords_works() { #[test] fn fragments_other_variant_should_generate_unknown_other_variant() { let query_string = include_str!("foobars_query.graphql"); - let query = graphql_parser::parse_query(query_string).expect("Parse foobars query"); - let schema = graphql_parser::parse_schema(include_str!("foobars_schema.graphql")) - .expect("Parse foobars schema"); + let query: graphql_parser::query::Document<'_, String> = + graphql_parser::parse_query(query_string).expect("Parse foobars query"); + let schema: graphql_parser::schema::Document<'_, String> = + graphql_parser::parse_schema(include_str!("foobars_schema.graphql")) + .expect("Parse foobars schema"); let schema = Schema::from(schema); let mut options = GraphQLClientCodegenOptions::new(CodegenMode::Cli); @@ -80,9 +84,11 @@ fn fragments_other_variant_should_generate_unknown_other_variant() { #[test] fn fragments_other_variant_false_should_not_generate_unknown_other_variant() { let query_string = include_str!("foobars_query.graphql"); - let query = graphql_parser::parse_query(query_string).expect("Parse foobars query"); - let schema = graphql_parser::parse_schema(include_str!("foobars_schema.graphql")) - .expect("Parse foobars schema"); + let query: graphql_parser::query::Document<'_, String> = + graphql_parser::parse_query(query_string).expect("Parse foobars query"); + let schema: graphql_parser::schema::Document<'_, String> = + graphql_parser::parse_schema(include_str!("foobars_schema.graphql")) + .expect("Parse foobars schema"); let schema = Schema::from(schema); let options = GraphQLClientCodegenOptions::new(CodegenMode::Cli); diff --git a/graphql_client_codegen/src/type_qualifiers.rs b/graphql_client_codegen/src/type_qualifiers.rs index 0803eb82e..0fd31ac9e 100644 --- a/graphql_client_codegen/src/type_qualifiers.rs +++ b/graphql_client_codegen/src/type_qualifiers.rs @@ -10,7 +10,10 @@ impl GraphqlTypeQualifier { } } -pub fn graphql_parser_depth(schema_type: &graphql_parser::schema::Type) -> usize { +pub fn graphql_parser_depth<'a, T>(schema_type: &graphql_parser::schema::Type<'a, T>) -> usize +where + T: graphql_parser::query::Text<'a>, +{ match schema_type { graphql_parser::schema::Type::ListType(inner) => 1 + graphql_parser_depth(inner), graphql_parser::schema::Type::NonNullType(inner) => 1 + graphql_parser_depth(inner),