opencv_binding_generator/writer/rust_native/
constant.rs

1use std::borrow::Cow;
2use std::borrow::Cow::{Borrowed, Owned};
3use std::collections::HashMap;
4use std::sync::LazyLock;
5
6use clang::EntityKind;
7
8use super::element::{DefaultRustNativeElement, RustElement};
9use super::RustNativeGeneratedElement;
10use crate::constant::{Value, ValueKind};
11use crate::debug::NameDebug;
12use crate::type_ref::{FishStyle, NameStyle};
13use crate::{settings, CompiledInterpolation, Const, StrExt, SupportedModule};
14
15impl RustElement for Const<'_> {
16	fn rust_module(&self) -> SupportedModule {
17		match self {
18			&Self::Clang { entity } => DefaultRustNativeElement::rust_module(entity),
19			Self::Desc(desc) => desc.rust_module,
20		}
21	}
22
23	fn rust_name(&self, style: NameStyle) -> Cow<'_, str> {
24		match self {
25			&Self::Clang { entity } => {
26				let mut out = DefaultRustNativeElement::rust_name(self, entity, style);
27				if let Some(without_suffix) = out.strip_suffix("_OCVRS_OVERRIDE") {
28					let new_len = without_suffix.len();
29					out.truncate(new_len);
30				}
31				out.into()
32			}
33			Self::Desc(_) => match style {
34				NameStyle::Declaration => self.rust_leafname(FishStyle::No),
35				NameStyle::Reference(fish_style) => format!(
36					"{}::{}",
37					DefaultRustNativeElement::rust_module_reference(self),
38					self.rust_leafname(fish_style)
39				)
40				.into(),
41			},
42		}
43	}
44}
45
46impl RustNativeGeneratedElement for Const<'_> {
47	fn element_safe_id(&self) -> String {
48		format!("{}-{}", self.rust_module().opencv_name(), self.rust_name(NameStyle::decl()))
49	}
50
51	fn gen_rust(&self, opencv_version: &str) -> String {
52		static RUST_TPL: LazyLock<CompiledInterpolation> =
53			LazyLock::new(|| include_str!("tpl/const/rust.tpl.rs").compile_interpolation());
54
55		let parent_is_class = match self {
56			&Self::Clang { entity } => entity
57				.get_lexical_parent()
58				.is_some_and(|p| matches!(p.get_kind(), EntityKind::ClassDecl | EntityKind::StructDecl)),
59			Self::Desc(_) => false,
60		};
61		let name = if parent_is_class {
62			self.rust_leafname(FishStyle::No)
63		} else {
64			self.rust_name(NameStyle::decl())
65		};
66
67		if let Some(value) = self.value() {
68			let typ = settings::CONST_TYPE_OVERRIDE
69				.get(name.as_ref())
70				.unwrap_or(&value.kind)
71				.rust_type();
72			RUST_TPL.interpolate(&HashMap::from([
73				("doc_comment", Owned(self.rust_doc_comment("///", opencv_version))),
74				("debug", self.get_debug().into()),
75				("name", name),
76				("type", typ.into()),
77				("value", value.rust_render()),
78			]))
79		} else {
80			"".to_string()
81		}
82	}
83}
84
85pub trait ValueKindExt {
86	fn rust_type(self) -> &'static str;
87}
88
89impl ValueKindExt for ValueKind {
90	fn rust_type(self) -> &'static str {
91		match self {
92			ValueKind::Integer => "i32",
93			ValueKind::UnsignedInteger => "u32",
94			ValueKind::Usize => "usize",
95			ValueKind::Float => "f32",
96			ValueKind::Double => "f64",
97			ValueKind::String => "&str",
98		}
99	}
100}
101
102pub trait ValueExt {
103	fn rust_render(&self) -> Cow<'_, str>;
104}
105
106impl ValueExt for Value {
107	fn rust_render(&self) -> Cow<'_, str> {
108		match self.kind {
109			ValueKind::Float | ValueKind::Double if !self.value.contains('.') => Owned(format!("{}.", self.value)),
110			ValueKind::Integer => {
111				if let Some(no_prefix) = self.value.strip_prefix("0x") {
112					// todo: use let chain when MSRV is 1.88
113					if i32::from_str_radix(no_prefix, 16).is_err() {
114						Owned(format!("{}u32 as i32", self.value))
115					} else {
116						Borrowed(&self.value)
117					}
118				} else {
119					Borrowed(&self.value)
120				}
121			}
122			ValueKind::UnsignedInteger | ValueKind::Usize | ValueKind::Float | ValueKind::Double | ValueKind::String => {
123				Borrowed(&self.value)
124			}
125		}
126	}
127}