use crate::metadata::Metadata;
use ferogram_tl_parser::tl::{Definition, Parameter, ParameterType, Type};
pub(crate) fn builtin_type(name: &str) -> Option<&'static str> {
Some(match name {
"Bool" => "bool",
"true" => "bool",
"int" => "i32",
"long" => "i64",
"double" => "f64",
"string" => "String",
"bytes" => "Vec<u8>",
"int128" => "[u8; 16]",
"int256" => "[u8; 32]",
"Vector" => "Vec",
"vector" => "crate::RawVec",
_ => return None,
})
}
pub(crate) fn to_pascal(name: &str) -> String {
let name = if let Some(pos) = name.rfind('.') {
&name[pos + 1..]
} else {
name
};
let mut out = String::with_capacity(name.len());
let mut next_upper = true;
let mut prev_upper = false;
for ch in name.chars() {
if ch == '_' {
next_upper = true;
prev_upper = false;
continue;
}
if next_upper {
out.push(ch.to_ascii_uppercase());
next_upper = false;
prev_upper = ch.is_ascii_uppercase();
} else if ch.is_ascii_uppercase() {
if prev_upper {
out.push(ch.to_ascii_lowercase());
} else {
out.push(ch);
}
prev_upper = true;
} else {
out.push(ch);
prev_upper = false;
}
}
out
}
pub(crate) fn def_type_name(def: &Definition) -> String {
to_pascal(&def.name)
}
pub(crate) fn def_qual_name(def: &Definition) -> String {
let mut s = String::from("crate::types::");
for ns in &def.namespace {
s.push_str(ns);
s.push_str("::");
}
s.push_str(&def_type_name(def));
s
}
pub(crate) fn def_variant_name(def: &Definition) -> String {
let full = def_type_name(def);
let ty = type_name(&def.ty);
let variant = if full.starts_with(&ty) {
&full[ty.len()..]
} else {
&full
};
match variant {
"Self" => {
let pos = full.as_bytes()[..full.len() - variant.len()]
.iter()
.rposition(|c| c.is_ascii_uppercase())
.unwrap_or(0);
full[pos..].to_owned()
}
v if !v.is_empty() && v.chars().all(char::is_numeric) => {
let pos = full
.as_bytes()
.iter()
.rposition(|c| c.is_ascii_uppercase())
.unwrap_or(0);
full[pos..].to_owned()
}
"" => full,
v => v.to_owned(),
}
}
pub(crate) fn type_name(ty: &Type) -> String {
to_pascal(&ty.name)
}
pub(crate) fn type_qual_name(ty: &Type, meta: &Metadata) -> String {
type_path(ty, meta, false)
}
pub(crate) fn type_item_path(ty: &Type, meta: &Metadata) -> String {
type_path(ty, meta, true)
}
fn type_path(ty: &Type, meta: &Metadata, turbofish: bool) -> String {
if ty.generic_ref {
return ty.name.clone();
}
let mut s = if let Some(b) = builtin_type(&ty.name) {
if turbofish {
if b.starts_with('[') {
format!("<{b}>")
} else if let Some(pos) = b.find('<') {
let mut out = b[..pos].to_owned();
out.push_str("::");
out.push_str(&b[pos..]);
out
} else {
b.to_owned()
}
} else {
b.to_owned()
}
} else if ty.bare {
let mut p = String::from("crate::types::");
for ns in &ty.namespace {
p.push_str(ns);
p.push_str("::");
}
p.push_str(&type_name(ty));
p
} else {
let mut p = String::from("crate::enums::");
for ns in &ty.namespace {
p.push_str(ns);
p.push_str("::");
}
p.push_str(&type_name(ty));
p
};
if let Some(arg) = &ty.generic_arg {
if turbofish {
s.push_str("::");
}
s.push('<');
s.push_str(&type_qual_name(arg, meta));
s.push('>');
}
s
}
pub(crate) fn param_attr_name(param: &Parameter) -> String {
match param.name.as_str() {
"final" => "r#final".into(),
"loop" => "r#loop".into(),
"self" => "is_self".into(),
"static" => "r#static".into(),
"type" => "r#type".into(),
other => other.to_ascii_lowercase(),
}
}
pub(crate) fn param_qual_name(param: &Parameter, meta: &Metadata) -> String {
match ¶m.ty {
ParameterType::Flags => "u32".into(),
ParameterType::Normal { ty, flag } => {
if flag.is_some() && ty.name == "true" {
return "bool".into();
}
let inner = type_qual_name(ty, meta);
if flag.is_some() {
format!("Option<{inner}>")
} else {
inner
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pascal_basic() {
assert_eq!(to_pascal("user_empty"), "UserEmpty");
assert_eq!(to_pascal("inputPeerSelf"), "InputPeerSelf");
assert_eq!(to_pascal("some_OK_name"), "SomeOkName");
}
#[test]
fn pascal_namespaced() {
assert_eq!(to_pascal("upload.fileCdnRedirect"), "FileCdnRedirect");
}
}