use std::{collections::HashSet, rc::Rc};
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use syn::{Path, Type};
pub struct Dependencies {
crate_rename: Rc<Path>,
dependencies: HashSet<Dependency>,
types: HashSet<Rc<Type>>,
}
#[derive(Hash, Eq, PartialEq)]
enum Dependency {
Transitive {
crate_rename: Rc<Path>,
ty: Rc<Type>,
},
Generics {
crate_rename: Rc<Path>,
ty: Rc<Type>,
},
Type(Rc<Type>),
}
impl Dependencies {
pub fn new(crate_rename: Path) -> Self {
Self {
dependencies: HashSet::new(),
crate_rename: Rc::new(crate_rename),
types: HashSet::new(),
}
}
pub fn used_types(&self) -> impl Iterator<Item = &Type> {
self.types.iter().map(Rc::as_ref)
}
pub fn append_from(&mut self, ty: &Type) {
let ty = self.push_type(ty);
self.dependencies.insert(Dependency::Transitive {
crate_rename: self.crate_rename.clone(),
ty: ty.clone(),
});
}
pub fn push(&mut self, ty: &Type) {
let ty = self.push_type(ty);
self.dependencies.insert(Dependency::Type(ty.clone()));
self.dependencies.insert(Dependency::Generics {
crate_rename: self.crate_rename.clone(),
ty: ty.clone(),
});
}
pub fn append(&mut self, other: Dependencies) {
self.dependencies.extend(other.dependencies);
self.types.extend(other.types);
}
fn push_type(&mut self, ty: &Type) -> Rc<Type> {
match self.types.get(ty) {
None => {
let ty = Rc::new(ty.clone());
self.types.insert(ty.clone());
ty
}
Some(ty) => ty.clone(),
}
}
}
impl ToTokens for Dependencies {
fn to_tokens(&self, tokens: &mut TokenStream) {
let lines = self.dependencies.iter();
tokens.extend(quote![
#(#lines;)*
]);
}
}
impl ToTokens for Dependency {
fn to_tokens(&self, tokens: &mut TokenStream) {
tokens.extend(match self {
Dependency::Transitive { crate_rename, ty } => {
quote![<#ty as #crate_rename::TS>::visit_dependencies(v)]
}
Dependency::Generics { crate_rename, ty } => {
quote![<#ty as #crate_rename::TS>::visit_generics(v)]
}
Dependency::Type(ty) => quote![v.visit::<#ty>()],
});
}
}