use std::{cell::RefCell, fmt, rc::Rc, string::ToString};
#[derive(Debug)]
pub enum Scope {
Attribute(String),
Const(String),
Documentation,
Fn(String),
Macro(String),
Static(String),
StructImpl(String),
Trait(String),
TraitImpl { trait_: String, type_: String },
Unexpected,
}
impl Default for Scope {
fn default() -> Self {
Scope::Unexpected
}
}
impl From<&syn::Item> for Scope {
fn from(node: &syn::Item) -> Self {
match node {
syn::Item::Const(item) => Scope::Const(item.ident.to_string()),
syn::Item::Fn(fn_) => Scope::Fn(fn_.sig.ident.to_string()),
syn::Item::Impl(impl_) => {
let type_ident = format_type_name(&impl_.self_ty);
if let Some((_, trait_path, _)) = &impl_.trait_ {
let trait_ident = path_ident(&trait_path);
Scope::TraitImpl {
trait_: trait_ident,
type_: type_ident,
}
} else {
Scope::StructImpl(type_ident)
}
}
syn::Item::Macro(macro_) => Scope::Macro(
macro_
.ident
.as_ref()
.map(|ident| ident.to_string())
.unwrap_or_else(|| "unnamed".to_string()),
),
syn::Item::Macro2(macro2) => Scope::Macro(macro2.ident.to_string()),
syn::Item::Static(static_) => Scope::Static(static_.ident.to_string()),
syn::Item::Trait(trait_) => Scope::Trait(trait_.ident.to_string()),
_ => Scope::Unexpected,
}
}
}
impl From<&syn::Attribute> for Scope {
fn from(node: &syn::Attribute) -> Self {
Scope::Attribute(path_ident(&node.path))
}
}
impl From<&syn::Macro> for Scope {
fn from(node: &syn::Macro) -> Self {
Scope::Macro(path_ident(&node.path))
}
}
impl From<Scope> for Rc<RefCell<Scope>> {
fn from(scope: Scope) -> Self {
Rc::new(RefCell::new(scope))
}
}
impl fmt::Display for Scope {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
use Scope::*;
match self {
Attribute(name) => write!(f, "attr {}", name),
Const(name) => write!(f, "const {}", name),
Documentation => f.write_str("doc code"),
Fn(name) => write!(f, "fn {}", name),
Macro(name) => write!(f, "macro! {}", name),
Static(name) => write!(f, "static {}", name),
StructImpl(struct_) => f.write_str(struct_),
Trait(trait_) => f.write_str(trait_),
TraitImpl { trait_, type_ } => write!(f, "impl {} for {}", trait_, type_),
Unexpected => f.write_str("**Unexpected scope**"),
}
}
}
fn path_ident(path: &syn::Path) -> String {
if path.segments.is_empty() {
return String::default();
}
path.segments.last().unwrap().ident.to_string()
}
fn format_type_name(self_ty: &syn::Type) -> String {
match self_ty {
syn::Type::Path(path) => path_ident(&path.path),
syn::Type::Reference(ref_) => {
let prefix = match &ref_.lifetime {
None => if ref_.mutability.is_some() {
"&mut "
} else {
"&"
}
.to_string(),
Some(lifetime) => {
if ref_.mutability.is_some() {
format!("&{} mut ", lifetime.to_string())
} else {
format!("&{} ", lifetime.to_string())
}
}
};
format!("{}{}", prefix, format_type_name(&ref_.elem))
}
syn::Type::Slice(slice) => format!("[{}]", format_type_name(&slice.elem)),
syn::Type::TraitObject(trait_obj) => {
let mut trait_bound = "dyn ".to_string();
for (idx, bound) in trait_obj.bounds.iter().enumerate() {
if idx > 0 {
trait_bound += " + ";
}
if let syn::TypeParamBound::Trait(trait_) = bound {
trait_bound += &path_ident(&trait_.path);
}
}
trait_bound
}
syn::Type::Tuple(tuple) => {
let mut tuple_str = "(".to_string();
for (idx, elem) in tuple.elems.iter().enumerate() {
if idx > 0 {
tuple_str += ", ";
}
tuple_str += &format_type_name(elem);
}
tuple_str + ")"
}
syn::Type::Paren(paren) => {
format!("({})", format_type_name(&paren.elem))
}
syn::Type::Ptr(ptr) => {
format!("*{}", format_type_name(&ptr.elem))
}
_ => unimplemented!("format type formatting for {:#?}", self_ty),
}
}