Skip to main content

libgir/analysis/
return_value.rs

1use log::error;
2
3use crate::{
4    analysis::{
5        self, imports::Imports, namespaces, override_string_type::override_string_type_return,
6        rust_type::RustType,
7    },
8    config,
9    env::Env,
10    library::{self, TypeId},
11};
12
13#[derive(Clone, Debug, Default)]
14pub struct Info {
15    pub parameter: Option<analysis::Parameter>,
16    pub base_tid: Option<library::TypeId>, // Some only if need downcast
17    pub commented: bool,
18    pub bool_return_is_error: Option<String>,
19    pub nullable_return_is_error: Option<String>,
20}
21
22pub fn analyze(
23    env: &Env,
24    obj: &config::gobjects::GObject,
25    func: &library::Function,
26    type_tid: library::TypeId,
27    configured_functions: &[&config::functions::Function],
28    used_types: &mut Vec<String>,
29    imports: &mut Imports,
30) -> Info {
31    let typ = configured_functions
32        .iter()
33        .find_map(|f| f.ret.type_name.as_ref())
34        .and_then(|typ| env.library.find_type(0, typ))
35        .unwrap_or_else(|| override_string_type_return(env, func.ret.typ(), configured_functions));
36    let mut parameter = if typ == Default::default() {
37        None
38    } else {
39        let mut is_nullable = func.ret.is_nullable();
40        if !obj.trust_return_value_nullability {
41            // Since GIRs are bad at specifying return value nullability, assume
42            // any returned pointer is nullable unless overridden by the config.
43            if !is_nullable && can_be_nullable_return(env, typ) {
44                is_nullable = true;
45            }
46        }
47
48        let nullable_override = configured_functions.iter().find_map(|f| f.ret.nullable);
49        if let Some(val) = nullable_override {
50            is_nullable = val;
51        }
52        let mut ret = func.ret.clone();
53        ret.set_tid(typ);
54        ret.set_nullable(is_nullable);
55        Some(ret)
56    };
57
58    let param_is_nullable = parameter.as_ref().is_some_and(|p| p.is_nullable());
59    let mut commented = false;
60
61    let bool_return_is_error = configured_functions
62        .iter()
63        .find_map(|f| f.ret.bool_return_is_error.as_ref());
64    let bool_return_error_message = bool_return_is_error.and_then(|m| {
65        if typ != TypeId::tid_bool() && typ != TypeId::tid_c_bool() {
66            error!(
67                "Ignoring bool_return_is_error configuration for non-bool returning function {}",
68                func.name
69            );
70            None
71        } else {
72            let ns = if env.namespaces.glib_ns_id == namespaces::MAIN {
73                "error"
74            } else {
75                "glib"
76            };
77            imports.add(ns);
78
79            Some(m.clone())
80        }
81    });
82
83    let nullable_return_is_error = configured_functions
84        .iter()
85        .find_map(|f| f.ret.nullable_return_is_error.as_ref());
86    let nullable_return_error_message = nullable_return_is_error.and_then(|m| {
87        if !param_is_nullable{
88            error!(
89                "Ignoring nullable_return_is_error configuration for non-none returning function {}",
90                func.name
91            );
92            None
93        } else {
94            let ns = if env.namespaces.glib_ns_id == namespaces::MAIN {
95                "crate::BoolError"
96            } else {
97                "glib"
98            };
99            imports.add(ns);
100
101            Some(m.clone())
102        }
103    });
104
105    let mut base_tid = None;
106
107    if func.kind == library::FunctionKind::Constructor
108        && let Some(ref mut par) = parameter
109    {
110        let nullable_override = configured_functions.iter().find_map(|f| f.ret.nullable);
111        if par.typ() != type_tid {
112            base_tid = Some(par.typ());
113        }
114        par.set_nullable(nullable_override.unwrap_or(func.ret.is_nullable()));
115        par.set_tid(type_tid);
116    }
117
118    let parameter = parameter.map(|lib_par| {
119        let par =
120            analysis::Parameter::from_return_value(env, lib_par.clone(), configured_functions);
121        if let Ok(rust_type) = RustType::builder(env, typ)
122            .direction(par.lib_par.direction())
123            .try_from_glib(&par.try_from_glib)
124            .try_build()
125        {
126            used_types.extend(rust_type.into_used_types());
127        }
128
129        commented = RustType::builder(env, typ)
130            .direction(func.ret.direction())
131            .try_from_glib(&par.try_from_glib)
132            .try_build_param()
133            .is_err();
134
135        par
136    });
137
138    Info {
139        parameter,
140        base_tid,
141        commented,
142        bool_return_is_error: bool_return_error_message,
143        nullable_return_is_error: nullable_return_error_message,
144    }
145}
146
147fn can_be_nullable_return(env: &Env, type_id: library::TypeId) -> bool {
148    use crate::library::{Basic::*, Type::*};
149    match env.library.type_(type_id) {
150        Basic(fund) => matches!(fund, Pointer | Utf8 | Filename | OsString),
151        Alias(alias) => can_be_nullable_return(env, alias.typ),
152        Enumeration(_) => false,
153        Bitfield(_) => false,
154        Record(_) => true,
155        Union(_) => true,
156        Function(_) => true,
157        Interface(_) => true,
158        Class(_) => true,
159        _ => true,
160    }
161}