apollo_encoder/
interface_def.rs1use std::fmt;
2
3use crate::{Directive, FieldDefinition, StringValue};
4
5#[derive(Debug, Clone)]
74pub struct InterfaceDefinition {
75 name: String,
77 description: Option<StringValue>,
79 interfaces: Vec<String>,
81 fields: Vec<FieldDefinition>,
83 directives: Vec<Directive>,
85 extend: bool,
86}
87
88impl InterfaceDefinition {
89 pub fn new(name: String) -> Self {
91 Self {
92 name,
93 description: None,
94 fields: Vec::new(),
95 interfaces: Vec::new(),
96 directives: Vec::new(),
97 extend: false,
98 }
99 }
100
101 pub fn description(&mut self, description: String) {
103 self.description = Some(StringValue::Top {
104 source: description,
105 });
106 }
107
108 pub fn interface(&mut self, interface: String) {
110 self.interfaces.push(interface)
111 }
112
113 pub fn extend(&mut self) {
115 self.extend = true;
116 }
117
118 pub fn field(&mut self, field: FieldDefinition) {
120 self.fields.push(field)
121 }
122
123 pub fn directive(&mut self, directive: Directive) {
125 self.directives.push(directive)
126 }
127}
128
129impl fmt::Display for InterfaceDefinition {
130 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131 if self.extend {
132 write!(f, "extend ")?;
133 } else {
134 if let Some(description) = &self.description {
136 writeln!(f, "{description}")?;
137 }
138 }
139
140 write!(f, "interface {}", &self.name)?;
141 for (i, interface) in self.interfaces.iter().enumerate() {
142 match i {
143 0 => write!(f, " implements {interface}")?,
144 _ => write!(f, "& {interface}")?,
145 }
146 }
147 for directive in &self.directives {
148 write!(f, " {directive}")?;
149 }
150
151 write!(f, " {{")?;
152
153 for field in &self.fields {
154 write!(f, "\n{field}")?;
155 }
156 writeln!(f, "\n}}")
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163 use crate::{Argument, Type_, Value};
164 use indoc::indoc;
165 use pretty_assertions::assert_eq;
166
167 #[test]
168 fn it_encodes_interfaces() {
169 let ty_1 = Type_::NamedType {
170 name: "String".to_string(),
171 };
172
173 let ty_2 = Type_::NamedType {
174 name: "String".to_string(),
175 };
176
177 let ty_3 = Type_::NonNull { ty: Box::new(ty_2) };
178 let ty_4 = Type_::List { ty: Box::new(ty_3) };
179 let ty_5 = Type_::NonNull { ty: Box::new(ty_4) };
180
181 let ty_6 = Type_::NamedType {
182 name: "Boolean".to_string(),
183 };
184
185 let mut field_1 = FieldDefinition::new("main".to_string(), ty_1);
186 field_1.description("Cat's main dish of a meal.".to_string());
187
188 let mut field_2 = FieldDefinition::new("snack".to_string(), ty_5);
189 field_2.description("Cat's post meal snack.".to_string());
190
191 let mut field_3 = FieldDefinition::new("pats".to_string(), ty_6);
192 field_3.description("Does cat get a pat\nafter meal?".to_string());
193
194 let mut directive = Directive::new(String::from("testDirective"));
195 directive.arg(Argument::new(
196 String::from("first"),
197 Value::String("one".to_string()),
198 ));
199
200 let mut interface = InterfaceDefinition::new("Meal".to_string());
202 interface.description("Meal interface for various\nmeals during the day.".to_string());
203 interface.field(field_1);
204 interface.field(field_2);
205 interface.field(field_3);
206 interface.directive(directive);
207
208 assert_eq!(
209 interface.to_string(),
210 indoc! { r#"
211 """
212 Meal interface for various
213 meals during the day.
214 """
215 interface Meal @testDirective(first: "one") {
216 "Cat's main dish of a meal."
217 main: String
218 "Cat's post meal snack."
219 snack: [String!]!
220 """
221 Does cat get a pat
222 after meal?
223 """
224 pats: Boolean
225 }
226 "# }
227 );
228 }
229
230 #[test]
231 fn it_encodes_interface_extension() {
232 let ty_1 = Type_::NamedType {
233 name: "String".to_string(),
234 };
235
236 let ty_2 = Type_::NamedType {
237 name: "String".to_string(),
238 };
239
240 let ty_3 = Type_::NonNull { ty: Box::new(ty_2) };
241 let ty_4 = Type_::List { ty: Box::new(ty_3) };
242 let ty_5 = Type_::NonNull { ty: Box::new(ty_4) };
243
244 let ty_6 = Type_::NamedType {
245 name: "Boolean".to_string(),
246 };
247
248 let mut field_1 = FieldDefinition::new("main".to_string(), ty_1);
249 field_1.description("Cat's main dish of a meal.".to_string());
250
251 let mut field_2 = FieldDefinition::new("snack".to_string(), ty_5);
252 field_2.description("Cat's post meal snack.".to_string());
253
254 let mut field_3 = FieldDefinition::new("pats".to_string(), ty_6);
255 field_3.description("Does cat get a pat\nafter meal?".to_string());
256
257 let mut directive = Directive::new(String::from("testDirective"));
258 directive.arg(Argument::new(
259 String::from("first"),
260 Value::String("one".to_string()),
261 ));
262
263 let mut interface = InterfaceDefinition::new("Meal".to_string());
265 interface.description("Meal interface for various\nmeals during the day.".to_string());
266 interface.field(field_1);
267 interface.field(field_2);
268 interface.field(field_3);
269 interface.directive(directive);
270 interface.extend();
271
272 assert_eq!(
273 interface.to_string(),
274 indoc! { r#"
275 extend interface Meal @testDirective(first: "one") {
276 "Cat's main dish of a meal."
277 main: String
278 "Cat's post meal snack."
279 snack: [String!]!
280 """
281 Does cat get a pat
282 after meal?
283 """
284 pats: Boolean
285 }
286 "# }
287 );
288 }
289}