term_rustdoc/type_name/
generic.rs

1use super::{short, typename, FindName, Short, COLON, COMMA, INFER, PLUS};
2use crate::util::{xformat, XString};
3use itertools::intersperse;
4use rustdoc_types::{
5    Constant, GenericArg, GenericArgs, GenericBound, GenericParamDef, GenericParamDefKind,
6    Generics, Term, TraitBoundModifier, TypeBinding, TypeBindingKind, WherePredicate,
7};
8
9pub fn generic_param_def_for_slice<Kind: FindName>(
10    generic_params: &[GenericParamDef],
11) -> Option<XString> {
12    if generic_params.is_empty() {
13        return None;
14    }
15    Some(XString::from_iter(intersperse(
16        generic_params.iter().map(generic_param_def::<Kind>),
17        COMMA,
18    )))
19}
20
21fn generic_param_def<Kind: FindName>(GenericParamDef { name, kind }: &GenericParamDef) -> XString {
22    let type_name = Kind::type_name();
23    match kind {
24        GenericParamDefKind::Lifetime { outlives } => {
25            if outlives.is_empty() {
26                name.as_str().into()
27            } else {
28                let outlives = outlives.iter().map(XString::from);
29                xformat!(
30                    "{name}: {}",
31                    XString::from_iter(intersperse(outlives, PLUS))
32                )
33            }
34        }
35        GenericParamDefKind::Type {
36            bounds, default, ..
37        } => {
38            let bound = generic_bound_for_slice::<Kind>(bounds);
39            let [sep, bound] = if let Some(b) = &bound {
40                [COLON, b]
41            } else {
42                [""; 2]
43            };
44            xformat!(
45                "{name}{sep}{bound}{}",
46                default
47                    .as_ref()
48                    .map(|ty| xformat!(" = {}", type_name(ty)))
49                    .unwrap_or_default()
50            )
51        }
52        GenericParamDefKind::Const { type_, default } => xformat!(
53            "const {name}: {}{}",
54            type_name(type_),
55            default
56                .as_deref()
57                .map(|s| xformat!(" = {s}"))
58                .unwrap_or_default()
59        ),
60    }
61}
62
63pub fn generic_bound_for_slice<Kind: FindName>(b: &[GenericBound]) -> Option<XString> {
64    if b.is_empty() {
65        return None;
66    }
67
68    Some(XString::from_iter(intersperse(
69        b.iter().map(generic_bound::<Kind>),
70        PLUS,
71    )))
72}
73
74fn generic_bound<Kind: FindName>(b: &GenericBound) -> XString {
75    match b {
76        GenericBound::TraitBound {
77            trait_,
78            generic_params,
79            modifier,
80        } => {
81            let path = (Kind::resolve_path())(trait_);
82            let args = generic_param_def_for_slice::<Kind>(generic_params);
83            if let Some(args) = args {
84                match modifier {
85                    TraitBoundModifier::None => xformat!("{path}<{args}>"),
86                    TraitBoundModifier::Maybe => xformat!("?{path}<{args}>"),
87                    TraitBoundModifier::MaybeConst => xformat!("~const {path}<{args}>"),
88                }
89            } else {
90                match modifier {
91                    TraitBoundModifier::None => xformat!("{path}"),
92                    TraitBoundModifier::Maybe => xformat!("?{path}"),
93                    TraitBoundModifier::MaybeConst => xformat!("~const {path}"),
94                }
95            }
96        }
97        GenericBound::Outlives(life) => XString::from(life.as_str()),
98    }
99}
100
101pub fn generic_arg_name<Kind: FindName>(arg: &GenericArg) -> XString {
102    let type_name = Kind::type_name();
103    match arg {
104        GenericArg::Lifetime(life) => life.as_str().into(),
105        GenericArg::Type(ty) => type_name(ty),
106        GenericArg::Const(c) => constant::<Kind>(c),
107        GenericArg::Infer => INFER,
108    }
109}
110
111fn generic_arg_name_for_slice<Kind: FindName>(a: &[GenericArg]) -> Option<XString> {
112    if a.is_empty() {
113        return None;
114    }
115    Some(XString::from_iter(intersperse(
116        a.iter().map(generic_arg_name::<Kind>),
117        COMMA,
118    )))
119}
120
121fn constant<Kind: FindName>(
122    Constant {
123        type_, expr, value, ..
124    }: &Constant,
125) -> XString {
126    let ty = typename::<Kind>(type_);
127    let mut res = xformat!("{ty}: {expr}");
128    if let Some(value) = value {
129        res.push_str(" = ");
130        res.push_str(value);
131    }
132    res
133}
134
135/// Generics definition and where bounds on items.
136///
137/// This use short names inside.
138pub fn generics(
139    Generics {
140        params,
141        where_predicates,
142    }: &Generics,
143) -> (Option<XString>, Option<XString>) {
144    fn where_(w: &WherePredicate) -> XString {
145        match w {
146            WherePredicate::BoundPredicate {
147                type_,
148                bounds,
149                generic_params,
150            } => {
151                let ty = short(type_);
152                let generic_bound = generic_bound_for_slice::<Short>(bounds);
153                let [sep_b, bound] = if let Some(b) = &generic_bound {
154                    [COLON, b]
155                } else {
156                    [COLON, ""]
157                };
158                let hrtb = generic_param_def_for_slice::<Short>(generic_params);
159                if let Some(hrtb) = &hrtb {
160                    xformat!("for<{hrtb}> {ty}{sep_b}{bound}")
161                } else {
162                    xformat!("{ty}{sep_b}{bound}")
163                }
164            }
165            WherePredicate::RegionPredicate { lifetime, bounds } => {
166                let generic_bound = generic_bound_for_slice::<Short>(bounds);
167                let [sep, bound] = if let Some(b) = &generic_bound {
168                    [COLON, b.as_str()]
169                } else {
170                    ["", ""]
171                };
172                xformat!("{lifetime}{sep}{bound}")
173            }
174            WherePredicate::EqPredicate { lhs, rhs } => {
175                let ty = short(lhs);
176                xformat!("{ty} = {}", term(rhs))
177            }
178        }
179    }
180    fn where_for_slice(w: &[WherePredicate]) -> Option<XString> {
181        if w.is_empty() {
182            return None;
183        }
184        Some(XString::from_iter(intersperse(
185            w.iter().map(|w| xformat!("    {}", where_(w))),
186            XString::new_inline(",\n"),
187        )))
188    }
189
190    (
191        generic_param_def_for_slice::<Short>(params),
192        where_for_slice(where_predicates),
193    )
194}
195
196fn term(rhs: &Term) -> XString {
197    match rhs {
198        Term::Type(t) => short(t),
199        Term::Constant(c) => constant::<Short>(c),
200    }
201}
202
203pub fn generic_args<Kind: FindName>(g: &GenericArgs) -> Option<XString> {
204    match g {
205        GenericArgs::AngleBracketed { args, bindings } => {
206            // FIXME: the logics here is unclear; need more usecases.
207            let binding = type_binding_for_slice::<Kind>(bindings);
208            let arg = generic_arg_name_for_slice::<Kind>(args);
209            Some(match (&arg, &binding) {
210                (None, None) => return None,
211                (Some(arg), None) => xformat!("<{arg}>"),
212                (None, Some(binding)) => xformat!("<{binding}>"),
213                (Some(arg), Some(binding)) => xformat!("<{arg}: {binding}>"),
214            })
215        }
216        GenericArgs::Parenthesized { inputs, output } => {
217            let type_name = Kind::type_name();
218            #[allow(clippy::redundant_closure)]
219            let args: XString = intersperse(inputs.iter().map(|x| type_name(x)), COMMA).collect();
220            let ret = output
221                .as_ref()
222                .map(|t| xformat!(" -> {}", type_name(t)))
223                .unwrap_or_default();
224            Some(xformat!("({args}){ret}"))
225        }
226    }
227}
228
229fn type_binding<Kind: FindName>(
230    TypeBinding {
231        name,
232        args,
233        binding,
234    }: &TypeBinding,
235) -> XString {
236    let arg = generic_args::<Kind>(args);
237    let arg = arg.as_deref().unwrap_or("");
238    match binding {
239        TypeBindingKind::Equality(t) => xformat!("{name}{arg} = {}", term(t)),
240        TypeBindingKind::Constraint(b) => {
241            let bound = generic_bound_for_slice::<Kind>(b);
242            let bound = bound.as_deref().unwrap_or("");
243            xformat!("{name}{arg}: {bound}")
244        }
245    }
246}
247
248fn type_binding_for_slice<Kind: FindName>(b: &[TypeBinding]) -> Option<XString> {
249    if b.is_empty() {
250        return None;
251    }
252    Some(XString::from_iter(intersperse(
253        b.iter().map(type_binding::<Kind>),
254        COMMA,
255    )))
256}