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::{settings, CowMapBorrowedExt, Element, IteratorExt};
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 format!(
63 "&{cnst}[{elem_type}{size}]",
64 cnst = constness.rust_qual(),
65 size = size.map_or_else(|| "".to_string(), |s| format!("; {s}"))
66 )
67 }
68}
69
70impl TypeRefRenderer<'_> for RustRenderer {
71 type Recursed = Self;
72
73 fn render<'t>(self, type_ref: &'t TypeRef) -> Cow<'t, str> {
74 let kind = type_ref.kind();
75 if let Some((_, str_type)) = kind.as_string(type_ref.type_hint()) {
76 if str_type.is_binary() {
77 format!("Vec{fish}<u8>", fish = self.name_style.turbo_fish_style().rust_qual()).into()
78 } else {
79 "String".into()
80 }
81 } else {
82 kind.map_borrowed(|kind| match kind {
83 TypeRefKind::Primitive(rust, _) => (*rust).into(),
84 TypeRefKind::Array(elem, size) => {
85 let typ = RustRenderer::format_as_array(type_ref.constness(), &self.recurse().render(elem), *size);
86 type_ref
87 .type_hint()
88 .nullability()
89 .rust_wrap_nullable_decl(typ.into(), self.name_style)
90 }
91 TypeRefKind::StdVector(vec) => vec.rust_name(self.name_style),
92 TypeRefKind::StdTuple(tuple) => tuple.rust_name(self.name_style),
93 TypeRefKind::RValueReference(inner) => self.recurse().render(inner),
94 kind @ TypeRefKind::Pointer(inner) if kind.is_rust_by_ptr(type_ref.type_hint()) => {
95 let typ = if inner.kind().is_void() {
96 "c_void".into()
97 } else {
98 self.recurse().render(inner)
99 };
100 format!("*{cnst}{typ}", cnst = type_ref.constness().rust_qual_ptr()).into()
101 }
102 TypeRefKind::Pointer(inner) | TypeRefKind::Reference(inner) => {
103 let typ = format!(
104 "&{lt: <}{cnst}{typ}",
105 cnst = type_ref.constness().rust_qual(),
106 lt = self.lifetime,
107 typ = self.recurse().render(inner)
108 );
109 type_ref
110 .type_hint()
111 .nullability()
112 .rust_wrap_nullable_decl(typ.into(), self.name_style)
113 }
114 TypeRefKind::SmartPtr(ptr) => {
115 let typ = ptr.rust_name(self.name_style);
116 type_ref
117 .type_hint()
118 .nullability()
119 .rust_wrap_nullable_decl(typ, self.name_style)
120 }
121 TypeRefKind::Class(cls) => {
122 let fish_style = self.name_style.turbo_fish_style();
123 let lifetime = cls.rust_lifetime().map(|_| self.lifetime);
124 format!(
125 "{name}{generic}",
126 name = cls.rust_name(self.name_style),
127 generic = render_rust_tpl(self, type_ref, lifetime, fish_style),
128 )
129 .into()
130 }
131 TypeRefKind::Enum(enm) => enm.rust_name(self.name_style),
132 TypeRefKind::Typedef(decl) => {
133 let mut out = decl.rust_name(self.name_style);
134 let lifetime_count = decl.underlying_type_ref().rust_lifetime_count();
135 if lifetime_count >= 1 && self.lifetime.is_explicit() {
136 write!(out.to_mut(), "<{}>", self.lifetime).expect("Impossible");
137 }
138 out
139 }
140 TypeRefKind::Generic(name) => name.into(),
141 TypeRefKind::Function(func) => func.rust_name(self.name_style),
142 TypeRefKind::Ignored => "<ignored>".into(),
143 })
144 }
145 }
146
147 fn recurse(&self) -> Self::Recursed {
148 Self {
149 lifetime: self.lifetime.next().unwrap_or(Lifetime::Elided),
150 ..*self
151 }
152 }
153}
154
155pub struct RustExternRenderer {
156 direction: ExternDir,
157}
158
159impl RustExternRenderer {
160 pub fn new(direction: ExternDir) -> Self {
161 Self { direction }
162 }
163}
164
165impl TypeRefRenderer<'_> for RustExternRenderer {
166 type Recursed = RustRenderer;
167
168 fn render<'t>(self, type_ref: &'t TypeRef) -> Cow<'t, str> {
169 let kind = type_ref.kind();
170 if let Some((arg_dir, _)) = kind.as_string(type_ref.type_hint()) {
171 match self.direction {
172 ExternDir::ToCpp | ExternDir::Contained => match arg_dir {
173 Dir::In => format!("*{cnst}c_char", cnst = Constness::Const.rust_qual_ptr()).into(),
174 Dir::Out => "*mut *mut c_void".into(),
175 },
176 ExternDir::FromCpp => "*mut c_void".into(),
177 }
178 } else if kind.extern_pass_kind().is_by_void_ptr() || kind.is_void_slice(type_ref.type_hint()) {
179 let void_ptr_constness = match self.direction {
180 ExternDir::Contained | ExternDir::ToCpp => type_ref.constness(),
181 ExternDir::FromCpp => Constness::Mut,
182 };
183 TypeRef::new_pointer(TypeRefDesc::void().with_inherent_constness(void_ptr_constness))
184 .rust_extern(self.direction)
185 .into_owned()
186 .into()
187 } else {
188 kind.map_borrowed(|kind| {
189 match kind {
190 TypeRefKind::Pointer(inner) | TypeRefKind::Reference(inner) | TypeRefKind::RValueReference(inner) => {
191 let typ = if inner.kind().is_void() {
192 "c_void".into()
193 } else {
194 inner.rust_extern(ExternDir::Contained)
195 };
196 format!("*{cnst}{typ}", cnst = type_ref.constness().rust_qual_ptr()).into()
197 }
198 TypeRefKind::Array(elem, None) => {
199 let typ = if matches!(elem.kind().as_string(elem.type_hint()), Some((Dir::Out, StrType::CharPtr(_)))) {
200 format!("*{cnst}c_char", cnst = elem.inherent_constness().rust_qual_ptr()).into()
203 } else {
204 elem.rust_extern(ExternDir::Contained)
205 };
206 format!("*{cnst}{typ}", cnst = type_ref.constness().rust_qual_ptr()).into()
207 }
208 TypeRefKind::Array(elem, Some(len)) => format!(
209 "*{cnst}[{typ}; {len}]",
210 cnst = type_ref.constness().rust_qual_ptr(),
211 typ = elem.rust_extern(ExternDir::Contained),
212 )
213 .into(),
214 TypeRefKind::Function(func) => func.rust_extern(),
215 TypeRefKind::Primitive(_, _)
216 | TypeRefKind::StdVector(_)
217 | TypeRefKind::StdTuple(_)
218 | TypeRefKind::SmartPtr(_)
219 | TypeRefKind::Enum(_)
220 | TypeRefKind::Typedef(_)
221 | TypeRefKind::Generic(_)
222 | TypeRefKind::Class(_)
223 | TypeRefKind::Ignored => type_ref.rust_name(NameStyle::ref_()),
224 }
225 })
226 }
227 }
228
229 fn recurse(&self) -> Self::Recursed {
230 RustRenderer::new(NameStyle::Reference(FishStyle::No), Lifetime::Elided)
231 }
232}
233
234pub struct RustReturnRenderer {
235 turbo_fish_style: FishStyle,
236 lifetime: Lifetime,
237}
238
239impl RustReturnRenderer {
240 pub fn new(turbo_fish_style: FishStyle, lifetime: Lifetime) -> Self {
241 Self {
242 turbo_fish_style,
243 lifetime,
244 }
245 }
246
247 fn next_lifetime(&mut self) -> Lifetime {
248 let out = self.lifetime;
249 self.lifetime = self.lifetime.next().unwrap_or(Lifetime::Elided);
250 out
251 }
252}
253
254impl TypeRefRenderer<'_> for RustReturnRenderer {
255 type Recursed = RustRenderer;
256
257 fn render<'t>(mut self, type_ref: &'t TypeRef) -> Cow<'t, str> {
258 let kind = type_ref.kind();
259 if kind.as_abstract_class_ptr().is_some() {
260 let lt = self.next_lifetime();
261 format!(
262 "types::AbstractRef{mut_suf}{fish}<{lt:,<#}{typ}>",
263 mut_suf = type_ref.constness().rust_name_qual(),
264 fish = self.turbo_fish_style.rust_qual(),
265 typ = self.recurse().render(&type_ref.source()),
266 )
267 .into()
268 } else if type_ref.type_hint().as_boxed_as_ref().is_some() {
269 let lt = self.next_lifetime();
270 format!(
271 "BoxedRef{mut_suf}{fish}<{lt:,<#}{typ}>",
272 mut_suf = type_ref.constness().rust_name_qual(),
273 fish = self.turbo_fish_style.rust_qual(),
274 typ = self.recurse().render(type_ref)
275 )
276 .into()
277 } else if kind.extern_pass_kind().is_by_void_ptr() {
278 self.recurse().render(&type_ref.source()).into_owned().into()
279 } else {
280 self.recurse().render(type_ref).into_owned().into()
281 }
282 }
283
284 fn recurse(&self) -> Self::Recursed {
285 RustRenderer::new(NameStyle::Reference(self.turbo_fish_style), self.lifetime)
286 }
287}