libgir/analysis/
ffi_type.rs

1use log::{info, trace};
2
3use crate::{
4    analysis::{
5        c_type::{implements_c_type, rustify_pointers},
6        is_gpointer,
7        rust_type::{Result, TypeError},
8    },
9    env::Env,
10    library::*,
11    nameutil::{use_glib_if_needed, use_glib_type},
12    traits::*,
13};
14
15pub fn used_ffi_type(env: &Env, type_id: TypeId, c_type: &str) -> Option<String> {
16    let (_ptr, inner) = rustify_pointers(c_type);
17    let type_ = ffi_inner(env, type_id, &inner);
18    type_.ok().and_then(|type_name| {
19        if type_name.as_str().find(':').is_some() {
20            Some(type_name.into_string())
21        } else {
22            None
23        }
24    })
25}
26
27pub fn ffi_type(env: &Env, tid: TypeId, c_type: &str) -> Result {
28    let (ptr, inner) = rustify_pointers(c_type);
29    let res = if ptr.is_empty() {
30        if let Some(c_tid) = env.library.find_type(0, c_type) {
31            // Fast track plain basic types avoiding some checks
32            if env.library.type_(c_tid).maybe_ref_as::<Basic>().is_some() {
33                match env.library.type_(tid) {
34                    Type::FixedArray(_, size, _) => {
35                        ffi_inner(env, c_tid, c_type).map_any(|rust_type| {
36                            rust_type.alter_type(|typ_| format!("[{typ_}; {size}]"))
37                        })
38                    }
39                    Type::Class(Class {
40                        c_type: expected, ..
41                    })
42                    | Type::Interface(Interface {
43                        c_type: expected, ..
44                    }) if is_gpointer(c_type) => {
45                        info!("[c:type `gpointer` instead of `*mut {}`, fixing]", expected);
46                        ffi_inner(env, tid, expected).map_any(|rust_type| {
47                            rust_type.alter_type(|typ_| format!("*mut {typ_}"))
48                        })
49                    }
50                    _ => ffi_inner(env, c_tid, c_type),
51                }
52            } else {
53                // c_type isn't basic
54                ffi_inner(env, tid, &inner)
55            }
56        } else {
57            // c_type doesn't match any type in the library by name
58            ffi_inner(env, tid, &inner)
59        }
60    } else {
61        // ptr not empty
62        ffi_inner(env, tid, &inner)
63            .map_any(|rust_type| rust_type.alter_type(|typ_| format!("{ptr} {typ_}")))
64    };
65    trace!("ffi_type({:?}, {}) -> {:?}", tid, c_type, res);
66    res
67}
68
69fn ffi_inner(env: &Env, tid: TypeId, inner: &str) -> Result {
70    let typ = env.library.type_(tid);
71    match *typ {
72        Type::Basic(fund) => {
73            use crate::library::Basic::*;
74            let inner = match fund {
75                None => "std::ffi::c_void",
76                Boolean => return Ok(use_glib_if_needed(env, "ffi::gboolean").into()),
77                Int8 => "i8",
78                UInt8 => "u8",
79                Int16 => "i16",
80                UInt16 => "u16",
81                Int32 => "i32",
82                UInt32 => "u32",
83                Int64 => "i64",
84                UInt64 => "u64",
85                Char => "std::ffi::c_char",
86                UChar => "std::ffi::c_uchar",
87                Short => "std::ffi::c_short",
88                UShort => "std::ffi::c_ushort",
89                Int => "std::ffi::c_int",
90                UInt => "std::ffi::c_uint",
91                Long => "std::ffi::c_long",
92                ULong => "std::ffi::c_ulong",
93                Size => "libc::size_t",
94                SSize => "libc::ssize_t",
95                Float => "std::ffi::c_float",
96                Double => "std::ffi::c_double",
97                TimeT => "libc::time_t",
98                OffT => "libc::off_t",
99                DevT => "libc::dev_t",
100                GidT => "libc::gid_t",
101                PidT => "libc::pid_t",
102                SockLenT => "libc::socklen_t",
103                UidT => "libc::uid_t",
104                UniChar => "u32",
105                Utf8 => "std::ffi::c_char",
106                Filename => "std::ffi::c_char",
107                Type => return Ok(use_glib_if_needed(env, "ffi::GType").into()),
108                IntPtr => "libc::intptr_t",
109                UIntPtr => "libc::uintptr_t",
110                Bool => "bool",
111                _ => return Err(TypeError::Unimplemented(inner.into())),
112            };
113            Ok(inner.into())
114        }
115        Type::Union(..) | Type::Record(..) | Type::Alias(..) | Type::Function(..) => {
116            if let Some(declared_c_type) = typ.get_glib_name() {
117                if declared_c_type != inner {
118                    let msg = format!(
119                        "[c:type mismatch `{}` != `{}` of `{}`]",
120                        inner,
121                        declared_c_type,
122                        typ.get_name()
123                    );
124                    warn_main!(tid, "{}", msg);
125                    return Err(TypeError::Mismatch(msg));
126                }
127            } else {
128                warn_main!(tid, "Type `{}` missing c_type", typ.get_name());
129            }
130            fix_name(env, tid, inner)
131        }
132        Type::CArray(inner_tid) => ffi_inner(env, inner_tid, inner),
133        Type::FixedArray(inner_tid, size, _) => ffi_inner(env, inner_tid, inner)
134            .map_any(|rust_type| rust_type.alter_type(|typ_| format!("[{typ_}; {size}]"))),
135        Type::Array(..)
136        | Type::PtrArray(..)
137        | Type::List(..)
138        | Type::SList(..)
139        | Type::HashTable(..) => fix_name(env, tid, inner),
140        _ => {
141            if let Some(glib_name) = env.library.type_(tid).get_glib_name() {
142                if inner != glib_name {
143                    if implements_c_type(env, tid, inner) {
144                        info!(
145                            "[c:type {} of {} <: {}, fixing]",
146                            glib_name,
147                            env.library.type_(tid).get_name(),
148                            inner
149                        );
150                        fix_name(env, tid, glib_name)
151                    } else {
152                        let msg = format!(
153                            "[c:type mismatch {} != {} of {}]",
154                            inner,
155                            glib_name,
156                            env.library.type_(tid).get_name()
157                        );
158                        warn_main!(tid, "{}", msg);
159                        Err(TypeError::Mismatch(msg))
160                    }
161                } else {
162                    fix_name(env, tid, inner)
163                }
164            } else {
165                let msg = format!(
166                    "[Missing glib_name of {}, can't match != {}]",
167                    env.library.type_(tid).get_name(),
168                    inner
169                );
170                warn_main!(tid, "{}", msg);
171                Err(TypeError::Mismatch(msg))
172            }
173        }
174    }
175}
176
177fn fix_name(env: &Env, type_id: TypeId, name: &str) -> Result {
178    if type_id.ns_id == INTERNAL_NAMESPACE {
179        match env.library.type_(type_id) {
180            Type::Array(..)
181            | Type::PtrArray(..)
182            | Type::List(..)
183            | Type::SList(..)
184            | Type::HashTable(..) => Ok(use_glib_if_needed(env, &format!("ffi::{name}")).into()),
185            _ => Ok(name.into()),
186        }
187    } else {
188        let sys_crate_name = &env.namespaces[type_id.ns_id].sys_crate_name;
189        let sys_crate_name = if sys_crate_name == "gobject_ffi" {
190            use_glib_type(env, "gobject_ffi")
191        } else if type_id.ns_id == MAIN_NAMESPACE {
192            sys_crate_name.clone()
193        } else {
194            format!(
195                "{}::{}",
196                env.namespaces[type_id.ns_id].crate_name, sys_crate_name
197            )
198        };
199        let name_with_prefix = format!("{sys_crate_name}::{name}");
200        if env
201            .type_status_sys(&type_id.full_name(&env.library))
202            .ignored()
203        {
204            Err(TypeError::Ignored(name_with_prefix))
205        } else {
206            Ok(name_with_prefix.into())
207        }
208    }
209}