use std::fmt::Debug;
use quote::ToTokens;
use serde::ser::SerializeMap;
use serde::{Serialize, Serializer};
use syn::punctuated::Punctuated;
use syn::{
Abi,
AngleBracketedGenericArguments,
BoundLifetimes,
Expr,
ExprLit,
GenericArgument,
GenericParam,
Generics,
LifetimeParam,
ParenthesizedGenericArguments,
Pat,
PatType,
Path,
PathArguments,
PathSegment,
ReturnType,
Token,
TraitBoundModifier,
Type,
TypeBareFn,
TypeParam,
TypeParamBound,
TypePath,
WhereClause,
WherePredicate,
};
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) enum Node {
Name(String),
Link { value: String, target: String },
Keyword(&'static str),
Punctuation(&'static str),
Space,
Newline,
Indent,
Operator(&'static str),
Returns,
Literal(String),
Lifetime(String),
}
impl Serialize for Node {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Node::Name(n) => {
let mut map = serializer.serialize_map(Some(2))?;
map.serialize_entry("type", "name")?;
map.serialize_entry("value", n)?;
map.end()
}
Node::Link {
value,
target,
} => {
let mut map = serializer.serialize_map(Some(3))?;
map.serialize_entry("type", "link")?;
map.serialize_entry("value", value)?;
map.serialize_entry("target", target)?;
map.end()
}
Node::Keyword(k) => {
let mut map = serializer.serialize_map(Some(2))?;
map.serialize_entry("type", "keyword")?;
map.serialize_entry("value", k)?;
map.end()
}
Node::Punctuation(p) => {
let mut map = serializer.serialize_map(Some(2))?;
map.serialize_entry("type", "punctuation")?;
map.serialize_entry("value", p)?;
map.end()
}
Node::Space => {
let mut map = serializer.serialize_map(Some(1))?;
map.serialize_entry("type", "space")?;
map.end()
}
Node::Newline => {
let mut map = serializer.serialize_map(Some(1))?;
map.serialize_entry("type", "newline")?;
map.end()
}
Node::Indent => {
let mut map = serializer.serialize_map(Some(1))?;
map.serialize_entry("type", "indent")?;
map.end()
}
Node::Operator(o) => {
let mut map = serializer.serialize_map(Some(2))?;
map.serialize_entry("type", "operator")?;
map.serialize_entry("value", o)?;
map.end()
}
Node::Returns => {
let mut map = serializer.serialize_map(Some(1))?;
map.serialize_entry("type", "returns")?;
map.end()
}
Node::Literal(l) => {
let mut map = serializer.serialize_map(Some(2))?;
map.serialize_entry("type", "literal")?;
map.serialize_entry("value", l)?;
map.end()
}
Node::Lifetime(lt) => {
let mut map = serializer.serialize_map(Some(2))?;
map.serialize_entry("type", "lifetime")?;
map.serialize_entry("value", lt)?;
map.end()
}
}
}
}
fn type_param_bound_name(type_param_bound: &TypeParamBound) -> String {
match type_param_bound {
TypeParamBound::Trait(t) => t.path.segments.last().map(|s| s.ident.to_string()).unwrap(),
TypeParamBound::Lifetime(l) => l.to_string(),
TypeParamBound::Verbatim(_) => panic!("Cannot determine name for verbatim type"),
x => {
panic!("Unsupported bound type {:?}", x)
}
}
}
fn type_path_name(type_path: &TypePath) -> String {
type_path
.path
.segments
.iter()
.map(|s| s.ident.to_string())
.collect::<Vec<String>>()
.join("::")
}
pub(crate) fn type_name(ty: &Type) -> String {
match ty {
Type::Array(a) => format!("[{}; ?]", type_name(&a.elem)),
Type::BareFn(f) => format!(
"fn ({}){}",
f.inputs
.iter()
.map(|i| type_name(&i.ty))
.collect::<Vec<String>>()
.join(", "),
match &f.output {
ReturnType::Type(_, r_ty) => {
format!(" -> {}", type_name(r_ty))
}
_ => String::new(),
}
),
Type::Group(g) => type_name(&g.elem),
Type::ImplTrait(t) => format!(
"impl {}",
t.bounds
.iter()
.map(type_param_bound_name)
.collect::<Vec<String>>()
.join(" + ")
),
Type::Infer(_) | Type::Macro(_) => "_".into(),
Type::Never(_) => "!".into(),
Type::Paren(p) => format!("({})", type_name(&p.elem)),
Type::Path(p) => type_path_name(p),
Type::Ptr(p) => format!("*{}", type_name(&p.elem)),
Type::Reference(r) => format!("&{}", type_name(&r.elem)),
Type::Slice(s) => format!("[{}]", type_name(&s.elem)),
Type::TraitObject(t) => format!(
"dyn {}",
t.bounds
.iter()
.map(type_param_bound_name)
.collect::<Vec<String>>()
.join(" + ")
),
Type::Tuple(t) => format!(
"({})",
t.elems
.iter()
.map(type_name)
.collect::<Vec<String>>()
.join(", ")
),
Type::Verbatim(_) => panic!("Cannot determine name for verbatim type"),
x => panic!("Unsupported type category {:?}", x),
}
}
fn nodes_for_expr_lit(expr_lit: &ExprLit) -> Vec<Node> {
vec![Node::Literal(expr_lit.lit.to_token_stream().to_string())]
}
fn nodes_for_generic_argument(generic_arg: &GenericArgument) -> Vec<Node> {
let mut nodes = vec![];
match generic_arg {
GenericArgument::Lifetime(lt) => nodes.push(Node::Lifetime(lt.to_string())),
GenericArgument::Type(t) => nodes.extend(nodes_for_type(t)),
GenericArgument::Const(_) => {
nodes.push(Node::Keyword("<const arg>"));
}
GenericArgument::AssocType(at) => {
nodes.push(Node::Name(at.ident.to_string()));
if let Some(ab) = &at.generics {
nodes.extend(nodes_for_angle_bracket_generic_args(ab));
}
nodes.push(Node::Punctuation(" = "));
nodes.extend(nodes_for_type(&at.ty));
}
GenericArgument::AssocConst(ac) => {
nodes.push(Node::Name(ac.ident.to_string()));
if let Some(ab) = &ac.generics {
nodes.extend(nodes_for_angle_bracket_generic_args(ab));
}
nodes.push(Node::Punctuation(" = "));
nodes.push(Node::Keyword("<default>"));
}
GenericArgument::Constraint(c) => {
nodes.push(Node::Name(c.ident.to_string()));
if let Some(ab) = &c.generics {
nodes.extend(nodes_for_angle_bracket_generic_args(ab));
}
nodes.push(Node::Punctuation(": "));
nodes.extend(nodes_for_type_param_bounds(&c.bounds));
}
x => panic!("Unknown generic argument type {:?}", x),
}
nodes
}
fn nodes_for_angle_bracket_generic_args(angled_args: &AngleBracketedGenericArguments) -> Vec<Node> {
let mut nodes = vec![];
if angled_args.colon2_token.is_some() {
nodes.push(Node::Punctuation("::"));
}
nodes.push(Node::Punctuation("<"));
for arg in &angled_args.args {
nodes.extend(nodes_for_generic_argument(arg));
nodes.push(Node::Punctuation(", "));
}
nodes.pop();
nodes.push(Node::Punctuation(">"));
nodes
}
fn nodes_for_parenthesized_generic_args(paren_args: &ParenthesizedGenericArguments) -> Vec<Node> {
let mut nodes = vec![Node::Punctuation("(")];
for ty in &paren_args.inputs {
nodes.extend(nodes_for_type(ty));
nodes.push(Node::Punctuation(", "));
}
nodes.pop();
nodes.push(Node::Punctuation(")"));
nodes.extend(nodes_for_return_type(&paren_args.output));
nodes
}
fn nodes_for_path_segment(segment: &PathSegment, make_link: bool) -> Vec<Node> {
let mut nodes = if make_link {
vec![Node::Link {
value: segment.ident.to_string(),
target: segment.ident.to_string(),
}]
}
else {
vec![Node::Name(segment.ident.to_string())]
};
match &segment.arguments {
PathArguments::None => {}
PathArguments::AngleBracketed(ab) => {
nodes.extend(nodes_for_angle_bracket_generic_args(ab));
}
PathArguments::Parenthesized(p) => {
nodes.extend(nodes_for_parenthesized_generic_args(p));
}
}
nodes
}
pub(crate) fn nodes_for_path(path: &Path) -> Vec<Node> {
let mut nodes = vec![];
if path.leading_colon.is_some() {
nodes.push(Node::Punctuation("::"));
}
for (idx, segment) in path.segments.iter().enumerate() {
nodes.extend(nodes_for_path_segment(segment, idx == 0));
nodes.push(Node::Punctuation("::"));
}
nodes.pop();
nodes
}
pub(crate) fn nodes_for_abi_opt(abi: &Option<Abi>) -> Vec<Node> {
abi.as_ref()
.map(|abi| {
let mut nodes = vec![Node::Keyword("extern"), Node::Space];
if let Some(s) = &abi.name {
nodes.extend_from_slice(&[Node::Literal(s.value()), Node::Space]);
}
nodes
})
.unwrap_or_default()
}
pub(crate) fn nodes_for_type(ty: &Type) -> Vec<Node> {
let mut nodes = vec![];
match ty {
Type::Array(a) => {
nodes.push(Node::Punctuation("["));
nodes.extend(nodes_for_type(&a.elem));
nodes.push(Node::Punctuation("; "));
if let Expr::Lit(l) = &a.len {
nodes.extend(nodes_for_expr_lit(l));
}
else {
nodes.push(Node::Punctuation("?"))
}
nodes.push(Node::Punctuation("]"));
}
Type::BareFn(bf) => {
nodes.extend(nodes_for_bare_fn(bf));
}
Type::Group(_) => {} Type::ImplTrait(i) => {
nodes.push(Node::Keyword("impl"));
nodes.push(Node::Space);
nodes.extend(nodes_for_type_param_bounds(&i.bounds));
}
Type::Infer(_) => {
nodes.push(Node::Punctuation("_"));
}
Type::Macro(_) => {
nodes.push(Node::Punctuation("?"));
}
Type::Never(_) => {
nodes.push(Node::Punctuation("!"));
}
Type::Paren(p) => {
nodes.push(Node::Punctuation("("));
nodes.extend(nodes_for_type(&p.elem));
nodes.push(Node::Punctuation(")"));
}
Type::Path(p) => {
nodes.extend(nodes_for_path(&p.path));
}
Type::Ptr(p) => {
nodes.push(Node::Operator("*"));
if p.const_token.is_some() {
nodes.push(Node::Keyword("const"));
nodes.push(Node::Space);
}
if p.mutability.is_some() {
nodes.push(Node::Keyword("mut"));
nodes.push(Node::Space);
}
nodes.extend(nodes_for_type(&p.elem));
}
Type::Reference(r) => {
nodes.push(Node::Punctuation("&"));
if let Some(lt) = &r.lifetime {
nodes.push(Node::Lifetime(lt.to_string()));
nodes.push(Node::Space);
}
if r.mutability.is_some() {
nodes.push(Node::Keyword("mut"));
nodes.push(Node::Space);
}
nodes.extend(nodes_for_type(&r.elem));
}
Type::Slice(s) => {
nodes.push(Node::Punctuation("["));
nodes.extend(nodes_for_type(&s.elem));
nodes.push(Node::Punctuation("]"));
}
Type::TraitObject(t) => {
nodes.push(Node::Keyword("dyn"));
nodes.push(Node::Space);
nodes.extend(nodes_for_type_param_bounds(&t.bounds));
}
Type::Tuple(t) => {
nodes.push(Node::Punctuation("("));
for elem in &t.elems {
nodes.extend(nodes_for_type(elem));
nodes.push(Node::Punctuation(", "));
}
if !t.elems.is_empty() {
nodes.pop();
}
nodes.push(Node::Punctuation(")"));
}
Type::Verbatim(_) => {}
x => panic!("Unsupported type category {:?}", x),
}
nodes
}
fn nodes_for_lifetime_param(lifetime_param: &LifetimeParam) -> Vec<Node> {
let mut nodes = vec![Node::Lifetime(lifetime_param.lifetime.to_string())];
if lifetime_param.colon_token.is_some() {
nodes.push(Node::Punctuation(": "));
for bound in &lifetime_param.bounds {
nodes.push(Node::Lifetime(bound.to_string()));
nodes.push(Node::Punctuation(" + "));
}
nodes.pop();
}
nodes
}
fn nodes_for_bound_lifetimes(bound_lts: &BoundLifetimes) -> Vec<Node> {
let mut nodes = vec![];
nodes.extend_from_slice(&[Node::Keyword("for"), Node::Punctuation("<")]);
for lt in &bound_lts.lifetimes {
nodes.extend(nodes_for_generic_param(lt));
nodes.push(Node::Punctuation(", "));
}
nodes.pop();
nodes.extend_from_slice(&[Node::Punctuation(">"), Node::Space]);
nodes
}
fn nodes_for_type_param_bound(type_param_bound: &TypeParamBound) -> Vec<Node> {
let mut nodes = vec![];
match type_param_bound {
TypeParamBound::Trait(t) => {
if t.paren_token.is_some() {
nodes.push(Node::Punctuation("("));
}
if matches!(t.modifier, TraitBoundModifier::Maybe(_)) {
nodes.push(Node::Punctuation("?"));
}
if let Some(blt) = &t.lifetimes {
nodes.extend(nodes_for_bound_lifetimes(blt));
}
nodes.extend(nodes_for_path(&t.path));
if t.paren_token.is_some() {
nodes.push(Node::Punctuation(")"));
}
}
TypeParamBound::Lifetime(lt) => {
nodes.push(Node::Lifetime(lt.to_string()));
}
TypeParamBound::Verbatim(_) => {}
x => {
panic!("Unsupported bound type {:?}", x)
}
}
nodes
}
fn nodes_for_type_param_bounds(
type_param_bounds: &Punctuated<TypeParamBound, Token![+]>,
) -> Vec<Node> {
let mut nodes = vec![];
for bound in type_param_bounds {
nodes.extend(nodes_for_type_param_bound(bound));
nodes.push(Node::Punctuation(" + "))
}
nodes.pop();
nodes
}
fn nodes_for_type_param(type_param: &TypeParam) -> Vec<Node> {
let mut nodes = vec![Node::Name(type_param.ident.to_string())];
if type_param.colon_token.is_some() {
nodes.push(Node::Punctuation(": "));
nodes.extend(nodes_for_type_param_bounds(&type_param.bounds));
}
nodes
}
fn nodes_for_generic_param(generic_param: &GenericParam) -> Vec<Node> {
match generic_param {
GenericParam::Lifetime(lt) => nodes_for_lifetime_param(lt),
GenericParam::Type(ty) => nodes_for_type_param(ty),
GenericParam::Const(c) => {
let mut nodes = vec![
Node::Keyword("const"),
Node::Space,
Node::Name(c.ident.to_string()),
Node::Punctuation(": "),
];
nodes.extend(nodes_for_type(&c.ty));
nodes
}
}
}
pub(crate) fn nodes_for_generics(generics: &Generics) -> Vec<Node> {
if generics.params.is_empty() {
return vec![];
}
let mut nodes = vec![Node::Punctuation("<")];
for generic in &generics.params {
nodes.extend(nodes_for_generic_param(generic));
nodes.push(Node::Punctuation(", "));
}
nodes.pop();
nodes.push(Node::Punctuation(">"));
nodes
}
pub(crate) fn nodes_for_where_clause(where_clause: &WhereClause) -> Vec<Node> {
let mut nodes = vec![Node::Newline, Node::Keyword("where")];
for predicate in &where_clause.predicates {
nodes.extend_from_slice(&[Node::Newline, Node::Indent]);
match predicate {
WherePredicate::Lifetime(lt) => {
nodes.extend_from_slice(&[
Node::Lifetime(lt.lifetime.to_string()),
Node::Punctuation(": "),
]);
for bound in <.bounds {
nodes.extend_from_slice(&[
Node::Lifetime(bound.to_string()),
Node::Punctuation(" + "),
]);
}
nodes.pop();
}
WherePredicate::Type(ty) => {
if let Some(blt) = &ty.lifetimes {
nodes.extend(nodes_for_bound_lifetimes(blt));
}
nodes.extend(nodes_for_type(&ty.bounded_ty));
nodes.push(Node::Punctuation(": "));
nodes.extend(nodes_for_type_param_bounds(&ty.bounds));
}
x => {
panic!("Unknown where predicate type {:?}", x)
}
}
nodes.push(Node::Punctuation(","))
}
nodes.pop();
nodes
}
pub(crate) fn nodes_for_pat_type(pat_type: &PatType) -> Vec<Node> {
let mut nodes = vec![];
nodes.extend(nodes_for_pat(&pat_type.pat));
nodes.push(Node::Punctuation(": "));
nodes.extend(nodes_for_type(&pat_type.ty));
nodes
}
fn nodes_for_pat(pat: &Pat) -> Vec<Node> {
let mut nodes = vec![];
match pat {
Pat::Const(_) => {
nodes.push(Node::Keyword("<const block>"));
}
Pat::Ident(i) => {
if i.by_ref.is_some() {
nodes.push(Node::Keyword("ref"));
nodes.push(Node::Space);
}
if i.mutability.is_some() {
nodes.push(Node::Keyword("mut"));
nodes.push(Node::Space);
}
nodes.push(Node::Name(i.ident.to_string()));
}
Pat::Lit(l) => nodes.extend(nodes_for_expr_lit(l)),
Pat::Macro(_) => nodes.push(Node::Literal("macro".into())),
Pat::Or(o) => {
if o.leading_vert.is_some() {
nodes.push(Node::Operator("| "));
}
for case in &o.cases {
nodes.extend(nodes_for_pat(case));
nodes.push(Node::Operator(" | "));
}
nodes.pop();
}
Pat::Paren(p) => {
nodes.push(Node::Punctuation("("));
nodes.extend(nodes_for_pat(&p.pat));
nodes.push(Node::Punctuation(")"));
}
Pat::Path(p) => {
nodes.extend(nodes_for_path(&p.path))
}
Pat::Range(_) => {
nodes.push(Node::Literal("range".into()));
}
Pat::Reference(r) => {
nodes.push(Node::Punctuation("&"));
if r.mutability.is_some() {
nodes.push(Node::Keyword("mut"));
}
nodes.push(Node::Space);
nodes.extend(nodes_for_pat(&r.pat))
}
Pat::Rest(_) => {
nodes.push(Node::Operator(".."));
}
Pat::Slice(s) => {
nodes.push(Node::Punctuation("["));
for pat in &s.elems {
nodes.extend(nodes_for_pat(pat));
nodes.push(Node::Punctuation(", "));
}
nodes.pop();
nodes.push(Node::Punctuation("]"));
}
Pat::Struct(s) => {
nodes.extend(nodes_for_path(&s.path));
nodes.push(Node::Space);
nodes.push(Node::Punctuation("{..}"));
}
Pat::Tuple(t) => {
nodes.push(Node::Punctuation("("));
for pat in &t.elems {
nodes.extend(nodes_for_pat(pat));
nodes.push(Node::Punctuation(", "));
}
nodes.pop();
nodes.push(Node::Punctuation(")"));
}
Pat::TupleStruct(ts) => {
nodes.extend(nodes_for_path(&ts.path));
nodes.push(Node::Punctuation("("));
for pat in &ts.elems {
nodes.extend(nodes_for_pat(pat));
nodes.push(Node::Punctuation(", "));
}
nodes.pop();
nodes.push(Node::Punctuation(")"));
}
Pat::Type(t) => {
nodes.extend(nodes_for_pat_type(t));
}
Pat::Verbatim(_) => {}
Pat::Wild(_) => {
nodes.push(Node::Punctuation("_"));
}
x => panic!("Unsupported pattern type {:?} in fn inputs", x),
}
nodes
}
pub(crate) fn nodes_for_return_type(return_type: &ReturnType) -> Vec<Node> {
let mut nodes = vec![];
if let ReturnType::Type(_, ty) = return_type {
nodes.extend_from_slice(&[Node::Space, Node::Returns, Node::Space]);
nodes.extend(nodes_for_type(ty));
}
nodes
}
pub(crate) fn nodes_for_bare_fn(bare_fn: &TypeBareFn) -> Vec<Node> {
let mut nodes = Vec::new();
if bare_fn.unsafety.is_some() {
nodes.push(Node::Keyword("unsafe"));
nodes.push(Node::Space);
}
nodes.extend(nodes_for_abi_opt(&bare_fn.abi));
nodes.push(Node::Keyword("fn"));
nodes.push(Node::Space);
nodes.push(Node::Punctuation("("));
for arg in &bare_fn.inputs {
nodes.extend(nodes_for_type(&arg.ty));
nodes.push(Node::Punctuation(", "));
}
if bare_fn.variadic.is_some() {
nodes.push(Node::Punctuation("..."));
}
else if !bare_fn.inputs.is_empty() {
nodes.pop();
}
nodes.push(Node::Punctuation(")"));
nodes.extend(nodes_for_return_type(&bare_fn.output));
nodes
}