summaryrefslogtreecommitdiffstats
path: root/third_party/rust/uniffi_bindgen/src/interface/callbacks.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/uniffi_bindgen/src/interface/callbacks.rs')
-rw-r--r--third_party/rust/uniffi_bindgen/src/interface/callbacks.rs169
1 files changed, 169 insertions, 0 deletions
diff --git a/third_party/rust/uniffi_bindgen/src/interface/callbacks.rs b/third_party/rust/uniffi_bindgen/src/interface/callbacks.rs
new file mode 100644
index 0000000000..a7164cec5b
--- /dev/null
+++ b/third_party/rust/uniffi_bindgen/src/interface/callbacks.rs
@@ -0,0 +1,169 @@
+/* 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/. */
+
+//! # Callback Interface definitions for a `ComponentInterface`.
+//!
+//! This module converts callback interface definitions from UDL into structures that
+//! can be added to a `ComponentInterface`. A declaration in the UDL like this:
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {};
+//! callback interface Example {
+//! string hello();
+//! };
+//! # "##)?;
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+//!
+//! Will result in a [`CallbackInterface`] member being added to the resulting
+//! [`ComponentInterface`]:
+//!
+//! ```
+//! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
+//! # namespace example {};
+//! # callback interface Example {
+//! # string hello();
+//! # };
+//! # "##)?;
+//! let callback = ci.get_callback_interface_definition("Example").unwrap();
+//! assert_eq!(callback.name(), "Example");
+//! assert_eq!(callback.methods()[0].name(), "hello");
+//! # Ok::<(), anyhow::Error>(())
+//! ```
+
+use anyhow::{bail, Result};
+use uniffi_meta::Checksum;
+
+use super::ffi::{FfiArgument, FfiFunction, FfiType};
+use super::object::Method;
+use super::types::{Type, TypeIterator};
+use super::{APIConverter, ComponentInterface};
+
+#[derive(Debug, Clone, Checksum)]
+pub struct CallbackInterface {
+ pub(super) name: String,
+ pub(super) methods: Vec<Method>,
+ // We don't include the FFIFunc in the hash calculation, because:
+ // - it is entirely determined by the other fields,
+ // so excluding it is safe.
+ // - its `name` property includes a checksum derived from the very
+ // hash value we're trying to calculate here, so excluding it
+ // avoids a weird circular dependency in the calculation.
+ #[checksum_ignore]
+ pub(super) ffi_init_callback: FfiFunction,
+}
+
+impl CallbackInterface {
+ fn new(name: String) -> CallbackInterface {
+ CallbackInterface {
+ name,
+ methods: Default::default(),
+ ffi_init_callback: Default::default(),
+ }
+ }
+
+ pub fn name(&self) -> &str {
+ &self.name
+ }
+
+ pub fn type_(&self) -> Type {
+ Type::CallbackInterface(self.name.clone())
+ }
+
+ pub fn methods(&self) -> Vec<&Method> {
+ self.methods.iter().collect()
+ }
+
+ pub fn ffi_init_callback(&self) -> &FfiFunction {
+ &self.ffi_init_callback
+ }
+
+ pub(super) fn derive_ffi_funcs(&mut self, ci_prefix: &str) {
+ self.ffi_init_callback.name = format!("ffi_{ci_prefix}_{}_init_callback", self.name);
+ self.ffi_init_callback.arguments = vec![FfiArgument {
+ name: "callback_stub".to_string(),
+ type_: FfiType::ForeignCallback,
+ }];
+ self.ffi_init_callback.return_type = None;
+ }
+
+ pub fn iter_types(&self) -> TypeIterator<'_> {
+ Box::new(self.methods.iter().flat_map(Method::iter_types))
+ }
+}
+
+impl APIConverter<CallbackInterface> for weedle::CallbackInterfaceDefinition<'_> {
+ fn convert(&self, ci: &mut ComponentInterface) -> Result<CallbackInterface> {
+ if self.attributes.is_some() {
+ bail!("callback interface attributes are not supported yet");
+ }
+ if self.inheritance.is_some() {
+ bail!("callback interface inheritance is not supported");
+ }
+ let mut object = CallbackInterface::new(self.identifier.0.to_string());
+ for member in &self.members.body {
+ match member {
+ weedle::interface::InterfaceMember::Operation(t) => {
+ let mut method: Method = t.convert(ci)?;
+ method.object_name = object.name.clone();
+ object.methods.push(method);
+ }
+ _ => bail!(
+ "no support for callback interface member type {:?} yet",
+ member
+ ),
+ }
+ }
+ Ok(object)
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn test_empty_interface() {
+ const UDL: &str = r#"
+ namespace test{};
+ // Weird, but allowed.
+ callback interface Testing {};
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL).unwrap();
+ assert_eq!(ci.callback_interface_definitions().len(), 1);
+ assert_eq!(
+ ci.get_callback_interface_definition("Testing")
+ .unwrap()
+ .methods()
+ .len(),
+ 0
+ );
+ }
+
+ #[test]
+ fn test_multiple_interfaces() {
+ const UDL: &str = r#"
+ namespace test{};
+ callback interface One {
+ void one();
+ };
+ callback interface Two {
+ u32 two();
+ u64 too();
+ };
+ "#;
+ let ci = ComponentInterface::from_webidl(UDL).unwrap();
+ assert_eq!(ci.callback_interface_definitions().len(), 2);
+
+ let callbacks_one = ci.get_callback_interface_definition("One").unwrap();
+ assert_eq!(callbacks_one.methods().len(), 1);
+ assert_eq!(callbacks_one.methods()[0].name(), "one");
+
+ let callbacks_two = ci.get_callback_interface_definition("Two").unwrap();
+ assert_eq!(callbacks_two.methods().len(), 2);
+ assert_eq!(callbacks_two.methods()[0].name(), "two");
+ assert_eq!(callbacks_two.methods()[1].name(), "too");
+ }
+}