From 8934f3390f220f8fd3b4916ecafcd4864602cb69 Mon Sep 17 00:00:00 2001 From: Lucas Pickering Date: Fri, 9 Apr 2021 17:26:03 -0400 Subject: [PATCH] [#137] Add support for multiple schema files Users can now pass multiple schema paths, and schema paths can also be glob patterns. All the schema files will simple be concatenated together, then parsing/codegen will continue like normal. --- juniper-from-schema-build-tests/file/build.rs | 2 +- .../schema1.graphql} | 2 +- .../file/schema/schema2.graphql | 3 ++ juniper-from-schema-build/src/lib.rs | 16 +++---- juniper-from-schema-code-gen/Cargo.toml | 1 + juniper-from-schema-code-gen/src/lib.rs | 47 +++++++++++++++---- juniper-from-schema-proc-macro/src/lib.rs | 4 +- .../src/parse_input.rs | 6 ++- 8 files changed, 57 insertions(+), 24 deletions(-) rename juniper-from-schema-build-tests/file/{schema.graphql => schema/schema1.graphql} (69%) create mode 100644 juniper-from-schema-build-tests/file/schema/schema2.graphql diff --git a/juniper-from-schema-build-tests/file/build.rs b/juniper-from-schema-build-tests/file/build.rs index b629682..170114d 100644 --- a/juniper-from-schema-build-tests/file/build.rs +++ b/juniper-from-schema-build-tests/file/build.rs @@ -1,5 +1,5 @@ fn main() { - juniper_from_schema_build::configure_for_file("schema.graphql") + juniper_from_schema_build::configure_for_files(vec!["schema/*.graphql"]) .context_type("()") .error_type("MyError") .compile() diff --git a/juniper-from-schema-build-tests/file/schema.graphql b/juniper-from-schema-build-tests/file/schema/schema1.graphql similarity index 69% rename from juniper-from-schema-build-tests/file/schema.graphql rename to juniper-from-schema-build-tests/file/schema/schema1.graphql index bd94ae1..295db89 100644 --- a/juniper-from-schema-build-tests/file/schema.graphql +++ b/juniper-from-schema-build-tests/file/schema/schema1.graphql @@ -3,5 +3,5 @@ schema { } type Query { - ping: Boolean! + foo: Foo } diff --git a/juniper-from-schema-build-tests/file/schema/schema2.graphql b/juniper-from-schema-build-tests/file/schema/schema2.graphql new file mode 100644 index 0000000..061e5fd --- /dev/null +++ b/juniper-from-schema-build-tests/file/schema/schema2.graphql @@ -0,0 +1,3 @@ +type Foo { + ping: Boolean! +} \ No newline at end of file diff --git a/juniper-from-schema-build/src/lib.rs b/juniper-from-schema-build/src/lib.rs index 1532adb..bfaaeae 100644 --- a/juniper-from-schema-build/src/lib.rs +++ b/juniper-from-schema-build/src/lib.rs @@ -73,19 +73,19 @@ pub fn configure_for_schema_literal(schema: &str) -> CodeGen { } /// Simple compilation of a GraphQL schema file. -pub fn compile_file>(path: P) -> Result<(), Box> { - configure_for_file(path).compile() +pub fn compile_files>(paths: Vec

) -> Result<(), Box> { + configure_for_files(paths).compile() } /// Configure a [`CodeGen`] with a GraphQL schema file. /// /// [`CodeGen`]: struct.CodeGen.html -pub fn configure_for_file>(path: P) -> CodeGen { +pub fn configure_for_files>(paths: Vec

) -> CodeGen { let root = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()); - let path = root.join(path); + let paths = paths.into_iter().map(|path| root.join(path)).collect(); CodeGen { - schema: SchemaLocation::File(path), + schema: SchemaLocation::Files(paths), context_type: None, error_type: None, } @@ -101,7 +101,7 @@ pub struct CodeGen { #[derive(Debug)] enum SchemaLocation { - File(PathBuf), + Files(Vec), Literal(String), } @@ -132,8 +132,8 @@ impl CodeGen { let dest_path = Path::new(&out_dir).join("juniper_from_schema_graphql_schema.rs"); let mut code_gen = match self.schema { - SchemaLocation::File(path) => { - juniper_from_schema_code_gen::CodeGen::build_from_schema_file(path) + SchemaLocation::Files(paths) => { + juniper_from_schema_code_gen::CodeGen::build_from_schema_files(paths) } SchemaLocation::Literal(schema) => { juniper_from_schema_code_gen::CodeGen::build_from_schema_literal(schema) diff --git a/juniper-from-schema-code-gen/Cargo.toml b/juniper-from-schema-code-gen/Cargo.toml index 204c2cf..b8e7dcf 100644 --- a/juniper-from-schema-code-gen/Cargo.toml +++ b/juniper-from-schema-code-gen/Cargo.toml @@ -19,6 +19,7 @@ graphql-parser = "0.3" proc-macro2 = "1" heck = "0.3" colored = "1.8" +glob = "0.3" [dev_dependencies] version-sync = "0.8" diff --git a/juniper-from-schema-code-gen/src/lib.rs b/juniper-from-schema-code-gen/src/lib.rs index 960ff98..aa46034 100644 --- a/juniper-from-schema-code-gen/src/lib.rs +++ b/juniper-from-schema-code-gen/src/lib.rs @@ -35,9 +35,9 @@ pub struct CodeGen { } impl CodeGen { - pub fn build_from_schema_file(path: PathBuf) -> CodeGenBuilder { + pub fn build_from_schema_files(paths: Vec) -> CodeGenBuilder { CodeGenBuilder { - schema: SchemaLocation::File(path), + schema: SchemaLocation::Files(paths), context_type: None, error_type: None, } @@ -52,12 +52,32 @@ impl CodeGen { } pub fn generate_code(self) -> Result { - let (schema, schema_path) = match self.schema { - SchemaLocation::File(path) => ( - std::fs::read_to_string(&path).map_err(Error::Io)?, - Some(path), - ), - SchemaLocation::Literal(schema) => (schema, None), + let (schema, schema_paths) = match self.schema { + SchemaLocation::Files(glob_patterns) => { + // Each input path is a glob pattern, expand each one and concat + // them all together + let mut paths: Vec = Vec::new(); + for path in glob_patterns { + let globbed_paths = glob::glob( + path.to_str() + .expect("Invalid UTF-8 characters in file name"), + ) + .map_err(Error::SchemaPathError)?; + for path in globbed_paths { + let path = path.map_err(Error::SchemaGlobError)?; + paths.push(path); + } + } + + // Read each schema file and put it all in one string + let schema: String = paths + .iter() + .map(|path| std::fs::read_to_string(&path)) + .collect::>() + .map_err(Error::Io)?; + (schema, paths) + } + SchemaLocation::Literal(schema) => (schema, Vec::new()), }; let doc = match parse_schema(&schema) { @@ -84,7 +104,8 @@ impl CodeGen { eprintln!("{}", tokens); } - if let Some(path) = schema_path { + // Force a Rust recompile whenever the schema changes + for path in schema_paths { include_literal_schema(&mut tokens, path.as_path()); } @@ -118,6 +139,10 @@ fn include_literal_schema(tokens: &mut proc_macro2::TokenStream, schema_path: &P #[derive(Debug)] pub enum Error { + /// User passed an invalid glob pattern + SchemaPathError(glob::PatternError), + /// Glob pattern couldn't be expanded (e.g. permission denied) + SchemaGlobError(glob::GlobError), SchemaParseError(graphql_parser::schema::ParseError), CodeGenErrors { errors: Vec, @@ -129,6 +154,8 @@ pub enum Error { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { + Error::SchemaPathError(inner) => write!(f, "{}", inner), + Error::SchemaGlobError(inner) => write!(f, "{}", inner), Error::SchemaParseError(inner) => write!(f, "{}", inner), Error::CodeGenErrors { errors, schema } => { assert!( @@ -186,7 +213,7 @@ impl CodeGenBuilder { #[derive(Debug)] enum SchemaLocation { - File(PathBuf), + Files(Vec), Literal(String), } diff --git a/juniper-from-schema-proc-macro/src/lib.rs b/juniper-from-schema-proc-macro/src/lib.rs index 0708b10..694e1ab 100644 --- a/juniper-from-schema-proc-macro/src/lib.rs +++ b/juniper-from-schema-proc-macro/src/lib.rs @@ -21,7 +21,7 @@ use parse_input::GraphqlSchemaFromFileInput; #[proc_macro] pub fn graphql_schema_from_file(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let GraphqlSchemaFromFileInput { - schema_path, + schema_paths, context_type, error_type, } = match syn::parse::(input) { @@ -29,7 +29,7 @@ pub fn graphql_schema_from_file(input: proc_macro::TokenStream) -> proc_macro::T Err(e) => return e.to_compile_error().into(), }; - let mut builder = CodeGen::build_from_schema_file(schema_path); + let mut builder = CodeGen::build_from_schema_files(schema_paths); if let Some(context_type) = context_type { builder = builder.context_type(context_type); } diff --git a/juniper-from-schema-proc-macro/src/parse_input.rs b/juniper-from-schema-proc-macro/src/parse_input.rs index f8c3359..bae132b 100644 --- a/juniper-from-schema-proc-macro/src/parse_input.rs +++ b/juniper-from-schema-proc-macro/src/parse_input.rs @@ -7,7 +7,7 @@ use syn::{ #[derive(Debug)] pub struct GraphqlSchemaFromFileInput { - pub schema_path: PathBuf, + pub schema_paths: Vec, pub error_type: Option, pub context_type: Option, } @@ -56,7 +56,9 @@ impl Parse for GraphqlSchemaFromFileInput { } Ok(GraphqlSchemaFromFileInput { - schema_path, + // Only supporting single paths for macro, since long term we're + // moving towards the build.rs style + schema_paths: vec![schema_path], error_type, context_type, })