use std::collections::HashMap;
use prebindgen::SourceLocation;
#[derive(Clone, Debug)]
pub struct Normalization {
pub source_modules: Vec<String>,
constructors: HashMap<String, String>,
}
impl Normalization {
pub const PRELUDE: &'static [(&'static str, &'static str)] = &[
("std::vec::Vec", "Vec"),
("std::option::Option", "Option"),
("std::result::Result", "Result"),
("std::string::String", "String"),
("std::boxed::Box", "Box"),
("std::mem::MaybeUninit", "MaybeUninit"),
("std::borrow::Cow", "Cow"),
];
pub fn prelude() -> Self {
Self {
source_modules: Vec::new(),
constructors: Self::PRELUDE
.iter()
.map(|(path, name)| ((*path).to_string(), (*name).to_string()))
.collect(),
}
}
pub fn from_items(items: &[(syn::Item, SourceLocation)]) -> Self {
let mut out = Self::prelude();
for (_, loc) in items {
if let Some(crate_name) = &loc.crate_name {
let module = crate_name.replace('-', "_");
if !out.source_modules.contains(&module) {
out.source_modules.push(module);
}
}
}
out
}
fn constructor_of(&self, path: &syn::Path) -> Option<&str> {
self.constructors
.get(&constructor_key(path))
.map(String::as_str)
}
}
impl Default for Normalization {
fn default() -> Self {
Self::prelude()
}
}
fn constructor_key(path: &syn::Path) -> String {
let mut out = String::new();
for (i, seg) in path.segments.iter().enumerate() {
if i > 0 {
out.push_str("::");
}
let mut ident = seg.ident.to_string();
if i == 0 && (ident == "core" || ident == "alloc") {
ident = "std".to_string();
}
out.push_str(&ident);
}
out
}
pub fn canonical_type(ty: &syn::Type) -> syn::Type {
let mut t = ty.clone();
normalize_type(&mut t, &Normalization::prelude());
t
}
pub fn canonical_spelling(ty: &syn::Type) -> String {
use quote::ToTokens;
canonical_type(ty).to_token_stream().to_string()
}
pub fn normalize_type(ty: &mut syn::Type, against: &Normalization) {
use syn::visit_mut::VisitMut;
struct Normalizer<'a> {
against: &'a Normalization,
}
impl VisitMut for Normalizer<'_> {
fn visit_type_mut(&mut self, ty: &mut syn::Type) {
loop {
match ty {
syn::Type::Group(g) => *ty = (*g.elem).clone(),
syn::Type::Paren(p) => *ty = (*p.elem).clone(),
_ => break,
}
}
if let syn::Type::Path(tp) = ty {
if tp.qself.is_none() {
reduce_flat_path(&mut tp.path, self.against);
}
}
syn::visit_mut::visit_type_mut(self, ty);
}
}
Normalizer { against }.visit_type_mut(ty);
}
pub fn normalize_item_types(item: &mut syn::Item, against: &Normalization) {
use syn::visit_mut::VisitMut;
struct ItemNormalizer<'a> {
against: &'a Normalization,
}
impl VisitMut for ItemNormalizer<'_> {
fn visit_type_mut(&mut self, ty: &mut syn::Type) {
normalize_type(ty, self.against);
}
}
ItemNormalizer { against }.visit_item_mut(item);
}
fn reduce_flat_path(path: &mut syn::Path, against: &Normalization) {
if path.segments.len() < 2 {
return;
}
if let Some(name) = against.constructor_of(path) {
let mut last = path.segments.last().expect("len checked").clone();
last.ident = syn::Ident::new(name, last.ident.span());
path.leading_colon = None;
path.segments = std::iter::once(last).collect();
return;
}
let head = path
.segments
.first()
.expect("len checked")
.ident
.to_string();
let reduce = match head.as_str() {
"crate" | "self" => true,
other => against.source_modules.iter().any(|m| m == other),
};
if reduce {
let last = path.segments.last().expect("len checked").clone();
path.leading_colon = None;
path.segments = std::iter::once(last).collect();
}
}