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
135pub 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 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}