libgir/codegen/sys/
functions.rs

1use std::{
2    io::{Result, Write},
3    sync::OnceLock,
4};
5
6use super::ffi_type::*;
7use crate::{
8    codegen::general::{cfg_condition, version_condition},
9    config::{functions::Function, gobjects::GObject},
10    env::Env,
11    library, nameutil,
12    traits::*,
13};
14
15// used as glib:get-type in GLib-2.0.gir
16const INTERN: &str = "intern";
17
18fn default_obj() -> &'static GObject {
19    static OBJ: OnceLock<GObject> = OnceLock::new();
20    OBJ.get_or_init(Default::default)
21}
22
23pub fn generate_records_funcs(
24    w: &mut dyn Write,
25    env: &Env,
26    records: &[&library::Record],
27) -> Result<()> {
28    let intern_str = INTERN.to_string();
29    for record in records {
30        let name = format!("{}.{}", env.config.library_name, record.name);
31        let obj = env.config.objects.get(&name).unwrap_or(default_obj());
32        let version = obj.version.or(record.version);
33        let glib_get_type = record.glib_get_type.as_ref().unwrap_or(&intern_str);
34        generate_object_funcs(
35            w,
36            env,
37            obj,
38            version,
39            &record.c_type,
40            glib_get_type,
41            &record.functions,
42        )?;
43    }
44
45    Ok(())
46}
47
48pub fn generate_classes_funcs(
49    w: &mut dyn Write,
50    env: &Env,
51    classes: &[&library::Class],
52) -> Result<()> {
53    for klass in classes {
54        let name = format!("{}.{}", env.config.library_name, klass.name);
55        let obj = env.config.objects.get(&name).unwrap_or(default_obj());
56        let version = obj.version.or(klass.version);
57        generate_object_funcs(
58            w,
59            env,
60            obj,
61            version,
62            &klass.c_type,
63            &klass.glib_get_type,
64            &klass.functions,
65        )?;
66    }
67
68    Ok(())
69}
70
71pub fn generate_bitfields_funcs(
72    w: &mut dyn Write,
73    env: &Env,
74    bitfields: &[&library::Bitfield],
75) -> Result<()> {
76    let intern_str = INTERN.to_string();
77    for bitfield in bitfields {
78        let name = format!("{}.{}", env.config.library_name, bitfield.name);
79        let obj = env.config.objects.get(&name).unwrap_or(default_obj());
80        let version = obj.version.or(bitfield.version);
81        let glib_get_type = bitfield.glib_get_type.as_ref().unwrap_or(&intern_str);
82        generate_object_funcs(
83            w,
84            env,
85            obj,
86            version,
87            &bitfield.c_type,
88            glib_get_type,
89            &bitfield.functions,
90        )?;
91    }
92
93    Ok(())
94}
95
96pub fn generate_enums_funcs(
97    w: &mut dyn Write,
98    env: &Env,
99    enums: &[&library::Enumeration],
100) -> Result<()> {
101    let intern_str = INTERN.to_string();
102    for en in enums {
103        let name = format!("{}.{}", env.config.library_name, en.name);
104        let obj = env.config.objects.get(&name).unwrap_or(default_obj());
105        let version = obj.version.or(en.version);
106        let glib_get_type = en.glib_get_type.as_ref().unwrap_or(&intern_str);
107        generate_object_funcs(
108            w,
109            env,
110            obj,
111            version,
112            &en.c_type,
113            glib_get_type,
114            &en.functions,
115        )?;
116    }
117
118    Ok(())
119}
120
121pub fn generate_unions_funcs(
122    w: &mut dyn Write,
123    env: &Env,
124    unions: &[&library::Union],
125) -> Result<()> {
126    let intern_str = INTERN.to_string();
127    for union in unions {
128        let Some(ref c_type) = union.c_type else {
129            return Ok(());
130        };
131        let name = format!("{}.{}", env.config.library_name, union.name);
132        let obj = env.config.objects.get(&name).unwrap_or(default_obj());
133        let glib_get_type = union.glib_get_type.as_ref().unwrap_or(&intern_str);
134        generate_object_funcs(
135            w,
136            env,
137            obj,
138            obj.version,
139            c_type,
140            glib_get_type,
141            &union.functions,
142        )?;
143    }
144
145    Ok(())
146}
147
148pub fn generate_interfaces_funcs(
149    w: &mut dyn Write,
150    env: &Env,
151    interfaces: &[&library::Interface],
152) -> Result<()> {
153    for interface in interfaces {
154        let name = format!("{}.{}", env.config.library_name, interface.name);
155        let obj = env.config.objects.get(&name).unwrap_or(default_obj());
156        let version = obj.version.or(interface.version);
157        generate_object_funcs(
158            w,
159            env,
160            obj,
161            version,
162            &interface.c_type,
163            &interface.glib_get_type,
164            &interface.functions,
165        )?;
166    }
167
168    Ok(())
169}
170
171pub fn generate_other_funcs(
172    w: &mut dyn Write,
173    env: &Env,
174    functions: &[library::Function],
175) -> Result<()> {
176    let name = format!("{}.*", env.config.library_name);
177    let obj = env.config.objects.get(&name).unwrap_or(default_obj());
178    generate_object_funcs(w, env, obj, None, "Other functions", INTERN, functions)
179}
180
181fn generate_cfg_configure(
182    w: &mut dyn Write,
183    configured_functions: &[&Function],
184    commented: bool,
185) -> Result<()> {
186    let cfg_condition_ = configured_functions
187        .iter()
188        .find_map(|f| f.cfg_condition.as_ref());
189    cfg_condition(w, cfg_condition_, commented, 1)?;
190    Ok(())
191}
192
193fn generate_object_funcs(
194    w: &mut dyn Write,
195    env: &Env,
196    obj: &GObject,
197    version: Option<crate::version::Version>,
198    c_type: &str,
199    glib_get_type: &str,
200    functions: &[library::Function],
201) -> Result<()> {
202    let write_get_type = glib_get_type != INTERN;
203    if write_get_type || !functions.is_empty() {
204        writeln!(w)?;
205        writeln!(
206            w,
207            "    //========================================================================="
208        )?;
209        writeln!(w, "    // {c_type}")?;
210        writeln!(
211            w,
212            "    //========================================================================="
213        )?;
214    }
215    if write_get_type {
216        let configured_functions = obj.functions.matched("get_type");
217
218        if configured_functions
219            .iter()
220            .all(|f| f.status.need_generate())
221        {
222            let version = std::iter::once(version)
223                .chain(configured_functions.iter().map(|f| f.version))
224                .max()
225                .flatten();
226            version_condition(w, env, None, version, false, 1)?;
227            generate_cfg_configure(w, &configured_functions, false)?;
228            writeln!(w, "    pub fn {glib_get_type}() -> GType;")?;
229        }
230    }
231
232    for func in functions {
233        let configured_functions = obj.functions.matched(&func.name);
234        if !configured_functions
235            .iter()
236            .all(|f| f.status.need_generate())
237        {
238            continue;
239        }
240
241        let (commented, sig) = function_signature(env, func, false);
242        let comment = if commented { "//" } else { "" };
243
244        // If a version was configured for this function specifically then use that,
245        // otherwise use the (fixed up!) version of the function, if any, otherwise
246        // use the version of the type.
247        let version = configured_functions
248            .iter()
249            .map(|f| f.version)
250            .max()
251            .flatten()
252            .or(func.version)
253            .or(version);
254
255        version_condition(w, env, None, version, commented, 1)?;
256        let name = func.c_identifier.as_ref().unwrap();
257        generate_cfg_configure(w, &configured_functions, commented)?;
258        writeln!(w, "    {comment}pub fn {name}{sig};")?;
259    }
260
261    Ok(())
262}
263
264pub fn generate_callbacks(
265    w: &mut dyn Write,
266    env: &Env,
267    callbacks: &[&library::Function],
268) -> Result<()> {
269    if !callbacks.is_empty() {
270        writeln!(w, "// Callbacks")?;
271    }
272    for func in callbacks {
273        let (commented, sig) = function_signature(env, func, true);
274        let comment = if commented { "//" } else { "" };
275        writeln!(
276            w,
277            "{}pub type {} = Option<unsafe extern \"C\" fn{}>;",
278            comment,
279            func.c_identifier.as_ref().unwrap(),
280            sig
281        )?;
282    }
283    if !callbacks.is_empty() {
284        writeln!(w)?;
285    }
286
287    Ok(())
288}
289
290pub fn function_signature(env: &Env, func: &library::Function, bare: bool) -> (bool, String) {
291    let (mut commented, ret_str) = function_return_value(env, func);
292
293    let mut parameter_strs: Vec<String> = Vec::new();
294    for par in &func.parameters {
295        let (c, par_str) = function_parameter(env, par, bare);
296        parameter_strs.push(par_str);
297        if c {
298            commented = true;
299        }
300    }
301
302    (
303        commented,
304        format!("({}){}", parameter_strs.join(", "), ret_str),
305    )
306}
307
308fn function_return_value(env: &Env, func: &library::Function) -> (bool, String) {
309    if func.ret.typ == Default::default() {
310        return (false, String::new());
311    }
312    let ffi_type = ffi_type(env, func.ret.typ, &func.ret.c_type);
313    let commented = ffi_type.is_err();
314    (commented, format!(" -> {}", ffi_type.into_string()))
315}
316
317fn function_parameter(env: &Env, par: &library::Parameter, bare: bool) -> (bool, String) {
318    if let library::Type::Basic(library::Basic::VarArgs) = env.library.type_(par.typ) {
319        return (false, "...".into());
320    }
321    let ffi_type = ffi_type(env, par.typ, &par.c_type);
322    let commented = ffi_type.is_err();
323    let res = if bare {
324        ffi_type.into_string()
325    } else {
326        format!(
327            "{}: {}",
328            nameutil::mangle_keywords(&*par.name),
329            ffi_type.into_string()
330        )
331    };
332    (commented, res)
333}