1use crate::types::{make_ident, QualifiedName};
10use indexmap::map::IndexMap as HashMap;
11use indoc::indoc;
12use once_cell::sync::OnceCell;
13use syn::{parse_quote, TypePath};
14
15#[derive(Debug)]
17enum Behavior {
18 CxxContainerPtr,
19 CxxContainerVector,
20 CxxString,
21 RustStr,
22 RustString,
23 RustByValue,
24 CByValue,
25 CByValueVecSafe,
26 CVariableLengthByValue,
27 CVoid,
28 CChar16,
29 RustContainerByValueSafe,
30}
31
32#[derive(Debug)]
34struct TypeDetails {
35 rs_name: String,
37 cpp_name: String,
39 behavior: Behavior,
41 extra_non_canonical_name: Option<String>,
43 has_const_copy_constructor: bool,
44 has_move_constructor: bool,
45}
46
47impl TypeDetails {
48 fn new(
49 rs_name: impl Into<String>,
50 cpp_name: impl Into<String>,
51 behavior: Behavior,
52 extra_non_canonical_name: Option<String>,
53 has_const_copy_constructor: bool,
54 has_move_constructor: bool,
55 ) -> Self {
56 TypeDetails {
57 rs_name: rs_name.into(),
58 cpp_name: cpp_name.into(),
59 behavior,
60 extra_non_canonical_name,
61 has_const_copy_constructor,
62 has_move_constructor,
63 }
64 }
65
66 fn get_prelude_entry(&self) -> Option<String> {
68 match self.behavior {
69 Behavior::RustString
70 | Behavior::RustStr
71 | Behavior::CxxString
72 | Behavior::CxxContainerPtr
73 | Behavior::CxxContainerVector
74 | Behavior::RustContainerByValueSafe => {
75 let tn = QualifiedName::new_from_cpp_name(&self.rs_name);
76 let cxx_name = tn.get_final_item();
77 let (templating, payload) = match self.behavior {
78 Behavior::CxxContainerPtr
79 | Behavior::CxxContainerVector
80 | Behavior::RustContainerByValueSafe => ("template<typename T> ", "T* ptr"),
81 _ => ("", "char* ptr"),
82 };
83 Some(format!(
84 indoc! {"
85 /**
86 * <div rustbindgen=\"true\" replaces=\"{}\"></div>
87 */
88 {}class {} {{
89 {};
90 }};
91 "},
92 self.cpp_name, templating, cxx_name, payload
93 ))
94 }
95 _ => None,
96 }
97 }
98
99 fn to_type_path(&self) -> TypePath {
100 let mut segs = self.rs_name.split("::").peekable();
101 if segs.peek().map(|seg| seg.is_empty()).unwrap_or_default() {
102 segs.next();
103 let segs = segs.map(make_ident);
104 parse_quote! {
105 ::#(#segs)::*
106 }
107 } else {
108 let segs = segs.map(make_ident);
109 parse_quote! {
110 #(#segs)::*
111 }
112 }
113 }
114
115 fn to_typename(&self) -> QualifiedName {
116 QualifiedName::new_from_cpp_name(&self.rs_name)
117 }
118
119 fn get_generic_behavior(&self) -> CxxGenericType {
120 match self.behavior {
121 Behavior::CxxContainerPtr => CxxGenericType::CppPtr,
122 Behavior::CxxContainerVector => CxxGenericType::CppVector,
123 Behavior::RustContainerByValueSafe => CxxGenericType::Rust,
124 _ => CxxGenericType::Not,
125 }
126 }
127}
128
129#[derive(Default)]
131pub(crate) struct TypeDatabase {
132 by_rs_name: HashMap<QualifiedName, TypeDetails>,
133 canonical_names: HashMap<QualifiedName, QualifiedName>,
134}
135
136pub(crate) fn known_types() -> &'static TypeDatabase {
138 static KNOWN_TYPES: OnceCell<TypeDatabase> = OnceCell::new();
139 KNOWN_TYPES.get_or_init(create_type_database)
140}
141
142#[derive(PartialEq, Eq, Clone, Copy)]
144pub enum CxxGenericType {
145 Not,
147 CppPtr,
150 CppVector,
153 Rust,
155}
156
157pub struct KnownTypeConstructorDetails {
158 pub has_move_constructor: bool,
159 pub has_const_copy_constructor: bool,
160}
161
162impl TypeDatabase {
163 fn get(&self, ty: &QualifiedName) -> Option<&TypeDetails> {
164 let canonical_name = self.canonical_names.get(ty).unwrap_or(ty);
169 self.by_rs_name.get(canonical_name)
170 }
171
172 pub(crate) fn get_prelude(&self) -> String {
181 itertools::join(
182 self.by_rs_name
183 .values()
184 .filter_map(|t| t.get_prelude_entry()),
185 "",
186 )
187 }
188
189 pub(crate) fn all_names(&self) -> impl Iterator<Item = &QualifiedName> {
191 self.canonical_names.keys().chain(self.by_rs_name.keys())
192 }
193
194 pub(crate) fn get_pod_safe_types(&self) -> impl Iterator<Item = (QualifiedName, bool)> {
197 let pod_safety = self
198 .all_names()
199 .map(|tn| {
200 (
201 tn.clone(),
202 match self.get(tn).unwrap().behavior {
203 Behavior::CxxContainerPtr
204 | Behavior::RustStr
205 | Behavior::RustString
206 | Behavior::RustByValue
207 | Behavior::CByValueVecSafe
208 | Behavior::CByValue
209 | Behavior::CVariableLengthByValue
210 | Behavior::CChar16
211 | Behavior::RustContainerByValueSafe => true,
212 Behavior::CxxString | Behavior::CxxContainerVector | Behavior::CVoid => {
213 false
214 }
215 },
216 )
217 })
218 .collect::<HashMap<_, _>>();
219 pod_safety.into_iter()
220 }
221
222 pub(crate) fn get_constructor_details(
223 &self,
224 qn: &QualifiedName,
225 ) -> Option<KnownTypeConstructorDetails> {
226 self.get(qn).map(|x| KnownTypeConstructorDetails {
227 has_move_constructor: x.has_move_constructor,
228 has_const_copy_constructor: x.has_const_copy_constructor,
229 })
230 }
231
232 pub(crate) fn should_dereference_in_cpp(&self, tn: &QualifiedName) -> bool {
236 self.get(tn)
237 .map(|td| matches!(td.behavior, Behavior::RustStr))
238 .unwrap_or(false)
239 }
240
241 pub(crate) fn lacks_copy_constructor(&self, tn: &QualifiedName) -> bool {
243 self.get(tn)
244 .map(|td| {
245 matches!(
246 td.behavior,
247 Behavior::CxxContainerPtr
248 | Behavior::CxxContainerVector
249 | Behavior::RustContainerByValueSafe
250 )
251 })
252 .unwrap_or(false)
253 }
254
255 pub(crate) fn consider_substitution(&self, tn: &QualifiedName) -> Option<TypePath> {
261 self.get(tn).map(|td| td.to_type_path())
262 }
263
264 pub(crate) fn special_cpp_name(&self, rs: &QualifiedName) -> Option<String> {
265 self.get(rs).map(|x| x.cpp_name.to_string())
266 }
267
268 pub(crate) fn is_known_type(&self, ty: &QualifiedName) -> bool {
269 self.get(ty).is_some()
270 }
271
272 pub(crate) fn is_known_subtitute_type(&self, ty: &QualifiedName) -> bool {
274 if ty.get_namespace().is_empty() {
275 self.all_names()
276 .any(|n| n.get_final_item() == ty.get_final_item())
277 } else {
278 false
279 }
280 }
281
282 pub(crate) fn known_type_type_path(&self, ty: &QualifiedName) -> Option<TypePath> {
283 self.get(ty).map(|td| td.to_type_path())
284 }
285
286 pub(crate) fn is_ctype(&self, ty: &QualifiedName) -> bool {
289 self.get(ty)
290 .map(|td| {
291 matches!(
292 td.behavior,
293 Behavior::CVariableLengthByValue | Behavior::CVoid | Behavior::CChar16
294 )
295 })
296 .unwrap_or(false)
297 }
298
299 pub(crate) fn cxx_generic_behavior(&self, ty: &QualifiedName) -> CxxGenericType {
303 self.get(ty)
304 .map(|x| x.get_generic_behavior())
305 .unwrap_or(CxxGenericType::Not)
306 }
307
308 pub(crate) fn is_cxx_acceptable_receiver(&self, ty: &QualifiedName) -> bool {
309 self.get(ty).is_none() }
312
313 pub(crate) fn permissible_within_vector(&self, ty: &QualifiedName) -> bool {
314 self.get(ty)
315 .map(|x| matches!(x.behavior, Behavior::CxxString | Behavior::CByValueVecSafe))
316 .unwrap_or(true)
317 }
318
319 pub(crate) fn permissible_within_unique_ptr(&self, ty: &QualifiedName) -> bool {
320 self.get(ty)
321 .map(|x| {
322 matches!(
323 x.behavior,
324 Behavior::CxxString | Behavior::CxxContainerVector
325 )
326 })
327 .unwrap_or(true)
328 }
329
330 pub(crate) fn conflicts_with_built_in_type(&self, ty: &QualifiedName) -> bool {
331 self.get(ty).is_some()
332 }
333
334 pub(crate) fn convertible_from_strs(&self, ty: &QualifiedName) -> bool {
335 self.get(ty)
336 .map(|x| matches!(x.behavior, Behavior::CxxString))
337 .unwrap_or(false)
338 }
339
340 fn insert(&mut self, td: TypeDetails) {
341 let rs_name = td.to_typename();
342 if let Some(extra_non_canonical_name) = &td.extra_non_canonical_name {
343 self.canonical_names.insert(
344 QualifiedName::new_from_cpp_name(extra_non_canonical_name),
345 rs_name.clone(),
346 );
347 }
348 self.canonical_names.insert(
349 QualifiedName::new_from_cpp_name(&td.cpp_name),
350 rs_name.clone(),
351 );
352 self.by_rs_name.insert(rs_name, td);
353 }
354
355 pub(crate) fn get_moveit_safe_types(&self) -> impl Iterator<Item = QualifiedName> + '_ {
356 self.all_names()
357 .filter(|tn| {
358 !matches!(
359 self.get(tn).unwrap().behavior,
360 Behavior::CxxString | Behavior::CxxContainerVector
361 )
362 })
363 .cloned()
364 }
365}
366
367fn create_type_database() -> TypeDatabase {
368 let mut db = TypeDatabase::default();
369 db.insert(TypeDetails::new(
370 "cxx::UniquePtr",
371 "std::unique_ptr",
372 Behavior::CxxContainerPtr,
373 None,
374 false,
375 true,
376 ));
377 db.insert(TypeDetails::new(
378 "cxx::CxxVector",
379 "std::vector",
380 Behavior::CxxContainerVector,
381 None,
382 false,
383 true,
384 ));
385 db.insert(TypeDetails::new(
386 "cxx::SharedPtr",
387 "std::shared_ptr",
388 Behavior::CxxContainerPtr,
389 None,
390 true,
391 true,
392 ));
393 db.insert(TypeDetails::new(
394 "cxx::WeakPtr",
395 "std::weak_ptr",
396 Behavior::CxxContainerPtr,
397 None,
398 true,
399 true,
400 ));
401 db.insert(TypeDetails::new(
402 "cxx::CxxString",
403 "std::string",
404 Behavior::CxxString,
405 None,
406 true,
407 true,
408 ));
409 db.insert(TypeDetails::new(
410 "str",
411 "rust::Str",
412 Behavior::RustStr,
413 None,
414 true,
415 false,
416 ));
417 db.insert(TypeDetails::new(
418 "String",
419 "rust::String",
420 Behavior::RustString,
421 None,
422 true,
423 true,
424 ));
425 db.insert(TypeDetails::new(
426 "std::boxed::Box",
427 "rust::Box",
428 Behavior::RustContainerByValueSafe,
429 None,
430 false,
431 true,
432 ));
433 db.insert(TypeDetails::new(
434 "i8",
435 "int8_t",
436 Behavior::CByValueVecSafe,
437 Some("std::os::raw::c_schar".into()),
438 true,
439 true,
440 ));
441 db.insert(TypeDetails::new(
442 "u8",
443 "uint8_t",
444 Behavior::CByValueVecSafe,
445 Some("std::os::raw::c_uchar".into()),
446 true,
447 true,
448 ));
449 for (cpp_type, rust_type) in (4..7).map(|x| 2i32.pow(x)).flat_map(|x| {
450 vec![
451 (format!("uint{x}_t"), format!("u{x}")),
452 (format!("int{x}_t"), format!("i{x}")),
453 ]
454 }) {
455 db.insert(TypeDetails::new(
456 rust_type,
457 cpp_type,
458 Behavior::CByValueVecSafe,
459 None,
460 true,
461 true,
462 ));
463 }
464 db.insert(TypeDetails::new(
465 "bool",
466 "bool",
467 Behavior::CByValue,
468 None,
469 true,
470 true,
471 ));
472
473 db.insert(TypeDetails::new(
474 "core::pin::Pin",
475 "Pin",
476 Behavior::RustByValue, Some("std::pin::Pin".to_string()),
478 true,
479 false,
480 ));
481
482 let mut insert_ctype = |cname: &str| {
483 let concatenated_name = cname.replace(' ', "");
484 db.insert(TypeDetails::new(
485 format!("autocxx::c_{concatenated_name}"),
486 cname,
487 Behavior::CVariableLengthByValue,
488 Some(format!("std::os::raw::c_{concatenated_name}")),
489 true,
490 true,
491 ));
492 db.insert(TypeDetails::new(
493 format!("autocxx::c_u{concatenated_name}"),
494 format!("unsigned {cname}"),
495 Behavior::CVariableLengthByValue,
496 Some(format!("std::os::raw::c_u{concatenated_name}")),
497 true,
498 true,
499 ));
500 };
501
502 insert_ctype("long");
503 insert_ctype("int");
504 insert_ctype("short");
505 insert_ctype("long long");
506
507 db.insert(TypeDetails::new(
508 "f32",
509 "float",
510 Behavior::CByValueVecSafe,
511 None,
512 true,
513 true,
514 ));
515 db.insert(TypeDetails::new(
516 "f64",
517 "double",
518 Behavior::CByValueVecSafe,
519 None,
520 true,
521 true,
522 ));
523 db.insert(TypeDetails::new(
524 "::std::os::raw::c_char",
525 "char",
526 Behavior::CByValue,
527 None,
528 true,
529 true,
530 ));
531 db.insert(TypeDetails::new(
532 "usize",
533 "size_t",
534 Behavior::CByValueVecSafe,
535 None,
536 true,
537 true,
538 ));
539 db.insert(TypeDetails::new(
540 "autocxx::c_void",
541 "void",
542 Behavior::CVoid,
543 Some("std::os::raw::c_void".into()),
544 false,
545 false,
546 ));
547 db.insert(TypeDetails::new(
548 "autocxx::c_char16_t",
549 "char16_t",
550 Behavior::CChar16,
551 Some("c_char16_t".into()),
552 false,
553 false,
554 ));
555 db
556}