Skip to main content

opencv_binding_generator/
class.rs

1use std::borrow::Cow;
2use std::collections::HashSet;
3use std::hash::{Hash, Hasher};
4use std::ops::ControlFlow;
5use std::rc::Rc;
6use std::{fmt, iter};
7
8use clang::{Accessibility, Entity, EntityKind};
9pub use desc::ClassDesc;
10
11use crate::debug::{DefinitionLocation, LocationName};
12use crate::element::ExcludeKind;
13use crate::entity::{ControlFlowExt, ToEntity};
14use crate::field::FieldDesc;
15use crate::func::{FuncCppBody, FuncDesc, FuncKind, ReturnKind};
16use crate::settings::PropertyReadWrite;
17use crate::type_ref::{Constness, CppNameStyle, StrEnc, StrType, TypeRef, TypeRefDesc, TypeRefTypeHint};
18use crate::writer::rust_native::element::RustElement;
19use crate::{
20	ClassKindOverride, Const, DefaultElement, Element, EntityExt, Enum, Field, Func, GeneratedType, GeneratorEnv, NameDebug,
21	StrExt, settings,
22};
23
24mod desc;
25
26#[derive(Clone)]
27pub enum Class<'tu, 'ge> {
28	Clang {
29		entity: Entity<'tu>,
30		custom_fullname: Option<Rc<str>>,
31		gen_env: &'ge GeneratorEnv<'tu>,
32	},
33	Desc(Rc<ClassDesc<'tu, 'ge>>),
34}
35
36impl<'tu, 'ge> Class<'tu, 'ge> {
37	pub fn new(entity: Entity<'tu>, gen_env: &'ge GeneratorEnv<'tu>) -> Self {
38		Self::Clang {
39			entity,
40			custom_fullname: None,
41			gen_env,
42		}
43	}
44
45	pub fn new_ext(entity: Entity<'tu>, custom_fullname: impl Into<Rc<str>>, gen_env: &'ge GeneratorEnv<'tu>) -> Self {
46		Self::Clang {
47			entity,
48			custom_fullname: Some(custom_fullname.into()),
49			gen_env,
50		}
51	}
52
53	pub fn new_desc(desc: ClassDesc<'tu, 'ge>) -> Self {
54		Self::Desc(Rc::new(desc))
55	}
56
57	/// Checks whether a class can be simple on Rust side, i.e. represented by plain struct with fields
58	pub fn can_be_simple(&self) -> bool {
59		let cpp_refname = self.cpp_name(CppNameStyle::Reference);
60		settings::IMPLEMENTED_GENERICS.contains(cpp_refname.as_ref())
61			|| self.has_fields()
62				&& !self.has_descendants()
63				&& !self.has_bases()
64				&& !self
65					.for_each_field(|field| {
66						let type_ref = field.type_ref();
67						ControlFlow::continue_until(!type_ref.kind().is_copy(type_ref.type_hint()))
68					})
69					.is_break()
70	}
71
72	pub fn kind(&self) -> ClassKind {
73		match self {
74			&Self::Clang { entity, gen_env, .. } => {
75				if settings::ELEMENT_EXCLUDE_KIND
76					.get(self.cpp_name(CppNameStyle::Reference).as_ref())
77					.is_some_and(|ek| ek.is_excluded())
78				{
79					return ClassKind::Other;
80				}
81				match gen_env.get_export_config(entity).map(|c| c.class_kind_override) {
82					Some(ClassKindOverride::Simple) => {
83						if self.can_be_simple() {
84							ClassKind::Simple
85						} else {
86							ClassKind::BoxedForced
87						}
88					}
89					Some(ClassKindOverride::Boxed) => ClassKind::Boxed,
90					Some(ClassKindOverride::BoxedForced) => ClassKind::BoxedForced,
91					Some(ClassKindOverride::System) => ClassKind::System,
92					None => {
93						if self.is_system() {
94							ClassKind::System
95						} else if let Some(kind) = gen_env.get_class_kind(entity) {
96							match kind {
97								ClassKind::Simple if !self.can_be_simple() => ClassKind::BoxedForced,
98								_ => kind,
99							}
100						} else {
101							ClassKind::Other
102						}
103					}
104				}
105			}
106			Self::Desc(desc) => desc.kind,
107		}
108	}
109
110	pub fn type_ref(&self) -> TypeRef<'tu, 'ge> {
111		match self {
112			&Self::Clang { entity, gen_env, .. } => TypeRef::new(entity.get_type().expect("Can't get class type"), gen_env),
113			Self::Desc(desc) => TypeRef::guess(desc.cpp_fullname.as_ref(), desc.rust_module),
114		}
115	}
116
117	/// Returns `Some` with the string type if the current class name refers to a C++ `std::string` or `cv::String`
118	pub fn string_type(&self) -> Option<StrType> {
119		let cpp_refname = self.cpp_name(CppNameStyle::Reference);
120		if cpp_refname.starts_with("std::") && cpp_refname.ends_with("::string") {
121			Some(StrType::StdString(StrEnc::Text))
122		} else if cpp_refname == "cv::String" {
123			Some(StrType::CvString(StrEnc::Text))
124		} else {
125			None
126		}
127	}
128
129	pub fn template_kind(&self) -> TemplateKind<'tu, 'ge> {
130		match self {
131			&Self::Clang { entity, gen_env, .. } => {
132				if entity.get_template_kind().is_some() {
133					TemplateKind::Template
134				} else if let Some(template_entity) = entity.get_template() {
135					TemplateKind::Specialization(Self::new(template_entity, gen_env))
136				} else {
137					TemplateKind::No
138				}
139			}
140			Self::Desc(desc) => desc.template_kind.clone(),
141		}
142	}
143
144	pub fn is_abstract(&self) -> bool {
145		match self {
146			&Self::Clang { entity, .. } => entity.is_abstract_record(),
147			Self::Desc(desc) => desc.is_abstract,
148		}
149	}
150
151	/// True if a class has virtual methods
152	pub fn is_polymorphic(&self) -> bool {
153		match self {
154			Self::Clang { entity, .. } => entity
155				.walk_methods_while(|f| ControlFlow::continue_until(f.is_virtual_method() || f.is_pure_virtual_method()))
156				.is_break(),
157			Self::Desc(_) => false,
158		}
159	}
160
161	/// Special case of an empty class with only an anonymous enum inside (e.g., DrawLinesMatchesFlags)
162	pub fn as_enum(&self) -> Option<Enum<'tu, 'ge>> {
163		match self {
164			&Self::Clang { entity, gen_env, .. } => {
165				if !self.has_methods() && !self.has_fields() && !self.has_descendants() && !self.has_bases() {
166					let children = entity.get_children();
167					if let [single] = children.as_slice() {
168						if matches!(single.get_kind(), EntityKind::EnumDecl) {
169							Some(Enum::new_ext(
170								*single,
171								self.cpp_name(CppNameStyle::Declaration).as_ref(),
172								gen_env,
173							))
174						} else {
175							None
176						}
177					} else {
178						None
179					}
180				} else {
181					None
182				}
183			}
184			Self::Desc(_) => None,
185		}
186	}
187
188	/// Class has an explicit method named `clone()`
189	pub fn has_explicit_clone(&self) -> bool {
190		self.for_each_method(|m| ControlFlow::continue_until(m.is_clone())).is_break()
191	}
192
193	/// Class is simple (i.e. constructor-copiable in C++), but can't be simple in Rust
194	pub fn has_implicit_clone(&self) -> bool {
195		!self.is_abstract() && matches!(self.kind(), ClassKind::BoxedForced) && !self.has_virtual_destructor()
196	}
197
198	pub fn has_implicit_default_constructor(&self) -> bool {
199		match self {
200			&Self::Clang { entity, .. } => !entity
201				.walk_children_while(|f| {
202					if matches!(f.get_kind(), EntityKind::Constructor) {
203						ControlFlow::Break(())
204					} else {
205						ControlFlow::Continue(())
206					}
207				})
208				.is_break(),
209			Self::Desc(_) => false,
210		}
211	}
212
213	pub fn has_virtual_destructor(&self) -> bool {
214		match self {
215			Class::Clang { entity, .. } => entity
216				.walk_children_while(|f| ControlFlow::continue_until(f.get_kind() == EntityKind::Destructor && f.is_virtual_method()))
217				.is_break(),
218			Class::Desc(_) => false,
219		}
220	}
221
222	pub fn has_private_destructor(&self) -> bool {
223		match self {
224			Class::Clang { entity, .. } => entity
225				.walk_children_while(|f| {
226					ControlFlow::continue_until(
227						f.get_kind() == EntityKind::Destructor && f.get_accessibility() != Some(Accessibility::Public),
228					)
229				})
230				.is_break(),
231			Class::Desc(_) => false,
232		}
233	}
234
235	pub fn has_bases(&self) -> bool {
236		match self {
237			&Self::Clang { entity, .. } => entity.walk_bases_while(|_| ControlFlow::Break(())).is_break(),
238			Self::Desc(desc) => !desc.bases.is_empty(),
239		}
240	}
241
242	pub fn bases(&self) -> Cow<'_, [Class<'tu, 'ge>]> {
243		match self {
244			&Self::Clang { entity, gen_env, .. } => {
245				let mut out = vec![];
246				let entity = entity.get_template().unwrap_or(entity);
247				let _ = entity.walk_bases_while(|child| {
248					out.push(Self::new(Self::definition_entity(child), gen_env));
249					ControlFlow::Continue(())
250				});
251				out.into()
252			}
253			Self::Desc(desc) => desc.bases.as_ref().into(),
254		}
255	}
256
257	pub fn all_bases(&self) -> HashSet<Class<'tu, 'ge>> {
258		#![expect(clippy::mutable_key_type)]
259		self
260			.bases()
261			.into_owned()
262			.into_iter()
263			.flat_map(|b| {
264				let mut out = b.all_bases();
265				out.insert(b);
266				out
267			})
268			.collect()
269	}
270
271	pub fn has_descendants(&self) -> bool {
272		match self {
273			&Self::Clang { gen_env, .. } => gen_env.descendants_of(&self.cpp_name(CppNameStyle::Reference)).is_some(),
274			Self::Desc(_) => false,
275		}
276	}
277
278	pub fn descendants(&self) -> HashSet<Class<'tu, 'ge>> {
279		#![expect(clippy::mutable_key_type)]
280		match self {
281			&Self::Clang { gen_env, .. } => gen_env
282				.descendants_of(&self.cpp_name(CppNameStyle::Reference))
283				.into_iter()
284				.flat_map(|desc| desc.iter().map(|e| Self::new(*e, gen_env)))
285				.collect(),
286			Self::Desc(_) => HashSet::new(),
287		}
288	}
289
290	pub fn all_descendants(&self) -> HashSet<Class<'tu, 'ge>> {
291		#![expect(clippy::mutable_key_type)]
292		self
293			.descendants()
294			.into_iter()
295			.flat_map(|descendant| {
296				let mut out = descendant.all_descendants();
297				out.insert(descendant);
298				out
299			})
300			.collect()
301	}
302
303	pub fn all_family(&self) -> HashSet<Class<'tu, 'ge>> {
304		#![expect(clippy::mutable_key_type)]
305		fn collect<'tu, 'ge>(out: &mut HashSet<Class<'tu, 'ge>>, cls: Class<'tu, 'ge>) {
306			if out.insert(cls.clone()) {
307				for base in cls.bases().into_owned() {
308					collect(out, base);
309				}
310				for desc in cls.descendants() {
311					collect(out, desc);
312				}
313			}
314		}
315
316		let mut out = HashSet::new();
317		collect(&mut out, self.clone());
318		out
319	}
320
321	pub fn has_methods(&self) -> bool {
322		self.for_each_method(|_| ControlFlow::Break(())).is_break()
323	}
324
325	#[inline]
326	pub fn for_each_method(&self, mut predicate: impl FnMut(Func<'tu, 'ge>) -> ControlFlow<()>) -> ControlFlow<()> {
327		match self {
328			&Self::Clang { entity, gen_env, .. } => entity.walk_methods_while(|f| predicate(Func::new(f, gen_env))),
329			Self::Desc(_) => ControlFlow::Continue(()),
330		}
331	}
332
333	pub fn methods(&self, filter: impl Fn(&Func) -> bool) -> Vec<Func<'tu, 'ge>> {
334		match self {
335			Class::Clang { entity, gen_env, .. } => {
336				let mut out = Vec::with_capacity(32);
337				let _ = entity.walk_methods_while(|func_entity| {
338					let func = Func::new(func_entity, gen_env);
339					let func: Func = if let Some(func_fact) = gen_env.settings.func_replace.get(&mut func.matcher()) {
340						func_fact(&func)
341					} else {
342						func
343					};
344					if func.is_generic()
345						&& let Some(specs) = gen_env.settings.func_specialize.get(&mut func.matcher())
346					{
347						for spec in specs {
348							let spec_func = func.clone().specialize(spec);
349							if filter(&spec_func) {
350								out.push(spec_func);
351							}
352						}
353						return ControlFlow::Continue(());
354					}
355					if filter(&func) {
356						out.push(func);
357					}
358					ControlFlow::Continue(())
359				});
360				for inject_func_fact in &gen_env.settings.func_inject {
361					let inject_func: Func = inject_func_fact();
362					if let Some(cls) = inject_func.kind().as_class_method()
363						&& cls == self
364						&& filter(&inject_func)
365					{
366						out.push(inject_func);
367					}
368				}
369				out
370			}
371			Class::Desc(_) => vec![],
372		}
373	}
374
375	pub fn has_fields(&self) -> bool {
376		self.for_each_field(|_| ControlFlow::Break(())).is_break()
377	}
378
379	#[inline]
380	pub fn for_each_field(&self, mut predicate: impl FnMut(Field<'tu, 'ge>) -> ControlFlow<()>) -> ControlFlow<()> {
381		match self {
382			&Self::Clang { entity, gen_env, .. } => entity.walk_fields_while(|f| predicate(Field::new(f, gen_env))),
383			Self::Desc(_) => ControlFlow::Continue(()),
384		}
385	}
386
387	pub fn fields(&self, filter: impl Fn(&Field) -> bool) -> Vec<Field<'tu, 'ge>> {
388		let mut out = Vec::with_capacity(32);
389		let _ = self.for_each_field(|f| {
390			if filter(&f) {
391				out.push(f);
392			}
393			ControlFlow::Continue(())
394		});
395		out
396	}
397
398	#[inline]
399	pub fn for_each_const(&self, mut predicate: impl FnMut(Const<'tu>) -> ControlFlow<()>) -> ControlFlow<()> {
400		match self {
401			&Self::Clang { entity, .. } => entity.walk_consts_while(|f| predicate(Const::new(f))),
402			Self::Desc(_) => ControlFlow::Continue(()),
403		}
404	}
405
406	pub fn consts(&self) -> Vec<Const<'tu>> {
407		let mut out = Vec::with_capacity(8);
408		let _ = self.for_each_const(|c| {
409			out.push(c);
410			ControlFlow::Continue(())
411		});
412		out
413	}
414
415	pub fn field_methods<'f>(
416		&self,
417		fields: &'f [Field<'tu, 'ge>],
418		constness_filter: Option<Constness>,
419	) -> impl Iterator<Item = Func<'tu, 'ge>> + 'f {
420		match self {
421			&Self::Clang { gen_env, .. } => {
422				let cls = self.clone();
423				let accessor_generator = move |fld: &Field<'tu, 'ge>| {
424					let doc_comment = Rc::from(fld.doc_comment());
425					let def_loc = fld.file_line_name().location;
426					let rust_module = fld.rust_module();
427					let mut fld_type_ref = fld.type_ref();
428					let fld_refname = fld.cpp_name(CppNameStyle::Reference);
429					if let Some(type_hint) = gen_env.settings.property_override.get(fld_refname.as_ref()) {
430						fld_type_ref.to_mut().set_type_hint(type_hint.clone());
431					} else {
432						let fld_type_kind = fld_type_ref.kind();
433						if fld_type_kind
434							.as_pointer()
435							.is_some_and(|inner| inner.kind().as_primitive().is_some())
436							&& !fld_type_kind.is_char_ptr_string(fld_type_ref.type_hint())
437						{
438							fld_type_ref.to_mut().set_type_hint(TypeRefTypeHint::PrimitivePtrAsRaw);
439						} else if fld_type_kind.as_class().is_some_and(|cls| cls.kind().is_trait()) {
440							fld_type_ref.to_mut().set_type_hint(TypeRefTypeHint::TraitClassConcrete);
441						}
442					}
443					let fld_type_kind = fld_type_ref.kind();
444					let return_kind = ReturnKind::infallible(fld_type_kind.return_as_naked(fld_type_ref.type_hint()));
445					let fld_const = fld.constness();
446					let passed_by_ref = fld_type_kind.can_return_as_direct_reference();
447					let prop_tweak = gen_env.settings.property_tweaks.get(fld_refname.as_ref());
448					let rust_custom_leafname = prop_tweak.and_then(|tweak| tweak.rename);
449					let read_write = prop_tweak
450						.and_then(|tweak| tweak.read_write)
451						.unwrap_or(PropertyReadWrite::ReadWrite);
452					let fld_declname = fld_refname.localname();
453					let (mut read_const_yield, mut read_mut_yield) = if read_write.is_read() {
454						if fld_const.is_mut() && passed_by_ref {
455							let read_const_func = if constness_filter.is_none_or(|c| c.is_const()) {
456								Some(Func::new_desc(
457									FuncDesc::new(
458										FuncKind::FieldAccessor(cls.clone(), fld.clone()),
459										Constness::Const,
460										return_kind,
461										fld_declname,
462										rust_module,
463										[],
464										fld_type_ref.as_ref().clone().with_inherent_constness(Constness::Const),
465									)
466									.def_loc(def_loc.clone())
467									.doc_comment(Rc::clone(&doc_comment))
468									.cpp_body(FuncCppBody::ManualCall("{{name}}".into()))
469									.maybe_rust_custom_leafname(rust_custom_leafname),
470								))
471							} else {
472								None
473							};
474							let read_mut_func = if constness_filter.is_none_or(|c| c.is_mut()) {
475								Some(Func::new_desc(
476									FuncDesc::new(
477										FuncKind::FieldAccessor(cls.clone(), fld.clone()),
478										Constness::Mut,
479										return_kind,
480										format!("{fld_declname}Mut"),
481										rust_module,
482										[],
483										fld_type_ref.as_ref().clone().with_inherent_constness(Constness::Mut),
484									)
485									.def_loc(def_loc.clone())
486									.doc_comment(Rc::clone(&doc_comment))
487									.cpp_body(FuncCppBody::ManualCall("{{name}}".into()))
488									.maybe_rust_custom_leafname(rust_custom_leafname.map(|name| format!("{name}_mut"))),
489								))
490							} else {
491								None
492							};
493							(read_const_func, read_mut_func)
494						} else {
495							let single_read_func = if constness_filter.is_none_or(|c| c == fld_const) {
496								Some(Func::new_desc(
497									FuncDesc::new(
498										FuncKind::FieldAccessor(cls.clone(), fld.clone()),
499										fld_const,
500										return_kind,
501										fld_declname,
502										rust_module,
503										[],
504										fld_type_ref.as_ref().clone(),
505									)
506									.def_loc(def_loc.clone())
507									.doc_comment(Rc::clone(&doc_comment))
508									.cpp_body(FuncCppBody::ManualCall("{{name}}".into()))
509									.maybe_rust_custom_leafname(rust_custom_leafname),
510								))
511							} else {
512								None
513							};
514							(single_read_func, None)
515						}
516					} else {
517						(None, None)
518					};
519					let mut write_yield = if read_write.is_write()
520						&& constness_filter.is_none_or(|c| c.is_mut())
521						&& !fld_type_ref.constness().is_const()
522						&& !fld_type_kind.as_fixed_array().is_some()
523					{
524						let (first_letter, rest) = fld_declname.capitalize_first_ascii_letter().expect("Empty fld_declname");
525						Some(Func::new_desc(
526							FuncDesc::new(
527								FuncKind::FieldAccessor(cls.clone(), fld.clone()),
528								Constness::Mut,
529								ReturnKind::InfallibleNaked,
530								format!("set{first_letter}{rest}"),
531								rust_module,
532								[Field::new_desc(FieldDesc {
533									cpp_fullname: "val".into(),
534									type_ref: fld_type_ref.as_ref().clone().with_inherent_constness(Constness::Const),
535									default_value: fld.default_value().map(|v| v.into()),
536								})],
537								TypeRefDesc::void(),
538							)
539							.doc_comment(doc_comment)
540							.def_loc(def_loc)
541							.cpp_body(FuncCppBody::ManualCall("{{name}} = {{args}}".into()))
542							.maybe_rust_custom_leafname(rust_custom_leafname.map(|name| format!("set_{name}"))),
543						))
544					} else {
545						None
546					};
547					iter::from_fn(move || {
548						read_const_yield
549							.take()
550							.or_else(|| read_mut_yield.take())
551							.or_else(|| write_yield.take())
552							.map(|f| {
553								if let Some(func_fact) = gen_env.settings.func_replace.get(&mut f.matcher()) {
554									func_fact(&f)
555								} else {
556									f
557								}
558							})
559					})
560				};
561				FieldMethodsIter::Clang(fields.iter().flat_map(accessor_generator))
562			}
563			Self::Desc(_) => FieldMethodsIter::Desc,
564		}
565	}
566
567	/// Returns an entity that defines current class, for specialized classes (Point_<int>) it's the template (Point_<T>), for
568	/// not fully defined classes it goes to its definition location.
569	fn definition_entity(entity: Entity<'tu>) -> Entity<'tu> {
570		entity.get_template().unwrap_or(entity).get_definition().unwrap_or(entity)
571	}
572
573	pub fn is_definition(&self) -> bool {
574		match self {
575			&Self::Clang { entity, .. } => {
576				let class_loc = entity.get_location();
577				let def_loc = entity.get_definition().and_then(|d| d.get_location());
578				match (class_loc, def_loc) {
579					(Some(class_loc), Some(def_loc)) => class_loc == def_loc,
580					(_, None) => false,
581					_ => true,
582				}
583			}
584			Self::Desc(_) => true,
585		}
586	}
587
588	pub fn generated_types(&self) -> Vec<GeneratedType<'tu, 'ge>> {
589		self
590			.fields(|f| f.exclude_kind().is_included())
591			.into_iter()
592			.flat_map(|f| f.type_ref().generated_types())
593			.chain(
594				self
595					.methods(|m| m.exclude_kind().is_included())
596					.into_iter()
597					.flat_map(|m| m.generated_types()),
598			)
599			.collect()
600	}
601}
602
603impl<'tu> ToEntity<'tu> for &Class<'tu, '_> {
604	fn to_entity(self) -> Option<Entity<'tu>> {
605		match self {
606			Class::Clang { entity, .. } => Some(*entity),
607			Class::Desc(_) => None,
608		}
609	}
610}
611
612impl Element for Class<'_, '_> {
613	fn exclude_kind(&self) -> ExcludeKind {
614		match self {
615			Self::Clang { .. } => DefaultElement::exclude_kind(self)
616				.with_is_ignored(|| match self.kind() {
617					ClassKind::Other => true,
618					ClassKind::System => {
619						!settings::IMPLEMENTED_SYSTEM_CLASSES.contains(self.cpp_name(CppNameStyle::Reference).as_ref())
620					}
621					ClassKind::Simple | ClassKind::Boxed | ClassKind::BoxedForced => match self.template_kind() {
622						TemplateKind::Template => true,
623						TemplateKind::No => !self.is_definition() || self.cpp_namespace() == "",
624						TemplateKind::Specialization(_) => {
625							!settings::IMPLEMENTED_GENERICS.contains(self.cpp_name(CppNameStyle::Reference).as_ref())
626						}
627					},
628				})
629				.with_is_excluded(|| match self.kind() {
630					ClassKind::System | ClassKind::Other => true,
631					ClassKind::Simple => false,
632					ClassKind::Boxed | ClassKind::BoxedForced => self.has_private_destructor(),
633				}),
634			Self::Desc(desc) => desc.exclude_kind,
635		}
636	}
637
638	fn is_system(&self) -> bool {
639		match self {
640			&Self::Clang { entity, .. } => DefaultElement::is_system(entity),
641			Self::Desc(desc) => matches!(desc.kind, ClassKind::System),
642		}
643	}
644
645	fn is_public(&self) -> bool {
646		match self {
647			&Self::Clang { entity, .. } => DefaultElement::is_public(entity),
648			Self::Desc(desc) => desc.is_public,
649		}
650	}
651
652	fn doc_comment(&self) -> Cow<'_, str> {
653		match self {
654			Self::Clang { entity, .. } => entity.doc_comment(),
655			Self::Desc(_) => "".into(),
656		}
657	}
658
659	fn cpp_namespace(&self) -> Cow<'_, str> {
660		#[inline(always)]
661		fn inner(cpp_fullname: &str) -> Cow<'_, str> {
662			cpp_fullname.namespace().into()
663		}
664
665		match self {
666			Self::Clang {
667				custom_fullname: Some(cpp_fullname),
668				..
669			} => inner(cpp_fullname.as_ref()),
670			Self::Clang { entity, .. } => DefaultElement::cpp_namespace(*entity).into(),
671			Self::Desc(desc) => inner(desc.cpp_fullname.as_ref()),
672		}
673	}
674
675	fn cpp_name(&self, style: CppNameStyle) -> Cow<'_, str> {
676		match self {
677			Self::Clang {
678				custom_fullname: Some(cpp_fullname),
679				..
680			} => cpp_fullname.cpp_name_from_fullname(style).into(),
681			&Self::Clang { entity, .. } => DefaultElement::cpp_name(self, entity, style),
682			Self::Desc(desc) => desc.cpp_fullname.cpp_name_from_fullname(style).into(),
683		}
684	}
685}
686
687impl Hash for Class<'_, '_> {
688	fn hash<H: Hasher>(&self, state: &mut H) {
689		match self {
690			Self::Clang { entity, .. } => entity.hash(state),
691			Self::Desc(desc) => desc.hash(state),
692		}
693	}
694}
695
696impl PartialEq for Class<'_, '_> {
697	fn eq(&self, other: &Self) -> bool {
698		self.cpp_name(CppNameStyle::Reference) == other.cpp_name(CppNameStyle::Reference) && self.kind() == other.kind()
699	}
700}
701
702impl Eq for Class<'_, '_> {}
703
704impl<'me> NameDebug<'me> for &'me Class<'me, '_> {
705	fn file_line_name(self) -> LocationName<'me> {
706		match self {
707			Class::Clang { entity, .. } => entity.file_line_name(),
708			Class::Desc(desc) => LocationName::new(DefinitionLocation::Generated, desc.cpp_fullname.as_ref()),
709		}
710	}
711}
712
713impl fmt::Debug for Class<'_, '_> {
714	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
715		let mut props = vec![];
716		if self.can_be_simple() {
717			props.push("can_be_simple");
718		}
719		if self.template_kind().is_template() {
720			props.push("template");
721		}
722		if self.template_kind().as_template_specialization().is_some() {
723			props.push("template_specialization");
724		}
725		if self.is_abstract() {
726			props.push("abstract");
727		}
728		if self.is_polymorphic() {
729			props.push("polymorphic");
730		}
731		if self.kind().is_trait() {
732			props.push("trait");
733		}
734		if self.as_enum().is_some() {
735			props.push("enum");
736		}
737		if self.has_explicit_clone() {
738			props.push("has_explicit_clone");
739		}
740		if self.has_implicit_clone() {
741			props.push("has_implicit_clone");
742		}
743		if self.has_virtual_destructor() {
744			props.push("has_virtual_dtor");
745		}
746		if self.has_private_destructor() {
747			props.push("has_private_dtor")
748		}
749		if self.has_bases() {
750			props.push("has_bases");
751		}
752		if self.has_descendants() {
753			props.push("has_descendants");
754		}
755		if self.has_methods() {
756			props.push("has_methods");
757		}
758		if self.has_fields() {
759			props.push("has_fields");
760		}
761		if !self.consts().is_empty() {
762			props.push("has_consts");
763		}
764		if self.is_definition() {
765			props.push("definition");
766		}
767		if matches!(
768			self,
769			Self::Clang {
770				custom_fullname: Some(_),
771				..
772			}
773		) {
774			props.push("custom_fullname");
775		}
776		let mut debug_struct = f.debug_struct(match self {
777			Self::Clang { .. } => "Class::Clang",
778			Self::Desc(_) => "Class::Desc",
779		});
780		self
781			.update_debug_struct(&mut debug_struct)
782			.field("kind", &self.kind())
783			.field("props", &props.join(", "))
784			.field("string_type", &self.string_type())
785			.finish()
786	}
787}
788
789#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
790pub enum ClassKind {
791	/// Simple class, check [`Class::can_be_simple`] for more details
792	Simple,
793	/// Opaque class where all access to its fields is happening by C++ side using a pointer
794	Boxed,
795	/// Marked simple but forced to be boxed by presence of non-simple fields or descendants
796	BoxedForced,
797	/// System class like `std::string`
798	System,
799	/// Class is something else, generally ignored
800	Other,
801}
802
803impl ClassKind {
804	pub fn is_simple(self) -> bool {
805		match self {
806			Self::Simple => true,
807			Self::Boxed | Self::BoxedForced | Self::System | Self::Other => false,
808		}
809	}
810
811	pub fn is_boxed(self) -> bool {
812		match self {
813			Self::Boxed | Self::BoxedForced => true,
814			Self::Simple | Self::Other | Self::System => false,
815		}
816	}
817
818	pub fn is_trait(&self) -> bool {
819		match self {
820			Self::Boxed | Self::BoxedForced | Self::System => true,
821			Self::Simple | Self::Other => false,
822		}
823	}
824}
825
826#[derive(Clone, Debug, PartialEq, Eq, Hash)]
827pub enum TemplateKind<'tu, 'ge> {
828	/// Not a template or a specialization
829	No,
830	/// Base class template, e.g. `Point_<T>`
831	Template,
832	/// A specific instance (`Point_<int>`) of the class template (`Point_<T>`)
833	Specialization(Class<'tu, 'ge>),
834}
835
836impl<'tu, 'ge> TemplateKind<'tu, 'ge> {
837	pub fn is_template(&self) -> bool {
838		match self {
839			TemplateKind::Template => true,
840			TemplateKind::No | TemplateKind::Specialization(_) => false,
841		}
842	}
843
844	pub fn as_template_specialization(&self) -> Option<&Class<'tu, 'ge>> {
845		match self {
846			TemplateKind::Specialization(cls) => Some(cls),
847			TemplateKind::No | TemplateKind::Template => None,
848		}
849	}
850}
851
852pub enum FieldMethodsIter<'tu: 'ge, 'ge, I: Iterator<Item = Func<'tu, 'ge>>> {
853	Clang(I),
854	Desc,
855}
856
857impl<'tu, 'ge, I: Iterator<Item = Func<'tu, 'ge>>> Iterator for FieldMethodsIter<'tu, 'ge, I> {
858	type Item = Func<'tu, 'ge>;
859
860	fn next(&mut self) -> Option<Self::Item> {
861		match self {
862			FieldMethodsIter::Clang(iter) => iter.next(),
863			FieldMethodsIter::Desc => None,
864		}
865	}
866}