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 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 ffi_inner(env, tid, &inner)
55 }
56 } else {
57 ffi_inner(env, tid, &inner)
59 }
60 } else {
61 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}