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