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