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::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 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}