cargo_wiki/generators/
generic_gen.rs

1use crate::generators::type_gen::TypeGenerator;
2use anyhow::Result;
3use rustdoc_types::{
4    GenericArg, GenericArgs, GenericBound, GenericParamDef, GenericParamDefKind, Generics, Term,
5    TraitBoundModifier, WherePredicate,
6};
7
8pub struct GenericGenerator;
9
10impl GenericGenerator {
11    pub fn generate_generics(generics: &Generics) -> Result<(String, String)> {
12        let params = Self::generate_generic_params(&generics.params)?;
13        let mut where_predicates = String::new();
14
15        if generics.where_predicates.len() > 0 {
16            where_predicates.push_str("\nwhere\n\t");
17
18            for (i, where_pred) in generics.where_predicates.iter().enumerate() {
19                if i != 0 {
20                    where_predicates.push_str(",\n\t");
21                }
22
23                match where_pred {
24                    WherePredicate::BoundPredicate {
25                        type_,
26                        bounds,
27                        generic_params,
28                    } => {
29                        let generic_params = Self::generate_generic_params(generic_params)?;
30
31                        if !generic_params.is_empty() {
32                            where_predicates.push_str("for");
33                            where_predicates.push_str(&generic_params);
34                            where_predicates.push_str(" ");
35                        }
36                        where_predicates.push_str(&TypeGenerator::type_to_string(type_));
37
38                        if bounds.len() > 0 {
39                            where_predicates.push_str(": ");
40
41                            for (i, bound) in bounds.iter().enumerate() {
42                                if i != 0 {
43                                    where_predicates.push_str(" + ");
44                                }
45
46                                where_predicates.push_str(&Self::generate_generic_bounds(bound)?)
47                            }
48                        }
49                    }
50                    WherePredicate::LifetimePredicate { lifetime, outlives } => {
51                        where_predicates.push_str(lifetime);
52
53                        if outlives.len() > 0 {
54                            where_predicates.push_str(": ");
55
56                            for (i, outlive) in outlives.iter().enumerate() {
57                                if i != 0 {
58                                    where_predicates.push_str(" + ");
59                                    where_predicates.push_str(outlive);
60                                }
61                            }
62                        }
63                    }
64                    WherePredicate::EqPredicate { lhs, rhs } => {
65                        where_predicates.push_str(&TypeGenerator::type_to_string(lhs));
66                        where_predicates.push_str(" = ");
67
68                        let rhs = match rhs {
69                            Term::Type(type_) => TypeGenerator::type_to_string(type_),
70                            Term::Constant(constant) => unimplemented!(),
71                        };
72
73                        where_predicates.push_str(&rhs)
74                    }
75                }
76            }
77        }
78
79        Ok((params, where_predicates))
80    }
81
82    pub fn generate_generic_params(generic_params: &Vec<GenericParamDef>) -> Result<String> {
83        let mut params = String::new();
84
85        if generic_params.len() > 0 {
86            params.push_str("<");
87            for (i, param_def) in generic_params.iter().enumerate() {
88                // Add a comma before, just for some nice formatting
89                if i != 0 {
90                    params.push_str(", ")
91                }
92
93                match &param_def.kind {
94                    GenericParamDefKind::Lifetime { outlives } => {
95                        // name already contains '
96                        params.push_str(&param_def.name);
97
98                        if outlives.len() > 0 {
99                            params.push_str(": ");
100
101                            for (x, other_name) in outlives.iter().enumerate() {
102                                if x != 0 {
103                                    // Add a comma before, just for some nice formatting
104                                    params.push_str(" + ");
105                                    params.push_str(&other_name);
106                                }
107                            }
108                        }
109                    }
110                    GenericParamDefKind::Type {
111                        bounds,
112                        default,
113                        is_synthetic,
114                    } => {
115                        params.push_str(&param_def.name);
116                        if bounds.len() > 0 {
117                            params.push_str(": ");
118                            for (i, bound) in bounds.iter().enumerate() {
119                                if i != 0 {
120                                    params.push_str(" + ");
121                                }
122                                params.push_str(&Self::generate_generic_bounds(bound)?);
123                            }
124                        }
125
126                        if let Some(default_type) = default {
127                            params.push_str(" = ");
128                            params.push_str(&TypeGenerator::type_to_string(default_type));
129                        }
130                        // TODO use `is_synthetic` field
131                    }
132                    GenericParamDefKind::Const { type_, default } => {
133                        params.push_str("const ");
134                        params.push_str(&param_def.name);
135
136                        params.push_str(": ");
137                        params.push_str(&TypeGenerator::type_to_string(type_));
138                        if let Some(default) = default {
139                            params.push_str(" = ");
140                            params.push_str(default);
141                        }
142                    }
143                }
144            }
145            params.push_str(">");
146        }
147
148        Ok(params)
149    }
150
151    pub fn generate_generic_bounds(bound: &GenericBound) -> Result<String> {
152        let mut bound_string = String::new();
153        match bound {
154            GenericBound::TraitBound {
155                trait_,
156                generic_params,
157                modifier,
158            } => {
159                bound_string.push_str(match modifier {
160                    TraitBoundModifier::None => "",
161                    TraitBoundModifier::Maybe => "?",
162                    // Not really sure if this is correct
163                    TraitBoundModifier::MaybeConst => "const? ",
164                });
165
166                let param = Self::generate_generic_params(generic_params)?;
167                if !param.is_empty() {
168                    bound_string.push_str("for");
169                    bound_string.push_str(&param);
170                    bound_string.push_str(" ");
171                }
172
173                bound_string.push_str(&TypeGenerator::path_to_string(&trait_));
174            }
175            GenericBound::Outlives(lifetime) => {
176                bound_string.push_str(lifetime);
177            }
178            GenericBound::Use(uses) => {
179                bound_string.push_str("use<");
180                for (i, use_) in uses.iter().enumerate() {
181                    if i != 0 {
182                        bound_string.push_str(", ");
183                    }
184                    bound_string.push_str(use_);
185                }
186                bound_string.push_str(">");
187            }
188        }
189        Ok(bound_string)
190    }
191
192    pub fn generate_generic_args(generic_args: &GenericArgs) -> String {
193        let mut generic_arg_string = String::new();
194
195        match generic_args {
196            GenericArgs::AngleBracketed { args, constraints } => {
197                if args.len() > 0 {
198                    generic_arg_string.push_str("<");
199
200                    for (i, arg) in args.iter().enumerate() {
201                        if i != 0 {
202                            generic_arg_string.push_str(", ");
203                        }
204
205                        match arg {
206                            GenericArg::Lifetime(lifetime) => generic_arg_string.push_str(lifetime),
207                            GenericArg::Type(type_) => {
208                                generic_arg_string.push_str(&TypeGenerator::type_to_string(type_))
209                            }
210                            // TODO: Account for other variables in constant i.e. value and is_literal
211                            GenericArg::Const(constant) => {
212                                generic_arg_string.push_str(&format!("{{ {} }}", constant.expr))
213                            }
214                            GenericArg::Infer => generic_arg_string.push_str("_"),
215                        }
216
217                        // TODO: Also, account for constraints
218                    }
219
220                    generic_arg_string.push_str(">");
221                }
222            }
223            GenericArgs::Parenthesized { inputs, output } => {
224                generic_arg_string.push_str("Fn(");
225
226                for (i, input) in inputs.iter().enumerate() {
227                    if i != 0 {
228                        generic_arg_string.push_str(", ");
229                    }
230                    generic_arg_string.push_str(&TypeGenerator::type_to_string(input));
231                }
232
233                generic_arg_string.push_str(")");
234
235                if let Some(type_) = output {
236                    generic_arg_string.push_str(" -> ");
237                    generic_arg_string.push_str(&TypeGenerator::type_to_string(type_));
238                }
239            }
240        }
241
242        generic_arg_string
243    }
244}