ritual/rust_type.rs
1//use common::errors::{unexpected, ResultExt, Result};
2//use common::string_utils::CaseOperations;
3//use common::utils::MapIfOk;
4//use cpp_ffi_data::CppIndirectionChange;
5//use cpp_type::CppType;
6use serde_derive::{Deserialize, Serialize};
7
8/// Rust identifier. Represented by
9/// a vector of name parts. For a regular name,
10/// first part is name of the crate,
11/// last part is own name of the entity,
12/// and intermediate names are module names.
13/// Built-in types are represented
14/// by a single vector item, like `vec!["i32"]`.
15#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
16pub struct RustPath {
17 /// Parts of the name
18 pub parts: Vec<String>,
19}
20
21/*
22/// Conversion from public Rust API type to
23/// the corresponding FFI type
24#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
25#[allow(dead_code)]
26pub enum RustToCTypeConversion {
27 /// Types are the same
28 None,
29 /// `&T` to `*const T` (or similar mutable types)
30 RefToPtr,
31 /// `Option<&T>` to `*const T` (or similar mutable types)
32 OptionRefToPtr,
33 /// `T` to `*const T` (or similar mutable type)
34 ValueToPtr,
35 /// `CppBox<T>` to `*const T` (or similar mutable type)
36 CppBoxToPtr,
37 /// `qt_core::flags::Flags<T>` to `libc::c_uint`
38 QFlagsToUInt,
39}
40
41/// Information about a completely processed type
42/// including its variations at each processing step.
43#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
44pub struct CompleteType {
45 /// Original C++ type used in the C++ library's API
46 pub cpp_type: CppType,
47 /// C++ type used in the C++ wrapper library's API
48 pub cpp_ffi_type: CppType,
49 /// Conversion from `cpp_type` to `cpp_ffi_type`
50 pub cpp_to_ffi_conversion: CppIndirectionChange,
51 /// Rust type used in FFI functions
52 /// (must be exactly the same as `cpp_ffi_type`)
53 pub rust_ffi_type: RustType,
54 /// Type used in public Rust API
55 pub rust_api_type: RustType,
56 /// Conversion from `rust_api_type` to `rust_ffi_type`
57 pub rust_api_to_c_conversion: RustToCTypeConversion,
58}
59
60/// Indirection of a Rust type
61#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
62pub enum RustTypeIndirection {
63 /// No indirection
64 None,
65 /// Raw pointer
66 Ptr,
67 /// Reference with a lifetime
68 Ref { lifetime: Option<String> },
69 /// Raw pointer to raw pointer
70 PtrPtr,
71 /// Raw pointer to reference
72 PtrRef { lifetime: Option<String> },
73}
74
75/// A Rust type
76#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
77pub enum RustType {
78 /// Empty tuple `()`, used as the replacement of C++'s `void` type.
79 EmptyTuple,
80 /// A numeric, enum or struct type with some indirection
81 Common {
82 /// Full name of the base type
83 base: RustName,
84 /// Generic arguments, if any
85 generic_arguments: Option<Vec<RustType>>,
86 /// If the type has no indirection, `is_const`
87 /// indicates constness of the type itself (e.g. `i32` vs `mut i32`).
88 /// If the type has one level of indirection, `is_const`
89 /// indicates constness of that indirection, i.e. if the pointer or the reference
90 /// is const. If the type has two levels of indirection,
91 /// `is_const` indicates constness of indirection that is applied first.
92 is_const: bool,
93 /// If the type has two levels of indirection,
94 /// `is_const2` indicates constness of indirection that is applied second.
95 /// In other cases it is `false`.
96 is_const2: bool,
97 /// Indirection of this type.
98 indirection: RustTypeIndirection,
99 },
100 /// A function pointer type.
101 FunctionPointer {
102 /// Return type of the function.
103 return_type: Box<RustType>,
104 /// Argument types of the function.
105 arguments: Vec<RustType>,
106 },
107}
108
109impl RustName {
110 /// Creates new `RustName` consisting of `parts`.
111 pub fn new(parts: Vec<String>) -> Result<RustName> {
112 if parts.is_empty() {
113 return Err(unexpected("RustName can't be empty").into());
114 }
115 Ok(RustName { parts: parts })
116 }
117
118 /// Returns crate name of this name, or `None`
119 /// if this name does not contain the crate name.
120 pub fn crate_name(&self) -> Option<&String> {
121 assert!(self.parts.len() > 0);
122 if self.parts.len() > 1 {
123 Some(&self.parts[0])
124 } else {
125 None
126 }
127 }
128
129 /// Returns last component of the name.
130 pub fn last_name(&self) -> Result<&String> {
131 self
132 .parts
133 .last()
134 .with_context(|| unexpected("RustName can't be empty"))
135 }
136
137 /// Returns formatted name for using within `current_crate`.
138 /// If `current_crate` is `None`, it's assumed that the formatted name
139 /// will be used outside of the crate it belongs to.
140 pub fn full_name(&self, current_crate: Option<&str>) -> String {
141 if let Some(current_crate) = current_crate {
142 if let Some(self_crate) = self.crate_name() {
143 if self_crate == current_crate {
144 return format!("::{}", self.parts[1..].join("::"));
145 }
146 }
147 }
148 if self.parts.len() == 1 {
149 self.parts[0].clone()
150 } else {
151 format!("::{}", self.parts.join("::"))
152 }
153 }
154
155 /// Returns true if `other` is nested within `self`.
156 pub fn includes(&self, other: &RustName) -> bool {
157 let extra_modules_count = other.parts.len() as isize - self.parts.len() as isize;
158 extra_modules_count > 0 && other.parts[0..self.parts.len()] == self.parts[..]
159 }
160
161 /// Returns true if `other` is a direct child of `self`.
162 pub fn includes_directly(&self, other: &RustName) -> bool {
163 let extra_modules_count = other.parts.len() as isize - self.parts.len() as isize;
164 self.includes(other) && extra_modules_count == 1
165 }
166}
167
168impl RustType {
169 /// Returns alphanumeric description of this type
170 /// for purposes of name disambiguation.
171 #[allow(dead_code)]
172 pub fn caption(&self, context: &RustName) -> Result<String> {
173 Ok(match *self {
174 RustType::EmptyTuple => "empty".to_string(),
175 RustType::Common {
176 ref base,
177 ref generic_arguments,
178 ref is_const,
179 ref is_const2,
180 ref indirection,
181 } => {
182 let mut name = if base.parts.len() == 1 {
183 base.parts[0].to_snake_case()
184 } else {
185 let mut remaining_context: &[String] = &context.parts;
186 let mut parts: &[String] = &base.parts;
187 if &parts[0] == "libc" {
188 parts = &parts[1..];
189 };
190 let mut good_parts = Vec::new();
191 for part in parts {
192 if !remaining_context.is_empty() && part == &remaining_context[0] {
193 remaining_context = &remaining_context[1..];
194 } else {
195 remaining_context = &[];
196 let snake_part = part.to_snake_case();
197 if good_parts.last() != Some(&snake_part) {
198 good_parts.push(snake_part);
199 } else {
200 }
201 }
202 }
203 if good_parts.is_empty() {
204 base.last_name()?.clone()
205 } else {
206 good_parts.join("_")
207 }
208 };
209 if let Some(ref args) = *generic_arguments {
210 name = format!(
211 "{}_{}",
212 name,
213 args.iter().map_if_ok(|x| x.caption(context))?.join("_")
214 );
215 }
216 let mut_text = if *is_const { "" } else { "_mut" };
217 match *indirection {
218 RustTypeIndirection::None => {}
219 RustTypeIndirection::Ref { .. } => {
220 name = format!("{}{}_ref", name, mut_text);
221 }
222 RustTypeIndirection::Ptr => {
223 name = format!("{}{}_ptr", name, mut_text);
224 }
225 RustTypeIndirection::PtrPtr => {
226 let mut_text2 = if *is_const2 { "" } else { "_mut" };
227 name = format!("{}{}_ptr{}_ptr", name, mut_text, mut_text2);
228 }
229 RustTypeIndirection::PtrRef { .. } => {
230 let mut_text2 = if *is_const2 { "" } else { "_mut" };
231 name = format!("{}{}_ptr{}_ref", name, mut_text, mut_text2);
232 }
233 }
234 name
235 }
236 RustType::FunctionPointer { .. } => "fn".to_string(),
237 })
238 }
239
240 /// Returns true if this type is a reference.
241 #[allow(dead_code)]
242 pub fn is_ref(&self) -> bool {
243 match *self {
244 RustType::Common {
245 ref indirection, ..
246 } => match *indirection {
247 RustTypeIndirection::Ref { .. } | RustTypeIndirection::PtrRef { .. } => true,
248 _ => false,
249 },
250 RustType::EmptyTuple | RustType::FunctionPointer { .. } => false,
251 }
252 }
253
254 /// Returns a copy of this type with `new_lifetime` added, if possible.
255 pub fn with_lifetime(&self, new_lifetime: String) -> RustType {
256 let mut r = self.clone();
257 if let RustType::Common {
258 ref mut indirection,
259 ..
260 } = r
261 {
262 match *indirection {
263 RustTypeIndirection::Ref { ref mut lifetime }
264 | RustTypeIndirection::PtrRef { ref mut lifetime } => *lifetime = Some(new_lifetime),
265 _ => {}
266 }
267 }
268 r
269 }
270
271 /// Returns name of the lifetime of this type,
272 /// or `None` if there isn't any lifetime in this type.
273 pub fn lifetime(&self) -> Option<&String> {
274 match *self {
275 RustType::Common {
276 ref indirection, ..
277 } => match *indirection {
278 RustTypeIndirection::Ref { ref lifetime }
279 | RustTypeIndirection::PtrRef { ref lifetime } => lifetime.as_ref(),
280 _ => None,
281 },
282 _ => None,
283 }
284 }
285 /// Returns true if indirection that is applied last has const qualifier.
286 pub fn last_is_const(&self) -> Result<bool> {
287 if let RustType::Common {
288 ref is_const,
289 ref is_const2,
290 ref indirection,
291 ..
292 } = *self
293 {
294 match *indirection {
295 RustTypeIndirection::PtrPtr { .. } | RustTypeIndirection::PtrRef { .. } => Ok(*is_const2),
296 _ => Ok(*is_const),
297 }
298 } else {
299 Err("not a Common type".into())
300 }
301 }
302
303 /// Returns true if this type (or first indirection of the type) is const.
304 pub fn is_const(&self) -> Result<bool> {
305 match *self {
306 RustType::Common { ref is_const, .. } => Ok(*is_const),
307 _ => Err("not a Common type".into()),
308 }
309 }
310
311 /// Sets value of `is_const` for a common type.
312 pub fn set_const(&mut self, value: bool) -> Result<()> {
313 match *self {
314 RustType::Common {
315 ref mut is_const, ..
316 } => {
317 *is_const = value;
318 Ok(())
319 }
320 _ => Err("not a Common type".into()),
321 }
322 }
323
324 /// Returns true if function with an argument of type `self`
325 /// should be assumed unsafe. Currently returns true if this type
326 /// is or contains a raw pointer.
327 pub fn is_unsafe_argument(&self) -> bool {
328 match *self {
329 RustType::Common {
330 ref indirection,
331 ref base,
332 ref generic_arguments,
333 ..
334 } => {
335 match *indirection {
336 RustTypeIndirection::None | RustTypeIndirection::Ref { .. } => {}
337 RustTypeIndirection::Ptr
338 | RustTypeIndirection::PtrPtr
339 | RustTypeIndirection::PtrRef { .. } => {
340 return true;
341 }
342 }
343 if base.full_name(None) == "std::option::Option" {
344 if let Some(ref args) = *generic_arguments {
345 if let Some(ref arg) = args.get(0) {
346 if arg.is_unsafe_argument() {
347 return true;
348 }
349 }
350 }
351 }
352 false
353 }
354 RustType::EmptyTuple => false,
355 RustType::FunctionPointer { .. } => true,
356 }
357 }
358}
359
360impl CompleteType {
361 /// Converts Rust API type from pointer to reference
362 /// and modifies `rust_api_to_c_conversion` accordingly.
363 /// `is_const1` specifies new constness of the created reference.
364 pub fn ptr_to_ref(&self, is_const1: bool) -> Result<CompleteType> {
365 let mut r = self.clone();
366 if let RustType::Common {
367 ref mut is_const,
368 ref mut indirection,
369 ..
370 } = r.rust_api_type
371 {
372 if *indirection != RustTypeIndirection::Ptr {
373 return Err("not a pointer type".into());
374 }
375 *indirection = RustTypeIndirection::Ref { lifetime: None };
376 *is_const = is_const1;
377 } else {
378 return Err("not a RustType::Common".into());
379 }
380 if r.rust_api_to_c_conversion != RustToCTypeConversion::None {
381 return Err("rust_api_to_c_conversion is not none".into());
382 }
383 r.rust_api_to_c_conversion = RustToCTypeConversion::RefToPtr;
384 Ok(r)
385 }
386
387 /// Converts Rust API type from pointer to value
388 /// and modifies `rust_api_to_c_conversion` accordingly.
389 pub fn ptr_to_value(&self) -> Result<CompleteType> {
390 let mut r = self.clone();
391 if let RustType::Common {
392 ref mut is_const,
393 ref mut indirection,
394 ..
395 } = r.rust_api_type
396 {
397 if *indirection != RustTypeIndirection::Ptr {
398 return Err("not a pointer type".into());
399 }
400 *indirection = RustTypeIndirection::None;
401 *is_const = true;
402 } else {
403 return Err("not a RustType::Common".into());
404 }
405 if r.rust_api_to_c_conversion != RustToCTypeConversion::None {
406 return Err("rust_api_to_c_conversion is not none".into());
407 }
408 r.rust_api_to_c_conversion = RustToCTypeConversion::ValueToPtr;
409 Ok(r)
410 }
411}
412*/