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