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