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