use std::fmt::{self, Write as _};
use crate::{Directive, SelectionSet};
#[derive(Debug, Clone)]
pub struct FragmentDefinition {
name: String,
type_condition: TypeCondition,
directives: Vec<Directive>,
selection_set: SelectionSet,
}
impl FragmentDefinition {
pub fn new(name: String, type_condition: TypeCondition, selection_set: SelectionSet) -> Self {
Self {
name,
type_condition,
selection_set,
directives: Vec::new(),
}
}
pub fn directive(&mut self, directive: Directive) {
self.directives.push(directive)
}
}
impl fmt::Display for FragmentDefinition {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let indent_level = 0;
write!(f, "fragment {} {}", self.name, self.type_condition)?;
for directive in &self.directives {
write!(f, " {directive}")?;
}
write!(
f,
" {}",
self.selection_set.format_with_indent(indent_level)
)?;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct FragmentSpread {
name: String,
directives: Vec<Directive>,
}
impl FragmentSpread {
pub fn new(name: String) -> Self {
Self {
name,
directives: Vec::new(),
}
}
pub fn directive(&mut self, directive: Directive) {
self.directives.push(directive)
}
}
impl fmt::Display for FragmentSpread {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "...{}", self.name)?;
for directive in &self.directives {
write!(f, " {directive}")?;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct InlineFragment {
type_condition: Option<TypeCondition>,
directives: Vec<Directive>,
selection_set: SelectionSet,
pub(crate) indent_level: usize,
}
impl InlineFragment {
pub fn new(selection_set: SelectionSet) -> Self {
Self {
selection_set,
type_condition: Option::default(),
directives: Vec::new(),
indent_level: 0,
}
}
pub fn directive(&mut self, directive: Directive) {
self.directives.push(directive)
}
pub fn type_condition(&mut self, type_condition: Option<TypeCondition>) {
self.type_condition = type_condition;
}
pub(crate) fn format_with_indent(&self, indent_level: usize) -> String {
let mut text = String::from("...");
if let Some(type_condition) = &self.type_condition {
let _ = write!(text, " {type_condition}");
}
for directive in &self.directives {
let _ = write!(text, " {directive}");
}
let _ = write!(
text,
" {}",
self.selection_set.format_with_indent(indent_level),
);
text
}
}
impl fmt::Display for InlineFragment {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let indent_level = 0;
write!(f, "{}", self.format_with_indent(indent_level))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TypeCondition {
name: String,
}
impl TypeCondition {
pub fn new(name: String) -> Self {
Self { name }
}
}
impl fmt::Display for TypeCondition {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "on {}", self.name)
}
}
#[cfg(test)]
mod tests {
use indoc::indoc;
use crate::{field::Field, Argument, Selection, Value};
use super::*;
#[test]
fn it_encodes_simple_inline_fragment() {
let selections = vec![Selection::Field(Field::new(String::from("myField")))];
let mut selection_set = SelectionSet::new();
selections
.into_iter()
.for_each(|s| selection_set.selection(s));
let mut inline_fragment = InlineFragment::new(selection_set);
inline_fragment.type_condition(Some(TypeCondition::new(String::from("User"))));
assert_eq!(
inline_fragment.to_string(),
indoc! {r#"
... on User {
myField
}
"#}
);
}
#[test]
fn it_encodes_simple_fragment_spread() {
let fragment = FragmentSpread::new(String::from("myFragment"));
assert_eq!(fragment.to_string(), indoc! {r#"...myFragment"#});
}
#[test]
fn it_encodes_deeper_inline_fragment() {
let another_nested_field_bis = Field::new(String::from("anotherNestedBisField"));
let mut another_nested_field = Field::new(String::from("anotherNestedField"));
let mut selection_set = SelectionSet::new();
selection_set.selection(Selection::Field(another_nested_field_bis));
another_nested_field.selection_set(selection_set.into());
let mut selection_set = SelectionSet::new();
selection_set.selection(Selection::Field(another_nested_field.clone()));
another_nested_field.selection_set(selection_set.into());
let nested_selections = vec![
Selection::Field(Field::new(String::from("nestedField"))),
Selection::Field(another_nested_field),
];
let mut nested_selection_set = SelectionSet::new();
nested_selections
.into_iter()
.for_each(|s| nested_selection_set.selection(s));
let other_inline_fragment = InlineFragment::new(nested_selection_set);
let selections = vec![
Selection::Field(Field::new(String::from("myField"))),
Selection::FragmentSpread(FragmentSpread::new(String::from("myFragment"))),
Selection::InlineFragment(other_inline_fragment),
];
let mut selection_set = SelectionSet::new();
selections
.into_iter()
.for_each(|s| selection_set.selection(s));
let mut inline_fragment = InlineFragment::new(selection_set);
inline_fragment.type_condition(Some(TypeCondition::new(String::from("User"))));
assert_eq!(
inline_fragment.to_string(),
indoc! {r#"
... on User {
myField
...myFragment
... {
nestedField
anotherNestedField {
anotherNestedField {
anotherNestedBisField
}
}
}
}
"#}
);
}
#[test]
fn it_encodes_fragment_definition() {
let selections = vec![Selection::Field(Field::new(String::from("myField")))];
let mut selection_set = SelectionSet::new();
selections
.into_iter()
.for_each(|s| selection_set.selection(s));
let mut fragment_def = FragmentDefinition::new(
String::from("myFragment"),
TypeCondition::new(String::from("User")),
selection_set,
);
let mut directive = Directive::new(String::from("myDirective"));
directive.arg(Argument::new(String::from("first"), Value::Int(5)));
fragment_def.directive(directive);
assert_eq!(
fragment_def.to_string(),
indoc! {r#"
fragment myFragment on User @myDirective(first: 5) {
myField
}
"#}
);
}
}