apollo_encoder/
directive_def.rs

1use std::fmt;
2
3use crate::{ArgumentsDefinition, InputValueDefinition, StringValue};
4
5/// The `DirectiveDefinition` type represents a Directive definition.
6///
7/// *DirectiveDefinition*:
8///     Description? **directive @** Name Arguments Definition? **repeatable**? **on** DirectiveLocations
9///
10/// Detailed documentation can be found in [GraphQL spec](https://spec.graphql.org/October2021/#sec-Type-System.Directives).
11///
12/// ### Example
13/// ```rust
14/// use apollo_encoder::DirectiveDefinition;
15/// use indoc::indoc;
16///
17/// let mut directive = DirectiveDefinition::new("infer".to_string());
18/// directive.description("Infer field types\nfrom field values.".to_string());
19/// directive.location("OBJECT".to_string());
20/// directive.location("FIELD_DEFINITION".to_string());
21/// directive.location("INPUT_FIELD_DEFINITION".to_string());
22///
23/// assert_eq!(
24///     directive.to_string(),
25///     r#""""
26/// Infer field types
27/// from field values.
28/// """
29/// directive @infer on OBJECT | FIELD_DEFINITION | INPUT_FIELD_DEFINITION
30/// "#
31/// );
32/// ```
33#[derive(Debug, Clone)]
34pub struct DirectiveDefinition {
35    // Name must return a String.
36    name: String,
37    // Description may return a String or null.
38    description: Option<StringValue>,
39    // Args returns a Vector of __InputValue representing the arguments this
40    // directive accepts.
41    args: ArgumentsDefinition,
42    // Locations returns a List of __DirectiveLocation representing the valid
43    // locations this directive may be placed.
44    locations: Vec<String>,
45    // If the directive is repeatable
46    repeatable: bool,
47}
48
49impl DirectiveDefinition {
50    /// Create a new instance of Directive definition.
51    pub fn new(name: String) -> Self {
52        Self {
53            name,
54            description: None,
55            args: ArgumentsDefinition::new(),
56            locations: Vec::new(),
57            repeatable: false,
58        }
59    }
60
61    /// Set the Directive's description.
62    pub fn description(&mut self, description: String) {
63        self.description = Some(StringValue::Top {
64            source: description,
65        });
66    }
67
68    /// Add a location where this Directive can be used.
69    pub fn location(&mut self, location: String) {
70        self.locations.push(location);
71    }
72
73    /// Add an argument to this Directive.
74    pub fn arg(&mut self, arg: InputValueDefinition) {
75        self.args.input_value(arg);
76    }
77
78    /// Set the Directive's repeatable
79    pub fn repeatable(&mut self) {
80        self.repeatable = true;
81    }
82}
83
84impl fmt::Display for DirectiveDefinition {
85    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86        if let Some(description) = &self.description {
87            writeln!(f, "{description}")?;
88        }
89        write!(f, "directive @{}", self.name)?;
90
91        if !self.args.input_values.is_empty() {
92            write!(f, "{}", self.args)?;
93        }
94
95        if self.repeatable {
96            write!(f, " repeatable")?;
97        }
98
99        for (i, location) in self.locations.iter().enumerate() {
100            match i {
101                0 => write!(f, " on {location}")?,
102                _ => write!(f, " | {location}")?,
103            }
104        }
105
106        // append a new line at the end
107        writeln!(f)
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use super::*;
114    use crate::Type_;
115    use pretty_assertions::assert_eq;
116
117    #[test]
118    fn it_encodes_directives_for_a_single_location() {
119        let mut directive = DirectiveDefinition::new("infer".to_string());
120        directive.description("Infer field types from field values.".to_string());
121        directive.location("OBJECT".to_string());
122
123        assert_eq!(
124            directive.to_string(),
125            r#""Infer field types from field values."
126directive @infer on OBJECT
127"#
128        );
129    }
130
131    #[test]
132    fn it_encodes_directives_for_multiple_location() {
133        let mut directive = DirectiveDefinition::new("infer".to_string());
134        directive.description("Infer field types\nfrom field values.".to_string());
135        directive.location("OBJECT".to_string());
136        directive.location("FIELD_DEFINITION".to_string());
137        directive.location("INPUT_FIELD_DEFINITION".to_string());
138
139        assert_eq!(
140            directive.to_string(),
141            r#""""
142Infer field types
143from field values.
144"""
145directive @infer on OBJECT | FIELD_DEFINITION | INPUT_FIELD_DEFINITION
146"#
147        );
148    }
149
150    #[test]
151    fn it_encodes_directives_with_arguments() {
152        let mut directive = DirectiveDefinition::new("infer".to_string());
153        directive.description("Infer field types from field values.".to_string());
154        directive.location("OBJECT".to_string());
155
156        let ty_1 = Type_::NamedType {
157            name: "SpaceProgram".to_string(),
158        };
159
160        let ty_2 = Type_::List { ty: Box::new(ty_1) };
161        let arg = InputValueDefinition::new("cat".to_string(), ty_2);
162        directive.arg(arg);
163
164        assert_eq!(
165            directive.to_string(),
166            r#""Infer field types from field values."
167directive @infer(cat: [SpaceProgram]) on OBJECT
168"#
169        );
170    }
171
172    #[test]
173    fn it_encodes_directives_with_arguments_with_description() {
174        let mut directive = DirectiveDefinition::new("infer".to_string());
175        directive.description("Infer field types from field values.".to_string());
176        directive.location("OBJECT".to_string());
177
178        let ty_1 = Type_::NamedType {
179            name: "SpaceProgram".to_string(),
180        };
181
182        let ty_2 = Type_::List { ty: Box::new(ty_1) };
183        let mut arg = InputValueDefinition::new("cat".to_string(), ty_2);
184        arg.description("Space Program for flying cats".to_string());
185        directive.arg(arg);
186
187        assert_eq!(
188            directive.to_string(),
189            r#""Infer field types from field values."
190directive @infer(
191    "Space Program for flying cats"
192    cat: [SpaceProgram]
193  ) on OBJECT
194"#
195        );
196    }
197}