libgir/analysis/
trampoline_parameters.rs

1use log::error;
2
3use super::{conversion_type::ConversionType, ref_mode::RefMode, try_from_glib::TryFromGlib};
4pub use crate::config::signals::TransformationType;
5use crate::{
6    analysis::{is_gpointer, rust_type::RustType},
7    config::{self, parameter_matchable::ParameterMatchable},
8    env::Env,
9    library, nameutil,
10};
11
12#[derive(Clone, Debug)]
13pub struct RustParameter {
14    pub name: String,
15    pub typ: library::TypeId,
16    pub direction: library::ParameterDirection,
17    pub nullable: library::Nullable,
18    pub ref_mode: RefMode,
19    pub try_from_glib: TryFromGlib,
20}
21
22#[derive(Clone, Debug)]
23pub struct CParameter {
24    pub name: String,
25    pub typ: library::TypeId,
26    pub c_type: String,
27}
28
29impl CParameter {
30    pub fn is_real_gpointer(&self, env: &Env) -> bool {
31        is_gpointer(&self.c_type) && RustType::try_new(env, self.typ).is_err()
32    }
33}
34
35#[derive(Clone, Debug)]
36pub struct Transformation {
37    pub ind_c: usize,    // index in `Vec<CParameter>`
38    pub ind_rust: usize, // index in `Vec<RustParameter>`
39    pub transformation: TransformationType,
40    pub name: String,
41    pub typ: library::TypeId,
42    pub transfer: library::Transfer,
43    pub ref_mode: RefMode,
44    pub conversion_type: ConversionType,
45}
46
47#[derive(Clone, Default, Debug)]
48pub struct Parameters {
49    pub rust_parameters: Vec<RustParameter>,
50    pub c_parameters: Vec<CParameter>,
51    pub transformations: Vec<Transformation>,
52}
53
54impl Parameters {
55    pub fn new(capacity: usize) -> Self {
56        Self {
57            rust_parameters: Vec::with_capacity(capacity),
58            c_parameters: Vec::with_capacity(capacity),
59            transformations: Vec::with_capacity(capacity),
60        }
61    }
62
63    pub fn prepare_transformation(
64        &mut self,
65        env: &Env,
66        type_tid: library::TypeId,
67        name: String,
68        c_type: String,
69        direction: library::ParameterDirection,
70        transfer: library::Transfer,
71        nullable: library::Nullable,
72        ref_mode: RefMode,
73        conversion_type: ConversionType,
74    ) -> Transformation {
75        let c_par = CParameter {
76            name: name.clone(),
77            typ: type_tid,
78            c_type,
79        };
80        let ind_c = self.c_parameters.len();
81        self.c_parameters.push(c_par);
82
83        let rust_par = RustParameter {
84            name: name.clone(),
85            typ: type_tid,
86            direction,
87            nullable,
88            ref_mode,
89            try_from_glib: TryFromGlib::from_type_defaults(env, type_tid),
90        };
91        let ind_rust = self.rust_parameters.len();
92        self.rust_parameters.push(rust_par);
93
94        Transformation {
95            ind_c,
96            ind_rust,
97            transformation: TransformationType::None,
98            name,
99            typ: type_tid,
100            transfer,
101            ref_mode,
102            conversion_type,
103        }
104    }
105
106    pub fn get(&self, ind_rust: usize) -> Option<&Transformation> {
107        self.transformations
108            .iter()
109            .find(|tr| tr.ind_rust == ind_rust)
110    }
111}
112
113pub fn analyze(
114    env: &Env,
115    signal_parameters: &[library::Parameter],
116    type_tid: library::TypeId,
117    configured_signals: &[&config::signals::Signal],
118    callback_parameters_config: Option<&config::functions::CallbackParameters>,
119) -> Parameters {
120    let mut parameters = Parameters::new(signal_parameters.len() + 1);
121
122    let owner = env.type_(type_tid);
123    let c_type = format!("{}*", owner.get_glib_name().unwrap());
124
125    let transform = parameters.prepare_transformation(
126        env,
127        type_tid,
128        "this".to_owned(),
129        c_type,
130        library::ParameterDirection::In,
131        library::Transfer::None,
132        library::Nullable(false),
133        RefMode::ByRef,
134        ConversionType::Borrow,
135    );
136    parameters.transformations.push(transform);
137
138    for par in signal_parameters {
139        let name = nameutil::mangle_keywords(&*par.name).into_owned();
140
141        let ref_mode = RefMode::without_unneeded_mut(env, par, false, false);
142
143        let nullable_override = configured_signals
144            .matched_parameters(&name)
145            .iter()
146            .find_map(|p| p.nullable)
147            .or_else(|| {
148                callback_parameters_config.and_then(|cp| {
149                    cp.iter()
150                        .find(|cp| cp.ident.is_match(&par.name))
151                        .and_then(|c| c.nullable)
152                })
153            });
154        let nullable = nullable_override.unwrap_or(par.nullable);
155
156        let conversion_type = {
157            match env.library.type_(par.typ) {
158                library::Type::Basic(library::Basic::Utf8)
159                | library::Type::Record(..)
160                | library::Type::Interface(..)
161                | library::Type::Class(..) => ConversionType::Borrow,
162                _ => ConversionType::of(env, par.typ),
163            }
164        };
165
166        let new_name = configured_signals
167            .matched_parameters(&name)
168            .iter()
169            .find_map(|p| p.new_name.clone());
170        let transformation_override = configured_signals
171            .matched_parameters(&name)
172            .iter()
173            .find_map(|p| p.transformation);
174
175        let mut transform = parameters.prepare_transformation(
176            env,
177            par.typ,
178            name,
179            par.c_type.clone(),
180            par.direction,
181            par.transfer,
182            nullable,
183            ref_mode,
184            conversion_type,
185        );
186
187        if let Some(new_name) = new_name {
188            transform.name = new_name;
189        }
190
191        if let Some(transformation_type) = transformation_override {
192            apply_transformation_type(env, &mut parameters, &mut transform, transformation_type);
193        }
194        parameters.transformations.push(transform);
195    }
196
197    parameters
198}
199
200fn apply_transformation_type(
201    env: &Env,
202    parameters: &mut Parameters,
203    transform: &mut Transformation,
204    transformation_type: TransformationType,
205) {
206    transform.transformation = transformation_type;
207    match transformation_type {
208        TransformationType::None => (),
209        TransformationType::Borrow => {
210            if transform.conversion_type == ConversionType::Pointer {
211                transform.conversion_type = ConversionType::Borrow;
212            } else if transform.conversion_type != ConversionType::Borrow {
213                error!(
214                    "Wrong conversion_type for borrow transformation {:?}",
215                    transform.conversion_type
216                );
217            }
218        }
219        TransformationType::TreePath => {
220            let type_ = env.type_(transform.typ);
221            if let library::Type::Basic(library::Basic::Utf8) = type_ {
222                if let Some(type_tid) = env.library.find_type(0, "Gtk.TreePath") {
223                    transform.typ = type_tid;
224                    transform.conversion_type = ConversionType::Direct;
225                    if let Some(rust_par) = parameters.rust_parameters.get_mut(transform.ind_rust) {
226                        rust_par.typ = type_tid;
227                        rust_par.ref_mode = RefMode::None;
228                    }
229                } else {
230                    error!("Type Gtk.TreePath not found for treepath transformation");
231                }
232            } else {
233                error!(
234                    "Wrong parameter type for treepath transformation {:?}",
235                    transform.typ
236                );
237            }
238        }
239    }
240}