term_rustdoc/type_name/
mod.rs

1use crate::util::{xformat, XString};
2use itertools::intersperse;
3use rustdoc_types::{DynTrait, Path, PolyTrait, Type};
4use std::fmt::Write;
5
6mod short_or_long;
7pub use short_or_long::{long_path, short_path};
8
9mod generic;
10pub use generic::generics;
11
12mod funcion;
13pub(crate) use funcion::{fn_decl, fn_header};
14
15trait TypeName: Copy + FnOnce(&Type) -> XString {}
16impl<F> TypeName for F where F: Copy + FnOnce(&Type) -> XString {}
17trait ResolvePath: Copy + FnOnce(&Path) -> XString {}
18impl<F> ResolvePath for F where F: Copy + FnOnce(&Path) -> XString {}
19
20trait FindName {
21    fn type_name() -> impl TypeName;
22    fn resolve_path() -> impl ResolvePath;
23    // fn type_and_path() -> (impl TypeName, impl ResolvePath) {
24    //     (Self::type_name(), Self::resolve_path())
25    // }
26}
27
28struct Short;
29
30impl FindName for Short {
31    fn type_name() -> impl TypeName {
32        short
33    }
34    fn resolve_path() -> impl ResolvePath {
35        short_path
36    }
37}
38
39struct Long;
40
41impl FindName for Long {
42    fn type_name() -> impl TypeName {
43        long
44    }
45    fn resolve_path() -> impl ResolvePath {
46        long_path
47    }
48}
49
50const COMMA: XString = XString::new_inline(", ");
51const PLUS: XString = XString::new_inline(" + ");
52const INFER: XString = XString::new_inline("_");
53const COLON: &str = ": ";
54
55fn typename<Kind: FindName>(ty: &Type) -> XString {
56    let resolve_path = Kind::resolve_path();
57    match ty {
58        Type::ResolvedPath(p) => resolve_path(p),
59        Type::Generic(t) | Type::Primitive(t) => t.as_str().into(),
60        Type::BorrowedRef {
61            lifetime,
62            mutable,
63            type_,
64        } => borrow_ref::<Kind>(type_, lifetime, mutable),
65        Type::RawPointer { mutable, type_ } => xformat!(
66            "*{} {}",
67            if *mutable { "mut" } else { "const" },
68            typename::<Kind>(type_)
69        ),
70        Type::QualifiedPath {
71            name,
72            args,
73            self_type,
74            trait_,
75        } => {
76            let self_ = typename::<Kind>(self_type);
77            if let Some(trait_) = trait_.as_ref().map(resolve_path).filter(|s| !s.is_empty()) {
78                if let Some(args) = generic::generic_args::<Kind>(args) {
79                    xformat!("<{self_} as {trait_}>::{name}{args}")
80                } else {
81                    xformat!("<{self_} as {trait_}>::{name}")
82                }
83            } else if let Some(args) = generic::generic_args::<Kind>(args) {
84                xformat!("{self_}::{name}{args}")
85            } else {
86                xformat!("{self_}::{name}")
87            }
88        }
89        Type::Slice(ty) => typename::<Kind>(ty),
90        Type::DynTrait(poly) => dyn_trait::<Kind>(poly),
91        Type::ImplTrait(b) => xformat!(
92            "impl {}",
93            generic::generic_bound_for_slice::<Kind>(b).unwrap_or_default()
94        ),
95        Type::Tuple(v) => {
96            let iter = v.iter().map(|ty| typename::<Kind>(ty));
97            let ty = XString::from_iter(intersperse(iter, COMMA));
98            xformat!("({ty})")
99        }
100        Type::Array { type_, len } => {
101            let ty = typename::<Kind>(type_);
102            xformat!("[{ty}; {len}]")
103        }
104        Type::FunctionPointer(f) => funcion::fn_pointer(f),
105        Type::Infer => INFER,
106    }
107}
108
109fn borrow_ref<Kind: FindName>(type_: &Type, lifetime: &Option<String>, mutable: &bool) -> XString {
110    let mut buf = match (lifetime, mutable) {
111        (None, false) => xformat!("&"),
112        (None, true) => xformat!("&mut "),
113        (Some(life), false) => xformat!("&{life} "),
114        (Some(life), true) => xformat!("&{life} mut "),
115    };
116    if let Type::DynTrait(d) = type_ {
117        let (ty, add) = parenthesized_type::<Kind>(d);
118        if add {
119            write!(buf, "({ty})").unwrap();
120        } else {
121            buf.push_str(&ty);
122        }
123    } else {
124        buf.push_str(&typename::<Kind>(type_));
125    }
126    buf
127}
128
129/// Format a [`rustdoc_types::Type`] with long Path inside.
130pub fn long(ty: &Type) -> XString {
131    typename::<Long>(ty)
132}
133
134/// Format a [`rustdoc_types::Type`] with short Path inside.
135pub fn short(ty: &Type) -> XString {
136    typename::<Short>(ty)
137}
138
139fn dyn_trait<Kind: FindName>(DynTrait { traits, lifetime }: &DynTrait) -> XString {
140    let resolve_path = Kind::resolve_path();
141    let iter = traits.iter().map(
142        |PolyTrait {
143             trait_,
144             generic_params,
145         }| {
146            let hrtb = generic::generic_param_def_for_slice::<Kind>(generic_params);
147            let [sep, hrtb] = if let Some(b) = &hrtb {
148                [" ", b]
149            } else {
150                [""; 2]
151            };
152            let ty = resolve_path(trait_);
153            xformat!("{hrtb}{sep}{ty}")
154        },
155    );
156    let path = intersperse(iter, PLUS).collect::<XString>();
157    lifetime.as_deref().map_or_else(
158        || xformat!("dyn {path}"),
159        |life| xformat!("dyn {life} + {path}"),
160    )
161}
162
163/// Ref: <https://doc.rust-lang.org/reference/types.html#parenthesized-types>
164///
165/// dyn multi-Traits behind a reference or raw pointer type needs `()` disambiguation.
166///
167/// bool means whether the XString should be added `()`.
168fn parenthesized_type<Kind: FindName>(d: &DynTrait) -> (XString, bool) {
169    let s = dyn_trait::<Kind>(d);
170    (s, d.traits.len() + d.lifetime.is_some() as usize > 1)
171}