Skip to main content

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}