opencv_binding_generator/
enumeration.rs1use 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() || 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 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 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}