rusty_gql/
context.rs

1use crate::{
2    error::GqlError, input::GqlInputType, operation::Operation, types::schema::Schema, GqlValue,
3    ResolverResult,
4};
5use graphql_parser::{
6    query::{Field, SelectionSet},
7    schema::{Directive, Value},
8};
9
10#[derive(Clone)]
11pub struct ExecutionContext<'a, T> {
12    pub schema: &'a Schema,
13    pub operation: &'a Operation<'a>,
14    pub item: T,
15}
16
17pub type Context<'a> = ExecutionContext<'a, &'a Field<'a, String>>;
18
19impl<'a> Context<'a> {
20    pub fn get_arg_value<T: GqlInputType>(&self, arg_name: &str) -> ResolverResult<T> {
21        let value = self
22            .item
23            .arguments
24            .iter()
25            .find(|(name, _)| name == arg_name)
26            .map(|(_, v)| v);
27        let gql_value = match value {
28            Some(v) => {
29                if let Value::Variable(var_name) = v {
30                    self.resolve_variable_value(var_name)?
31                } else {
32                    GqlValue::from(v.clone())
33                }
34            }
35            None => GqlValue::Null,
36        };
37        match T::from_gql_value(Some(gql_value)) {
38            Ok(v) => Ok(v),
39            Err(err) => Err(GqlError::new(err, None)),
40        }
41    }
42}
43
44pub type SelectionSetContext<'a> = ExecutionContext<'a, &'a SelectionSet<'a, String>>;
45
46impl<'a, T> ExecutionContext<'a, T> {
47    pub fn with_field(
48        &self,
49        field: &'a Field<'a, String>,
50    ) -> ExecutionContext<'a, &'a Field<'a, String>> {
51        ExecutionContext {
52            schema: self.schema,
53            operation: self.operation,
54            item: field,
55        }
56    }
57
58    pub fn with_selection_set(
59        &self,
60        selection_set: &'a SelectionSet<'a, String>,
61    ) -> ExecutionContext<'a, &'a SelectionSet<'a, String>> {
62        ExecutionContext {
63            schema: self.schema,
64            operation: self.operation,
65            item: selection_set,
66        }
67    }
68
69    pub fn is_skip(&self, directives: &'a [Directive<'a, String>]) -> bool {
70        for dir in directives {
71            let skip = match dir.name.as_str() {
72                "skip" => true,
73                "include" => false,
74                _ => continue,
75            };
76
77            for (key, value) in &dir.arguments {
78                if key != "if" {
79                    continue;
80                } else if let Value::Boolean(cond) = value {
81                    if skip && *cond {
82                        return true;
83                    }
84                }
85            }
86
87            return skip;
88        }
89
90        false
91    }
92    pub fn add_error(&self, error: &GqlError) {
93        self.operation.errors.lock().unwrap().push(error.clone());
94    }
95
96    pub fn resolve_variable_value(&self, name: &str) -> ResolverResult<GqlValue> {
97        let v = self
98            .operation
99            .variable_definitions
100            .iter()
101            .find(|var_def| var_def.name == name)
102            .and_then(|var_def| self.operation.variables.0.get(&var_def.name));
103        match v {
104            Some(value) => Ok(value.clone()),
105            None => Err(GqlError::new(
106                format!("Variable {} is not defined", name),
107                None,
108            )),
109        }
110    }
111}
112
113pub(crate) fn build_context<'a>(
114    schema: &'a Schema,
115    operation: &'a Operation<'a>,
116) -> ExecutionContext<'a, &'a SelectionSet<'a, String>> {
117    ExecutionContext {
118        schema,
119        operation,
120        item: &operation.selection_set,
121    }
122}