juniper_from_schema_code_gen/
lib.rs1#![deny(
4 unused_imports,
5 mutable_borrow_reservation_conflict,
6 dead_code,
7 unused_variables,
8 unused_must_use
9)]
10#![recursion_limit = "256"]
11#![doc(html_root_url = "https://docs.rs/juniper-from-schema-code-gen/0.5.2")]
12
13extern crate proc_macro;
14extern crate proc_macro2;
15
16mod ast_pass;
17mod nullable_type;
18mod parse_input;
19mod pretty_print;
20
21use self::{
22 ast_pass::{ast_data_pass::AstData, error::Error, CodeGenPass},
23 parse_input::{default_context_type, default_error_type, GraphqlSchemaFromFileInput},
24};
25use graphql_parser::parse_schema;
26use proc_macro2::{Span, TokenStream};
27use quote::quote;
28use std::{collections::BTreeSet, path::Path};
29use syn::Type;
30
31const DATE_TIME_SCALAR_NAME: &str = "DateTimeUtc";
32const DATE_SCALAR_NAME: &str = "Date";
33const UUID_SCALAR_NAME: &str = "Uuid";
34const URL_SCALAR_NAME: &str = "Url";
35
36#[proc_macro]
40pub fn graphql_schema_from_file(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
41 let parsed = match syn::parse::<GraphqlSchemaFromFileInput>(input) {
42 Ok(p) => p,
43 Err(e) => return e.to_compile_error().into(),
44 };
45
46 match std::fs::read_to_string(&parsed.schema_path) {
47 Ok(schema) => {
48 let mut tokens = parse_and_gen_schema(&schema, parsed.error_type, parsed.context_type);
49 include_literal_schema(&mut tokens, &parsed.schema_path);
50 tokens
51 }
52 Err(err) => panic!("{}", err),
53 }
54}
55
56fn include_literal_schema(tokens: &mut proc_macro::TokenStream, schema_path: &Path) {
59 let schema_path = syn::LitStr::new(
60 schema_path
61 .to_str()
62 .expect("Invalid UTF-8 characters in file name"),
63 Span::call_site(),
64 );
65
66 tokens.extend(proc_macro::TokenStream::from(quote! {
67 const _: &str = std::include_str!(#schema_path);
68 }));
69}
70
71#[proc_macro]
103pub fn graphql_schema(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
104 let input: TokenStream = input.into();
105 let schema = input.to_string();
106 parse_and_gen_schema(&schema, default_error_type(), default_context_type())
107}
108
109fn parse_and_gen_schema(
110 schema: &str,
111 error_type: Type,
112 context_type: Type,
113) -> proc_macro::TokenStream {
114 let doc = match parse_schema(&schema) {
115 Ok(doc) => doc,
116 Err(parse_error) => panic!("{}", parse_error),
117 };
118
119 let ast_data = match AstData::new_from_schema_and_doc(schema, &doc) {
120 Ok(x) => x,
121 Err(errors) => print_and_panic_if_errors(errors),
122 };
123
124 let output = CodeGenPass::new(schema, error_type, context_type, ast_data);
125
126 match output.gen_juniper_code(&doc) {
127 Ok(tokens) => {
128 let out: proc_macro::TokenStream = tokens.into();
129
130 if debugging_enabled() {
131 self::pretty_print::code_gen_debug(out.to_string());
132 }
133
134 out
135 }
136 Err(errors) => print_and_panic_if_errors(errors),
137 }
138}
139
140fn print_and_panic_if_errors<T>(errors: BTreeSet<Error>) -> T {
141 let count = errors.len();
142
143 let out = errors
144 .into_iter()
145 .map(|error| error.to_string())
146 .collect::<Vec<_>>()
147 .join("\n\n");
148
149 if count == 1 {
150 panic!("\n\n{}\n\naborting due to previous error\n", out)
151 } else {
152 panic!("\n\n{}\n\naborting due to {} errors\n", out, count)
153 }
154}
155
156fn debugging_enabled() -> bool {
157 if let Ok(val) = std::env::var("JUNIPER_FROM_SCHEMA_DEBUG") {
158 if &val == "1" {
159 return true;
160 }
161 }
162
163 false
164}