summaryrefslogtreecommitdiffstats
path: root/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/uniffi_bindgen/src/bindings/python/gen_python')
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/callback_interface.rs31
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/compounds.rs108
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/custom.rs26
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/enum_.rs39
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/error.rs31
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/executor.rs18
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/external.rs26
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/miscellany.rs34
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/mod.rs474
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/object.rs31
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/primitives.rs71
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/record.rs31
12 files changed, 920 insertions, 0 deletions
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/callback_interface.rs b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/callback_interface.rs
new file mode 100644
index 0000000000..9c93965e35
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/callback_interface.rs
@@ -0,0 +1,31 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::CodeType;
+use crate::backend::Literal;
+
+#[derive(Debug)]
+pub struct CallbackInterfaceCodeType {
+ id: String,
+}
+
+impl CallbackInterfaceCodeType {
+ pub fn new(id: String) -> Self {
+ Self { id }
+ }
+}
+
+impl CodeType for CallbackInterfaceCodeType {
+ fn type_label(&self) -> String {
+ super::PythonCodeOracle.class_name(&self.id)
+ }
+
+ fn canonical_name(&self) -> String {
+ format!("CallbackInterface{}", self.id)
+ }
+
+ fn literal(&self, _literal: &Literal) -> String {
+ unreachable!();
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/compounds.rs b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/compounds.rs
new file mode 100644
index 0000000000..b91bcbe18f
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/compounds.rs
@@ -0,0 +1,108 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::CodeType;
+use crate::backend::{Literal, Type};
+
+#[derive(Debug)]
+pub struct OptionalCodeType {
+ inner: Type,
+}
+
+impl OptionalCodeType {
+ pub fn new(inner: Type) -> Self {
+ Self { inner }
+ }
+}
+
+impl CodeType for OptionalCodeType {
+ fn type_label(&self) -> String {
+ format!(
+ "typing.Optional[{}]",
+ super::PythonCodeOracle.find(&self.inner).type_label()
+ )
+ }
+
+ fn canonical_name(&self) -> String {
+ format!(
+ "Optional{}",
+ super::PythonCodeOracle.find(&self.inner).canonical_name(),
+ )
+ }
+
+ fn literal(&self, literal: &Literal) -> String {
+ match literal {
+ Literal::Null => "None".into(),
+ _ => super::PythonCodeOracle.find(&self.inner).literal(literal),
+ }
+ }
+}
+
+#[derive(Debug)]
+pub struct SequenceCodeType {
+ inner: Type,
+}
+
+impl SequenceCodeType {
+ pub fn new(inner: Type) -> Self {
+ Self { inner }
+ }
+}
+
+impl CodeType for SequenceCodeType {
+ fn type_label(&self) -> String {
+ // Python 3.8 and below do not support `list[T]`
+ format!(
+ "typing.List[{}]",
+ super::PythonCodeOracle.find(&self.inner).type_label()
+ )
+ }
+
+ fn canonical_name(&self) -> String {
+ format!(
+ "Sequence{}",
+ super::PythonCodeOracle.find(&self.inner).canonical_name(),
+ )
+ }
+
+ fn literal(&self, literal: &Literal) -> String {
+ match literal {
+ Literal::EmptySequence => "[]".into(),
+ _ => unimplemented!(),
+ }
+ }
+}
+
+#[derive(Debug)]
+pub struct MapCodeType {
+ key: Type,
+ value: Type,
+}
+
+impl MapCodeType {
+ pub fn new(key: Type, value: Type) -> Self {
+ Self { key, value }
+ }
+}
+
+impl CodeType for MapCodeType {
+ fn type_label(&self) -> String {
+ "dict".to_string()
+ }
+
+ fn canonical_name(&self) -> String {
+ format!(
+ "Map{}{}",
+ super::PythonCodeOracle.find(&self.key).canonical_name(),
+ super::PythonCodeOracle.find(&self.value).canonical_name(),
+ )
+ }
+
+ fn literal(&self, literal: &Literal) -> String {
+ match literal {
+ Literal::EmptyMap => "{}".into(),
+ _ => unimplemented!(),
+ }
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/custom.rs b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/custom.rs
new file mode 100644
index 0000000000..f735899f3d
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/custom.rs
@@ -0,0 +1,26 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::CodeType;
+
+#[derive(Debug)]
+pub struct CustomCodeType {
+ name: String,
+}
+
+impl CustomCodeType {
+ pub fn new(name: String) -> Self {
+ Self { name }
+ }
+}
+
+impl CodeType for CustomCodeType {
+ fn type_label(&self) -> String {
+ self.name.clone()
+ }
+
+ fn canonical_name(&self) -> String {
+ format!("Type{}", self.name)
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/enum_.rs b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/enum_.rs
new file mode 100644
index 0000000000..83ce177e07
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/enum_.rs
@@ -0,0 +1,39 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::CodeType;
+use crate::backend::Literal;
+
+#[derive(Debug)]
+pub struct EnumCodeType {
+ id: String,
+}
+
+impl EnumCodeType {
+ pub fn new(id: String) -> Self {
+ Self { id }
+ }
+}
+
+impl CodeType for EnumCodeType {
+ fn type_label(&self) -> String {
+ super::PythonCodeOracle.class_name(&self.id)
+ }
+
+ fn canonical_name(&self) -> String {
+ format!("Type{}", self.id)
+ }
+
+ fn literal(&self, literal: &Literal) -> String {
+ if let Literal::Enum(v, _) = literal {
+ format!(
+ "{}.{}",
+ self.type_label(),
+ super::PythonCodeOracle.enum_variant_name(v)
+ )
+ } else {
+ unreachable!();
+ }
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/error.rs b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/error.rs
new file mode 100644
index 0000000000..aa1c0db75e
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/error.rs
@@ -0,0 +1,31 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use crate::backend::{ Literal};
+use super::CodeType;
+
+#[derive(Debug)]
+pub struct ErrorCodeType {
+ id: String,
+}
+
+impl ErrorCodeType {
+ pub fn new(id: String) -> Self {
+ Self { id }
+ }
+}
+
+impl CodeType for ErrorCodeType {
+ fn type_label(&self) -> String {
+ super::PythonCodeOracle.class_name(&self.id)
+ }
+
+ fn canonical_name(&self) -> String {
+ format!("Type{}", self.id)
+ }
+
+ fn literal(&self, _literal: &Literal) -> String {
+ unreachable!();
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/executor.rs b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/executor.rs
new file mode 100644
index 0000000000..be3ba1d791
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/executor.rs
@@ -0,0 +1,18 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::CodeType;
+
+#[derive(Debug)]
+pub struct ForeignExecutorCodeType;
+
+impl CodeType for ForeignExecutorCodeType {
+ fn type_label(&self) -> String {
+ "asyncio.BaseEventLoop".into()
+ }
+
+ fn canonical_name(&self) -> String {
+ "ForeignExecutor".into()
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/external.rs b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/external.rs
new file mode 100644
index 0000000000..0d19c4bb3c
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/external.rs
@@ -0,0 +1,26 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::CodeType;
+
+#[derive(Debug)]
+pub struct ExternalCodeType {
+ name: String,
+}
+
+impl ExternalCodeType {
+ pub fn new(name: String) -> Self {
+ Self { name }
+ }
+}
+
+impl CodeType for ExternalCodeType {
+ fn type_label(&self) -> String {
+ self.name.clone()
+ }
+
+ fn canonical_name(&self) -> String {
+ format!("Type{}", self.name)
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/miscellany.rs b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/miscellany.rs
new file mode 100644
index 0000000000..07ff5cd0d7
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/miscellany.rs
@@ -0,0 +1,34 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::CodeType;
+use crate::backend::Literal;
+use paste::paste;
+
+macro_rules! impl_code_type_for_miscellany {
+ ($T:ty, $canonical_name:literal) => {
+ paste! {
+ #[derive(Debug)]
+ pub struct $T;
+
+ impl CodeType for $T {
+ fn type_label(&self) -> String {
+ format!("{}", $canonical_name)
+ }
+
+ fn canonical_name(&self) -> String {
+ format!("{}", $canonical_name)
+ }
+
+ fn literal(&self, _literal: &Literal) -> String {
+ unreachable!()
+ }
+ }
+ }
+ };
+}
+
+impl_code_type_for_miscellany!(TimestampCodeType, "Timestamp");
+
+impl_code_type_for_miscellany!(DurationCodeType, "Duration");
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/mod.rs b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/mod.rs
new file mode 100644
index 0000000000..8178fcc102
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/mod.rs
@@ -0,0 +1,474 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use anyhow::{Context, Result};
+use askama::Template;
+use heck::{ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase};
+use once_cell::sync::Lazy;
+use serde::{Deserialize, Serialize};
+use std::borrow::Borrow;
+use std::cell::RefCell;
+use std::collections::{BTreeSet, HashMap, HashSet};
+use std::fmt::Debug;
+
+use crate::backend::TemplateExpression;
+use crate::interface::*;
+use crate::BindingsConfig;
+
+mod callback_interface;
+mod compounds;
+mod custom;
+mod enum_;
+mod executor;
+mod external;
+mod miscellany;
+mod object;
+mod primitives;
+mod record;
+
+/// A trait tor the implementation.
+trait CodeType: Debug {
+ /// The language specific label used to reference this type. This will be used in
+ /// method signatures and property declarations.
+ fn type_label(&self) -> String;
+
+ /// A representation of this type label that can be used as part of another
+ /// identifier. e.g. `read_foo()`, or `FooInternals`.
+ ///
+ /// This is especially useful when creating specialized objects or methods to deal
+ /// with this type only.
+ fn canonical_name(&self) -> String {
+ self.type_label()
+ }
+
+ fn literal(&self, _literal: &Literal) -> String {
+ unimplemented!("Unimplemented for {}", self.type_label())
+ }
+
+ /// Name of the FfiConverter
+ ///
+ /// This is the object that contains the lower, write, lift, and read methods for this type.
+ fn ffi_converter_name(&self) -> String {
+ format!("FfiConverter{}", self.canonical_name())
+ }
+
+ /// A list of imports that are needed if this type is in use.
+ /// Classes are imported exactly once.
+ fn imports(&self) -> Option<Vec<String>> {
+ None
+ }
+
+ /// Function to run at startup
+ fn initialization_fn(&self) -> Option<String> {
+ None
+ }
+}
+
+// Taken from Python's `keyword.py` module.
+static KEYWORDS: Lazy<HashSet<String>> = Lazy::new(|| {
+ let kwlist = vec![
+ "False",
+ "None",
+ "True",
+ "__peg_parser__",
+ "and",
+ "as",
+ "assert",
+ "async",
+ "await",
+ "break",
+ "class",
+ "continue",
+ "def",
+ "del",
+ "elif",
+ "else",
+ "except",
+ "finally",
+ "for",
+ "from",
+ "global",
+ "if",
+ "import",
+ "in",
+ "is",
+ "lambda",
+ "nonlocal",
+ "not",
+ "or",
+ "pass",
+ "raise",
+ "return",
+ "try",
+ "while",
+ "with",
+ "yield",
+ ];
+ HashSet::from_iter(kwlist.into_iter().map(|s| s.to_string()))
+});
+
+// Config options to customize the generated python.
+#[derive(Debug, Clone, Default, Serialize, Deserialize)]
+pub struct Config {
+ cdylib_name: Option<String>,
+ #[serde(default)]
+ custom_types: HashMap<String, CustomTypeConfig>,
+}
+
+#[derive(Debug, Clone, Default, Serialize, Deserialize)]
+pub struct CustomTypeConfig {
+ // This `CustomTypeConfig` doesn't have a `type_name` like the others -- which is why we have
+ // separate structs rather than a shared one.
+ imports: Option<Vec<String>>,
+ into_custom: TemplateExpression,
+ from_custom: TemplateExpression,
+}
+
+impl Config {
+ pub fn cdylib_name(&self) -> String {
+ if let Some(cdylib_name) = &self.cdylib_name {
+ cdylib_name.clone()
+ } else {
+ "uniffi".into()
+ }
+ }
+}
+
+impl BindingsConfig for Config {
+ fn update_from_ci(&mut self, ci: &ComponentInterface) {
+ self.cdylib_name
+ .get_or_insert_with(|| format!("uniffi_{}", ci.namespace()));
+ }
+
+ fn update_from_cdylib_name(&mut self, cdylib_name: &str) {
+ self.cdylib_name
+ .get_or_insert_with(|| cdylib_name.to_string());
+ }
+
+ fn update_from_dependency_configs(&mut self, _config_map: HashMap<&str, &Self>) {}
+}
+
+// Generate python bindings for the given ComponentInterface, as a string.
+pub fn generate_python_bindings(config: &Config, ci: &ComponentInterface) -> Result<String> {
+ PythonWrapper::new(config.clone(), ci)
+ .render()
+ .context("failed to render python bindings")
+}
+
+/// A struct to record a Python import statement.
+#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
+pub enum ImportRequirement {
+ /// A simple module import.
+ Module { mod_name: String },
+ /// A single symbol from a module.
+ Symbol {
+ mod_name: String,
+ symbol_name: String,
+ },
+ /// A single symbol from a module with the specified local name.
+ SymbolAs {
+ mod_name: String,
+ symbol_name: String,
+ as_name: String,
+ },
+}
+
+impl ImportRequirement {
+ /// Render the Python import statement.
+ fn render(&self) -> String {
+ match &self {
+ ImportRequirement::Module { mod_name } => format!("import {mod_name}"),
+ ImportRequirement::Symbol {
+ mod_name,
+ symbol_name,
+ } => format!("from {mod_name} import {symbol_name}"),
+ ImportRequirement::SymbolAs {
+ mod_name,
+ symbol_name,
+ as_name,
+ } => format!("from {mod_name} import {symbol_name} as {as_name}"),
+ }
+ }
+}
+
+/// Renders Python helper code for all types
+///
+/// This template is a bit different than others in that it stores internal state from the render
+/// process. Make sure to only call `render()` once.
+#[derive(Template)]
+#[template(syntax = "py", escape = "none", path = "Types.py")]
+pub struct TypeRenderer<'a> {
+ python_config: &'a Config,
+ ci: &'a ComponentInterface,
+ // Track included modules for the `include_once()` macro
+ include_once_names: RefCell<HashSet<String>>,
+ // Track imports added with the `add_import()` macro
+ imports: RefCell<BTreeSet<ImportRequirement>>,
+}
+
+impl<'a> TypeRenderer<'a> {
+ fn new(python_config: &'a Config, ci: &'a ComponentInterface) -> Self {
+ Self {
+ python_config,
+ ci,
+ include_once_names: RefCell::new(HashSet::new()),
+ imports: RefCell::new(BTreeSet::new()),
+ }
+ }
+
+ // The following methods are used by the `Types.py` macros.
+
+ // Helper for the including a template, but only once.
+ //
+ // The first time this is called with a name it will return true, indicating that we should
+ // include the template. Subsequent calls will return false.
+ fn include_once_check(&self, name: &str) -> bool {
+ self.include_once_names
+ .borrow_mut()
+ .insert(name.to_string())
+ }
+
+ // Helper to add an import statement
+ //
+ // Call this inside your template to cause an import statement to be added at the top of the
+ // file. Imports will be sorted and de-deuped.
+ //
+ // Returns an empty string so that it can be used inside an askama `{{ }}` block.
+ fn add_import(&self, name: &str) -> &str {
+ self.imports.borrow_mut().insert(ImportRequirement::Module {
+ mod_name: name.to_owned(),
+ });
+ ""
+ }
+
+ // Like add_import, but arranges for `from module import name`.
+ fn add_import_of(&self, mod_name: &str, name: &str) -> &str {
+ self.imports.borrow_mut().insert(ImportRequirement::Symbol {
+ mod_name: mod_name.to_owned(),
+ symbol_name: name.to_owned(),
+ });
+ ""
+ }
+
+ // Like add_import, but arranges for `from module import name as other`.
+ fn add_import_of_as(&self, mod_name: &str, symbol_name: &str, as_name: &str) -> &str {
+ self.imports
+ .borrow_mut()
+ .insert(ImportRequirement::SymbolAs {
+ mod_name: mod_name.to_owned(),
+ symbol_name: symbol_name.to_owned(),
+ as_name: as_name.to_owned(),
+ });
+ ""
+ }
+}
+
+#[derive(Template)]
+#[template(syntax = "py", escape = "none", path = "wrapper.py")]
+pub struct PythonWrapper<'a> {
+ ci: &'a ComponentInterface,
+ config: Config,
+ type_helper_code: String,
+ type_imports: BTreeSet<ImportRequirement>,
+}
+impl<'a> PythonWrapper<'a> {
+ pub fn new(config: Config, ci: &'a ComponentInterface) -> Self {
+ let type_renderer = TypeRenderer::new(&config, ci);
+ let type_helper_code = type_renderer.render().unwrap();
+ let type_imports = type_renderer.imports.into_inner();
+ Self {
+ config,
+ ci,
+ type_helper_code,
+ type_imports,
+ }
+ }
+
+ pub fn imports(&self) -> Vec<ImportRequirement> {
+ self.type_imports.iter().cloned().collect()
+ }
+}
+
+fn fixup_keyword(name: String) -> String {
+ if KEYWORDS.contains(&name) {
+ format!("_{name}")
+ } else {
+ name
+ }
+}
+
+#[derive(Clone, Default)]
+pub struct PythonCodeOracle;
+
+impl PythonCodeOracle {
+ fn find(&self, type_: &Type) -> Box<dyn CodeType> {
+ type_.clone().as_type().as_codetype()
+ }
+
+ /// Get the idiomatic Python rendering of a class name (for enums, records, errors, etc).
+ fn class_name(&self, nm: &str) -> String {
+ fixup_keyword(nm.to_string().to_upper_camel_case())
+ }
+
+ /// Get the idiomatic Python rendering of a function name.
+ fn fn_name(&self, nm: &str) -> String {
+ fixup_keyword(nm.to_string().to_snake_case())
+ }
+
+ /// Get the idiomatic Python rendering of a variable name.
+ fn var_name(&self, nm: &str) -> String {
+ fixup_keyword(nm.to_string().to_snake_case())
+ }
+
+ /// Get the idiomatic Python rendering of an individual enum variant.
+ fn enum_variant_name(&self, nm: &str) -> String {
+ fixup_keyword(nm.to_string().to_shouty_snake_case())
+ }
+
+ fn ffi_type_label(ffi_type: &FfiType) -> String {
+ match ffi_type {
+ FfiType::Int8 => "ctypes.c_int8".to_string(),
+ FfiType::UInt8 => "ctypes.c_uint8".to_string(),
+ FfiType::Int16 => "ctypes.c_int16".to_string(),
+ FfiType::UInt16 => "ctypes.c_uint16".to_string(),
+ FfiType::Int32 => "ctypes.c_int32".to_string(),
+ FfiType::UInt32 => "ctypes.c_uint32".to_string(),
+ FfiType::Int64 => "ctypes.c_int64".to_string(),
+ FfiType::UInt64 => "ctypes.c_uint64".to_string(),
+ FfiType::Float32 => "ctypes.c_float".to_string(),
+ FfiType::Float64 => "ctypes.c_double".to_string(),
+ FfiType::RustArcPtr(_) => "ctypes.c_void_p".to_string(),
+ FfiType::RustBuffer(maybe_suffix) => match maybe_suffix {
+ Some(suffix) => format!("_UniffiRustBuffer{suffix}"),
+ None => "_UniffiRustBuffer".to_string(),
+ },
+ FfiType::ForeignBytes => "_UniffiForeignBytes".to_string(),
+ FfiType::ForeignCallback => "_UNIFFI_FOREIGN_CALLBACK_T".to_string(),
+ // Pointer to an `asyncio.EventLoop` instance
+ FfiType::ForeignExecutorHandle => "ctypes.c_size_t".to_string(),
+ FfiType::ForeignExecutorCallback => "_UNIFFI_FOREIGN_EXECUTOR_CALLBACK_T".to_string(),
+ FfiType::RustFutureHandle => "ctypes.c_void_p".to_string(),
+ FfiType::RustFutureContinuationCallback => "_UNIFFI_FUTURE_CONTINUATION_T".to_string(),
+ FfiType::RustFutureContinuationData => "ctypes.c_size_t".to_string(),
+ }
+ }
+}
+
+trait AsCodeType {
+ fn as_codetype(&self) -> Box<dyn CodeType>;
+}
+
+impl<T: AsType> AsCodeType for T {
+ fn as_codetype(&self) -> Box<dyn CodeType> {
+ // Map `Type` instances to a `Box<dyn CodeType>` for that type.
+ //
+ // There is a companion match in `templates/Types.py` which performs a similar function for the
+ // template code.
+ //
+ // - When adding additional types here, make sure to also add a match arm to the `Types.py` template.
+ // - To keep things manageable, let's try to limit ourselves to these 2 mega-matches
+ match self.as_type() {
+ Type::UInt8 => Box::new(primitives::UInt8CodeType),
+ Type::Int8 => Box::new(primitives::Int8CodeType),
+ Type::UInt16 => Box::new(primitives::UInt16CodeType),
+ Type::Int16 => Box::new(primitives::Int16CodeType),
+ Type::UInt32 => Box::new(primitives::UInt32CodeType),
+ Type::Int32 => Box::new(primitives::Int32CodeType),
+ Type::UInt64 => Box::new(primitives::UInt64CodeType),
+ Type::Int64 => Box::new(primitives::Int64CodeType),
+ Type::Float32 => Box::new(primitives::Float32CodeType),
+ Type::Float64 => Box::new(primitives::Float64CodeType),
+ Type::Boolean => Box::new(primitives::BooleanCodeType),
+ Type::String => Box::new(primitives::StringCodeType),
+ Type::Bytes => Box::new(primitives::BytesCodeType),
+
+ Type::Timestamp => Box::new(miscellany::TimestampCodeType),
+ Type::Duration => Box::new(miscellany::DurationCodeType),
+
+ Type::Enum { name, .. } => Box::new(enum_::EnumCodeType::new(name)),
+ Type::Object { name, .. } => Box::new(object::ObjectCodeType::new(name)),
+ Type::Record { name, .. } => Box::new(record::RecordCodeType::new(name)),
+ Type::CallbackInterface { name, .. } => {
+ Box::new(callback_interface::CallbackInterfaceCodeType::new(name))
+ }
+ Type::ForeignExecutor => Box::new(executor::ForeignExecutorCodeType),
+ Type::Optional { inner_type } => {
+ Box::new(compounds::OptionalCodeType::new(*inner_type))
+ }
+ Type::Sequence { inner_type } => {
+ Box::new(compounds::SequenceCodeType::new(*inner_type))
+ }
+ Type::Map {
+ key_type,
+ value_type,
+ } => Box::new(compounds::MapCodeType::new(*key_type, *value_type)),
+ Type::External { name, .. } => Box::new(external::ExternalCodeType::new(name)),
+ Type::Custom { name, .. } => Box::new(custom::CustomCodeType::new(name)),
+ }
+ }
+}
+
+pub mod filters {
+ use super::*;
+ pub use crate::backend::filters::*;
+
+ pub(super) fn type_name(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
+ Ok(as_ct.as_codetype().type_label())
+ }
+
+ pub(super) fn ffi_converter_name(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
+ Ok(String::from("_Uniffi") + &as_ct.as_codetype().ffi_converter_name()[3..])
+ }
+
+ pub(super) fn canonical_name(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
+ Ok(as_ct.as_codetype().canonical_name())
+ }
+
+ pub(super) fn lift_fn(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
+ Ok(format!("{}.lift", ffi_converter_name(as_ct)?))
+ }
+
+ pub(super) fn lower_fn(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
+ Ok(format!("{}.lower", ffi_converter_name(as_ct)?))
+ }
+
+ pub(super) fn read_fn(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
+ Ok(format!("{}.read", ffi_converter_name(as_ct)?))
+ }
+
+ pub(super) fn write_fn(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
+ Ok(format!("{}.write", ffi_converter_name(as_ct)?))
+ }
+
+ pub(super) fn literal_py(
+ literal: &Literal,
+ as_ct: &impl AsCodeType,
+ ) -> Result<String, askama::Error> {
+ Ok(as_ct.as_codetype().literal(literal))
+ }
+
+ pub fn ffi_type_name(type_: &FfiType) -> Result<String, askama::Error> {
+ Ok(PythonCodeOracle::ffi_type_label(type_))
+ }
+
+ /// Get the idiomatic Python rendering of a class name (for enums, records, errors, etc).
+ pub fn class_name(nm: &str) -> Result<String, askama::Error> {
+ Ok(PythonCodeOracle.class_name(nm))
+ }
+
+ /// Get the idiomatic Python rendering of a function name.
+ pub fn fn_name(nm: &str) -> Result<String, askama::Error> {
+ Ok(PythonCodeOracle.fn_name(nm))
+ }
+
+ /// Get the idiomatic Python rendering of a variable name.
+ pub fn var_name(nm: &str) -> Result<String, askama::Error> {
+ Ok(PythonCodeOracle.var_name(nm))
+ }
+
+ /// Get the idiomatic Python rendering of an individual enum variant.
+ pub fn enum_variant_py(nm: &str) -> Result<String, askama::Error> {
+ Ok(PythonCodeOracle.enum_variant_name(nm))
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/object.rs b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/object.rs
new file mode 100644
index 0000000000..1165bb0e54
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/object.rs
@@ -0,0 +1,31 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::CodeType;
+use crate::backend::Literal;
+
+#[derive(Debug)]
+pub struct ObjectCodeType {
+ id: String,
+}
+
+impl ObjectCodeType {
+ pub fn new(id: String) -> Self {
+ Self { id }
+ }
+}
+
+impl CodeType for ObjectCodeType {
+ fn type_label(&self) -> String {
+ super::PythonCodeOracle.class_name(&self.id)
+ }
+
+ fn canonical_name(&self) -> String {
+ format!("Type{}", self.id)
+ }
+
+ fn literal(&self, _literal: &Literal) -> String {
+ unreachable!();
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/primitives.rs b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/primitives.rs
new file mode 100644
index 0000000000..4b3edecad4
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/primitives.rs
@@ -0,0 +1,71 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::CodeType;
+use crate::backend::Literal;
+use crate::interface::Radix;
+use paste::paste;
+
+fn render_literal(literal: &Literal) -> String {
+ match literal {
+ Literal::Boolean(v) => {
+ if *v {
+ "True".into()
+ } else {
+ "False".into()
+ }
+ }
+ Literal::String(s) => format!("\"{s}\""),
+ // https://docs.python.org/3/reference/lexical_analysis.html#integer-literals
+ Literal::Int(i, radix, _) => match radix {
+ Radix::Octal => format!("int(0o{i:o})"),
+ Radix::Decimal => format!("{i}"),
+ Radix::Hexadecimal => format!("{i:#x}"),
+ },
+ Literal::UInt(i, radix, _) => match radix {
+ Radix::Octal => format!("0o{i:o}"),
+ Radix::Decimal => format!("{i}"),
+ Radix::Hexadecimal => format!("{i:#x}"),
+ },
+ Literal::Float(string, _type_) => string.clone(),
+
+ _ => unreachable!("Literal"),
+ }
+}
+
+macro_rules! impl_code_type_for_primitive {
+ ($T:ty, $python_name:literal, $canonical_name:literal) => {
+ paste! {
+ #[derive(Debug)]
+ pub struct $T;
+ impl CodeType for $T {
+ fn type_label(&self) -> String {
+ $python_name.into()
+ }
+
+ fn canonical_name(&self) -> String {
+ $canonical_name.into()
+ }
+
+ fn literal(&self, literal: &Literal) -> String {
+ render_literal(&literal)
+ }
+ }
+ }
+ };
+}
+
+impl_code_type_for_primitive!(BooleanCodeType, "bool", "Bool");
+impl_code_type_for_primitive!(StringCodeType, "str", "String");
+impl_code_type_for_primitive!(BytesCodeType, "bytes", "Bytes");
+impl_code_type_for_primitive!(Int8CodeType, "int", "Int8");
+impl_code_type_for_primitive!(Int16CodeType, "int", "Int16");
+impl_code_type_for_primitive!(Int32CodeType, "int", "Int32");
+impl_code_type_for_primitive!(Int64CodeType, "int", "Int64");
+impl_code_type_for_primitive!(UInt8CodeType, "int", "UInt8");
+impl_code_type_for_primitive!(UInt16CodeType, "int", "UInt16");
+impl_code_type_for_primitive!(UInt32CodeType, "int", "UInt32");
+impl_code_type_for_primitive!(UInt64CodeType, "int", "UInt64");
+impl_code_type_for_primitive!(Float32CodeType, "float", "Float");
+impl_code_type_for_primitive!(Float64CodeType, "float", "Double");
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/record.rs b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/record.rs
new file mode 100644
index 0000000000..df00f98e8b
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/python/gen_python/record.rs
@@ -0,0 +1,31 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::CodeType;
+use crate::backend::Literal;
+
+#[derive(Debug)]
+pub struct RecordCodeType {
+ id: String,
+}
+
+impl RecordCodeType {
+ pub fn new(id: String) -> Self {
+ Self { id }
+ }
+}
+
+impl CodeType for RecordCodeType {
+ fn type_label(&self) -> String {
+ super::PythonCodeOracle.class_name(&self.id)
+ }
+
+ fn canonical_name(&self) -> String {
+ format!("Type{}", self.id)
+ }
+
+ fn literal(&self, _literal: &Literal) -> String {
+ unreachable!();
+ }
+}