Skip to main content

libgraphql_macros/
lib.rs

1mod emittable_schema;
2mod graphql_schema_from_str_token_consumer;
3mod graphql_schema_token_consumer;
4mod parse_error_converter;
5mod rust_macro_graphql_token_source;
6mod span_map;
7
8#[cfg(test)]
9mod tests;
10
11use crate::graphql_schema_token_consumer::GraphQLSchemaTokenConsumer;
12use crate::graphql_schema_from_str_token_consumer::GraphQLSchemaFromStrTokenConsumer;
13
14/// Evaluates to a [`Schema`](libgraphql_core::schema::Schema) object given
15/// direct GraphQL schema document syntax.
16///
17/// This macro is effectively a compile-time version of
18/// [`SchemaBuilder::build_from_str()`](libgraphql_core::schema::SchemaBuilder::build_from_ast()),
19/// except you write GraphQL syntax in your Rust file and the macro parses it as
20/// GraphQL for you.
21///
22/// Example usage:
23///
24/// ```rust
25/// use libgraphql::macros::graphql_schema;
26///
27/// let schema = graphql_schema! {
28///     type Query {
29///         // This field always resolves the currently-authenticated `User`.
30///         me: User,
31///     }
32///
33///     type User {
34///         firstName: String,
35///         lastName: String,
36///     }
37/// };
38///
39/// let user_type =
40///     schema.defined_types()
41///         .get("User")
42///         .unwrap()
43///         .as_object()
44///         .unwrap();
45///
46/// assert_eq!(user_type.name(), "User");
47/// assert_eq!(user_type.fields().get("firstName").is_some(), true);
48/// assert_eq!(user_type.fields().get("firstName").is_some(), true);
49/// assert_eq!(user_type.fields().get("doesntExist").is_some(), false);
50/// ```
51///
52/// ## **⚠️ NOTE:**
53///
54/// Due to limitations downstream of how Rust macros tokenize syntax, there
55/// are a few inline GraphQL syntax edge-cases that are not supported by this
56/// macro:
57///
58///   1) `#` cannot be used to specify GraphQL comments. Instead, you can use
59///      Rust's `//` or `/*` comment syntax.
60///
61///      So for example, this won't compile:
62///
63///      ```rust,compile_fail
64///      let schema = graphql_schema! {
65///        type Query {
66///          me: User,
67///        }
68///
69///        ## Represents a user in the system.
70///        type User {
71///          firstName: String,
72///          lastName: String,
73///        }
74///      };
75///      ```
76///
77///      But you can use rust's `//` and `/*` comment syntax instead:
78///      ```rust
79///      # use libgraphql::macros::graphql_schema;
80///      let schema = graphql_schema! {
81///        type Query {
82///          me: User,
83///        }
84///
85///        // Represents a user in the system.
86///        type User {
87///          /* The user's first name. */
88///          firstName: String,
89///
90///          /* The user's last name. */
91///          lastName: String,
92///        }
93///      };
94///      ```
95///
96///   2) Block-quoted strings (`"""`) *are* supported, but if you need to
97///      nest a quoted string /within/ a block-quoted string, you must
98///      escape the inner quotes with `\"`. This is because Rust's
99///      tokenizer treats unescaped `"` as string delimiters, which
100///      breaks the block string recombination.
101///
102///      So for example, this won't compile:
103///      ```rust,compile_fail
104///      let schema = graphql_schema! {
105///        type Query {
106///          me: User,
107///        }
108///
109///         type User {
110///           """
111///           The user's "primary" address.
112///           """
113///           address: String,
114///         }
115///      };
116///      ```
117///
118///      But the workaround is to escape the inner quotes:
119///      ```rust
120///      # use libgraphql::macros::graphql_schema;
121///      let schema = graphql_schema! {
122///        type Query {
123///          me: User,
124///        }
125///
126///        type User {
127///          """
128///          The user's \"primary\" address.
129///          """
130///          address: String,
131///        }
132///      };
133///      ```
134#[proc_macro]
135pub fn graphql_schema(
136    input: proc_macro::TokenStream,
137) -> proc_macro::TokenStream {
138    GraphQLSchemaTokenConsumer::new(input).into()
139}
140
141/// Evaluates to a [`Schema`](libgraphql_core::schema::Schema) object given a literal
142/// Rust `str` containing GraphQL document text that represents a GraphQL
143/// schema.
144///
145/// This macro is effectively a compile-time version of
146/// [`SchemaBuilder::build_from_str()`](libgraphql_core::schema::SchemaBuilder::build_from_str()).
147///
148/// Example usage:
149///
150/// ```rust
151/// use libgraphql::macros::graphql_schema_from_str;
152///
153/// let schema = graphql_schema_from_str!(r#"
154///     type Query {
155///         me: User,
156///
157///     }
158///
159///     type User {
160///         firstName: String,
161///         lastName: String,
162///     }
163/// "#r);
164///
165/// let user_type =
166///     schema.defined_types()
167///         .get("User")
168///         .unwrap()
169///         .as_object()
170///         .unwrap();
171///
172/// assert_eq!(user_type.name(), "User");
173/// assert_eq!(user_type.fields().get("firstName").is_some(), true);
174/// assert_eq!(user_type.fields().get("firstName").is_some(), true);
175/// assert_eq!(user_type.fields().get("doesntExist").is_some(), false);
176/// ```
177#[proc_macro]
178pub fn graphql_schema_from_str(
179    input: proc_macro::TokenStream,
180) -> proc_macro::TokenStream {
181    GraphQLSchemaFromStrTokenConsumer::new(input).into()
182}