Skip to main content

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