libgir/codegen/
flags.rs

1use std::{
2    io::{prelude::*, Result},
3    path::Path,
4};
5
6use super::{function, general::allow_deprecated, trait_impls};
7use crate::{
8    analysis::flags::Info,
9    codegen::{
10        general::{
11            self, cfg_condition, cfg_condition_doc, cfg_condition_no_doc, cfg_condition_string,
12            cfg_deprecated, derives, doc_alias, version_condition, version_condition_doc,
13            version_condition_no_doc, version_condition_string,
14        },
15        generate_default_impl,
16    },
17    config::gobjects::GObject,
18    env::Env,
19    file_saver,
20    library::*,
21    nameutil::{bitfield_member_name, use_glib_type},
22    traits::*,
23};
24
25pub fn generate(env: &Env, root_path: &Path, mod_rs: &mut Vec<String>) {
26    if !env
27        .analysis
28        .flags
29        .iter()
30        .any(|f| env.config.objects[&f.full_name].status.need_generate())
31    {
32        return;
33    }
34
35    let path = root_path.join("flags.rs");
36    file_saver::save_to_file(path, env.config.make_backup, |w| {
37        general::start_comments(w, &env.config)?;
38        general::uses(w, env, &env.analysis.flags_imports, None)?;
39        writeln!(w)?;
40
41        mod_rs.push("\nmod flags;".into());
42        for flags_analysis in &env.analysis.flags {
43            let config = &env.config.objects[&flags_analysis.full_name];
44            if !config.status.need_generate() {
45                continue;
46            }
47            let flags = flags_analysis.type_(&env.library);
48
49            if let Some(cfg) = version_condition_string(env, None, flags.version, false, 0) {
50                mod_rs.push(cfg);
51            }
52            if let Some(cfg) = cfg_condition_string(config.cfg_condition.as_ref(), false, 0) {
53                mod_rs.push(cfg);
54            }
55            mod_rs.push(format!(
56                "{}{} use self::flags::{};",
57                flags
58                    .deprecated_version
59                    .map(|_| "#[allow(deprecated)]\n")
60                    .unwrap_or(""),
61                flags_analysis.visibility.export_visibility(),
62                flags.name
63            ));
64            generate_flags(env, w, flags, config, flags_analysis)?;
65        }
66
67        Ok(())
68    });
69}
70
71fn generate_flags(
72    env: &Env,
73    w: &mut dyn Write,
74    flags: &Bitfield,
75    config: &GObject,
76    analysis: &Info,
77) -> Result<()> {
78    let sys_crate_name = env.sys_crate_import(analysis.type_id);
79    cfg_condition_no_doc(w, config.cfg_condition.as_ref(), false, 0)?;
80    version_condition_no_doc(w, env, None, flags.version, false, 0)?;
81    writeln!(w, "bitflags! {{")?;
82    cfg_condition_doc(w, config.cfg_condition.as_ref(), false, 1)?;
83    version_condition_doc(w, env, flags.version, false, 1)?;
84    cfg_deprecated(
85        w,
86        env,
87        Some(analysis.type_id),
88        flags.deprecated_version,
89        false,
90        1,
91    )?;
92    if config.must_use {
93        writeln!(w, "    #[must_use]")?;
94    }
95
96    if let Some(ref d) = config.derives {
97        derives(w, d, 1)?;
98    }
99    writeln!(w, "    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]")?;
100
101    doc_alias(w, &flags.c_type, "", 1)?;
102    writeln!(
103        w,
104        "    {} struct {}: u32 {{",
105        analysis.visibility, flags.name
106    )?;
107    for member in &flags.members {
108        let member_config = config.members.matched(&member.name);
109        if member.status.ignored() {
110            continue;
111        }
112
113        let name = bitfield_member_name(&member.name);
114        let deprecated_version = member_config
115            .iter()
116            .find_map(|m| m.deprecated_version)
117            .or(member.deprecated_version);
118        let version = member_config
119            .iter()
120            .find_map(|m| m.version)
121            .or(member.version);
122        let cfg_cond = member_config.iter().find_map(|m| m.cfg_condition.as_ref());
123        cfg_deprecated(w, env, Some(analysis.type_id), deprecated_version, false, 2)?;
124        version_condition(w, env, None, version, false, 2)?;
125        cfg_condition(w, cfg_cond, false, 2)?;
126        if member.c_identifier != member.name {
127            doc_alias(w, &member.c_identifier, "", 2)?;
128        }
129        writeln!(
130            w,
131            "\t\tconst {} = {}::{} as _;",
132            name, sys_crate_name, member.c_identifier,
133        )?;
134    }
135
136    writeln!(
137        w,
138        "    }}
139}}"
140    )?;
141
142    let functions = analysis
143        .functions
144        .iter()
145        .filter(|f| f.status.need_generate())
146        .collect::<Vec<_>>();
147
148    if !functions.is_empty() {
149        writeln!(w)?;
150        version_condition(w, env, None, flags.version, false, 0)?;
151        cfg_condition_no_doc(w, config.cfg_condition.as_ref(), false, 0)?;
152        allow_deprecated(w, flags.deprecated_version, false, 0)?;
153        write!(w, "impl {} {{", analysis.name)?;
154        for func_analysis in functions {
155            function::generate(
156                w,
157                env,
158                Some(analysis.type_id),
159                func_analysis,
160                Some(&analysis.specials),
161                flags.version,
162                false,
163                false,
164                1,
165            )?;
166        }
167        writeln!(w, "}}")?;
168    }
169
170    trait_impls::generate(
171        w,
172        env,
173        &analysis.name,
174        &analysis.functions,
175        &analysis.specials,
176        None,
177        None,
178        config.cfg_condition.as_deref(),
179    )?;
180
181    writeln!(w)?;
182
183    generate_default_impl(
184        w,
185        env,
186        config,
187        &flags.name,
188        flags.version,
189        flags.members.iter(),
190        |member| {
191            let member_config = config.members.matched(&member.name);
192            if member.status.ignored() {
193                return None;
194            }
195            let version = member_config
196                .iter()
197                .find_map(|m| m.version)
198                .or(member.version);
199            let cfg_cond = member_config.iter().find_map(|m| m.cfg_condition.as_ref());
200            Some((version, cfg_cond, bitfield_member_name(&member.name)))
201        },
202    )?;
203
204    version_condition(w, env, None, flags.version, false, 0)?;
205    cfg_condition_no_doc(w, config.cfg_condition.as_ref(), false, 0)?;
206    allow_deprecated(w, flags.deprecated_version, false, 0)?;
207    writeln!(
208        w,
209        "#[doc(hidden)]
210impl IntoGlib for {name} {{
211    type GlibType = {sys_crate_name}::{ffi_name};
212
213    #[inline]
214    fn into_glib(self) -> {sys_crate_name}::{ffi_name} {{
215        self.bits()
216    }}
217}}
218",
219        sys_crate_name = sys_crate_name,
220        name = flags.name,
221        ffi_name = flags.c_type
222    )?;
223
224    let assert = if env.config.generate_safety_asserts {
225        "skip_assert_initialized!();\n\t\t"
226    } else {
227        ""
228    };
229
230    version_condition(w, env, None, flags.version, false, 0)?;
231    cfg_condition_no_doc(w, config.cfg_condition.as_ref(), false, 0)?;
232    allow_deprecated(w, flags.deprecated_version, false, 0)?;
233    writeln!(
234        w,
235        "#[doc(hidden)]
236impl FromGlib<{sys_crate_name}::{ffi_name}> for {name} {{
237    #[inline]
238    unsafe fn from_glib(value: {sys_crate_name}::{ffi_name}) -> Self {{
239        {assert}Self::from_bits_truncate(value)
240    }}
241}}
242",
243        sys_crate_name = sys_crate_name,
244        name = flags.name,
245        ffi_name = flags.c_type,
246        assert = assert
247    )?;
248
249    if let Some(ref get_type) = flags.glib_get_type {
250        let configured_functions = config.functions.matched("get_type");
251        let version = std::iter::once(flags.version)
252            .chain(configured_functions.iter().map(|f| f.version))
253            .max()
254            .flatten();
255
256        version_condition(w, env, None, version, false, 0)?;
257        cfg_condition_no_doc(w, config.cfg_condition.as_ref(), false, 0)?;
258        allow_deprecated(w, flags.deprecated_version, false, 0)?;
259        writeln!(
260            w,
261            "impl StaticType for {name} {{
262                #[inline]",
263            name = flags.name,
264        )?;
265        doc_alias(w, get_type, "", 1)?;
266        writeln!(
267            w,
268            "   fn static_type() -> {glib_type} {{
269                    unsafe {{ from_glib({sys_crate_name}::{get_type}()) }}
270                }}
271            }}",
272            sys_crate_name = sys_crate_name,
273            get_type = get_type,
274            glib_type = use_glib_type(env, "Type")
275        )?;
276        writeln!(w)?;
277
278        version_condition(w, env, None, version, false, 0)?;
279        cfg_condition_no_doc(w, config.cfg_condition.as_ref(), false, 0)?;
280        allow_deprecated(w, flags.deprecated_version, false, 0)?;
281        writeln!(
282            w,
283            "impl {has_param_spec} for {name} {{
284                type ParamSpec = {param_spec_flags};
285                type SetValue = Self;
286                type BuilderFn = fn(&str) -> {param_spec_builder}<Self>;
287    
288                fn param_spec_builder() -> Self::BuilderFn {{
289                    Self::ParamSpec::builder
290                }}
291}}",
292            name = flags.name,
293            has_param_spec = use_glib_type(env, "HasParamSpec"),
294            param_spec_flags = use_glib_type(env, "ParamSpecFlags"),
295            param_spec_builder = use_glib_type(env, "ParamSpecFlagsBuilder"),
296        )?;
297        writeln!(w)?;
298
299        version_condition(w, env, None, version, false, 0)?;
300        cfg_condition_no_doc(w, config.cfg_condition.as_ref(), false, 0)?;
301        allow_deprecated(w, flags.deprecated_version, false, 0)?;
302        writeln!(
303            w,
304            "impl {valuetype} for {name} {{
305    type Type = Self;
306}}",
307            name = flags.name,
308            valuetype = use_glib_type(env, "value::ValueType"),
309        )?;
310        writeln!(w)?;
311
312        version_condition(w, env, None, version, false, 0)?;
313        cfg_condition_no_doc(w, config.cfg_condition.as_ref(), false, 0)?;
314        allow_deprecated(w, flags.deprecated_version, false, 0)?;
315        writeln!(
316            w,
317            "unsafe impl<'a> {from_value_type}<'a> for {name} {{
318    type Checker = {genericwrongvaluetypechecker}<Self>;
319
320    #[inline]
321    unsafe fn from_value(value: &'a {gvalue}) -> Self {{
322        {assert}from_glib({glib}(value.to_glib_none().0))
323    }}
324}}",
325            name = flags.name,
326            glib = use_glib_type(env, "gobject_ffi::g_value_get_flags"),
327            gvalue = use_glib_type(env, "Value"),
328            genericwrongvaluetypechecker = use_glib_type(env, "value::GenericValueTypeChecker"),
329            assert = assert,
330            from_value_type = use_glib_type(env, "value::FromValue"),
331        )?;
332        writeln!(w)?;
333
334        version_condition(w, env, None, version, false, 0)?;
335        cfg_condition_no_doc(w, config.cfg_condition.as_ref(), false, 0)?;
336        allow_deprecated(w, flags.deprecated_version, false, 0)?;
337        writeln!(
338            w,
339            "impl ToValue for {name} {{
340    #[inline]
341    fn to_value(&self) -> {gvalue} {{
342        let mut value = {gvalue}::for_value_type::<Self>();
343        unsafe {{
344            {glib}(value.to_glib_none_mut().0, self.into_glib());
345        }}
346        value
347    }}
348
349    #[inline]
350    fn value_type(&self) -> {gtype} {{
351        Self::static_type()
352    }}
353}}",
354            name = flags.name,
355            glib = use_glib_type(env, "gobject_ffi::g_value_set_flags"),
356            gvalue = use_glib_type(env, "Value"),
357            gtype = use_glib_type(env, "Type"),
358        )?;
359        writeln!(w)?;
360
361        version_condition(w, env, None, version, false, 0)?;
362        cfg_condition_no_doc(w, config.cfg_condition.as_ref(), false, 0)?;
363        allow_deprecated(w, flags.deprecated_version, false, 0)?;
364        writeln!(
365            w,
366            "impl From<{name}> for {gvalue} {{
367    #[inline]
368    fn from(v: {name}) -> Self {{
369        {assert}ToValue::to_value(&v)
370    }}
371}}",
372            name = flags.name,
373            gvalue = use_glib_type(env, "Value"),
374            assert = assert,
375        )?;
376        writeln!(w)?;
377    }
378
379    Ok(())
380}