Backed out 5 changesets (bug 1890092, bug 1888683) for causing build bustages & crash...
[gecko.git] / third_party / rust / uniffi_bindgen / src / bindings / python / gen_python / mod.rs
blob8178fcc10298337f1776775382b1caa4b5b82629
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 use anyhow::{Context, Result};
6 use askama::Template;
7 use heck::{ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase};
8 use once_cell::sync::Lazy;
9 use serde::{Deserialize, Serialize};
10 use std::borrow::Borrow;
11 use std::cell::RefCell;
12 use std::collections::{BTreeSet, HashMap, HashSet};
13 use std::fmt::Debug;
15 use crate::backend::TemplateExpression;
16 use crate::interface::*;
17 use crate::BindingsConfig;
19 mod callback_interface;
20 mod compounds;
21 mod custom;
22 mod enum_;
23 mod executor;
24 mod external;
25 mod miscellany;
26 mod object;
27 mod primitives;
28 mod record;
30 /// A trait tor the implementation.
31 trait CodeType: Debug {
32     /// The language specific label used to reference this type. This will be used in
33     /// method signatures and property declarations.
34     fn type_label(&self) -> String;
36     /// A representation of this type label that can be used as part of another
37     /// identifier. e.g. `read_foo()`, or `FooInternals`.
38     ///
39     /// This is especially useful when creating specialized objects or methods to deal
40     /// with this type only.
41     fn canonical_name(&self) -> String {
42         self.type_label()
43     }
45     fn literal(&self, _literal: &Literal) -> String {
46         unimplemented!("Unimplemented for {}", self.type_label())
47     }
49     /// Name of the FfiConverter
50     ///
51     /// This is the object that contains the lower, write, lift, and read methods for this type.
52     fn ffi_converter_name(&self) -> String {
53         format!("FfiConverter{}", self.canonical_name())
54     }
56     /// A list of imports that are needed if this type is in use.
57     /// Classes are imported exactly once.
58     fn imports(&self) -> Option<Vec<String>> {
59         None
60     }
62     /// Function to run at startup
63     fn initialization_fn(&self) -> Option<String> {
64         None
65     }
68 // Taken from Python's `keyword.py` module.
69 static KEYWORDS: Lazy<HashSet<String>> = Lazy::new(|| {
70     let kwlist = vec![
71         "False",
72         "None",
73         "True",
74         "__peg_parser__",
75         "and",
76         "as",
77         "assert",
78         "async",
79         "await",
80         "break",
81         "class",
82         "continue",
83         "def",
84         "del",
85         "elif",
86         "else",
87         "except",
88         "finally",
89         "for",
90         "from",
91         "global",
92         "if",
93         "import",
94         "in",
95         "is",
96         "lambda",
97         "nonlocal",
98         "not",
99         "or",
100         "pass",
101         "raise",
102         "return",
103         "try",
104         "while",
105         "with",
106         "yield",
107     ];
108     HashSet::from_iter(kwlist.into_iter().map(|s| s.to_string()))
111 // Config options to customize the generated python.
112 #[derive(Debug, Clone, Default, Serialize, Deserialize)]
113 pub struct Config {
114     cdylib_name: Option<String>,
115     #[serde(default)]
116     custom_types: HashMap<String, CustomTypeConfig>,
119 #[derive(Debug, Clone, Default, Serialize, Deserialize)]
120 pub struct CustomTypeConfig {
121     // This `CustomTypeConfig` doesn't have a `type_name` like the others -- which is why we have
122     // separate structs rather than a shared one.
123     imports: Option<Vec<String>>,
124     into_custom: TemplateExpression,
125     from_custom: TemplateExpression,
128 impl Config {
129     pub fn cdylib_name(&self) -> String {
130         if let Some(cdylib_name) = &self.cdylib_name {
131             cdylib_name.clone()
132         } else {
133             "uniffi".into()
134         }
135     }
138 impl BindingsConfig for Config {
139     fn update_from_ci(&mut self, ci: &ComponentInterface) {
140         self.cdylib_name
141             .get_or_insert_with(|| format!("uniffi_{}", ci.namespace()));
142     }
144     fn update_from_cdylib_name(&mut self, cdylib_name: &str) {
145         self.cdylib_name
146             .get_or_insert_with(|| cdylib_name.to_string());
147     }
149     fn update_from_dependency_configs(&mut self, _config_map: HashMap<&str, &Self>) {}
152 // Generate python bindings for the given ComponentInterface, as a string.
153 pub fn generate_python_bindings(config: &Config, ci: &ComponentInterface) -> Result<String> {
154     PythonWrapper::new(config.clone(), ci)
155         .render()
156         .context("failed to render python bindings")
159 /// A struct to record a Python import statement.
160 #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
161 pub enum ImportRequirement {
162     /// A simple module import.
163     Module { mod_name: String },
164     /// A single symbol from a module.
165     Symbol {
166         mod_name: String,
167         symbol_name: String,
168     },
169     /// A single symbol from a module with the specified local name.
170     SymbolAs {
171         mod_name: String,
172         symbol_name: String,
173         as_name: String,
174     },
177 impl ImportRequirement {
178     /// Render the Python import statement.
179     fn render(&self) -> String {
180         match &self {
181             ImportRequirement::Module { mod_name } => format!("import {mod_name}"),
182             ImportRequirement::Symbol {
183                 mod_name,
184                 symbol_name,
185             } => format!("from {mod_name} import {symbol_name}"),
186             ImportRequirement::SymbolAs {
187                 mod_name,
188                 symbol_name,
189                 as_name,
190             } => format!("from {mod_name} import {symbol_name} as {as_name}"),
191         }
192     }
195 /// Renders Python helper code for all types
197 /// This template is a bit different than others in that it stores internal state from the render
198 /// process.  Make sure to only call `render()` once.
199 #[derive(Template)]
200 #[template(syntax = "py", escape = "none", path = "Types.py")]
201 pub struct TypeRenderer<'a> {
202     python_config: &'a Config,
203     ci: &'a ComponentInterface,
204     // Track included modules for the `include_once()` macro
205     include_once_names: RefCell<HashSet<String>>,
206     // Track imports added with the `add_import()` macro
207     imports: RefCell<BTreeSet<ImportRequirement>>,
210 impl<'a> TypeRenderer<'a> {
211     fn new(python_config: &'a Config, ci: &'a ComponentInterface) -> Self {
212         Self {
213             python_config,
214             ci,
215             include_once_names: RefCell::new(HashSet::new()),
216             imports: RefCell::new(BTreeSet::new()),
217         }
218     }
220     // The following methods are used by the `Types.py` macros.
222     // Helper for the including a template, but only once.
223     //
224     // The first time this is called with a name it will return true, indicating that we should
225     // include the template.  Subsequent calls will return false.
226     fn include_once_check(&self, name: &str) -> bool {
227         self.include_once_names
228             .borrow_mut()
229             .insert(name.to_string())
230     }
232     // Helper to add an import statement
233     //
234     // Call this inside your template to cause an import statement to be added at the top of the
235     // file.  Imports will be sorted and de-deuped.
236     //
237     // Returns an empty string so that it can be used inside an askama `{{ }}` block.
238     fn add_import(&self, name: &str) -> &str {
239         self.imports.borrow_mut().insert(ImportRequirement::Module {
240             mod_name: name.to_owned(),
241         });
242         ""
243     }
245     // Like add_import, but arranges for `from module import name`.
246     fn add_import_of(&self, mod_name: &str, name: &str) -> &str {
247         self.imports.borrow_mut().insert(ImportRequirement::Symbol {
248             mod_name: mod_name.to_owned(),
249             symbol_name: name.to_owned(),
250         });
251         ""
252     }
254     // Like add_import, but arranges for `from module import name as other`.
255     fn add_import_of_as(&self, mod_name: &str, symbol_name: &str, as_name: &str) -> &str {
256         self.imports
257             .borrow_mut()
258             .insert(ImportRequirement::SymbolAs {
259                 mod_name: mod_name.to_owned(),
260                 symbol_name: symbol_name.to_owned(),
261                 as_name: as_name.to_owned(),
262             });
263         ""
264     }
267 #[derive(Template)]
268 #[template(syntax = "py", escape = "none", path = "wrapper.py")]
269 pub struct PythonWrapper<'a> {
270     ci: &'a ComponentInterface,
271     config: Config,
272     type_helper_code: String,
273     type_imports: BTreeSet<ImportRequirement>,
275 impl<'a> PythonWrapper<'a> {
276     pub fn new(config: Config, ci: &'a ComponentInterface) -> Self {
277         let type_renderer = TypeRenderer::new(&config, ci);
278         let type_helper_code = type_renderer.render().unwrap();
279         let type_imports = type_renderer.imports.into_inner();
280         Self {
281             config,
282             ci,
283             type_helper_code,
284             type_imports,
285         }
286     }
288     pub fn imports(&self) -> Vec<ImportRequirement> {
289         self.type_imports.iter().cloned().collect()
290     }
293 fn fixup_keyword(name: String) -> String {
294     if KEYWORDS.contains(&name) {
295         format!("_{name}")
296     } else {
297         name
298     }
301 #[derive(Clone, Default)]
302 pub struct PythonCodeOracle;
304 impl PythonCodeOracle {
305     fn find(&self, type_: &Type) -> Box<dyn CodeType> {
306         type_.clone().as_type().as_codetype()
307     }
309     /// Get the idiomatic Python rendering of a class name (for enums, records, errors, etc).
310     fn class_name(&self, nm: &str) -> String {
311         fixup_keyword(nm.to_string().to_upper_camel_case())
312     }
314     /// Get the idiomatic Python rendering of a function name.
315     fn fn_name(&self, nm: &str) -> String {
316         fixup_keyword(nm.to_string().to_snake_case())
317     }
319     /// Get the idiomatic Python rendering of a variable name.
320     fn var_name(&self, nm: &str) -> String {
321         fixup_keyword(nm.to_string().to_snake_case())
322     }
324     /// Get the idiomatic Python rendering of an individual enum variant.
325     fn enum_variant_name(&self, nm: &str) -> String {
326         fixup_keyword(nm.to_string().to_shouty_snake_case())
327     }
329     fn ffi_type_label(ffi_type: &FfiType) -> String {
330         match ffi_type {
331             FfiType::Int8 => "ctypes.c_int8".to_string(),
332             FfiType::UInt8 => "ctypes.c_uint8".to_string(),
333             FfiType::Int16 => "ctypes.c_int16".to_string(),
334             FfiType::UInt16 => "ctypes.c_uint16".to_string(),
335             FfiType::Int32 => "ctypes.c_int32".to_string(),
336             FfiType::UInt32 => "ctypes.c_uint32".to_string(),
337             FfiType::Int64 => "ctypes.c_int64".to_string(),
338             FfiType::UInt64 => "ctypes.c_uint64".to_string(),
339             FfiType::Float32 => "ctypes.c_float".to_string(),
340             FfiType::Float64 => "ctypes.c_double".to_string(),
341             FfiType::RustArcPtr(_) => "ctypes.c_void_p".to_string(),
342             FfiType::RustBuffer(maybe_suffix) => match maybe_suffix {
343                 Some(suffix) => format!("_UniffiRustBuffer{suffix}"),
344                 None => "_UniffiRustBuffer".to_string(),
345             },
346             FfiType::ForeignBytes => "_UniffiForeignBytes".to_string(),
347             FfiType::ForeignCallback => "_UNIFFI_FOREIGN_CALLBACK_T".to_string(),
348             // Pointer to an `asyncio.EventLoop` instance
349             FfiType::ForeignExecutorHandle => "ctypes.c_size_t".to_string(),
350             FfiType::ForeignExecutorCallback => "_UNIFFI_FOREIGN_EXECUTOR_CALLBACK_T".to_string(),
351             FfiType::RustFutureHandle => "ctypes.c_void_p".to_string(),
352             FfiType::RustFutureContinuationCallback => "_UNIFFI_FUTURE_CONTINUATION_T".to_string(),
353             FfiType::RustFutureContinuationData => "ctypes.c_size_t".to_string(),
354         }
355     }
358 trait AsCodeType {
359     fn as_codetype(&self) -> Box<dyn CodeType>;
362 impl<T: AsType> AsCodeType for T {
363     fn as_codetype(&self) -> Box<dyn CodeType> {
364         // Map `Type` instances to a `Box<dyn CodeType>` for that type.
365         //
366         // There is a companion match in `templates/Types.py` which performs a similar function for the
367         // template code.
368         //
369         //   - When adding additional types here, make sure to also add a match arm to the `Types.py` template.
370         //   - To keep things manageable, let's try to limit ourselves to these 2 mega-matches
371         match self.as_type() {
372             Type::UInt8 => Box::new(primitives::UInt8CodeType),
373             Type::Int8 => Box::new(primitives::Int8CodeType),
374             Type::UInt16 => Box::new(primitives::UInt16CodeType),
375             Type::Int16 => Box::new(primitives::Int16CodeType),
376             Type::UInt32 => Box::new(primitives::UInt32CodeType),
377             Type::Int32 => Box::new(primitives::Int32CodeType),
378             Type::UInt64 => Box::new(primitives::UInt64CodeType),
379             Type::Int64 => Box::new(primitives::Int64CodeType),
380             Type::Float32 => Box::new(primitives::Float32CodeType),
381             Type::Float64 => Box::new(primitives::Float64CodeType),
382             Type::Boolean => Box::new(primitives::BooleanCodeType),
383             Type::String => Box::new(primitives::StringCodeType),
384             Type::Bytes => Box::new(primitives::BytesCodeType),
386             Type::Timestamp => Box::new(miscellany::TimestampCodeType),
387             Type::Duration => Box::new(miscellany::DurationCodeType),
389             Type::Enum { name, .. } => Box::new(enum_::EnumCodeType::new(name)),
390             Type::Object { name, .. } => Box::new(object::ObjectCodeType::new(name)),
391             Type::Record { name, .. } => Box::new(record::RecordCodeType::new(name)),
392             Type::CallbackInterface { name, .. } => {
393                 Box::new(callback_interface::CallbackInterfaceCodeType::new(name))
394             }
395             Type::ForeignExecutor => Box::new(executor::ForeignExecutorCodeType),
396             Type::Optional { inner_type } => {
397                 Box::new(compounds::OptionalCodeType::new(*inner_type))
398             }
399             Type::Sequence { inner_type } => {
400                 Box::new(compounds::SequenceCodeType::new(*inner_type))
401             }
402             Type::Map {
403                 key_type,
404                 value_type,
405             } => Box::new(compounds::MapCodeType::new(*key_type, *value_type)),
406             Type::External { name, .. } => Box::new(external::ExternalCodeType::new(name)),
407             Type::Custom { name, .. } => Box::new(custom::CustomCodeType::new(name)),
408         }
409     }
412 pub mod filters {
413     use super::*;
414     pub use crate::backend::filters::*;
416     pub(super) fn type_name(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
417         Ok(as_ct.as_codetype().type_label())
418     }
420     pub(super) fn ffi_converter_name(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
421         Ok(String::from("_Uniffi") + &as_ct.as_codetype().ffi_converter_name()[3..])
422     }
424     pub(super) fn canonical_name(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
425         Ok(as_ct.as_codetype().canonical_name())
426     }
428     pub(super) fn lift_fn(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
429         Ok(format!("{}.lift", ffi_converter_name(as_ct)?))
430     }
432     pub(super) fn lower_fn(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
433         Ok(format!("{}.lower", ffi_converter_name(as_ct)?))
434     }
436     pub(super) fn read_fn(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
437         Ok(format!("{}.read", ffi_converter_name(as_ct)?))
438     }
440     pub(super) fn write_fn(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
441         Ok(format!("{}.write", ffi_converter_name(as_ct)?))
442     }
444     pub(super) fn literal_py(
445         literal: &Literal,
446         as_ct: &impl AsCodeType,
447     ) -> Result<String, askama::Error> {
448         Ok(as_ct.as_codetype().literal(literal))
449     }
451     pub fn ffi_type_name(type_: &FfiType) -> Result<String, askama::Error> {
452         Ok(PythonCodeOracle::ffi_type_label(type_))
453     }
455     /// Get the idiomatic Python rendering of a class name (for enums, records, errors, etc).
456     pub fn class_name(nm: &str) -> Result<String, askama::Error> {
457         Ok(PythonCodeOracle.class_name(nm))
458     }
460     /// Get the idiomatic Python rendering of a function name.
461     pub fn fn_name(nm: &str) -> Result<String, askama::Error> {
462         Ok(PythonCodeOracle.fn_name(nm))
463     }
465     /// Get the idiomatic Python rendering of a variable name.
466     pub fn var_name(nm: &str) -> Result<String, askama::Error> {
467         Ok(PythonCodeOracle.var_name(nm))
468     }
470     /// Get the idiomatic Python rendering of an individual enum variant.
471     pub fn enum_variant_py(nm: &str) -> Result<String, askama::Error> {
472         Ok(PythonCodeOracle.enum_variant_name(nm))
473     }