use std::fmt;
use crate::{Directive, SelectionSet, VariableDefinition};
#[derive(Debug, Clone)]
pub struct OperationDefinition {
operation_type: OperationType,
name: Option<String>,
variable_definitions: Vec<VariableDefinition>,
directives: Vec<Directive>,
selection_set: SelectionSet,
shorthand: bool,
}
impl OperationDefinition {
pub fn new(operation_type: OperationType, selection_set: SelectionSet) -> Self {
Self {
operation_type,
selection_set,
name: None,
variable_definitions: Vec::new(),
directives: Vec::new(),
shorthand: false,
}
}
pub fn name(&mut self, name: Option<String>) {
self.name = name;
}
pub fn variable_definition(&mut self, variable_definition: VariableDefinition) {
self.variable_definitions.push(variable_definition);
}
pub fn directive(&mut self, directive: Directive) {
self.directives.push(directive);
}
pub fn shorthand(&mut self) {
self.shorthand = true;
}
}
impl fmt::Display for OperationDefinition {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let indent_level = 0;
if !self.shorthand {
write!(f, "{}", self.operation_type)?;
if let Some(name) = &self.name {
write!(f, " {name}")?;
}
if !self.variable_definitions.is_empty() {
write!(f, "(")?;
for (i, var_def) in self.variable_definitions.iter().enumerate() {
if i == self.variable_definitions.len() - 1 {
write!(f, "{var_def}")?;
} else {
write!(f, "{var_def}, ")?;
}
}
write!(f, ")")?;
}
for directive in &self.directives {
write!(f, " {directive}")?;
}
write!(f, " ")?;
}
write!(f, "{}", self.selection_set.format_with_indent(indent_level))?;
Ok(())
}
}
#[derive(Debug, Clone)]
pub enum OperationType {
Query,
Mutation,
Subscription,
}
impl fmt::Display for OperationType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
OperationType::Query => write!(f, "query"),
OperationType::Mutation => write!(f, "mutation"),
OperationType::Subscription => write!(f, "subscription"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{field::Field, Argument, FragmentSpread, Selection, Type_, Value};
use indoc::indoc;
#[test]
fn it_encodes_a_query_operation() {
let selection_set = {
let sels = vec![
Selection::Field(Field::new(String::from("first"))),
Selection::Field(Field::new(String::from("second"))),
];
let mut sel_set = SelectionSet::new();
sels.into_iter().for_each(|sel| sel_set.selection(sel));
sel_set
};
let var_def = VariableDefinition::new(
String::from("variable_def"),
Type_::List {
ty: Box::new(Type_::NamedType {
name: String::from("Int"),
}),
},
);
let mut new_op = OperationDefinition::new(OperationType::Query, selection_set);
let mut directive = Directive::new(String::from("testDirective"));
directive.arg(Argument::new(
String::from("first"),
Value::String("one".to_string()),
));
new_op.variable_definition(var_def);
new_op.directive(directive);
assert_eq!(
new_op.to_string(),
indoc! { r#"
query($variable_def: [Int]) @testDirective(first: "one") {
first
second
}
"#}
);
}
#[test]
fn it_encodes_a_shorthand_query_operation() {
let selection_set = {
let sels = vec![
Selection::Field(Field::new(String::from("first"))),
Selection::Field(Field::new(String::from("second"))),
];
let mut sel_set = SelectionSet::new();
sels.into_iter().for_each(|sel| sel_set.selection(sel));
sel_set
};
let mut new_op = OperationDefinition::new(OperationType::Query, selection_set);
new_op.shorthand();
assert_eq!(
new_op.to_string(),
indoc! { r#"
{
first
second
}
"#}
);
}
#[test]
fn it_encodes_a_deeper_query_operation() {
let fourth_field = Field::new("fourth".to_string());
let mut third_field = Field::new("third".to_string());
third_field.selection_set(Some(SelectionSet::with_selections(vec![Selection::Field(
fourth_field,
)])));
let mut second_field = Field::new("second".to_string());
second_field.selection_set(Some(SelectionSet::with_selections(vec![Selection::Field(
third_field,
)])));
let mut first_field = Field::new("first".to_string());
first_field.selection_set(Some(SelectionSet::with_selections(vec![Selection::Field(
second_field,
)])));
let selections = vec![
Selection::Field(first_field),
Selection::FragmentSpread(FragmentSpread::new(String::from("myFragment"))),
];
let mut selection_set = SelectionSet::new();
selections
.into_iter()
.for_each(|s| selection_set.selection(s));
let var_def = VariableDefinition::new(
String::from("variable_def"),
Type_::List {
ty: Box::new(Type_::NamedType {
name: String::from("Int"),
}),
},
);
let mut new_op = OperationDefinition::new(OperationType::Query, selection_set);
let mut directive = Directive::new(String::from("testDirective"));
directive.arg(Argument::new(
String::from("first"),
Value::String("one".to_string()),
));
new_op.variable_definition(var_def);
new_op.directive(directive);
assert_eq!(
new_op.to_string(),
indoc! { r#"
query($variable_def: [Int]) @testDirective(first: "one") {
first {
second {
third {
fourth
}
}
}
...myFragment
}
"#}
);
}
}