1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![cfg_attr(any(doc, test), doc = include_str!("../README.md"))]
3#![cfg_attr(not(any(doc, test)), doc = env!("CARGO_PKG_NAME"))]
4#![cfg_attr(test, recursion_limit = "256")]
6
7extern crate core;
10extern crate self as juniper;
11
12#[cfg(test)]
13mod for_benches_only {
14 use bencher as _;
15}
16
17#[doc(hidden)]
19pub use {async_trait::async_trait, futures, serde, static_assertions as sa};
20
21#[doc(inline)]
22pub use futures::future::{BoxFuture, LocalBoxFuture};
23
24pub use arcstr::{self, ArcStr};
25
26pub use juniper_codegen::{
30 GraphQLEnum, GraphQLInputObject, GraphQLInterface, GraphQLObject, GraphQLScalar, GraphQLUnion,
31 graphql_interface, graphql_object, graphql_scalar, graphql_subscription, graphql_union,
32};
33
34#[doc(hidden)]
35#[macro_use]
36pub mod macros;
37mod ast;
38pub mod executor;
39mod introspection;
40pub mod parser;
41pub(crate) mod schema;
42mod types;
43mod util;
44pub mod validation;
45mod value;
46pub mod http;
49pub mod integrations;
50
51#[cfg(all(test, not(feature = "expose-test-schema")))]
52mod tests;
53#[cfg(feature = "expose-test-schema")]
54pub mod tests;
55
56#[cfg(test)]
57mod executor_tests;
58
59use derive_more::with_trait::{Display, From};
60use itertools::Itertools as _;
61
62pub use crate::util::to_camel_case;
64
65use crate::{
66 executor::{execute_validated_query, get_operation},
67 introspection::{INTROSPECTION_QUERY, INTROSPECTION_QUERY_WITHOUT_DESCRIPTIONS},
68 parser::parse_document_source,
69 validation::{
70 MultiVisitorNil, ValidatorContext, rules, validate_input_values, visit as visit_rule,
71 visit_all_rules,
72 },
73};
74
75pub use crate::{
76 ast::{
77 Definition, Document, FromInputValue, InputValue, IntoInputValue, Operation, OperationType,
78 Selection, ToInputValue, Type,
79 },
80 executor::{
81 Applies, Context, ExecutionError, ExecutionResult, Executor, FieldError, FieldResult,
82 FromContext, IntoFieldError, IntoResolvable, LookAheadArgument, LookAheadChildren,
83 LookAheadList, LookAheadObject, LookAheadSelection, LookAheadValue, OwnedExecutor,
84 Registry, ValuesStream, Variables,
85 },
86 introspection::IntrospectionFormat,
87 macros::helper::subscription::{ExtractTypeFromStream, IntoFieldResult},
88 parser::{ParseError, ScalarToken, Span, Spanning},
89 schema::{
90 meta,
91 model::{RootNode, SchemaType},
92 },
93 types::{
94 async_await::{GraphQLTypeAsync, GraphQLValueAsync},
95 base::{Arguments, GraphQLType, GraphQLValue, TypeKind},
96 marker::{self, GraphQLInterface, GraphQLObject, GraphQLUnion},
97 nullable::Nullable,
98 scalars::{EmptyMutation, EmptySubscription, ID},
99 subscriptions::{
100 ExecutionOutput, GraphQLSubscriptionType, GraphQLSubscriptionValue,
101 SubscriptionConnection, SubscriptionCoordinator,
102 },
103 },
104 validation::RuleError,
105 value::{
106 AnyExt, DefaultScalarValue, FromScalarValue, IntoValue, Object, ParseScalarResult,
107 ParseScalarValue, Scalar, ScalarValue, ToScalarValue, TryToPrimitive, Value,
108 WrongInputScalarTypeError,
109 },
110};
111
112#[expect(missing_docs, reason = "self-explanatory")]
114#[derive(Clone, Debug, Display, Eq, From, PartialEq)]
115pub enum GraphQLError {
116 ParseError(Spanning<ParseError>),
117 #[display("{}", _0.iter().format("\n"))]
118 ValidationError(Vec<RuleError>),
119 #[display("No operation provided")]
120 NoOperationProvided,
121 #[display("Multiple operations provided")]
122 MultipleOperationsProvided,
123 #[display("Unknown operation name")]
124 UnknownOperationName,
125 #[display("Operation is a subscription")]
126 IsSubscription,
127 #[display("Operation is not a subscription")]
128 NotSubscription,
129}
130
131impl From<RuleError> for GraphQLError {
132 fn from(value: RuleError) -> Self {
133 vec![value].into()
134 }
135}
136
137impl std::error::Error for GraphQLError {
138 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
139 match self {
140 Self::ParseError(e) => Some(e),
141 Self::ValidationError(errs) => Some(errs.first()?),
142 Self::NoOperationProvided
143 | Self::MultipleOperationsProvided
144 | Self::UnknownOperationName
145 | Self::IsSubscription
146 | Self::NotSubscription => None,
147 }
148 }
149}
150
151pub fn execute_sync<'a, S, QueryT, MutationT, SubscriptionT>(
153 document_source: &'a str,
154 operation_name: Option<&str>,
155 root_node: &'a RootNode<QueryT, MutationT, SubscriptionT, S>,
156 variables: &Variables<S>,
157 context: &QueryT::Context,
158) -> Result<(Value<S>, Vec<ExecutionError<S>>), GraphQLError>
159where
160 S: ScalarValue,
161 QueryT: GraphQLType<S>,
162 MutationT: GraphQLType<S, Context = QueryT::Context>,
163 SubscriptionT: GraphQLType<S, Context = QueryT::Context>,
164{
165 let document = parse_document_source(document_source, &root_node.schema)?;
166
167 {
168 let mut ctx = ValidatorContext::new(&root_node.schema, &document);
169 visit_all_rules(&mut ctx, &document);
170 if root_node.introspection_disabled {
171 visit_rule(
172 &mut MultiVisitorNil.with(rules::disable_introspection::factory()),
173 &mut ctx,
174 &document,
175 );
176 }
177
178 let errors = ctx.into_errors();
179 if !errors.is_empty() {
180 return Err(errors.into());
181 }
182 }
183
184 let operation = get_operation(&document, operation_name)?;
185
186 {
187 let errors = validate_input_values(variables, operation, &root_node.schema);
188
189 if !errors.is_empty() {
190 return Err(errors.into());
191 }
192 }
193
194 execute_validated_query(&document, operation, root_node, variables, context)
195}
196
197pub async fn execute<'a, S, QueryT, MutationT, SubscriptionT>(
199 document_source: &'a str,
200 operation_name: Option<&str>,
201 root_node: &'a RootNode<QueryT, MutationT, SubscriptionT, S>,
202 variables: &Variables<S>,
203 context: &QueryT::Context,
204) -> Result<(Value<S>, Vec<ExecutionError<S>>), GraphQLError>
205where
206 QueryT: GraphQLTypeAsync<S>,
207 QueryT::TypeInfo: Sync,
208 QueryT::Context: Sync,
209 MutationT: GraphQLTypeAsync<S, Context = QueryT::Context>,
210 MutationT::TypeInfo: Sync,
211 SubscriptionT: GraphQLType<S, Context = QueryT::Context> + Sync,
212 SubscriptionT::TypeInfo: Sync,
213 S: ScalarValue + Send + Sync,
214{
215 let document = parse_document_source(document_source, &root_node.schema)?;
216
217 {
218 let mut ctx = ValidatorContext::new(&root_node.schema, &document);
219 visit_all_rules(&mut ctx, &document);
220 if root_node.introspection_disabled {
221 visit_rule(
222 &mut MultiVisitorNil.with(rules::disable_introspection::factory()),
223 &mut ctx,
224 &document,
225 );
226 }
227
228 let errors = ctx.into_errors();
229 if !errors.is_empty() {
230 return Err(errors.into());
231 }
232 }
233
234 let operation = get_operation(&document, operation_name)?;
235
236 {
237 let errors = validate_input_values(variables, operation, &root_node.schema);
238
239 if !errors.is_empty() {
240 return Err(errors.into());
241 }
242 }
243
244 executor::execute_validated_query_async(&document, operation, root_node, variables, context)
245 .await
246}
247
248pub async fn resolve_into_stream<'a, S, QueryT, MutationT, SubscriptionT>(
250 document_source: &'a str,
251 operation_name: Option<&str>,
252 root_node: &'a RootNode<QueryT, MutationT, SubscriptionT, S>,
253 variables: &Variables<S>,
254 context: &'a QueryT::Context,
255) -> Result<(Value<ValuesStream<'a, S>>, Vec<ExecutionError<S>>), GraphQLError>
256where
257 QueryT: GraphQLTypeAsync<S>,
258 QueryT::TypeInfo: Sync,
259 QueryT::Context: Sync,
260 MutationT: GraphQLTypeAsync<S, Context = QueryT::Context>,
261 MutationT::TypeInfo: Sync,
262 SubscriptionT: GraphQLSubscriptionType<S, Context = QueryT::Context>,
263 SubscriptionT::TypeInfo: Sync,
264 S: ScalarValue + Send + Sync,
265{
266 let document: ast::OwnedDocument<'a, S> =
267 parse_document_source(document_source, &root_node.schema)?;
268
269 {
270 let mut ctx = ValidatorContext::new(&root_node.schema, &document);
271 visit_all_rules(&mut ctx, &document);
272 if root_node.introspection_disabled {
273 visit_rule(
274 &mut MultiVisitorNil.with(rules::disable_introspection::factory()),
275 &mut ctx,
276 &document,
277 );
278 }
279
280 let errors = ctx.into_errors();
281 if !errors.is_empty() {
282 return Err(errors.into());
283 }
284 }
285
286 let operation = get_operation(&document, operation_name)?;
287
288 {
289 let errors = validate_input_values(variables, operation, &root_node.schema);
290
291 if !errors.is_empty() {
292 return Err(errors.into());
293 }
294 }
295
296 executor::resolve_validated_subscription(&document, operation, root_node, variables, context)
297 .await
298}
299
300pub fn introspect<S, QueryT, MutationT, SubscriptionT>(
302 root_node: &RootNode<QueryT, MutationT, SubscriptionT, S>,
303 context: &QueryT::Context,
304 format: IntrospectionFormat,
305) -> Result<(Value<S>, Vec<ExecutionError<S>>), GraphQLError>
306where
307 S: ScalarValue,
308 QueryT: GraphQLType<S>,
309 MutationT: GraphQLType<S, Context = QueryT::Context>,
310 SubscriptionT: GraphQLType<S, Context = QueryT::Context>,
311{
312 execute_sync(
313 match format {
314 IntrospectionFormat::All => INTROSPECTION_QUERY,
315 IntrospectionFormat::WithoutDescriptions => INTROSPECTION_QUERY_WITHOUT_DESCRIPTIONS,
316 },
317 None,
318 root_node,
319 &Variables::new(),
320 context,
321 )
322}