1use crate::DocumentBuilder;
2use arbitrary::Result as ArbitraryResult;
3use std::fmt::Write as _;
4
5const CHARSET_LETTERS: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_";
6const CHARSET_NUMBERS: &[u8] = b"0123456789";
7const RESERVED_KEYWORDS: &[&str] = &[
8 "on",
9 "Int",
10 "Float",
11 "String",
12 "Boolean",
13 "ID",
14 "type",
15 "enum",
16 "union",
17 "extend",
18 "scalar",
19 "directive",
20 "query",
21 "mutation",
22 "subscription",
23 "schema",
24 "interface",
25];
26
27#[derive(Debug, Clone, PartialEq, Eq, Hash)]
36pub struct Name {
37 pub(crate) name: String,
38}
39
40impl From<Name> for String {
41 fn from(val: Name) -> Self {
42 val.name
43 }
44}
45
46impl From<Name> for apollo_compiler::Name {
47 fn from(value: Name) -> Self {
48 (&value).into()
49 }
50}
51
52impl From<&'_ Name> for apollo_compiler::Name {
53 fn from(value: &'_ Name) -> Self {
54 apollo_compiler::Name::new(&value.name).unwrap()
58 }
59}
60
61impl From<apollo_parser::cst::Name> for Name {
62 fn from(name: apollo_parser::cst::Name) -> Self {
63 Self {
64 name: name.ident_token().unwrap().to_string(),
65 }
66 }
67}
68
69impl Name {
70 pub const fn new(name: String) -> Self {
71 Self { name }
72 }
73}
74
75impl DocumentBuilder<'_> {
76 pub fn name(&mut self) -> ArbitraryResult<Name> {
78 Ok(Name::new(self.limited_string(30)?))
79 }
80
81 pub fn type_name(&mut self) -> ArbitraryResult<Name> {
83 let mut new_name = self.limited_string(30)?;
84 if self.list_existing_type_names().any(|n| n.name == new_name) {
85 let _ = write!(
86 new_name,
87 "{}",
88 self.object_type_defs.len() + self.enum_type_defs.len() + self.directive_defs.len()
89 );
90 }
91 Ok(Name::new(new_name))
92 }
93
94 pub fn name_with_index(&mut self, index: usize) -> ArbitraryResult<Name> {
96 let mut name = self.limited_string(30)?;
97 let _ = write!(name, "{index}");
98
99 Ok(Name::new(name))
100 }
101
102 pub(crate) fn limited_string(&mut self, max_size: usize) -> ArbitraryResult<String> {
104 loop {
105 let size = self.u.int_in_range(1..=max_size)?;
106
107 let gen_str = String::from_utf8(
108 (0..size)
109 .map(|curr_idx| {
110 let idx = self.u.arbitrary::<usize>()?;
111
112 let ch = if curr_idx == 0 {
114 CHARSET_LETTERS[idx % (CHARSET_LETTERS.len() - 1)]
116 } else {
117 let idx = idx % (CHARSET_LETTERS.len() + CHARSET_NUMBERS.len());
118 if idx < CHARSET_LETTERS.len() {
119 CHARSET_LETTERS[idx]
120 } else {
121 CHARSET_NUMBERS[idx - CHARSET_LETTERS.len()]
122 }
123 };
124
125 Ok(ch)
126 })
127 .collect::<ArbitraryResult<Vec<u8>>>()?,
128 )
129 .unwrap();
130 let new_gen = gen_str.trim_end_matches('_');
131 if !new_gen.is_empty() && !RESERVED_KEYWORDS.contains(&new_gen) {
132 break Ok(new_gen.to_string());
133 }
134 }
135 }
136
137 fn list_existing_type_names(&self) -> impl Iterator<Item = &Name> {
138 self.object_type_defs
139 .iter()
140 .map(|o| &o.name)
141 .chain(self.interface_type_defs.iter().map(|itf| &itf.name))
142 .chain(self.enum_type_defs.iter().map(|itf| &itf.name))
143 .chain(self.directive_defs.iter().map(|itf| &itf.name))
144 .chain(self.union_type_defs.iter().map(|itf| &itf.name))
145 .chain(self.input_object_type_defs.iter().map(|itf| &itf.name))
146 .chain(self.scalar_type_defs.iter().map(|itf| &itf.name))
147 .chain(self.directive_defs.iter().map(|itf| &itf.name))
148 .chain(self.fragment_defs.iter().map(|itf| &itf.name))
149 .chain(self.operation_defs.iter().filter_map(|op| op.name.as_ref()))
150 }
151}