libgir/codegen/
trait_impls.rs

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}