1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
use castle_query_parser::parse_message;
use castle_types::{
    CastleError, State, Directive, Message, ResolvesFields, SchemaDefinition, SchemaItem, Value,
};
use std::collections::HashMap;

use crate::{
    executor::execute_message,
    validation::{
        validate_directives_exist::validate_directives_exist,
        validate_projection::validate_projection, validate_schema::validate_schema,
    },
};
#[derive(derivative::Derivative)]
#[derivative(Debug)]
pub struct Castle {
    root: Box<dyn ResolvesFields>,
    parsed_schema: SchemaDefinition,
    #[derivative(Debug = "ignore")]
    directives: HashMap<Box<str>, Box<dyn Directive>>,
}

impl Castle {

    ///This function runs self validation and cross validates the schema types and enums.
    /// It also checks if the necessary resolvers have also been provided
    /// - Self validate schema
    ///     - all schema_types and enums used as types have been defined in the schema
    /// - Validate schema resolvers & directives (functions) match the ones we've built in Rust
    pub(crate) fn new_and_validate(
        root: Box<dyn ResolvesFields>,
        schema_def: SchemaDefinition,
        directives: HashMap<Box<str>, Box<dyn Directive>>,
    ) -> Result<Self, CastleError> {
        let castle = Castle {
            root,
            parsed_schema: schema_def,
            directives,
        };

        validate_schema(&castle.parsed_schema)?;
        validate_directives_exist(&castle.parsed_schema, &castle.directives)?;
        Ok(castle)
    }

    pub fn parse_and_validate_message(&self, query: &str) -> Result<Message, CastleError> {
        let parsed_message = parse_message(query)?;
        validate_projection(&self.parsed_schema, &parsed_message.projection)?;
        Ok(parsed_message)
    }

    /// Runs a query
    /// - Validates query against the schema for validity and type correctness
    /// - Runs the query using the resolvers
    /// - Returns the result
    pub async fn run_message(
        &self,
        query: &str,
        ctx: &State,
    ) -> Result<(Value, Vec<anyhow::Error>), CastleError> {
        Ok(execute_message(
            &*self.root,
            self.parse_and_validate_message(query)?,
            &self.directives,
            &self.parsed_schema,
            ctx,
        )
        .await)
    }
}

#[derive(derivative::Derivative)]
#[derivative(Debug)]
pub struct CastleBuilder {
    root: Option<Box<dyn ResolvesFields>>,
    schema_def: Option<SchemaDefinition>,
    #[derivative(Debug = "ignore")]
    directives: HashMap<Box<str>, Box<dyn Directive>>,
}

impl CastleBuilder {
    pub fn new<Root: ResolvesFields + SchemaItem + 'static>(root: Root) -> Self {
        let mut schema_def = SchemaDefinition::new();
        Root::initialize_item(&mut schema_def);

        Self {
            root: Some(Box::new(root)),
            schema_def: Some(schema_def),
            directives: HashMap::new(),
        }
    }

    pub fn add_directive<T: Directive + SchemaItem + 'static>(&mut self, name: &str, directive: T) -> &mut Self {
        self.schema_def.as_mut().map(|schema_def| T::initialize_item(schema_def));
        self.directives
            .insert(name.into(), Box::new(directive));
        self
    }

    pub fn build(&mut self) -> Result<Castle, CastleError> {
        Castle::new_and_validate(
            self.root.take().unwrap(),
            self.schema_def.take().unwrap(),
            self.directives.drain().collect(),
        )
    }
}