cynic/lib.rs
1#![deny(rust_2018_idioms)]
2//! # Cynic
3//!
4//! Cynic is a GraphQL query builder & data mapper for Rust.
5//!
6//! This documentation is primarily intended to be a reference for specific
7//! functions, for a guide to using `Cynic` see the website: [cynic-rs.dev](https://cynic-rs.dev).
8//!
9//! ## Overview
10//!
11//! To get started with Cynic you'll need a GraphQL schema for the API you wish
12//! to query. The examples will be using the star wars API.
13//!
14//! ### Generating a Query DSL
15//!
16//! Once you've got your schema installed locally, you'll need to use the
17//! `use_schema` macro to generate a schema module:
18//!
19//! ```rust
20//! mod schema {
21//! cynic::use_schema!("../schemas/starwars.schema.graphql");
22//! }
23//! ```
24//!
25//! This macro generates a few things:
26//!
27//! 1. Some structs to represent the Input types the underlying schema.
28//! You may need to use these to build mutations or as parameters to queries.
29//! 2. Definitions of all the Enums in the provided schema. You'll need these
30//! if you want to use any enum types.
31//! 3. Type safe selection set functions. These can be used to build up a query
32//! manually, though it's usually easier to use the `QueryFragment` derive
33//! functionality explained below. Hopefully you'll not need to use these
34//! directly too often.
35//!
36//! Though using macros to generate these is convenient, it does leave a lot of
37//! code to the imagination. You can get a glimpse of the things this defines
38//! by running `cargo doc --document-private-items` and having a look in the
39//! `schema` module. It's not ideal, but at least provides some visibility into
40//! the various enum types.
41//!
42//! ### Creating QueryFragments
43//!
44//! Now that you have a schema defined, you can start building some queries.
45//! Cynic lets you do this by deriving `QueryFragment` for a struct. For
46//! example, if we wanted to know what director title & director a Star Wars
47//! film had, we could define this `QueryFragment`:
48//!
49//! ```rust
50//! # mod schema {
51//! # cynic::use_schema!("../schemas/starwars.schema.graphql");
52//! # }
53//!
54//! #[derive(cynic::QueryFragment)]
55//! #[cynic(schema_path = "../schemas/starwars.schema.graphql")]
56//! struct Film {
57//! title: Option<String>,
58//! director: Option<String>,
59//! }
60//!
61//! // This `Film` struct can now be used as the type of a field on any other
62//! // `QueryFragment` struct and cynic will know how to turn that into a GraphQL
63//! // query, and populate the `Film` struct from the response.
64//!
65//! // For example, if we wanted to know the Director for a particular film:
66//!
67//! #[derive(cynic::QueryFragment)]
68//! #[cynic(
69//! schema_path = "../schemas/starwars.schema.graphql",
70//! graphql_type = "Root"
71//! )]
72//! struct FilmDirectorQuery {
73//! // Here we use the `#[arguments()]` attribute on the `film` field to provide a
74//! // hard coded film ID to look up. Though useful for demonstration, hard coded
75//! // arguments like this aren't much use in reality. For more details on providing
76//! // runtime arguments please see below.
77//! #[arguments(id = cynic::Id::new("ZmlsbXM6MQ=="))]
78//! film: Option<Film>,
79//! }
80//!
81//! // You can then build a `cynic::Operation` from this fragment
82//! use cynic::QueryBuilder;
83//! let operation = FilmDirectorQuery::build(());
84//! ```
85//!
86//! `operation` above implements `serde::Serialize` so can be used with any HTTP
87//! client. A selection of HTTP client integrations are provided in
88//! `cynic::http` - see the docs there for examples of using a
89//! `cynic::Operation`
90//!
91//! ```rust,ignore
92//! let response = reqwest::blocking::Client::new()
93//! .post("a_url")
94//! .json(&operation)
95//! .send()?;
96//! let result = response.json::<GraphQlResponse<FilmDirectorQuery>>()?;
97//! ```
98//!
99//! After this code has run, result will be an instance of
100//! `GraphQlResponse<FilmDirectorQuery>` with the film populated appropriately.
101//!
102//! ### Dynamic Query Arguments
103//!
104//! The query above was useful for demonstration, but you'll usually want to be
105//! able to provide parameters to your query. To do this, you should define a
106//! struct that contains all of the parameters you want to provide:
107//!
108//! ```rust
109//! # mod schema {
110//! # cynic::use_schema!("../schemas/starwars.schema.graphql");
111//! # }
112//!
113//! # #[derive(cynic::QueryFragment)]
114//! # #[cynic(
115//! # schema_path = "../schemas/starwars.schema.graphql",
116//! # )]
117//! # struct Film {
118//! # title: Option<String>,
119//! # director: Option<String>
120//! # }
121//! // Deriving `QueryVariables` allows this struct to be used as variables in a
122//! // `QueryFragment`, whether it represents part of a query or a whole query.
123//! #[derive(cynic::QueryVariables)]
124//! struct FilmArguments {
125//! id: Option<cynic::Id>,
126//! }
127//!
128//! // You can now define a query to use these arguments on. For example, to make
129//! // `FilmDirectorQuery` a bit more dynamic:
130//! #[derive(cynic::QueryFragment)]
131//! #[cynic(
132//! schema_path = "../schemas/starwars.schema.graphql",
133//! graphql_type = "Root",
134//! // By adding the `variables` parameter to our `QueryFragment` we've made a variable
135//! // named `args` available for use in the `arguments` attribute.
136//! variables = "FilmArguments"
137//! )]
138//! struct FilmDirectorQueryWithArgs {
139//! // Here we use `args`, which we've declared above to be an instance of `FilmArguments`
140//! #[arguments(id: $id)]
141//! film: Option<Film>,
142//! }
143//!
144//! // Then we can build a query using this new struct;
145//! use cynic::QueryBuilder;
146//! let operation = FilmDirectorQueryWithArgs::build(FilmArguments {
147//! id: Some("ZmlsbXM6MQ==".into()),
148//! });
149//! ```
150//!
151//! ## Feature Flags
152//!
153//! Cynic has a few features that are controlled by feature flags.
154//!
155//! - `http-surf` adds integration with the [`surf`](https://github.com/http-rs/surf)
156//! http client.
157//! - `http-reqwest` adds async integration with the [`reqwest`](https://github.com/seanmonstar/reqwest)
158//! http client.
159//! - `http-reqwest-blocking` adds blocking integration with the [`reqwest`](https://github.com/seanmonstar/reqwest)
160//! http client.
161//! - `rkyv` can be used to speed up compiles when working with large schemas.
162//!
163//! It's worth noting that each of these features pulls in extra
164//! dependencies, which may impact your build size. Particularly
165//! if you're targeting WASM. In particular the `url` crate has
166//! [known issues](https://github.com/servo/rust-url/issues/557) when
167//! targeting web assembly.
168
169#![cfg_attr(docsrs, feature(doc_cfg))]
170#![warn(missing_docs)]
171
172mod builders;
173mod core;
174mod id;
175mod operation;
176mod result;
177
178pub mod coercions;
179pub mod queries;
180pub mod variables;
181
182pub mod http;
183pub mod schema;
184
185#[path = "private/mod.rs"]
186pub mod __private;
187
188pub use {
189 self::core::{Enum, InlineFragments, InputObject, QueryFragment},
190 builders::{MutationBuilder, QueryBuilder, SubscriptionBuilder},
191 id::Id,
192 operation::{Operation, OperationBuildError, OperationBuilder, StreamingOperation},
193 result::*,
194 variables::{QueryVariableLiterals, QueryVariables, QueryVariablesFields},
195};
196
197pub use cynic_proc_macros::{
198 Enum, InlineFragments, InputObject, QueryFragment, QueryVariableLiterals, QueryVariables,
199 Scalar, schema, schema_for_derives, use_schema,
200};
201
202#[doc(hidden)]
203pub use static_assertions::assert_type_eq_all;
204
205// We re-export serde as the output from a lot of our derive macros require it,
206// and this way we can point at our copy rather than forcing users to add it to
207// their Cargo.toml
208pub use serde;
209
210/// Implements a set of scalar traits for the given type & type lock.
211///
212/// For example, to use `uuid::Uuid` for a `Uuid` type defined in a schema:
213///
214/// ```rust
215/// # #[macro_use] extern crate cynic;
216/// # // Faking the uuid module here because it's easier than
217/// # // actually pulling it in
218/// #
219/// # mod schema { cynic::use_schema!("../schemas/test_cases.graphql"); }
220/// # mod uuid { pub struct Uuid(String); }
221/// impl_scalar!(uuid::Uuid, schema::UUID);
222/// ```
223///
224/// This macro can be used on any type that implements `serde::Serialize`,
225/// provided the `schema` is defined in the current crate
226#[macro_export]
227macro_rules! impl_scalar {
228 ($type:path, $type_lock:ident) => {
229 $crate::impl_scalar!($type, $type_lock, self);
230 };
231 ($type:path, $type_lock_segment:ident $(:: $type_lock_rest :ident)::+) => {
232 $crate::impl_scalar!($type, $($type_lock_rest)::*, $type_lock_segment);
233 };
234 ($type:path, $type_lock_segment:ident $(:: $type_lock_rest :ident)::+, $schema_module:ident $(:: $schema_module_rest : ident)*) => {
235 $crate::impl_scalar!($type, $($type_lock_rest)::*, $schema_module(::$schema_module_rest)*::$type_lock_segment)
236 };
237 ($type:path, $type_lock:ident, $schema_module:ident $(:: $schema_module_rest : ident)*) => {
238 #[automatically_derived]
239 impl $crate::schema::IsScalar<$schema_module$(::$schema_module_rest)*::$type_lock> for $type {
240 type SchemaType = $schema_module$(::$schema_module_rest)*::$type_lock;
241 }
242
243 // We derive a simple CoercesTo here, but since we don't own $type we can't
244 // impl any of the more advanced coercions.
245 #[automatically_derived]
246 impl $crate::coercions::CoercesTo<$schema_module$(::$schema_module_rest)*::$type_lock> for $type {}
247
248 #[automatically_derived]
249 impl $schema_module$(::$schema_module_rest)*::variable::Variable for $type {
250 const TYPE: cynic::variables::VariableType = cynic::variables::VariableType::Named(
251 <$schema_module$(::$schema_module_rest)*::$type_lock as $crate::schema::NamedType>::NAME,
252 );
253 }
254 };
255}
256
257#[macro_export(local_inner_macros)]
258#[doc(hidden)]
259/// Asserts that the type implements _all_ of the given traits.
260macro_rules! assert_impl {
261 ($type:ty [$($impl_generics: tt)*] [$($where_clause: tt)*]: $($trait:path),+ $(,)?) => {
262 const _: () = {
263 // Only callable when `$type` implements all traits in `$($trait)+`.
264 fn assert_impl_all<T: ?Sized $(+ $trait)+>() {}
265 fn do_assert $($impl_generics)* () $($where_clause)* {
266 assert_impl_all::<$type>();
267 }
268 };
269 };
270}