1use crate::DocumentBuilder;
2use arbitrary::Result as ArbitraryResult;
3use std::fmt::Write as _;
4
5const CHARSET_NAME_HEAD: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
8const CHARSET_NAME_BODY: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789";
9const RESERVED_KEYWORDS: &[&str] = &[
10 "on",
11 "Int",
12 "Float",
13 "String",
14 "Boolean",
15 "ID",
16 "type",
17 "enum",
18 "union",
19 "extend",
20 "scalar",
21 "directive",
22 "query",
23 "mutation",
24 "subscription",
25 "schema",
26 "interface",
27];
28
29#[derive(Debug, Clone, PartialEq, Eq, Hash)]
38pub struct Name {
39 pub(crate) name: String,
40}
41
42impl From<Name> for String {
43 fn from(val: Name) -> Self {
44 val.name
45 }
46}
47
48impl From<Name> for apollo_compiler::Name {
49 fn from(value: Name) -> Self {
50 (&value).into()
51 }
52}
53
54impl From<&'_ Name> for apollo_compiler::Name {
55 fn from(value: &'_ Name) -> Self {
56 apollo_compiler::Name::new(&value.name).unwrap()
60 }
61}
62
63impl From<apollo_parser::cst::Name> for Name {
64 fn from(name: apollo_parser::cst::Name) -> Self {
65 Self {
66 name: name.ident_token().unwrap().to_string(),
67 }
68 }
69}
70
71impl Name {
72 pub const fn new(name: String) -> Self {
73 Self { name }
74 }
75}
76
77impl DocumentBuilder<'_> {
78 pub fn name(&mut self) -> ArbitraryResult<Name> {
80 Ok(Name::new(self.limited_string(30)?))
81 }
82
83 pub fn type_name(&mut self) -> ArbitraryResult<Name> {
85 let base = self.limited_string(30)?;
86 let mut suffix = 0usize;
87 let mut new_name = base.clone();
88 while self.used_type_names.contains(new_name.as_str()) {
89 new_name.clear();
90 let _ = write!(new_name, "{base}{suffix}");
91 suffix += 1;
92 }
93 self.used_type_names.insert(new_name.clone());
94 Ok(Name::new(new_name))
95 }
96
97 pub fn name_with_index(&mut self, index: usize) -> ArbitraryResult<Name> {
99 let mut name = self.limited_string(30)?;
100 let _ = write!(name, "{index}");
101
102 Ok(Name::new(name))
103 }
104
105 pub(crate) fn limited_string(&mut self, max_size: usize) -> ArbitraryResult<String> {
107 loop {
108 let size = self.u.int_in_range(1..=max_size)?;
109
110 let gen_str = String::from_utf8(
111 (0..size)
112 .map(|curr_idx| {
113 let charset = if curr_idx == 0 {
115 CHARSET_NAME_HEAD
116 } else {
117 CHARSET_NAME_BODY
118 };
119 Ok(*self.u.choose(charset)?)
120 })
121 .collect::<ArbitraryResult<Vec<u8>>>()?,
122 )
123 .unwrap();
124 let new_gen = gen_str.trim_end_matches('_');
125 if !new_gen.is_empty() && !RESERVED_KEYWORDS.contains(&new_gen) {
126 break Ok(new_gen.to_string());
127 }
128 }
129 }
130}