use std::fmt::{self, Write as _};
use crate::{Field, FragmentSpread, InlineFragment};
#[derive(Debug, PartialEq, Clone, Default)]
pub struct SelectionSet {
selections: Vec<Selection>,
}
impl SelectionSet {
pub fn new() -> Self {
Self::default()
}
pub fn with_selections(selections: Vec<Selection>) -> Self {
Self { selections }
}
pub fn selection(&mut self, selection: Selection) {
self.selections.push(selection);
}
pub(crate) fn format_with_indent(&self, mut indent_level: usize) -> String {
let mut text = String::from("{\n");
indent_level += 1;
let indent = " ".repeat(indent_level);
for sel in &self.selections {
let _ = writeln!(text, "{}{}", indent, sel.format_with_indent(indent_level));
}
if indent_level <= 1 {
text.push_str("}\n");
} else {
let _ = write!(text, "{}}}", " ".repeat(indent_level - 1));
}
text
}
}
impl fmt::Display for SelectionSet {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let indent_level = 1;
writeln!(f, "{{")?;
let indent = " ".repeat(indent_level);
for sel in &self.selections {
writeln!(f, "{}{}", indent, sel.format_with_indent(indent_level))?;
}
writeln!(f, "}}")?;
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Selection {
Field(Field),
FragmentSpread(FragmentSpread),
InlineFragment(InlineFragment),
}
impl Selection {
pub(crate) fn format_with_indent(&self, indent_level: usize) -> String {
match self {
Selection::Field(field) => field.format_with_indent(indent_level),
Selection::FragmentSpread(frag_spread) => frag_spread.to_string(),
Selection::InlineFragment(inline_frag) => inline_frag.format_with_indent(indent_level),
}
}
}
impl fmt::Display for Selection {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let indent_level = 0;
match self {
Selection::Field(field) => write!(f, "{}", field.format_with_indent(indent_level)),
Selection::FragmentSpread(fragment_spread) => write!(f, "{fragment_spread}"),
Selection::InlineFragment(inline_fragment) => {
write!(f, "{}", inline_fragment.format_with_indent(indent_level))
}
}
}
}
#[cfg(test)]
mod tests {
use indoc::indoc;
use super::*;
#[test]
fn it_encodes_selection_set() {
let mut aliased_field = Field::new(String::from("myField"));
aliased_field.alias(Some(String::from("myAlias")));
let selections = vec![
Selection::Field(aliased_field),
Selection::FragmentSpread(FragmentSpread::new(String::from("myFragment"))),
];
let mut selection_set = SelectionSet::new();
selections
.into_iter()
.for_each(|s| selection_set.selection(s));
assert_eq!(
selection_set.to_string(),
indoc! {r#"
{
myAlias: myField
...myFragment
}
"#}
)
}
#[test]
fn it_encodes_deeper_selection_set() {
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));
assert_eq!(
selection_set.to_string(),
indoc! {r#"
{
first {
second {
third {
fourth
}
}
}
...myFragment
}
"#}
)
}
}