opencv_binding_generator/writer/rust_native/
renderer.rs

1use std::borrow::Cow;
2use std::fmt::Write;
3
4use crate::renderer::TypeRefRenderer;
5use crate::type_ref::{
6	Constness, CppNameStyle, Dir, ExternDir, FishStyle, NameStyle, StrType, TemplateArg, TypeRef, TypeRefDesc, TypeRefKind,
7};
8use crate::writer::rust_native::element::RustElement;
9use crate::writer::rust_native::function::FunctionExt;
10use crate::writer::rust_native::type_ref::{Lifetime, NullabilityExt, TypeRefExt};
11use crate::{settings, CowMapBorrowedExt, Element, IteratorExt};
12
13fn render_rust_tpl<'a>(renderer: impl TypeRefRenderer<'a>, type_ref: &TypeRef, fish_style: FishStyle) -> String {
14	let generic_types = type_ref.template_specialization_args();
15	if !generic_types.is_empty() {
16		let const_generics_implemented = type_ref
17			.kind()
18			.as_class()
19			.is_some_and(|cls| settings::IMPLEMENTED_CONST_GENERICS.contains(cls.cpp_name(CppNameStyle::Reference).as_ref()));
20		let mut constant_suffix = String::new();
21		let generic_types = generic_types.iter().filter_map(|t| match t {
22			TemplateArg::Typename(type_ref) => Some(renderer.recurse().render(type_ref)),
23			TemplateArg::Constant(literal) => {
24				if const_generics_implemented {
25					Some(literal.into())
26				} else {
27					constant_suffix += literal;
28					None
29				}
30			}
31			TemplateArg::Unknown => None,
32		});
33		let generics = generic_types.join(", ");
34		format!("{constant_suffix}{fish}<{generics}>", fish = fish_style.rust_qual())
35	} else {
36		"".to_string()
37	}
38}
39
40pub struct RustRenderer {
41	pub name_style: NameStyle,
42	pub lifetime: Lifetime,
43}
44
45impl RustRenderer {
46	pub fn new(name_style: NameStyle, lifetime: Lifetime) -> Self {
47		Self { name_style, lifetime }
48	}
49
50	pub fn format_as_array(constness: Constness, elem_type: &str, size: Option<usize>) -> String {
51		format!(
52			"&{cnst}[{elem_type}{size}]",
53			cnst = constness.rust_qual(),
54			size = size.map_or_else(|| "".to_string(), |s| format!("; {s}"))
55		)
56	}
57}
58
59impl TypeRefRenderer<'_> for RustRenderer {
60	type Recursed = Self;
61
62	fn render<'t>(self, type_ref: &'t TypeRef) -> Cow<'t, str> {
63		let kind = type_ref.kind();
64		if let Some((_, str_type)) = kind.as_string(type_ref.type_hint()) {
65			if str_type.is_binary() {
66				format!("Vec{fish}<u8>", fish = self.name_style.turbo_fish_style().rust_qual()).into()
67			} else {
68				"String".into()
69			}
70		} else {
71			kind.map_borrowed(|kind| match kind {
72				TypeRefKind::Primitive(rust, _) => (*rust).into(),
73				TypeRefKind::Array(elem, size) => {
74					let typ = RustRenderer::format_as_array(type_ref.constness(), &self.recurse().render(elem), *size);
75					type_ref
76						.type_hint()
77						.nullability()
78						.rust_wrap_nullable_decl(typ.into(), self.name_style)
79				}
80				TypeRefKind::StdVector(vec) => vec.rust_name(self.name_style),
81				TypeRefKind::StdTuple(tuple) => tuple.rust_name(self.name_style),
82				TypeRefKind::RValueReference(inner) => self.recurse().render(inner),
83				kind @ TypeRefKind::Pointer(inner) if kind.is_rust_by_ptr(type_ref.type_hint()) => {
84					let typ = if inner.kind().is_void() {
85						"c_void".into()
86					} else {
87						self.recurse().render(inner)
88					};
89					format!("*{cnst}{typ}", cnst = type_ref.constness().rust_qual_ptr()).into()
90				}
91				TypeRefKind::Pointer(inner) | TypeRefKind::Reference(inner) => {
92					let typ = format!(
93						"&{lt: <}{cnst}{typ}",
94						cnst = type_ref.constness().rust_qual(),
95						lt = self.lifetime,
96						typ = self.recurse().render(inner)
97					);
98					type_ref
99						.type_hint()
100						.nullability()
101						.rust_wrap_nullable_decl(typ.into(), self.name_style)
102				}
103				TypeRefKind::SmartPtr(ptr) => {
104					let typ = ptr.rust_name(self.name_style);
105					type_ref
106						.type_hint()
107						.nullability()
108						.rust_wrap_nullable_decl(typ, self.name_style)
109				}
110				TypeRefKind::Class(cls) => {
111					let fish_style = self.name_style.turbo_fish_style();
112					format!(
113						"{name}{generic}",
114						name = cls.rust_name(self.name_style),
115						generic = render_rust_tpl(self, type_ref, fish_style),
116					)
117					.into()
118				}
119				TypeRefKind::Enum(enm) => enm.rust_name(self.name_style),
120				TypeRefKind::Typedef(decl) => {
121					let mut out = decl.rust_name(self.name_style);
122					let lifetime_count = decl.underlying_type_ref().rust_lifetime_count();
123					if lifetime_count >= 1 && self.lifetime.is_explicit() {
124						write!(out.to_mut(), "<{}>", self.lifetime).expect("Impossible");
125					}
126					out
127				}
128				TypeRefKind::Generic(name) => name.into(),
129				TypeRefKind::Function(func) => func.rust_name(self.name_style),
130				TypeRefKind::Ignored => "<ignored>".into(),
131			})
132		}
133	}
134
135	fn recurse(&self) -> Self::Recursed {
136		Self {
137			lifetime: self.lifetime.next().unwrap_or(Lifetime::Elided),
138			..*self
139		}
140	}
141}
142
143pub struct RustExternRenderer {
144	direction: ExternDir,
145}
146
147impl RustExternRenderer {
148	pub fn new(direction: ExternDir) -> Self {
149		Self { direction }
150	}
151}
152
153impl TypeRefRenderer<'_> for RustExternRenderer {
154	type Recursed = RustRenderer;
155
156	fn render<'t>(self, type_ref: &'t TypeRef) -> Cow<'t, str> {
157		let kind = type_ref.kind();
158		if let Some((arg_dir, _)) = kind.as_string(type_ref.type_hint()) {
159			match self.direction {
160				ExternDir::ToCpp | ExternDir::Contained => match arg_dir {
161					Dir::In => format!("*{cnst}c_char", cnst = Constness::Const.rust_qual_ptr()).into(),
162					Dir::Out => "*mut *mut c_void".into(),
163				},
164				ExternDir::FromCpp => "*mut c_void".into(),
165			}
166		} else if kind.extern_pass_kind().is_by_void_ptr() || kind.is_void_slice(type_ref.type_hint()) {
167			let void_ptr_constness = match self.direction {
168				ExternDir::Contained | ExternDir::ToCpp => type_ref.constness(),
169				ExternDir::FromCpp => Constness::Mut,
170			};
171			TypeRef::new_pointer(TypeRefDesc::void().with_inherent_constness(void_ptr_constness))
172				.rust_extern(self.direction)
173				.into_owned()
174				.into()
175		} else {
176			kind.map_borrowed(|kind| {
177				match kind {
178					TypeRefKind::Pointer(inner) | TypeRefKind::Reference(inner) | TypeRefKind::RValueReference(inner) => {
179						let typ = if inner.kind().is_void() {
180							"c_void".into()
181						} else {
182							inner.rust_extern(ExternDir::Contained)
183						};
184						format!("*{cnst}{typ}", cnst = type_ref.constness().rust_qual_ptr()).into()
185					}
186					TypeRefKind::Array(elem, None) => {
187						let typ = if matches!(elem.kind().as_string(elem.type_hint()), Some((Dir::Out, StrType::CharPtr(_)))) {
188							// kind of special casing for cv_startLoop_int__X__int__charXX__int_charXX, without that
189							// argv is treated as array of output arguments, and it doesn't seem to be meant this way
190							format!("*{cnst}c_char", cnst = elem.inherent_constness().rust_qual_ptr()).into()
191						} else {
192							elem.rust_extern(ExternDir::Contained)
193						};
194						format!("*{cnst}{typ}", cnst = type_ref.constness().rust_qual_ptr()).into()
195					}
196					TypeRefKind::Array(elem, Some(len)) => format!(
197						"*{cnst}[{typ}; {len}]",
198						cnst = type_ref.constness().rust_qual_ptr(),
199						typ = elem.rust_extern(ExternDir::Contained),
200					)
201					.into(),
202					TypeRefKind::Function(func) => func.rust_extern(),
203					TypeRefKind::Primitive(_, _)
204					| TypeRefKind::StdVector(_)
205					| TypeRefKind::StdTuple(_)
206					| TypeRefKind::SmartPtr(_)
207					| TypeRefKind::Enum(_)
208					| TypeRefKind::Typedef(_)
209					| TypeRefKind::Generic(_)
210					| TypeRefKind::Class(_)
211					| TypeRefKind::Ignored => type_ref.rust_name(NameStyle::ref_()),
212				}
213			})
214		}
215	}
216
217	fn recurse(&self) -> Self::Recursed {
218		RustRenderer::new(NameStyle::Reference(FishStyle::No), Lifetime::Elided)
219	}
220}
221
222pub struct RustReturnRenderer {
223	turbo_fish_style: FishStyle,
224	lifetime: Lifetime,
225}
226
227impl RustReturnRenderer {
228	pub fn new(turbo_fish_style: FishStyle, lifetime: Lifetime) -> Self {
229		Self {
230			turbo_fish_style,
231			lifetime,
232		}
233	}
234
235	fn next_lifetime(&mut self) -> Lifetime {
236		let out = self.lifetime;
237		self.lifetime = self.lifetime.next().unwrap_or(Lifetime::Elided);
238		out
239	}
240}
241
242impl TypeRefRenderer<'_> for RustReturnRenderer {
243	type Recursed = RustRenderer;
244
245	fn render<'t>(mut self, type_ref: &'t TypeRef) -> Cow<'t, str> {
246		let kind = type_ref.kind();
247		if kind.as_abstract_class_ptr().is_some() {
248			let lt = self.next_lifetime();
249			format!(
250				"types::AbstractRef{mut_suf}{fish}<{lt:,<}{typ}>",
251				mut_suf = type_ref.constness().rust_name_qual(),
252				fish = self.turbo_fish_style.rust_qual(),
253				typ = self.recurse().render(&type_ref.source()),
254			)
255			.into()
256		} else if type_ref.type_hint().as_boxed_as_ref().is_some() {
257			let lt = self.next_lifetime();
258			format!(
259				"BoxedRef{mut_suf}{fish}<{lt:,<}{typ}>",
260				mut_suf = type_ref.constness().rust_name_qual(),
261				fish = self.turbo_fish_style.rust_qual(),
262				typ = self.recurse().render(type_ref)
263			)
264			.into()
265		} else if kind.extern_pass_kind().is_by_void_ptr() {
266			self.recurse().render(&type_ref.source()).into_owned().into()
267		} else {
268			self.recurse().render(type_ref).into_owned().into()
269		}
270	}
271
272	fn recurse(&self) -> Self::Recursed {
273		RustRenderer::new(NameStyle::Reference(self.turbo_fish_style), self.lifetime)
274	}
275}