use crate::DocumentBuilder;
use arbitrary::Result as ArbitraryResult;
use std::fmt::Write as _;
const CHARSET_NAME_HEAD: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
const CHARSET_NAME_BODY: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789";
const RESERVED_KEYWORDS: &[&str] = &[
"on",
"Int",
"Float",
"String",
"Boolean",
"ID",
"type",
"enum",
"union",
"extend",
"scalar",
"directive",
"query",
"mutation",
"subscription",
"schema",
"interface",
];
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Name {
pub(crate) name: String,
}
impl From<Name> for String {
fn from(val: Name) -> Self {
val.name
}
}
impl From<Name> for apollo_compiler::Name {
fn from(value: Name) -> Self {
(&value).into()
}
}
impl From<&'_ Name> for apollo_compiler::Name {
fn from(value: &'_ Name) -> Self {
apollo_compiler::Name::new(&value.name).unwrap()
}
}
impl From<apollo_parser::cst::Name> for Name {
fn from(name: apollo_parser::cst::Name) -> Self {
Self {
name: name.ident_token().unwrap().to_string(),
}
}
}
impl Name {
pub const fn new(name: String) -> Self {
Self { name }
}
}
impl DocumentBuilder<'_> {
pub fn name(&mut self) -> ArbitraryResult<Name> {
Ok(Name::new(self.limited_string(30)?))
}
pub fn type_name(&mut self) -> ArbitraryResult<Name> {
let base = self.limited_string(30)?;
let mut suffix = 0usize;
let mut new_name = base.clone();
while self.used_type_names.contains(new_name.as_str()) {
new_name.clear();
let _ = write!(new_name, "{base}{suffix}");
suffix += 1;
}
self.used_type_names.insert(new_name.clone());
Ok(Name::new(new_name))
}
pub fn name_with_index(&mut self, index: usize) -> ArbitraryResult<Name> {
let mut name = self.limited_string(30)?;
let _ = write!(name, "{index}");
Ok(Name::new(name))
}
pub(crate) fn limited_string(&mut self, max_size: usize) -> ArbitraryResult<String> {
loop {
let size = self.u.int_in_range(1..=max_size)?;
let gen_str = String::from_utf8(
(0..size)
.map(|curr_idx| {
let charset = if curr_idx == 0 {
CHARSET_NAME_HEAD
} else {
CHARSET_NAME_BODY
};
Ok(*self.u.choose(charset)?)
})
.collect::<ArbitraryResult<Vec<u8>>>()?,
)
.unwrap();
let new_gen = gen_str.trim_end_matches('_');
if !new_gen.is_empty() && !RESERVED_KEYWORDS.contains(&new_gen) {
break Ok(new_gen.to_string());
}
}
}
}