Skip to main content

apollo_smith/
selection_set.rs

1use crate::field::Field;
2use crate::fragment::FragmentSpread;
3use crate::fragment::InlineFragment;
4use crate::name::Name;
5use crate::DocumentBuilder;
6use apollo_compiler::ast;
7use apollo_compiler::Node;
8use arbitrary::Result as ArbitraryResult;
9use indexmap::IndexSet;
10
11/// The __selectionSet type represents a selection_set type in a fragment spread, an operation or a field
12///
13/// *SelectionSet*:
14///     Selection*
15///
16/// Detailed documentation can be found in [GraphQL spec](https://spec.graphql.org/October2021/#sec-Selection-Sets).
17#[derive(Debug, Clone)]
18pub struct SelectionSet {
19    pub(crate) selections: Vec<Selection>,
20}
21
22impl From<SelectionSet> for Vec<ast::Selection> {
23    fn from(sel_set: SelectionSet) -> Self {
24        sel_set.selections.into_iter().map(Into::into).collect()
25    }
26}
27
28impl SelectionSet {
29    pub(crate) fn collect_fragment_spreads(&self, into: &mut IndexSet<Name>) {
30        for selection in &self.selections {
31            match selection {
32                Selection::Field(field) => {
33                    if let Some(inner) = &field.selection_set {
34                        inner.collect_fragment_spreads(into);
35                    }
36                }
37                Selection::FragmentSpread(spread) => {
38                    into.insert(spread.name.clone());
39                }
40                Selection::InlineFragment(inline) => {
41                    inline.selection_set.collect_fragment_spreads(into);
42                }
43            }
44        }
45    }
46}
47
48impl TryFrom<apollo_parser::cst::SelectionSet> for SelectionSet {
49    type Error = crate::FromError;
50
51    fn try_from(selection_set: apollo_parser::cst::SelectionSet) -> Result<Self, Self::Error> {
52        Ok(Self {
53            selections: selection_set
54                .selections()
55                .map(Selection::try_from)
56                .collect::<Result<_, _>>()?,
57        })
58    }
59}
60
61/// The __selection type represents a selection in a selection set
62/// *Selection*:
63///     Field | FragmentSpread | InlineFragment
64///
65/// Detailed documentation can be found in [GraphQL spec](https://spec.graphql.org/October2021/#Selection).
66#[derive(Debug, Clone)]
67pub enum Selection {
68    /// Represents a field
69    Field(Field),
70    /// Represents a fragment spread
71    FragmentSpread(FragmentSpread),
72    /// Represents an inline fragment
73    InlineFragment(InlineFragment),
74}
75
76impl From<Selection> for ast::Selection {
77    fn from(selection: Selection) -> Self {
78        match selection {
79            Selection::Field(field) => Self::Field(Node::new(field.into())),
80            Selection::FragmentSpread(fragment_spread) => {
81                Self::FragmentSpread(Node::new(fragment_spread.into()))
82            }
83            Selection::InlineFragment(inline_fragment) => {
84                Self::InlineFragment(Node::new(inline_fragment.into()))
85            }
86        }
87    }
88}
89
90impl TryFrom<apollo_parser::cst::Selection> for Selection {
91    type Error = crate::FromError;
92
93    fn try_from(selection: apollo_parser::cst::Selection) -> Result<Self, Self::Error> {
94        match selection {
95            apollo_parser::cst::Selection::Field(field) => field.try_into().map(Self::Field),
96            apollo_parser::cst::Selection::FragmentSpread(fragment_spread) => {
97                fragment_spread.try_into().map(Self::FragmentSpread)
98            }
99            apollo_parser::cst::Selection::InlineFragment(inline_fragment) => {
100                inline_fragment.try_into().map(Self::InlineFragment)
101            }
102        }
103    }
104}
105
106impl DocumentBuilder<'_> {
107    /// Create an arbitrary `SelectionSet`
108    pub fn selection_set(&mut self) -> ArbitraryResult<SelectionSet> {
109        let mut exclude_names = Vec::new();
110        let selection_nb = self.stack.last().map(|o| o.fields_def().len()).unwrap_or(0);
111
112        let selections = (0..self.u.int_in_range(1..=5)?)
113            .map(|_| {
114                let index = self.u.int_in_range(0..=selection_nb)?;
115                self.selection(index, &mut exclude_names)
116            }) // TODO do not generate duplication variable name
117            .collect::<ArbitraryResult<Vec<_>>>()?;
118        Ok(SelectionSet { selections })
119    }
120
121    /// Create an arbitrary `Selection`
122    pub fn selection(
123        &mut self,
124        index: usize,
125        excludes: &mut Vec<Name>,
126    ) -> ArbitraryResult<Selection> {
127        let selection = match self.u.int_in_range(0..=2usize)? {
128            0 => Selection::Field(self.field(index)?),
129            1 => match self.fragment_spread(excludes)? {
130                Some(frag_spread) => Selection::FragmentSpread(frag_spread),
131                None => Selection::Field(self.field(index)?),
132            },
133            2 => Selection::InlineFragment(self.inline_fragment()?),
134            _ => unreachable!(),
135        };
136
137        Ok(selection)
138    }
139}