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, Nullable, 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 nullable = func.ret.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 !*nullable && can_be_nullable_return(env, typ) {
44                *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            nullable = val;
51        }
52        Some(library::Parameter {
53            typ,
54            nullable,
55            ..func.ret.clone()
56        })
57    };
58
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 let Some(library::Parameter { nullable: Nullable(false), ..}) = parameter {
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        if let Some(par) = parameter {
109            let nullable_override = configured_functions.iter().find_map(|f| f.ret.nullable);
110            if par.typ != type_tid {
111                base_tid = Some(par.typ);
112            }
113            parameter = Some(library::Parameter {
114                typ: type_tid,
115                nullable: nullable_override.unwrap_or(func.ret.nullable),
116                ..par
117            });
118        }
119    }
120
121    let parameter = parameter.as_ref().map(|lib_par| {
122        let par = analysis::Parameter::from_return_value(env, lib_par, configured_functions);
123        if let Ok(rust_type) = RustType::builder(env, typ)
124            .direction(par.lib_par.direction)
125            .try_from_glib(&par.try_from_glib)
126            .try_build()
127        {
128            used_types.extend(rust_type.into_used_types());
129        }
130
131        commented = RustType::builder(env, typ)
132            .direction(func.ret.direction)
133            .try_from_glib(&par.try_from_glib)
134            .try_build_param()
135            .is_err();
136
137        par
138    });
139
140    Info {
141        parameter,
142        base_tid,
143        commented,
144        bool_return_is_error: bool_return_error_message,
145        nullable_return_is_error: nullable_return_error_message,
146    }
147}
148
149fn can_be_nullable_return(env: &Env, type_id: library::TypeId) -> bool {
150    use crate::library::{Basic::*, Type::*};
151    match env.library.type_(type_id) {
152        Basic(fund) => matches!(fund, Pointer | Utf8 | Filename | OsString),
153        Alias(alias) => can_be_nullable_return(env, alias.typ),
154        Enumeration(_) => false,
155        Bitfield(_) => false,
156        Record(_) => true,
157        Union(_) => true,
158        Function(_) => true,
159        Interface(_) => true,
160        Class(_) => true,
161        _ => true,
162    }
163}