opencv_binding_generator/writer/rust_native/
renderer.rs1use 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 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}