opencv_binding_generator/
enumeration.rs

1use std::borrow::Cow;
2use std::fmt;
3use std::ops::ControlFlow;
4use std::rc::Rc;
5
6use clang::{Entity, EntityKind, EntityVisitResult};
7
8use crate::debug::LocationName;
9use crate::element::ExcludeKind;
10use crate::type_ref::CppNameStyle;
11use crate::{Const, DefaultElement, Element, EntityElement, EntityExt, GeneratorEnv, NameDebug, StrExt};
12
13#[derive(Debug, Clone, Copy)]
14pub enum EnumBitfield {
15	NotBitfield,
16	BitfieldWithoutZero,
17	BitfieldWithZero,
18}
19
20impl EnumBitfield {
21	pub fn is_bitfield(self) -> bool {
22		match self {
23			Self::NotBitfield => false,
24			Self::BitfieldWithoutZero | Self::BitfieldWithZero => true,
25		}
26	}
27}
28
29#[derive(Clone)]
30pub struct Enum<'tu, 'ge> {
31	entity: Entity<'tu>,
32	gen_env: &'ge GeneratorEnv<'tu>,
33	custom_fullname: Option<Rc<str>>,
34}
35
36impl<'tu, 'ge> Enum<'tu, 'ge> {
37	pub fn new(entity: Entity<'tu>, gen_env: &'ge GeneratorEnv<'tu>) -> Self {
38		Self {
39			entity,
40			gen_env,
41			custom_fullname: None,
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 {
47			entity,
48			gen_env,
49			custom_fullname: Some(custom_fullname.into()),
50		}
51	}
52
53	pub fn is_anonymous(&self) -> bool {
54		self.entity.is_anonymous() || /* clang-6 quirk */ self.cpp_name(CppNameStyle::Declaration).starts_with("(anonymous enum")
55	}
56
57	pub fn as_typedefed(&self) -> Option<Entity<'tu>> {
58		if matches!(self.entity.get_kind(), EntityKind::TypedefDecl | EntityKind::TypeAliasDecl) {
59			let mut child = None;
60			let _ = self.entity.walk_children_while(|c| {
61				child = Some(c);
62				ControlFlow::Break(())
63			});
64			Some(child.expect("Invalid anonymous typedefed enum"))
65		} else {
66			None
67		}
68	}
69
70	pub fn consts(&self) -> Vec<Const<'tu>> {
71		let mut out = vec![];
72		self.as_typedefed().unwrap_or(self.entity).visit_children(|const_decl, _| {
73			if const_decl.get_kind() == EntityKind::EnumConstantDecl {
74				out.push(Const::new(const_decl));
75			}
76			EntityVisitResult::Continue
77		});
78		out
79	}
80
81	/// True if this enum is bitfield, e.g. intended to be used as a combination of flags.
82	pub fn bitfield(&self) -> EnumBitfield {
83		let name = self.cpp_name(CppNameStyle::Reference);
84		if name.ends_with("Flags") || name.ends_with("FLAGS") || name.ends_with("Settings") {
85			self
86				.gen_env
87				.settings
88				.enum_bitfield_override
89				.get(name.as_ref())
90				.copied()
91				.unwrap_or_else(|| {
92					let mut has_zero = false;
93					let mut var_count = 0;
94					let not_bitfield = self.as_typedefed().unwrap_or(self.entity).visit_children(|const_decl, _| {
95						if const_decl.get_kind() != EntityKind::EnumConstantDecl {
96							return EntityVisitResult::Continue;
97						}
98						if let Some(val) = const_decl
99							.get_enum_constant_value()
100							.and_then(|(val, _)| u64::try_from(val).ok())
101						{
102							var_count += 1;
103							if val == 0 {
104								has_zero = true;
105								EntityVisitResult::Continue
106							} else if val.is_power_of_two() {
107								EntityVisitResult::Continue
108							} else {
109								EntityVisitResult::Break
110							}
111						} else {
112							EntityVisitResult::Break
113						}
114					});
115					if not_bitfield {
116						EnumBitfield::NotBitfield
117					} else if has_zero {
118						if var_count <= 2 {
119							// enum with 0 and one/no other option is self-exclusive, no point in bitfield machinery
120							EnumBitfield::NotBitfield
121						} else {
122							EnumBitfield::BitfieldWithZero
123						}
124					} else if var_count <= 1 {
125						EnumBitfield::NotBitfield
126					} else {
127						EnumBitfield::BitfieldWithoutZero
128					}
129				})
130		} else {
131			EnumBitfield::NotBitfield
132		}
133	}
134}
135
136impl<'tu> EntityElement<'tu> for Enum<'tu, '_> {
137	fn entity(&self) -> Entity<'tu> {
138		self.entity
139	}
140}
141
142impl Element for Enum<'_, '_> {
143	fn exclude_kind(&self) -> ExcludeKind {
144		DefaultElement::exclude_kind(self).with_is_excluded(|| self.as_typedefed().is_some())
145	}
146
147	fn is_system(&self) -> bool {
148		DefaultElement::is_system(self.entity)
149	}
150
151	fn is_public(&self) -> bool {
152		DefaultElement::is_public(self.entity)
153	}
154
155	fn doc_comment(&self) -> Cow<'_, str> {
156		self.entity.doc_comment()
157	}
158
159	fn cpp_namespace(&self) -> Cow<'_, str> {
160		if let Some(custom_fullname) = &self.custom_fullname {
161			custom_fullname.namespace().into()
162		} else {
163			DefaultElement::cpp_namespace(self.entity).into()
164		}
165	}
166
167	fn cpp_name(&self, style: CppNameStyle) -> Cow<'_, str> {
168		if let Some(custom_fullname) = self.custom_fullname.as_deref() {
169			custom_fullname.cpp_name_from_fullname(style).into()
170		} else {
171			DefaultElement::cpp_name(self, self.entity, style)
172		}
173	}
174}
175
176impl PartialEq for Enum<'_, '_> {
177	fn eq(&self, other: &Self) -> bool {
178		self.entity == other.entity && self.custom_fullname == other.custom_fullname
179	}
180}
181
182impl<'me> NameDebug<'me> for &'me Enum<'_, '_> {
183	fn file_line_name(self) -> LocationName<'me> {
184		self.entity.file_line_name()
185	}
186}
187
188impl fmt::Debug for Enum<'_, '_> {
189	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
190		let mut debug_struct = f.debug_struct("Enum");
191		self
192			.update_debug_struct(&mut debug_struct)
193			.field("consts", &self.consts())
194			.finish()
195	}
196}