1use crate::description::Description;
2use crate::directive::Directive;
3use crate::directive::DirectiveLocation;
4use crate::name::Name;
5use crate::DocumentBuilder;
6use apollo_compiler::ast;
7use arbitrary::Result as ArbitraryResult;
8use indexmap::IndexMap;
9use indexmap::IndexSet;
10
11#[derive(Debug, Clone)]
18pub struct UnionTypeDef {
19 pub(crate) name: Name,
20 pub(crate) description: Option<Description>,
21 pub(crate) members: IndexSet<Name>,
22 pub(crate) directives: IndexMap<Name, Directive>,
23 pub(crate) extend: bool,
24}
25
26impl From<UnionTypeDef> for ast::Definition {
27 fn from(x: UnionTypeDef) -> Self {
28 if x.extend {
29 ast::UnionTypeExtension {
30 name: x.name.into(),
31 directives: Directive::to_ast(x.directives),
32 members: x.members.into_iter().map(Into::into).collect(),
33 }
34 .into()
35 } else {
36 ast::UnionTypeDefinition {
37 description: x.description.map(Into::into),
38 name: x.name.into(),
39 directives: Directive::to_ast(x.directives),
40 members: x.members.into_iter().map(Into::into).collect(),
41 }
42 .into()
43 }
44 }
45}
46
47impl TryFrom<apollo_parser::cst::UnionTypeDefinition> for UnionTypeDef {
48 type Error = crate::FromError;
49
50 fn try_from(union_def: apollo_parser::cst::UnionTypeDefinition) -> Result<Self, Self::Error> {
51 Ok(Self {
52 name: union_def
53 .name()
54 .expect("object type definition must have a name")
55 .into(),
56 description: union_def.description().map(Description::from),
57 directives: union_def
58 .directives()
59 .map(Directive::convert_directives)
60 .transpose()?
61 .unwrap_or_default(),
62 extend: false,
63 members: union_def
64 .union_member_types()
65 .map(|members| {
66 members
67 .named_types()
68 .map(|n| n.name().unwrap().into())
69 .collect()
70 })
71 .unwrap_or_default(),
72 })
73 }
74}
75
76impl TryFrom<apollo_parser::cst::UnionTypeExtension> for UnionTypeDef {
77 type Error = crate::FromError;
78
79 fn try_from(union_def: apollo_parser::cst::UnionTypeExtension) -> Result<Self, Self::Error> {
80 Ok(Self {
81 name: union_def
82 .name()
83 .expect("object type definition must have a name")
84 .into(),
85 description: None,
86 directives: union_def
87 .directives()
88 .map(|d| {
89 d.directives()
90 .map(|d| Ok((d.name().unwrap().into(), Directive::try_from(d)?)))
91 .collect::<Result<_, crate::FromError>>()
92 })
93 .transpose()?
94 .unwrap_or_default(),
95 extend: true,
96 members: union_def
97 .union_member_types()
98 .map(|members| {
99 members
100 .named_types()
101 .map(|n| n.name().unwrap().into())
102 .collect()
103 })
104 .unwrap_or_default(),
105 })
106 }
107}
108
109impl DocumentBuilder<'_> {
110 pub fn union_type_definition(&mut self) -> ArbitraryResult<UnionTypeDef> {
112 let extend = !self.union_type_defs.is_empty() && self.u.arbitrary().unwrap_or(false);
113 let name = if extend {
114 let available_unions: Vec<&Name> = self
115 .union_type_defs
116 .iter()
117 .filter_map(|union| {
118 if union.extend {
119 None
120 } else {
121 Some(&union.name)
122 }
123 })
124 .collect();
125 (*self.u.choose(&available_unions)?).clone()
126 } else {
127 self.type_name()?
128 };
129 let description = self
130 .u
131 .arbitrary()
132 .unwrap_or(false)
133 .then(|| self.description())
134 .transpose()?;
135 let directives = self.directives(DirectiveLocation::Union)?;
136 let existing_members: IndexSet<Name> = self
142 .union_type_defs
143 .iter()
144 .filter(|u| u.name == name)
145 .flat_map(|u| u.members.iter().cloned())
146 .collect();
147 let object_types: Vec<_> = self
148 .list_existing_object_types()
149 .into_iter()
150 .filter(|o| !existing_members.contains(o.name()))
151 .collect();
152 if object_types.is_empty() {
153 return Err(arbitrary::Error::IncorrectFormat);
154 }
155 let members = (0..self.u.int_in_range(2..=10)?)
156 .map(|_| Ok(self.u.choose(&object_types)?.name().clone()))
157 .collect::<ArbitraryResult<IndexSet<_>>>()?;
158
159 if extend && directives.is_empty() && members.is_empty() {
160 return Err(arbitrary::Error::IncorrectFormat);
161 }
162
163 Ok(UnionTypeDef {
164 name,
165 description,
166 members,
167 directives,
168 extend,
169 })
170 }
171}