opencv_binding_generator/
element.rs1use std::borrow::Cow;
2use std::ffi::OsStr;
3use std::fmt;
4use std::path::{Component, Path};
5
6use clang::{Accessibility, Entity, EntityKind};
7
8use crate::type_ref::CppNameStyle;
9use crate::{settings, IteratorExt, StrExt, StringExt, SupportedModule};
10
11pub const UNNAMED: &str = "unnamed";
12
13pub struct DefaultElement;
14
15impl DefaultElement {
16 pub fn exclude_kind(this: &(impl Element + ?Sized)) -> ExcludeKind {
17 let cpp_refname = this.cpp_name(CppNameStyle::Reference);
18 ExcludeKind::Included.with_is_ignored(|| {
19 !this.is_public()
20 || settings::ELEMENT_EXCLUDE_KIND
21 .get(cpp_refname.as_ref())
22 .is_some_and(|ek| ek.is_ignored())
23 })
24 }
25
26 pub fn is_system(entity: Entity) -> bool {
27 entity.is_in_system_header()
28 && !is_opencv_path(
29 &entity
30 .get_location()
31 .expect("Can't get entity location")
32 .get_spelling_location()
33 .file
34 .expect("Can't get location path")
35 .get_path(),
36 )
37 }
38
39 pub fn is_public(entity: Entity) -> bool {
40 entity.get_accessibility().is_none_or(|a| Accessibility::Public == a)
41 }
42
43 pub fn cpp_namespace(entity: Entity) -> String {
44 let mut entity = entity;
45 let mut parts = vec![];
46 while let Some(parent) = entity.get_semantic_parent() {
47 let is_namespace_element = match parent.get_kind() {
48 EntityKind::Namespace => !parent.is_inline_namespace(),
49 EntityKind::ClassDecl
50 | EntityKind::StructDecl
51 | EntityKind::EnumDecl
52 | EntityKind::UnionDecl
53 | EntityKind::ClassTemplate
54 | EntityKind::ClassTemplatePartialSpecialization
55 | EntityKind::FunctionTemplate
56 | EntityKind::Method
57 | EntityKind::FunctionDecl => true,
58 EntityKind::TranslationUnit
59 | EntityKind::UnexposedDecl
60 | EntityKind::LinkageSpec
61 | EntityKind::NotImplemented
62 | EntityKind::Constructor => false,
63 _ => unreachable!("Can't get kind of parent for cpp namespace: {:#?}", parent),
64 };
65 if is_namespace_element {
66 if let Some(parent_name) = parent.get_name() {
68 parts.push(parent_name);
69 }
70 }
71 entity = parent;
72 }
73 parts.into_iter().rev().join("::")
74 }
75
76 pub fn cpp_decl_name_with_namespace<'t>(this: &'t (impl Element + ?Sized), decl_name: &str) -> Cow<'t, str> {
77 let mut out = this.cpp_namespace();
78 out.to_mut().extend_sep("::", decl_name);
79 out
80 }
81
82 pub fn cpp_name<'t>(this: &'t (impl Element + ?Sized), entity: Entity, style: CppNameStyle) -> Cow<'t, str> {
83 let decl_name = entity
84 .get_name()
85 .or_else(|| {
86 if matches!(
87 entity.get_kind(),
88 EntityKind::StructDecl | EntityKind::ClassDecl | EntityKind::EnumDecl
89 ) {
90 entity.get_type().map(|typ| {
93 typ.get_display_name()
94 .cpp_name_from_fullname(CppNameStyle::Declaration)
95 .to_string()
96 })
97 } else {
98 None
99 }
100 })
101 .map_or(Cow::Borrowed(UNNAMED), Cow::Owned);
102 match style {
103 CppNameStyle::Declaration => decl_name,
104 CppNameStyle::Reference => DefaultElement::cpp_decl_name_with_namespace(this, &decl_name),
105 }
106 }
107}
108
109pub trait Element: fmt::Debug {
110 fn update_debug_struct<'dref, 'a, 'b>(
111 &self,
112 struct_debug: &'dref mut fmt::DebugStruct<'a, 'b>,
113 ) -> &'dref mut fmt::DebugStruct<'a, 'b> {
114 struct_debug
115 .field("cpp_fullname", &self.cpp_name(CppNameStyle::Reference))
116 .field("exclude_kind", &self.exclude_kind())
117 .field("is_system", &self.is_system())
118 .field("is_public", &self.is_public())
119 }
120
121 fn exclude_kind(&self) -> ExcludeKind {
122 DefaultElement::exclude_kind(self)
123 }
124
125 fn is_system(&self) -> bool;
126
127 fn is_public(&self) -> bool;
128
129 fn doc_comment(&self) -> Cow<'_, str>;
130
131 fn cpp_namespace(&self) -> Cow<'_, str>;
132
133 fn cpp_name(&self, style: CppNameStyle) -> Cow<'_, str>;
134}
135
136#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
137pub enum ExcludeKind {
138 Included,
139 Excluded,
141 Ignored,
143}
144
145impl ExcludeKind {
146 pub fn is_excluded(self) -> bool {
147 match self {
148 Self::Excluded | Self::Ignored => true,
149 Self::Included => false,
150 }
151 }
152
153 pub fn is_ignored(self) -> bool {
154 match self {
155 Self::Ignored => true,
156 Self::Included | Self::Excluded => false,
157 }
158 }
159
160 pub fn is_included(self) -> bool {
161 match self {
162 Self::Included => true,
163 Self::Ignored | Self::Excluded => false,
164 }
165 }
166
167 pub fn with_is_excluded(self, is_excluded: impl FnOnce() -> bool) -> ExcludeKind {
170 match self {
171 Self::Included => {
172 if is_excluded() {
173 Self::Excluded
174 } else {
175 self
176 }
177 }
178 Self::Excluded | Self::Ignored => self,
179 }
180 }
181
182 pub fn with_is_ignored(self, is_ignored: impl FnOnce() -> bool) -> ExcludeKind {
184 match self {
185 Self::Included | Self::Excluded => {
186 if is_ignored() {
187 Self::Ignored
188 } else {
189 self
190 }
191 }
192 Self::Ignored => self,
193 }
194 }
195
196 pub fn with_exclude_kind(self, new: impl FnOnce() -> ExcludeKind) -> ExcludeKind {
198 match self {
199 Self::Included => new(),
200 Self::Excluded => match new() {
201 Self::Ignored => Self::Ignored,
202 Self::Included | Self::Excluded => self,
203 },
204 Self::Ignored => self,
205 }
206 }
207
208 pub fn with_reference_exclude_kind(self, new: impl FnOnce() -> ExcludeKind) -> ExcludeKind {
211 match self {
212 Self::Included => match new() {
213 Self::Ignored => Self::Excluded,
214 Self::Included | Self::Excluded => self,
215 },
216 Self::Excluded | Self::Ignored => self,
217 }
218 }
219}
220
221pub trait EntityElement<'tu> {
222 fn entity(&self) -> Entity<'tu>;
223}
224
225pub fn is_opencv_path(path: &Path) -> bool {
226 path
227 .components()
228 .rfind(|c| {
229 if let Component::Normal(c) = c {
230 if *c == "opencv2" || *c == "Headers" {
231 return true;
232 }
233 }
234 false
235 })
236 .is_some()
237}
238
239fn opencv_module_component(path: &Path) -> Option<&OsStr> {
242 let mut module_comp = path
243 .components()
244 .rev()
245 .filter_map(|c| {
246 if let Component::Normal(c) = c {
247 Some(c)
248 } else {
249 None
250 }
251 })
252 .peekable();
253 let mut module = None;
254 while let Some(cur) = module_comp.next() {
255 if let Some(&parent) = module_comp.peek() {
256 if parent == "opencv2" || parent == "src_cpp" || parent == "Headers" {
257 module = Some(cur);
258 break;
259 }
260 }
261 }
262 module
263}
264
265pub fn opencv_module_from_path(path: &Path) -> Option<SupportedModule> {
267 opencv_module_component(path)
268 .and_then(|m| m.to_str())
269 .and_then(|m| m.strip_suffix(".hpp").or(Some(m)))
270 .and_then(SupportedModule::try_from_opencv_name)
271}