summaryrefslogtreecommitdiffstats
path: root/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin')
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/callback_interface.rs31
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/compounds.rs98
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/custom.rs27
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/enum_.rs40
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/executor.rs24
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/external.rs27
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/miscellany.rs30
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs530
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/object.rs27
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/primitives.rs86
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/record.rs27
-rw-r--r--third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/variant.rs33
12 files changed, 980 insertions, 0 deletions
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/callback_interface.rs b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/callback_interface.rs
new file mode 100644
index 0000000000..e20020e87c
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/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::ComponentInterface;
+
+#[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, ci: &ComponentInterface) -> String {
+ super::KotlinCodeOracle.class_name(ci, &self.id)
+ }
+
+ fn canonical_name(&self) -> String {
+ format!("Type{}", self.id)
+ }
+
+ fn initialization_fn(&self) -> Option<String> {
+ Some(format!("{}.register", self.ffi_converter_name()))
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/compounds.rs b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/compounds.rs
new file mode 100644
index 0000000000..4329f32f4c
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/compounds.rs
@@ -0,0 +1,98 @@
+/* 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::{AsCodeType, CodeType};
+use crate::backend::{Literal, Type};
+use crate::ComponentInterface;
+use paste::paste;
+
+fn render_literal(literal: &Literal, inner: &Type, ci: &ComponentInterface) -> String {
+ match literal {
+ Literal::Null => "null".into(),
+ Literal::EmptySequence => "listOf()".into(),
+ Literal::EmptyMap => "mapOf()".into(),
+
+ // For optionals
+ _ => super::KotlinCodeOracle.find(inner).literal(literal, ci),
+ }
+}
+
+macro_rules! impl_code_type_for_compound {
+ ($T:ty, $type_label_pattern:literal, $canonical_name_pattern: literal) => {
+ paste! {
+ #[derive(Debug)]
+ pub struct $T {
+ inner: Type,
+ }
+
+ impl $T {
+ pub fn new(inner: Type) -> Self {
+ Self { inner }
+ }
+ fn inner(&self) -> &Type {
+ &self.inner
+ }
+ }
+
+ impl CodeType for $T {
+ fn type_label(&self, ci: &ComponentInterface) -> String {
+ format!($type_label_pattern, super::KotlinCodeOracle.find(self.inner()).type_label(ci))
+ }
+
+ fn canonical_name(&self) -> String {
+ format!($canonical_name_pattern, super::KotlinCodeOracle.find(self.inner()).canonical_name())
+ }
+
+ fn literal(&self, literal: &Literal, ci: &ComponentInterface) -> String {
+ render_literal(literal, self.inner(), ci)
+ }
+ }
+ }
+ }
+ }
+
+impl_code_type_for_compound!(OptionalCodeType, "{}?", "Optional{}");
+impl_code_type_for_compound!(SequenceCodeType, "List<{}>", "Sequence{}");
+
+#[derive(Debug)]
+pub struct MapCodeType {
+ key: Type,
+ value: Type,
+}
+
+impl MapCodeType {
+ pub fn new(key: Type, value: Type) -> Self {
+ Self { key, value }
+ }
+
+ fn key(&self) -> &Type {
+ &self.key
+ }
+
+ fn value(&self) -> &Type {
+ &self.value
+ }
+}
+
+impl CodeType for MapCodeType {
+ fn type_label(&self, ci: &ComponentInterface) -> String {
+ format!(
+ "Map<{}, {}>",
+ super::KotlinCodeOracle.find(self.key()).type_label(ci),
+ super::KotlinCodeOracle.find(self.value()).type_label(ci),
+ )
+ }
+
+ fn canonical_name(&self) -> String {
+ format!(
+ "Map{}{}",
+ self.key().as_codetype().canonical_name(),
+ self.value().as_codetype().canonical_name(),
+ )
+ }
+
+ fn literal(&self, literal: &Literal, ci: &ComponentInterface) -> String {
+ render_literal(literal, &self.value, ci)
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/custom.rs b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/custom.rs
new file mode 100644
index 0000000000..137cd0d8d9
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/custom.rs
@@ -0,0 +1,27 @@
+/* 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::ComponentInterface;
+
+#[derive(Debug)]
+pub struct CustomCodeType {
+ name: String,
+}
+
+impl CustomCodeType {
+ pub fn new(name: String) -> Self {
+ CustomCodeType { name }
+ }
+}
+
+impl CodeType for CustomCodeType {
+ fn type_label(&self, _ci: &ComponentInterface) -> String {
+ self.name.clone()
+ }
+
+ fn canonical_name(&self) -> String {
+ format!("Type{}", self.name)
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/enum_.rs b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/enum_.rs
new file mode 100644
index 0000000000..f5300c10ee
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/enum_.rs
@@ -0,0 +1,40 @@
+/* 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::ComponentInterface;
+
+#[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, ci: &ComponentInterface) -> String {
+ super::KotlinCodeOracle.class_name(ci, &self.id)
+ }
+
+ fn canonical_name(&self) -> String {
+ format!("Type{}", self.id)
+ }
+
+ fn literal(&self, literal: &Literal, ci: &ComponentInterface) -> String {
+ if let Literal::Enum(v, _) = literal {
+ format!(
+ "{}.{}",
+ self.type_label(ci),
+ super::KotlinCodeOracle.enum_variant_name(v)
+ )
+ } else {
+ unreachable!();
+ }
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/executor.rs b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/executor.rs
new file mode 100644
index 0000000000..154e12a381
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/executor.rs
@@ -0,0 +1,24 @@
+/* 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::ComponentInterface;
+
+#[derive(Debug)]
+pub struct ForeignExecutorCodeType;
+
+impl CodeType for ForeignExecutorCodeType {
+ fn type_label(&self, _ci: &ComponentInterface) -> String {
+ // Kotlin uses a CoroutineScope for ForeignExecutor
+ "CoroutineScope".into()
+ }
+
+ fn canonical_name(&self) -> String {
+ "ForeignExecutor".into()
+ }
+
+ fn initialization_fn(&self) -> Option<String> {
+ Some("FfiConverterForeignExecutor.register".into())
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/external.rs b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/external.rs
new file mode 100644
index 0000000000..3ecf09d47f
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/external.rs
@@ -0,0 +1,27 @@
+/* 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::ComponentInterface;
+
+#[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, _ci: &ComponentInterface) -> String {
+ self.name.clone()
+ }
+
+ fn canonical_name(&self) -> String {
+ format!("Type{}", self.name)
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/miscellany.rs b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/miscellany.rs
new file mode 100644
index 0000000000..17331ff511
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/miscellany.rs
@@ -0,0 +1,30 @@
+/* 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::ComponentInterface;
+use paste::paste;
+
+macro_rules! impl_code_type_for_miscellany {
+ ($T:ty, $class_name:literal, $canonical_name:literal) => {
+ paste! {
+ #[derive(Debug)]
+ pub struct $T;
+
+ impl CodeType for $T {
+ fn type_label(&self, _ci: &ComponentInterface) -> String {
+ $class_name.into()
+ }
+
+ fn canonical_name(&self) -> String {
+ $canonical_name.into()
+ }
+ }
+ }
+ };
+}
+
+impl_code_type_for_miscellany!(TimestampCodeType, "java.time.Instant", "Timestamp");
+
+impl_code_type_for_miscellany!(DurationCodeType, "java.time.Duration", "Duration");
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs
new file mode 100644
index 0000000000..1ed0575a9a
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/mod.rs
@@ -0,0 +1,530 @@
+/* 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 std::borrow::Borrow;
+use std::cell::RefCell;
+use std::collections::{BTreeSet, HashMap, HashSet};
+use std::fmt::Debug;
+
+use anyhow::{Context, Result};
+use askama::Template;
+use heck::{ToLowerCamelCase, ToShoutySnakeCase, ToUpperCamelCase};
+use serde::{Deserialize, Serialize};
+
+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;
+mod variant;
+
+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, ci: &ComponentInterface) -> 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;
+
+ fn literal(&self, _literal: &Literal, ci: &ComponentInterface) -> String {
+ unimplemented!("Unimplemented for {}", self.type_label(ci))
+ }
+
+ /// Name of the FfiConverter
+ ///
+ /// This is the object that contains the lower, write, lift, and read methods for this type.
+ /// Depending on the binding this will either be a singleton or a class with static methods.
+ ///
+ /// This is the newer way of handling these methods and replaces the lower, write, lift, and
+ /// read CodeType methods. Currently only used by Kotlin, but the plan is to move other
+ /// backends to using this.
+ 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
+ }
+}
+
+// config options to customize the generated Kotlin.
+#[derive(Debug, Default, Clone, Serialize, Deserialize)]
+pub struct Config {
+ package_name: Option<String>,
+ cdylib_name: Option<String>,
+ #[serde(default)]
+ custom_types: HashMap<String, CustomTypeConfig>,
+ #[serde(default)]
+ external_packages: HashMap<String, String>,
+}
+
+#[derive(Debug, Default, Clone, Serialize, Deserialize)]
+pub struct CustomTypeConfig {
+ imports: Option<Vec<String>>,
+ type_name: Option<String>,
+ into_custom: TemplateExpression,
+ from_custom: TemplateExpression,
+}
+
+impl Config {
+ pub fn package_name(&self) -> String {
+ if let Some(package_name) = &self.package_name {
+ package_name.clone()
+ } else {
+ "uniffi".into()
+ }
+ }
+
+ 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.package_name
+ .get_or_insert_with(|| format!("uniffi.{}", ci.namespace()));
+ 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>) {
+ for (crate_name, config) in config_map {
+ if !self.external_packages.contains_key(crate_name) {
+ self.external_packages
+ .insert(crate_name.to_string(), config.package_name());
+ }
+ }
+ }
+}
+
+// Generate kotlin bindings for the given ComponentInterface, as a string.
+pub fn generate_bindings(config: &Config, ci: &ComponentInterface) -> Result<String> {
+ KotlinWrapper::new(config.clone(), ci)
+ .render()
+ .context("failed to render kotlin bindings")
+}
+
+/// A struct to record a Kotlin import statement.
+#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
+pub enum ImportRequirement {
+ /// The name we are importing.
+ Import { name: String },
+ /// Import the name with the specified local name.
+ ImportAs { name: String, as_name: String },
+}
+
+impl ImportRequirement {
+ /// Render the Kotlin import statement.
+ fn render(&self) -> String {
+ match &self {
+ ImportRequirement::Import { name } => format!("import {name}"),
+ ImportRequirement::ImportAs { name, as_name } => {
+ format!("import {name} as {as_name}")
+ }
+ }
+ }
+}
+
+/// Renders Kotlin 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 = "kt", escape = "none", path = "Types.kt")]
+pub struct TypeRenderer<'a> {
+ 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(config: &'a Config, ci: &'a ComponentInterface) -> Self {
+ Self {
+ config,
+ ci,
+ include_once_names: RefCell::new(HashSet::new()),
+ imports: RefCell::new(BTreeSet::new()),
+ }
+ }
+
+ // Get the package name for an external type
+ fn external_type_package_name(&self, module_path: &str, namespace: &str) -> String {
+ // config overrides are keyed by the crate name, default fallback is the namespace.
+ let crate_name = module_path.split("::").next().unwrap();
+ match self.config.external_packages.get(crate_name) {
+ Some(name) => name.clone(),
+ // unreachable in library mode - all deps are in our config with correct namespace.
+ None => format!("uniffi.{namespace}"),
+ }
+ }
+
+ // The following methods are used by the `Types.kt` 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::Import {
+ name: name.to_owned(),
+ });
+ ""
+ }
+
+ // Like add_import, but arranges for `import name as as_name`
+ fn add_import_as(&self, name: &str, as_name: &str) -> &str {
+ self.imports
+ .borrow_mut()
+ .insert(ImportRequirement::ImportAs {
+ name: name.to_owned(),
+ as_name: as_name.to_owned(),
+ });
+ ""
+ }
+}
+
+#[derive(Template)]
+#[template(syntax = "kt", escape = "none", path = "wrapper.kt")]
+pub struct KotlinWrapper<'a> {
+ config: Config,
+ ci: &'a ComponentInterface,
+ type_helper_code: String,
+ type_imports: BTreeSet<ImportRequirement>,
+ has_async_fns: bool,
+}
+
+impl<'a> KotlinWrapper<'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,
+ has_async_fns: ci.has_async_fns(),
+ }
+ }
+
+ pub fn initialization_fns(&self) -> Vec<String> {
+ self.ci
+ .iter_types()
+ .map(|t| KotlinCodeOracle.find(t))
+ .filter_map(|ct| ct.initialization_fn())
+ .chain(
+ self.has_async_fns
+ .then(|| "uniffiRustFutureContinuationCallback.register".into()),
+ )
+ .collect()
+ }
+
+ pub fn imports(&self) -> Vec<ImportRequirement> {
+ self.type_imports.iter().cloned().collect()
+ }
+}
+
+#[derive(Clone)]
+pub struct KotlinCodeOracle;
+
+impl KotlinCodeOracle {
+ fn find(&self, type_: &Type) -> Box<dyn CodeType> {
+ type_.clone().as_type().as_codetype()
+ }
+
+ /// Get the idiomatic Kotlin rendering of a class name (for enums, records, errors, etc).
+ fn class_name(&self, ci: &ComponentInterface, nm: &str) -> String {
+ let name = nm.to_string().to_upper_camel_case();
+ // fixup errors.
+ ci.is_name_used_as_error(nm)
+ .then(|| self.convert_error_suffix(&name))
+ .unwrap_or(name)
+ }
+
+ fn convert_error_suffix(&self, nm: &str) -> String {
+ match nm.strip_suffix("Error") {
+ None => nm.to_string(),
+ Some(stripped) => format!("{stripped}Exception"),
+ }
+ }
+
+ /// Get the idiomatic Kotlin rendering of a function name.
+ fn fn_name(&self, nm: &str) -> String {
+ format!("`{}`", nm.to_string().to_lower_camel_case())
+ }
+
+ /// Get the idiomatic Kotlin rendering of a variable name.
+ fn var_name(&self, nm: &str) -> String {
+ format!("`{}`", nm.to_string().to_lower_camel_case())
+ }
+
+ /// Get the idiomatic Kotlin rendering of an individual enum variant.
+ fn enum_variant_name(&self, nm: &str) -> String {
+ nm.to_string().to_shouty_snake_case()
+ }
+
+ fn ffi_type_label_by_value(ffi_type: &FfiType) -> String {
+ match ffi_type {
+ FfiType::RustBuffer(_) => format!("{}.ByValue", Self::ffi_type_label(ffi_type)),
+ _ => Self::ffi_type_label(ffi_type),
+ }
+ }
+
+ fn ffi_type_label(ffi_type: &FfiType) -> String {
+ match ffi_type {
+ // Note that unsigned integers in Kotlin are currently experimental, but java.nio.ByteBuffer does not
+ // support them yet. Thus, we use the signed variants to represent both signed and unsigned
+ // types from the component API.
+ FfiType::Int8 | FfiType::UInt8 => "Byte".to_string(),
+ FfiType::Int16 | FfiType::UInt16 => "Short".to_string(),
+ FfiType::Int32 | FfiType::UInt32 => "Int".to_string(),
+ FfiType::Int64 | FfiType::UInt64 => "Long".to_string(),
+ FfiType::Float32 => "Float".to_string(),
+ FfiType::Float64 => "Double".to_string(),
+ FfiType::RustArcPtr(_) => "Pointer".to_string(),
+ FfiType::RustBuffer(maybe_suffix) => {
+ format!("RustBuffer{}", maybe_suffix.as_deref().unwrap_or_default())
+ }
+ FfiType::ForeignBytes => "ForeignBytes.ByValue".to_string(),
+ FfiType::ForeignCallback => "ForeignCallback".to_string(),
+ FfiType::ForeignExecutorHandle => "USize".to_string(),
+ FfiType::ForeignExecutorCallback => "UniFfiForeignExecutorCallback".to_string(),
+ FfiType::RustFutureHandle => "Pointer".to_string(),
+ FfiType::RustFutureContinuationCallback => {
+ "UniFffiRustFutureContinuationCallbackType".to_string()
+ }
+ FfiType::RustFutureContinuationData => "USize".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.kt` 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.kt` 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)),
+ }
+ }
+}
+
+mod filters {
+ use super::*;
+ pub use crate::backend::filters::*;
+
+ pub(super) fn type_name(
+ as_ct: &impl AsCodeType,
+ ci: &ComponentInterface,
+ ) -> Result<String, askama::Error> {
+ Ok(as_ct.as_codetype().type_label(ci))
+ }
+
+ pub(super) fn canonical_name(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
+ Ok(as_ct.as_codetype().canonical_name())
+ }
+
+ pub(super) fn ffi_converter_name(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
+ Ok(as_ct.as_codetype().ffi_converter_name())
+ }
+
+ pub(super) fn lower_fn(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
+ Ok(format!(
+ "{}.lower",
+ as_ct.as_codetype().ffi_converter_name()
+ ))
+ }
+
+ pub(super) fn allocation_size_fn(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
+ Ok(format!(
+ "{}.allocationSize",
+ as_ct.as_codetype().ffi_converter_name()
+ ))
+ }
+
+ pub(super) fn write_fn(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
+ Ok(format!(
+ "{}.write",
+ as_ct.as_codetype().ffi_converter_name()
+ ))
+ }
+
+ pub(super) fn lift_fn(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
+ Ok(format!("{}.lift", as_ct.as_codetype().ffi_converter_name()))
+ }
+
+ pub(super) fn read_fn(as_ct: &impl AsCodeType) -> Result<String, askama::Error> {
+ Ok(format!("{}.read", as_ct.as_codetype().ffi_converter_name()))
+ }
+
+ pub fn render_literal(
+ literal: &Literal,
+ as_ct: &impl AsType,
+ ci: &ComponentInterface,
+ ) -> Result<String, askama::Error> {
+ Ok(as_ct.as_codetype().literal(literal, ci))
+ }
+
+ pub fn ffi_type_name_by_value(type_: &FfiType) -> Result<String, askama::Error> {
+ Ok(KotlinCodeOracle::ffi_type_label_by_value(type_))
+ }
+
+ /// Get the idiomatic Kotlin rendering of a function name.
+ pub fn fn_name(nm: &str) -> Result<String, askama::Error> {
+ Ok(KotlinCodeOracle.fn_name(nm))
+ }
+
+ /// Get the idiomatic Kotlin rendering of a variable name.
+ pub fn var_name(nm: &str) -> Result<String, askama::Error> {
+ Ok(KotlinCodeOracle.var_name(nm))
+ }
+
+ /// Get a String representing the name used for an individual enum variant.
+ pub fn variant_name(v: &Variant) -> Result<String, askama::Error> {
+ Ok(KotlinCodeOracle.enum_variant_name(v.name()))
+ }
+
+ pub fn error_variant_name(v: &Variant) -> Result<String, askama::Error> {
+ let name = v.name().to_string().to_upper_camel_case();
+ Ok(KotlinCodeOracle.convert_error_suffix(&name))
+ }
+
+ pub fn async_poll(
+ callable: impl Callable,
+ ci: &ComponentInterface,
+ ) -> Result<String, askama::Error> {
+ let ffi_func = callable.ffi_rust_future_poll(ci);
+ Ok(format!(
+ "{{ future, continuation -> _UniFFILib.INSTANCE.{ffi_func}(future, continuation) }}"
+ ))
+ }
+
+ pub fn async_complete(
+ callable: impl Callable,
+ ci: &ComponentInterface,
+ ) -> Result<String, askama::Error> {
+ let ffi_func = callable.ffi_rust_future_complete(ci);
+ let call = format!("_UniFFILib.INSTANCE.{ffi_func}(future, continuation)");
+ let call = match callable.return_type() {
+ Some(Type::External {
+ kind: ExternalKind::DataClass,
+ name,
+ ..
+ }) => {
+ // Need to convert the RustBuffer from our package to the RustBuffer of the external package
+ let suffix = KotlinCodeOracle.class_name(ci, &name);
+ format!("{call}.let {{ RustBuffer{suffix}.create(it.capacity, it.len, it.data) }}")
+ }
+ _ => call,
+ };
+ Ok(format!("{{ future, continuation -> {call} }}"))
+ }
+
+ pub fn async_free(
+ callable: impl Callable,
+ ci: &ComponentInterface,
+ ) -> Result<String, askama::Error> {
+ let ffi_func = callable.ffi_rust_future_free(ci);
+ Ok(format!(
+ "{{ future -> _UniFFILib.INSTANCE.{ffi_func}(future) }}"
+ ))
+ }
+
+ /// Remove the "`" chars we put around function/variable names
+ ///
+ /// These are used to avoid name clashes with kotlin identifiers, but sometimes you want to
+ /// render the name unquoted. One example is the message property for errors where we want to
+ /// display the name for the user.
+ pub fn unquote(nm: &str) -> Result<String, askama::Error> {
+ Ok(nm.trim_matches('`').to_string())
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/object.rs b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/object.rs
new file mode 100644
index 0000000000..c39ae59cce
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/object.rs
@@ -0,0 +1,27 @@
+/* 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::ComponentInterface;
+
+#[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, ci: &ComponentInterface) -> String {
+ super::KotlinCodeOracle.class_name(ci, &self.id)
+ }
+
+ fn canonical_name(&self) -> String {
+ format!("Type{}", self.id)
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/primitives.rs b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/primitives.rs
new file mode 100644
index 0000000000..22495fa209
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/primitives.rs
@@ -0,0 +1,86 @@
+/* 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::{ComponentInterface, Radix, Type};
+use paste::paste;
+
+fn render_literal(literal: &Literal, _ci: &ComponentInterface) -> String {
+ fn typed_number(type_: &Type, num_str: String) -> String {
+ match type_ {
+ // Bytes, Shorts and Ints can all be inferred from the type.
+ Type::Int8 | Type::Int16 | Type::Int32 => num_str,
+ Type::Int64 => format!("{num_str}L"),
+
+ Type::UInt8 | Type::UInt16 | Type::UInt32 => format!("{num_str}u"),
+ Type::UInt64 => format!("{num_str}uL"),
+
+ Type::Float32 => format!("{num_str}f"),
+ Type::Float64 => num_str,
+ _ => panic!("Unexpected literal: {num_str} is not a number"),
+ }
+ }
+
+ match literal {
+ Literal::Boolean(v) => format!("{v}"),
+ Literal::String(s) => format!("\"{s}\""),
+ Literal::Int(i, radix, type_) => typed_number(
+ type_,
+ match radix {
+ Radix::Octal => format!("{i:#x}"),
+ Radix::Decimal => format!("{i}"),
+ Radix::Hexadecimal => format!("{i:#x}"),
+ },
+ ),
+ Literal::UInt(i, radix, type_) => typed_number(
+ type_,
+ match radix {
+ Radix::Octal => format!("{i:#x}"),
+ Radix::Decimal => format!("{i}"),
+ Radix::Hexadecimal => format!("{i:#x}"),
+ },
+ ),
+ Literal::Float(string, type_) => typed_number(type_, string.clone()),
+
+ _ => unreachable!("Literal"),
+ }
+}
+
+macro_rules! impl_code_type_for_primitive {
+ ($T:ty, $class_name:literal) => {
+ paste! {
+ #[derive(Debug)]
+ pub struct $T;
+
+ impl CodeType for $T {
+ fn type_label(&self, _ci: &ComponentInterface) -> String {
+ $class_name.into()
+ }
+
+ fn canonical_name(&self) -> String {
+ $class_name.into()
+ }
+
+ fn literal(&self, literal: &Literal, ci: &ComponentInterface) -> String {
+ render_literal(&literal, ci)
+ }
+ }
+ }
+ };
+}
+
+impl_code_type_for_primitive!(BooleanCodeType, "Boolean");
+impl_code_type_for_primitive!(StringCodeType, "String");
+impl_code_type_for_primitive!(BytesCodeType, "ByteArray");
+impl_code_type_for_primitive!(Int8CodeType, "Byte");
+impl_code_type_for_primitive!(Int16CodeType, "Short");
+impl_code_type_for_primitive!(Int32CodeType, "Int");
+impl_code_type_for_primitive!(Int64CodeType, "Long");
+impl_code_type_for_primitive!(UInt8CodeType, "UByte");
+impl_code_type_for_primitive!(UInt16CodeType, "UShort");
+impl_code_type_for_primitive!(UInt32CodeType, "UInt");
+impl_code_type_for_primitive!(UInt64CodeType, "ULong");
+impl_code_type_for_primitive!(Float32CodeType, "Float");
+impl_code_type_for_primitive!(Float64CodeType, "Double");
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/record.rs b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/record.rs
new file mode 100644
index 0000000000..17781c2220
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/record.rs
@@ -0,0 +1,27 @@
+/* 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::ComponentInterface;
+
+#[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, ci: &ComponentInterface) -> String {
+ super::KotlinCodeOracle.class_name(ci, &self.id)
+ }
+
+ fn canonical_name(&self) -> String {
+ format!("Type{}", self.id)
+ }
+}
diff --git a/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/variant.rs b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/variant.rs
new file mode 100644
index 0000000000..c7673882d9
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/bindings/kotlin/gen_kotlin/variant.rs
@@ -0,0 +1,33 @@
+/* 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::{AsCodeType, CodeType, KotlinCodeOracle};
+use crate::interface::{ComponentInterface, Variant};
+
+#[derive(Debug)]
+pub(super) struct VariantCodeType {
+ pub v: Variant,
+}
+
+impl CodeType for VariantCodeType {
+ fn type_label(&self, ci: &ComponentInterface) -> String {
+ KotlinCodeOracle.class_name(ci, self.v.name())
+ }
+
+ fn canonical_name(&self) -> String {
+ self.v.name().to_string()
+ }
+}
+
+impl AsCodeType for Variant {
+ fn as_codetype(&self) -> Box<dyn CodeType> {
+ Box::new(VariantCodeType { v: self.clone() })
+ }
+}
+
+impl AsCodeType for &Variant {
+ fn as_codetype(&self) -> Box<dyn CodeType> {
+ Box::new(VariantCodeType { v: (*self).clone() })
+ }
+}