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