1use std::io::{Result, Write};
2
3use crate::{
4 analysis::{
5 functions::Info,
6 special_functions::{Infos, Type},
7 },
8 codegen::general::{cfg_condition_no_doc, version_condition},
9 version::Version,
10 Env,
11};
12
13pub fn generate(
14 w: &mut dyn Write,
15 env: &Env,
16 type_name: &str,
17 functions: &[Info],
18 specials: &Infos,
19 trait_name: Option<&str>,
20 scope_version: Option<Version>,
21 cfg_condition: Option<&str>,
22) -> Result<()> {
23 for (type_, special_info) in specials.traits().iter() {
24 if let Some(info) = lookup(functions, &special_info.glib_name) {
25 match type_ {
26 Type::Compare => {
27 if !specials.has_trait(Type::Equal) {
28 generate_eq_compare(
29 w,
30 env,
31 type_name,
32 info,
33 trait_name,
34 scope_version,
35 cfg_condition,
36 )?;
37 }
38 generate_ord(
39 w,
40 env,
41 type_name,
42 info,
43 trait_name,
44 scope_version,
45 cfg_condition,
46 )?;
47 }
48 Type::Equal => generate_eq(
49 w,
50 env,
51 type_name,
52 info,
53 trait_name,
54 scope_version,
55 cfg_condition,
56 )?,
57 Type::Display => generate_display(
58 w,
59 env,
60 type_name,
61 info,
62 trait_name,
63 scope_version,
64 cfg_condition,
65 )?,
66 Type::Hash => generate_hash(
67 w,
68 env,
69 type_name,
70 info,
71 trait_name,
72 scope_version,
73 cfg_condition,
74 )?,
75 _ => {}
76 }
77 }
78 }
79 Ok(())
80}
81
82fn lookup<'a>(functions: &'a [Info], name: &str) -> Option<&'a Info> {
83 functions
84 .iter()
85 .find(|f| !f.status.ignored() && f.glib_name == name)
86}
87
88fn generate_call(func_name: &str, args: &[&str], trait_name: Option<&str>) -> String {
89 let mut args_string = String::new();
90 let in_trait = trait_name.is_some();
91
92 if in_trait {
93 args_string.push_str("self");
94 }
95
96 if !args.is_empty() {
97 if in_trait {
98 args_string.push_str(", ");
99 }
100 args_string.push_str(&args.join(", "));
101 }
102
103 if let Some(trait_name) = trait_name {
104 format!("{trait_name}::{func_name}({args_string})")
105 } else {
106 format!("self.{func_name}({args_string})")
107 }
108}
109
110fn generate_display(
111 w: &mut dyn Write,
112 env: &Env,
113 type_name: &str,
114 func: &Info,
115 trait_name: Option<&str>,
116 scope_version: Option<Version>,
117 cfg_condition: Option<&str>,
118) -> Result<()> {
119 use crate::analysis::out_parameters::Mode;
120
121 writeln!(w)?;
122 let version = Version::if_stricter_than(func.version, scope_version);
123 version_condition(w, env, None, version, false, 0)?;
124 cfg_condition_no_doc(w, cfg_condition, false, 0)?;
125
126 let call = generate_call(func.codegen_name(), &[], trait_name);
127 let body = if let Mode::Throws(_) = func.outs.mode {
128 format!(
129 "\
130 if let Ok(val) = {call} {{
131 f.write_str(val)
132 }} else {{
133 Err(fmt::Error)
134 }}"
135 )
136 } else {
137 format!("f.write_str(&{call})")
138 };
139
140 writeln!(
141 w,
142 "\
143impl std::fmt::Display for {type_name} {{
144 #[inline]
145 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {{
146 {body}
147 }}
148}}"
149 )
150}
151
152fn generate_hash(
153 w: &mut dyn Write,
154 env: &Env,
155 type_name: &str,
156 func: &Info,
157 trait_name: Option<&str>,
158 scope_version: Option<Version>,
159 cfg_condition: Option<&str>,
160) -> Result<()> {
161 writeln!(w)?;
162 let version = Version::if_stricter_than(func.version, scope_version);
163 version_condition(w, env, None, version, false, 0)?;
164 cfg_condition_no_doc(w, cfg_condition, false, 0)?;
165
166 let call = generate_call(func.codegen_name(), &[], trait_name);
167
168 writeln!(
169 w,
170 "\
171impl std::hash::Hash for {type_name} {{
172 #[inline]
173 fn hash<H>(&self, state: &mut H) where H: std::hash::Hasher {{
174 std::hash::Hash::hash(&{call}, state)
175 }}
176}}"
177 )
178}
179
180fn generate_eq(
181 w: &mut dyn Write,
182 env: &Env,
183 type_name: &str,
184 func: &Info,
185 trait_name: Option<&str>,
186 scope_version: Option<Version>,
187 cfg_condition: Option<&str>,
188) -> Result<()> {
189 writeln!(w)?;
190 let version = Version::if_stricter_than(func.version, scope_version);
191 version_condition(w, env, None, version, false, 0)?;
192 cfg_condition_no_doc(w, cfg_condition, false, 0)?;
193
194 let call = generate_call(func.codegen_name(), &["other"], trait_name);
195
196 writeln!(
197 w,
198 "\
199impl PartialEq for {type_name} {{
200 #[inline]
201 fn eq(&self, other: &Self) -> bool {{
202 {call}
203 }}
204}}"
205 )?;
206 version_condition(w, env, None, version, false, 0)?;
207 cfg_condition_no_doc(w, cfg_condition, false, 0)?;
208
209 writeln!(w)?;
210 writeln!(
211 w,
212 "\
213 impl Eq for {type_name} {{}}"
214 )
215}
216
217fn generate_eq_compare(
218 w: &mut dyn Write,
219 env: &Env,
220 type_name: &str,
221 func: &Info,
222 trait_name: Option<&str>,
223 scope_version: Option<Version>,
224 cfg_condition: Option<&str>,
225) -> Result<()> {
226 writeln!(w)?;
227 let version = Version::if_stricter_than(func.version, scope_version);
228 version_condition(w, env, None, version, false, 0)?;
229 cfg_condition_no_doc(w, cfg_condition, false, 0)?;
230
231 let call = generate_call(func.codegen_name(), &["other"], trait_name);
232
233 writeln!(
234 w,
235 "\
236impl PartialEq for {type_name} {{
237 #[inline]
238 fn eq(&self, other: &Self) -> bool {{
239 {call} == 0
240 }}
241}}"
242 )?;
243
244 version_condition(w, env, None, version, false, 0)?;
245 cfg_condition_no_doc(w, cfg_condition, false, 0)?;
246
247 writeln!(w)?;
248 writeln!(
249 w,
250 "\
251 impl Eq for {type_name} {{}}"
252 )
253}
254
255fn generate_ord(
256 w: &mut dyn Write,
257 env: &Env,
258 type_name: &str,
259 func: &Info,
260 trait_name: Option<&str>,
261 scope_version: Option<Version>,
262 cfg_condition: Option<&str>,
263) -> Result<()> {
264 writeln!(w)?;
265 let version = Version::if_stricter_than(func.version, scope_version);
266 version_condition(w, env, None, version, false, 0)?;
267 cfg_condition_no_doc(w, cfg_condition, false, 0)?;
268
269 let call = generate_call(func.codegen_name(), &["other"], trait_name);
270
271 writeln!(
272 w,
273 "\
274impl PartialOrd for {type_name} {{
275 #[inline]
276 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {{
277 Some(self.cmp(other))
278 }}
279}}"
280 )?;
281 version_condition(w, env, None, version, false, 0)?;
282 cfg_condition_no_doc(w, cfg_condition, false, 0)?;
283
284 writeln!(w)?;
285 writeln!(
286 w,
287 "\
288impl Ord for {type_name} {{
289 #[inline]
290 fn cmp(&self, other: &Self) -> std::cmp::Ordering {{
291 {call}.cmp(&0)
292 }}
293}}"
294 )
295}