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 match parent.get_kind() {
48 EntityKind::ClassDecl
49 | EntityKind::Namespace
50 | EntityKind::StructDecl
51 | EntityKind::EnumDecl
52 | EntityKind::UnionDecl
53 | EntityKind::ClassTemplate
54 | EntityKind::ClassTemplatePartialSpecialization
55 | EntityKind::FunctionTemplate
56 | EntityKind::Method
57 | EntityKind::FunctionDecl => {
58 if let Some(parent_name) = parent.get_name() {
60 parts.push(parent_name);
61 }
62 }
63 EntityKind::TranslationUnit
64 | EntityKind::UnexposedDecl
65 | EntityKind::LinkageSpec
66 | EntityKind::NotImplemented
67 | EntityKind::Constructor => {}
68 _ => {
69 unreachable!("Can't get kind of parent for cpp namespace: {:#?}", parent)
70 }
71 }
72 entity = parent;
73 }
74 parts.into_iter().rev().join("::")
75 }
76
77 pub fn cpp_decl_name_with_namespace<'t>(this: &'t (impl Element + ?Sized), decl_name: &str) -> Cow<'t, str> {
78 let mut out = this.cpp_namespace();
79 out.to_mut().extend_sep("::", decl_name);
80 out
81 }
82
83 pub fn cpp_name<'t>(this: &'t (impl Element + ?Sized), entity: Entity, style: CppNameStyle) -> Cow<'t, str> {
84 let decl_name = entity
85 .get_name()
86 .or_else(|| {
87 if matches!(
88 entity.get_kind(),
89 EntityKind::StructDecl | EntityKind::ClassDecl | EntityKind::EnumDecl
90 ) {
91 entity.get_type().map(|typ| {
94 typ.get_display_name()
95 .cpp_name_from_fullname(CppNameStyle::Declaration)
96 .to_string()
97 })
98 } else {
99 None
100 }
101 })
102 .map_or(Cow::Borrowed(UNNAMED), Cow::Owned);
103 match style {
104 CppNameStyle::Declaration => decl_name,
105 CppNameStyle::Reference => DefaultElement::cpp_decl_name_with_namespace(this, &decl_name),
106 }
107 }
108}
109
110pub trait Element: fmt::Debug {
111 fn update_debug_struct<'dref, 'a, 'b>(
112 &self,
113 struct_debug: &'dref mut fmt::DebugStruct<'a, 'b>,
114 ) -> &'dref mut fmt::DebugStruct<'a, 'b> {
115 struct_debug
116 .field("cpp_fullname", &self.cpp_name(CppNameStyle::Reference))
117 .field("exclude_kind", &self.exclude_kind())
118 .field("is_system", &self.is_system())
119 .field("is_public", &self.is_public())
120 }
121
122 fn exclude_kind(&self) -> ExcludeKind {
123 DefaultElement::exclude_kind(self)
124 }
125
126 fn is_system(&self) -> bool;
127
128 fn is_public(&self) -> bool;
129
130 fn doc_comment(&self) -> Cow<'_, str>;
131
132 fn cpp_namespace(&self) -> Cow<'_, str>;
133
134 fn cpp_name(&self, style: CppNameStyle) -> Cow<'_, str>;
135}
136
137#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
138pub enum ExcludeKind {
139 Included,
140 Excluded,
142 Ignored,
144}
145
146impl ExcludeKind {
147 pub fn is_excluded(self) -> bool {
148 match self {
149 Self::Excluded | Self::Ignored => true,
150 Self::Included => false,
151 }
152 }
153
154 pub fn is_ignored(self) -> bool {
155 match self {
156 Self::Ignored => true,
157 Self::Included | Self::Excluded => false,
158 }
159 }
160
161 pub fn is_included(self) -> bool {
162 match self {
163 Self::Included => true,
164 Self::Ignored | Self::Excluded => false,
165 }
166 }
167
168 pub fn with_is_excluded(self, is_excluded: impl FnOnce() -> bool) -> ExcludeKind {
171 match self {
172 Self::Included => {
173 if is_excluded() {
174 Self::Excluded
175 } else {
176 self
177 }
178 }
179 Self::Excluded | Self::Ignored => self,
180 }
181 }
182
183 pub fn with_is_ignored(self, is_ignored: impl FnOnce() -> bool) -> ExcludeKind {
185 match self {
186 Self::Included | Self::Excluded => {
187 if is_ignored() {
188 Self::Ignored
189 } else {
190 self
191 }
192 }
193 Self::Ignored => self,
194 }
195 }
196
197 pub fn with_exclude_kind(self, new: impl FnOnce() -> ExcludeKind) -> ExcludeKind {
199 match self {
200 Self::Included => new(),
201 Self::Excluded => match new() {
202 Self::Ignored => Self::Ignored,
203 Self::Included | Self::Excluded => self,
204 },
205 Self::Ignored => self,
206 }
207 }
208
209 pub fn with_reference_exclude_kind(self, new: impl FnOnce() -> ExcludeKind) -> ExcludeKind {
212 match self {
213 Self::Included => match new() {
214 Self::Ignored => Self::Excluded,
215 Self::Included | Self::Excluded => self,
216 },
217 Self::Excluded | Self::Ignored => self,
218 }
219 }
220}
221
222pub trait EntityElement<'tu> {
223 fn entity(&self) -> Entity<'tu>;
224}
225
226pub fn is_opencv_path(path: &Path) -> bool {
227 path
228 .components()
229 .rfind(|c| {
230 if let Component::Normal(c) = c {
231 if *c == "opencv2" || *c == "Headers" {
232 return true;
233 }
234 }
235 false
236 })
237 .is_some()
238}
239
240fn opencv_module_component(path: &Path) -> Option<&OsStr> {
243 let mut module_comp = path
244 .components()
245 .rev()
246 .filter_map(|c| {
247 if let Component::Normal(c) = c {
248 Some(c)
249 } else {
250 None
251 }
252 })
253 .peekable();
254 let mut module = None;
255 while let Some(cur) = module_comp.next() {
256 if let Some(&parent) = module_comp.peek() {
257 if parent == "opencv2" || parent == "src_cpp" || parent == "Headers" {
258 module = Some(cur);
259 break;
260 }
261 }
262 }
263 module
264}
265
266pub fn opencv_module_from_path(path: &Path) -> Option<SupportedModule> {
268 opencv_module_component(path)
269 .and_then(|m| m.to_str())
270 .and_then(|m| m.strip_suffix(".hpp").or(Some(m)))
271 .and_then(SupportedModule::try_from_opencv_name)
272}