diff --git a/purs/app/spago.yaml b/purs/app/spago.yaml index a7aa618..f650f07 100644 --- a/purs/app/spago.yaml +++ b/purs/app/spago.yaml @@ -23,7 +23,7 @@ package: - variant - oa-gql-schema-all-users - oa-enums - - oa-gql-enums + - gql-enums test: main: Test.Main dependencies: diff --git a/src/build_schema.rs b/src/build_schema.rs index 509498c..21172e8 100644 --- a/src/build_schema.rs +++ b/src/build_schema.rs @@ -9,12 +9,13 @@ use cynic_introspection::{ Directive, DirectiveLocation, FieldWrapping, InputValue, InterfaceType, IntrospectionQuery, Type, UnionType, WrappingType, }; -use stringcase::{kebab_case, pascal_case}; +use stringcase::kebab_case; use tokio::task::spawn_blocking; use crate::{ config::{ - parse_outside_types::{Mod, OutsideTypes}, parse_scalar_types::ScalarTypes, + parse_outside_types::{Mod, OutsideTypes}, + parse_scalar_types::ScalarTypes, workspace::WorkspaceConfig, }, enums::generate_enum::generate_enum, @@ -28,6 +29,7 @@ use crate::{ purescript_record::{show_field_name, Field, PurescriptRecord}, purescript_type::PurescriptType, purescript_variant::Variant, + upper_first::upper_first, }, write::write, }; @@ -97,41 +99,57 @@ pub async fn build_schema( // Adds the root schema record let mut schema_record = PurescriptRecord::new("Schema"); - // The schema must always at least have a query type, add it now. - let query_type = PurescriptType::new( - "Query", - vec![], - Argument::new_type(&pascal_case(schema.query_type.as_str())), - ); - schema_record.add_field(Field::new("query").with_type(&query_type.name)); - types.push(query_type); - // Add the directives field (imported above) schema_record.add_field(Field::new("directives").with_type_arg( Argument::new_type("Proxy").with_argument(Argument::new_type("Directives")), )); - // Optionally add mutation - if let Some(mut_type) = &schema.mutation_type { - let mutation_type = PurescriptType::new( - "Mutation", - vec![], - Argument::new_type(&pascal_case(&mut_type)), - ); - schema_record.add_field(Field::new("mutation").with_type(&mutation_type.name)); - types.push(mutation_type); - }; - - // and subscription types - if let Some(mut_type) = &schema.subscription_type { - let mutation_type = PurescriptType::new( - "Subscription", + if workspace_config.create_root_aliases { + let query_type = PurescriptType::new( + "Query", vec![], - Argument::new_type(&pascal_case(&mut_type)), + Argument::new_type(&upper_first(schema.query_type.as_str())), ); - schema_record.add_field(Field::new("subscription").with_type(&mutation_type.name)); - types.push(mutation_type); - }; + + schema_record.add_field(Field::new("query").with_type(&query_type.name)); + types.push(query_type); + + // Optionally add mutation + if let Some(mut_type) = &schema.mutation_type { + let mutation_type = PurescriptType::new( + "Mutation", + vec![], + Argument::new_type(&upper_first(&mut_type)), + ); + schema_record.add_field(Field::new("mutation").with_type(&mutation_type.name)); + types.push(mutation_type); + }; + + // and subscription types + if let Some(sub_type) = &schema.subscription_type { + let sub_type = PurescriptType::new( + "Subscription", + vec![], + Argument::new_type(&upper_first(&sub_type)), + ); + schema_record.add_field(Field::new("subscription").with_type(&sub_type.name)); + types.push(sub_type); + }; + } else { + schema_record.add_field(Field::new("query").with_type(&schema.query_type)); + + // Optionally add mutation + if let Some(mut_type) = &schema.mutation_type { + schema_record.add_field(Field::new("mutation").with_type(&mut_type)); + }; + + // and subscription types + if let Some(sub_type) = &schema.subscription_type { + schema_record.add_field(Field::new("subscription").with_type(&sub_type)); + }; + } + + // The schema must always at least have a query type, add it now. // Process the schema types for type_ in schema.types.iter() { @@ -142,7 +160,7 @@ pub async fn build_schema( } // Convert the gql_type_name to a PurescriptTypeName - let name = pascal_case(&obj.name); + let name = upper_first(&obj.name); // Creates a new record for the object let mut record = PurescriptRecord::new("Ignored"); @@ -163,6 +181,7 @@ pub async fn build_schema( &mut imports, &postgres_types, &outside_types, + &scalar_types, ), &arg.ty.wrapping, &mut imports, @@ -183,6 +202,7 @@ pub async fn build_schema( &mut imports, &postgres_types, &outside_types, + &scalar_types, ), &field.ty.wrapping, &mut imports, @@ -218,19 +238,28 @@ pub async fn build_schema( add_import("argonaut-core", "Data.Argonaut.Core", "Json", &mut imports) } "time" => add_import("datetime", "Data.Time", "Time", &mut imports), - scalar_name => { - match scalar_types.lock().unwrap().get(scalar_name) { - Some(Mod { package, import, name }) => { - add_import(package, import, name, &mut imports); - types.push(PurescriptType::new(scalar_name, vec![], Argument::new_type(name))); + scalar_name => match scalar_types.lock().unwrap().get(scalar_name) { + Some(Mod { + package, + import, + name, + }) => { + add_import(package, import, name, &mut imports); + types.push(PurescriptType::new( + &&upper_first(scalar_name), + vec![], + Argument::new_type(name), + )); } None => { - add_import("argonaut-core", "Data.Argonaut.Core", "Json", &mut imports); - types.push(PurescriptType::new(scalar_name, vec![], Argument::new_type("Json"))); - + add_import("argonaut-core", "Data.Argonaut.Core", "Json", &mut imports); + types.push(PurescriptType::new( + &upper_first(scalar_name), + vec![], + Argument::new_type("Json"), + )); } - } - } + }, } } Type::Enum(en) => { @@ -255,7 +284,7 @@ pub async fn build_schema( } // Convert the gql_type_name to a PurescriptTypeName - let name = pascal_case(&obj.name); + let name: String = upper_first(&obj.name); // Build a purescript record with all fields let mut record = PurescriptRecord::new("Query"); @@ -270,6 +299,7 @@ pub async fn build_schema( &mut imports, &postgres_types, &outside_types, + &scalar_types, ), &field.ty.wrapping, &mut imports, @@ -318,7 +348,7 @@ pub async fn build_schema( union.with_values( &possible_types .iter() - .map(|t| (t.clone(), pascal_case(&t))) + .map(|t| (t.clone(), upper_first(&t))) .collect(), ); diff --git a/src/config/workspace.rs b/src/config/workspace.rs index 079e99b..cdacb40 100644 --- a/src/config/workspace.rs +++ b/src/config/workspace.rs @@ -22,4 +22,11 @@ pub struct WorkspaceConfig { pub schema_libs_dir: String, #[serde(default = "Vec::new")] pub variant_enums: Vec, + #[serde(default = "mk_false")] + pub create_root_aliases: bool, + pub enums_package_name: String, } + +fn mk_false () -> bool { + false +} \ No newline at end of file diff --git a/src/enums/generate_enum.rs b/src/enums/generate_enum.rs index 95e2465..f16bbc0 100644 --- a/src/enums/generate_enum.rs +++ b/src/enums/generate_enum.rs @@ -35,7 +35,7 @@ pub async fn generate_enum( en.values.iter().map(|v| upper_first(&v.name)).collect() }; let original_values: Vec = en.values.iter().map(|v| v.name.clone()).collect(); - let name: String = pascal_case(&en.name); + let name: String = upper_first(&en.name); // Some enums are shared between all schemas // Hasura suffixes 'Enum' to the end of custom enums created @@ -51,7 +51,8 @@ pub async fn generate_enum( &workspace_config.shared_graphql_enums_lib ); let package_name = pascal_case(&workspace_config.shared_graphql_enums_lib); - let module_name = format!("{package_name}.{name}"); + let name_pascal = pascal_case(&name); + let module_name = format!("{package_name}.{name_pascal}"); let helper_module = format!("{package_name}.Utils.VariantHelpers"); if let Some(variant) = variant_mod( &name, @@ -60,17 +61,17 @@ pub async fn generate_enum( &format!("\nimport {helper_module} (var, match)"), ) { imports - .push(PurescriptImport::new(&module_name, "oa-gql-enums").add_specified(&name)); + .push(PurescriptImport::new(&module_name, &workspace_config.enums_package_name).add_specified(&name)); write( - &format!("{lib_path}/src/{package_name}/{name}.purs"), + &format!("{lib_path}/src/{package_name}/{module_name}.purs"), &variant, ); write( &format!("{lib_path}/src/{package_name}/Utils/VariantHelpers.purs"), &format!("module {helper_module} where \n{VARIANT_HELPERS_MOD}"), ); - write(&format!("{lib_path}/spago.yaml"), &enums_spago_yaml()); + write(&format!("{lib_path}/spago.yaml"), &enums_spago_yaml(&workspace_config.enums_package_name)); } None @@ -79,8 +80,9 @@ pub async fn generate_enum( let instances = enum_instances(&name, &values, &original_values); let package_name = pascal_case(&workspace_config.shared_graphql_enums_lib); - let module_name = format!("{package_name}.{name}"); - imports.push(PurescriptImport::new(&module_name, "oa-gql-enums").add_specified(&name)); + let name_pascal = pascal_case(&name); + let module_name = format!("{package_name}.{name_pascal}"); + imports.push(PurescriptImport::new(&module_name, &workspace_config.enums_package_name).add_specified(&name)); let lib_path = format!( "{}{}", @@ -88,12 +90,12 @@ pub async fn generate_enum( &workspace_config.shared_graphql_enums_lib ); write( - &format!("{lib_path}/src/{package_name}/{name}.purs"), + &format!("{lib_path}/src/{package_name}/{name_pascal}.purs"), &format!( "module {module_name} ({name}(..)) where\n\n{MODULE_IMPORTS}\n\n{e}{instances}" ), ); - write(&format!("{lib_path}/spago.yaml"), &enums_spago_yaml()); + write(&format!("{lib_path}/spago.yaml"), &enums_spago_yaml(&workspace_config.enums_package_name)); None } // Otherwise write schema-specific variant enums @@ -397,9 +399,11 @@ fn to_variant(type_name: &str, name: &str) -> String { ) } -fn enums_spago_yaml() -> String { +fn enums_spago_yaml(package_name: &str) -> String { + + format!( r#"package: - name: oa-gql-enums + name: {package_name} dependencies: - argonaut - argonaut-codecs @@ -416,8 +420,8 @@ fn enums_spago_yaml() -> String { - variant - oa-make-fixture - oa-encode-decode -"# - .to_string() +"#) + } const MODULE_IMPORTS: &str = r#"import Prelude diff --git a/src/hasura_types.rs b/src/hasura_types.rs index 8defeca..5ca8930 100644 --- a/src/hasura_types.rs +++ b/src/hasura_types.rs @@ -3,11 +3,12 @@ use std::{ sync::{Arc, Mutex}, }; -use stringcase::pascal_case; - use crate::{ - config::parse_outside_types::{Mod, OutsideTypes}, - purescript_gen::{purescript_argument::Argument, purescript_import::PurescriptImport}, + config::{ + parse_outside_types::{Mod, OutsideTypes}, + parse_scalar_types::ScalarTypes, + }, + purescript_gen::{purescript_argument::Argument, purescript_import::PurescriptImport, upper_first::upper_first}, }; pub fn as_gql_field( @@ -17,17 +18,32 @@ pub fn as_gql_field( imports: &mut Vec, purs_types: &Arc>>, outside_types: &Arc>, + scalar_types: &Arc>, ) -> Argument { - let (import, type_) = outside_type(object, field, name, &purs_types, &outside_types); + let (import, type_) = outside_type( + object, + field, + name, + &purs_types, + &outside_types, + &scalar_types, + ); if let Some((field_package, field_import)) = import { imports.push(PurescriptImport::new(&field_import, &field_package).add_specified(&type_)); return Argument::new_type("AsGql") .with_argument(Argument::new_type(&format!("\"{}\"", name))) .with_argument(Argument::new_type(&type_)); } + + if name == "ID" { + return Argument::new_type("AsGql") + .with_argument(Argument::new_type(&format!("\"{}\"", name))) + .with_argument(Argument::new_type("ID")); + } + Argument::new_type("AsGql") .with_argument(Argument::new_type(&format!("\"{}\"", name))) - .with_argument(Argument::new_type(&pascal_case(&type_))) + .with_argument(Argument::new_type(&upper_first(&type_))) } fn outside_type( @@ -36,6 +52,7 @@ fn outside_type( name: &str, purs_types: &Arc>>, outside_types: &Arc>, + scalar_types: &Arc>, ) -> (Option<(String, String)>, String) { let is_comparison_fn = name.ends_with("_comparison_exp"); @@ -60,7 +77,13 @@ fn outside_type( } } - if let Some((package, import, type_)) = get_outside_type(new_object, field, outside_types) { + if let Some(Mod { + package, + import, + name: type_, + }) = get_outside_type(new_object, field, outside_types) + .or_else(|| get_scalar_type(scalar_types, name)) + { if is_comparison_fn { match name { "String_comparison_exp" => { @@ -111,20 +134,22 @@ fn get_outside_type( object: &str, field: &str, outside_types: &Arc>, -) -> Option<(String, String, String)> { +) -> Option { outside_types .lock() .expect("Failed to lock outside types to thread.") .get(object) .map(|table| table.get(field)) .flatten() - .map( - |Mod { - package, - import, - name, - }| (package.to_string(), import.to_string(), name.to_string()), - ) + .cloned() +} + +fn get_scalar_type(scalar_types: &Arc>, type_name: &str) -> Option { + scalar_types + .lock() + .expect("Failed to lock scalar types to thread.") + .get(type_name) + .cloned() } pub fn base_types(type_name: &str) -> &str { diff --git a/src/purescript_gen/purescript_argument.rs b/src/purescript_gen/purescript_argument.rs index a88b56f..ac2e80d 100644 --- a/src/purescript_gen/purescript_argument.rs +++ b/src/purescript_gen/purescript_argument.rs @@ -1,10 +1,14 @@ use super::purescript_record::PurescriptRecord; + +#[derive(Debug)] pub enum Argument { Type(String, Vec), Function(Box), Record(PurescriptRecord), } +#[derive(Debug)] + pub struct PurescriptFunctionType { pub arguments: Vec, pub return_type: Argument, diff --git a/src/purescript_gen/purescript_print_module.rs b/src/purescript_gen/purescript_print_module.rs index 1e0cca5..68dcb04 100644 --- a/src/purescript_gen/purescript_print_module.rs +++ b/src/purescript_gen/purescript_print_module.rs @@ -15,7 +15,7 @@ pub fn print_module( ) -> String { let mut module = format!("module Schema.{role} where"); types.sort_by_key(|t| t.name.clone()); - types.dedup_by_key(|t| t.name.clone()); + // types.dedup_by_key(|t| t.name.clone()); let types = types .iter_mut() diff --git a/src/purescript_gen/purescript_record.rs b/src/purescript_gen/purescript_record.rs index 6e53b42..3fd9106 100644 --- a/src/purescript_gen/purescript_record.rs +++ b/src/purescript_gen/purescript_record.rs @@ -1,11 +1,15 @@ use super::purescript_argument::Argument; +#[derive(Debug)] + pub struct PurescriptRecord { pub name: String, arguments: Vec, pub fields: Vec, } +#[derive(Debug)] + pub struct Field { name: String, pub type_name: Argument, diff --git a/src/purescript_gen/purescript_type.rs b/src/purescript_gen/purescript_type.rs index e7a55a3..b335398 100644 --- a/src/purescript_gen/purescript_type.rs +++ b/src/purescript_gen/purescript_type.rs @@ -1,5 +1,7 @@ use super::purescript_argument::Argument; + +#[derive(Debug)] pub struct PurescriptType { pub name: String, arguments: Vec,