Skip to main content

opencv_binding_generator/writer/rust_native/
element.rs

1use std::borrow::Cow;
2use std::fmt::Debug;
3use std::ops::ControlFlow;
4
5use clang::{Entity, EntityKind};
6use semver::Version;
7
8use super::comment::RenderComment;
9use crate::type_ref::FishStyle;
10use crate::{
11	CppNameStyle, Element, EntityExt, GeneratedType, IteratorExt, NameStyle, StringExt, SupportedModule, opencv_module_from_path,
12	reserved_rename, settings,
13};
14
15pub struct DefaultRustNativeElement;
16
17impl DefaultRustNativeElement {
18	pub fn rust_module(entity: Entity) -> SupportedModule {
19		entity
20			.get_location()
21			.expect("Can't get location")
22			.get_spelling_location()
23			.file
24			.and_then(|file| opencv_module_from_path(&file.get_path()))
25			.map_or(SupportedModule::Core, |m| rust_module_hack(entity, m))
26	}
27
28	pub fn rust_module_reference(this: &(impl RustElement + ?Sized)) -> Cow<'_, str> {
29		let module = this.rust_module();
30		let module_rust_name = module.rust_name();
31		if settings::STATIC_RUST_MODULES.contains(module_rust_name) {
32			module_rust_name.into()
33		} else {
34			format!("crate::{module_rust_name}").into()
35		}
36	}
37
38	pub fn rust_leafname(this: &(impl Element + ?Sized)) -> Cow<'_, str> {
39		reserved_rename(this.cpp_name(CppNameStyle::Declaration))
40	}
41
42	pub fn rust_name(this: &(impl RustElement + ?Sized), entity: Entity, name_style: NameStyle) -> String {
43		let mut parts = Vec::with_capacity(4);
44		parts.push(this.rust_leafname(name_style.turbo_fish_style()));
45		let mut entity = entity;
46		let module = Self::rust_module(entity);
47		while let Some(parent) = entity.get_semantic_parent() {
48			match parent.get_kind() {
49				EntityKind::ClassDecl | EntityKind::StructDecl | EntityKind::ClassTemplate => {
50					let parent_name = parent.get_name().expect("Can't get parent name");
51					if parts.last().is_none_or(|last| last != &parent_name) {
52						parts.push(parent_name.into());
53					}
54				}
55				EntityKind::EnumDecl => {
56					if parent.is_scoped() {
57						parts.push(parent.get_name().expect("Can't get parent name").into());
58					}
59				}
60				EntityKind::TranslationUnit | EntityKind::UnexposedDecl | EntityKind::FunctionTemplate => break,
61				EntityKind::Namespace => {
62					let parent_namespace = parent.get_name().expect("Can't get parent name");
63					let no_skip_prefix = settings::NO_SKIP_NAMESPACE_IN_LOCALNAME
64						.get(&Some(module))
65						.and_then(|module_specific| module_specific.get(parent_namespace.as_str()))
66						.or_else(|| {
67							settings::NO_SKIP_NAMESPACE_IN_LOCALNAME
68								.get(&None)
69								.and_then(|generic| generic.get(parent_namespace.as_str()))
70						});
71					if let Some(&prefix) = no_skip_prefix {
72						parts.push(prefix.into());
73					}
74				}
75				EntityKind::Constructor | EntityKind::FunctionDecl | EntityKind::Method | EntityKind::NotImplemented => {}
76				_ => unreachable!("Can't get kind of parent: {parent:#?} for element: {entity:#?}"),
77			}
78			entity = parent;
79		}
80		let decl_name = parts.into_iter().rev().join("_");
81		match name_style {
82			NameStyle::Declaration => decl_name,
83			NameStyle::Reference(_) => {
84				let mut out = this.rust_module_reference().into_owned();
85				out.extend_sep("::", &decl_name);
86				out
87			}
88		}
89	}
90}
91
92pub trait RustNativeGeneratedElement {
93	/// Element order in the output file, lower means earlier
94	fn element_order(&self) -> u8 {
95		50
96	}
97
98	fn element_safe_id(&self) -> String;
99
100	fn gen_rust(&self, _opencv_version: &Version) -> String {
101		"".to_string()
102	}
103
104	fn gen_rust_externs(&self) -> String {
105		"".to_string()
106	}
107
108	fn gen_cpp(&self) -> String {
109		"".to_string()
110	}
111}
112
113pub trait RustElement: Element {
114	fn rust_module(&self) -> SupportedModule;
115
116	fn rust_module_reference(&self) -> Cow<'_, str> {
117		DefaultRustNativeElement::rust_module_reference(self)
118	}
119
120	fn rust_name(&self, style: NameStyle) -> Cow<'_, str>;
121
122	/// The very last concrete part of the name in Rust
123	///
124	/// This might not match `rust_name(NameStyle::Declaration)` because some classes in Rust are prefixed with their namespace. E.g.
125	/// `Detail_Blender`, in this case the `rust_leafname()` == `Blender` and `rust_name(NameStyle::Declaration)` == `Detail_Blender`.
126	fn rust_leafname(&self, _fish_style: FishStyle) -> Cow<'_, str> {
127		DefaultRustNativeElement::rust_leafname(self)
128	}
129
130	fn rust_doc_comment(&self, comment_marker: &str, opencv_version: &Version) -> String {
131		RenderComment::new(self.doc_comment().into_owned(), opencv_version)
132			.render_with_comment_marker(comment_marker)
133			.into_owned()
134	}
135}
136
137pub trait DebugRust {
138	type DebugType: Debug;
139
140	fn dbg_rust(self) -> Self::DebugType;
141}
142
143impl<'ne, 'tu: 'ne, 'ge: 'ne> AsRef<dyn RustNativeGeneratedElement + 'ne> for GeneratedType<'tu, 'ge> {
144	fn as_ref(&self) -> &(dyn RustNativeGeneratedElement + 'ne) {
145		match self {
146			GeneratedType::Vector(vec) => vec,
147			GeneratedType::SmartPtr(ptr) => ptr,
148			GeneratedType::Tuple(tuple) => tuple,
149			GeneratedType::AbstractRefWrapper(aref) => aref,
150		}
151	}
152}
153
154/// Adjusts the module for certain entities that are technically in one module but actually implemented in another.
155///
156/// One (and for now the only) example is that some class declarations are technically in `hpp` files in the `video` module, but
157/// their code is actually in the `tracking` module of `opencv_contrib`. This mismatch leads to linking errors if only the
158/// `opencv_video` module is linked.
159fn rust_module_hack(entity: Entity, module_from_path: SupportedModule) -> SupportedModule {
160	match module_from_path {
161		SupportedModule::Video => {
162			let break_res = entity.walk_parents(|parent| match parent.get_kind() {
163				EntityKind::Namespace if parent.is_inline_namespace() && parent.get_name().is_some_and(|n| n == "tracking") => {
164					ControlFlow::Break(SupportedModule::Tracking)
165				}
166				_ => ControlFlow::Continue(()),
167			});
168			break_res.break_value().unwrap_or(module_from_path)
169		}
170		_ => module_from_path,
171	}
172}