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/. */ 4 5 //! Callback interfaces are traits specified in UDL which can be implemented by foreign languages. 6 //! 7 //! # Using callback interfaces 8 //! 9 //! 1. Define a Rust trait. 10 //! 11 //! This toy example defines a way of Rust accessing a key-value store exposed 12 //! by the host operating system (e.g. the key chain). 13 //! 14 //! ``` 15 //! trait Keychain: Send { 16 //! fn get(&self, key: String) -> Option<String>; 17 //! fn put(&self, key: String, value: String); 18 //! } 19 //! ``` 20 //! 21 //! 2. Define a callback interface in the UDL 22 //! 23 //! ```idl 24 //! callback interface Keychain { 25 //! string? get(string key); 26 //! void put(string key, string data); 27 //! }; 28 //! ``` 29 //! 30 //! 3. And allow it to be passed into Rust. 31 //! 32 //! Here, we define a constructor to pass the keychain to rust, and then another method 33 //! which may use it. 34 //! 35 //! In UDL: 36 //! ```idl 37 //! object Authenticator { 38 //! constructor(Keychain keychain); 39 //! void login(); 40 //! } 41 //! ``` 42 //! 43 //! In Rust: 44 //! 45 //! ``` 46 //!# trait Keychain: Send { 47 //!# fn get(&self, key: String) -> Option<String>; 48 //!# fn put(&self, key: String, value: String); 49 //!# } 50 //! struct Authenticator { 51 //! keychain: Box<dyn Keychain>, 52 //! } 53 //! 54 //! impl Authenticator { 55 //! pub fn new(keychain: Box<dyn Keychain>) -> Self { 56 //! Self { keychain } 57 //! } 58 //! pub fn login(&self) { 59 //! let username = self.keychain.get("username".into()); 60 //! let password = self.keychain.get("password".into()); 61 //! } 62 //! } 63 //! ``` 64 //! 4. Create an foreign language implementation of the callback interface. 65 //! 66 //! In this example, here's a Kotlin implementation. 67 //! 68 //! ```kotlin 69 //! class AndroidKeychain: Keychain { 70 //! override fun get(key: String): String? { 71 //! // … elide the implementation. 72 //! return value 73 //! } 74 //! override fun put(key: String) { 75 //! // … elide the implementation. 76 //! } 77 //! } 78 //! ``` 79 //! 5. Pass the implementation to Rust. 80 //! 81 //! Again, in Kotlin 82 //! 83 //! ```kotlin 84 //! val authenticator = Authenticator(AndroidKeychain()) 85 //! authenticator.login() 86 //! ``` 87 //! 88 //! # How it works. 89 //! 90 //! ## High level 91 //! 92 //! Uniffi generates a protocol or interface in client code in the foreign language must implement. 93 //! 94 //! For each callback interface, UniFFI defines a VTable. 95 //! This is a `repr(C)` struct where each field is a `repr(C)` callback function pointer. 96 //! There is one field for each method, plus an extra field for the `uniffi_free` method. 97 //! The foreign code registers one VTable per callback interface with Rust. 98 //! 99 //! VTable methods have a similar signature to Rust scaffolding functions. 100 //! The one difference is that values are returned via an out pointer to work around a Python bug (https://bugs.python.org/issue5710). 101 //! 102 //! The foreign object that implements the interface is represented by an opaque handle. 103 //! UniFFI generates a struct that implements the trait by calling VTable methods, passing the handle as the first parameter. 104 //! When the struct is dropped, the `uniffi_free` method is called. 105 106 use std::fmt; 107 108 /// Used when internal/unexpected error happened when calling a foreign callback, for example when 109 /// a unknown exception is raised 110 /// 111 /// User callback error types must implement a From impl from this type to their own error type. 112 #[derive(Debug)] 113 pub struct UnexpectedUniFFICallbackError { 114 pub reason: String, 115 } 116 117 impl UnexpectedUniFFICallbackError { new(reason: impl fmt::Display) -> Self118 pub fn new(reason: impl fmt::Display) -> Self { 119 Self { 120 reason: reason.to_string(), 121 } 122 } 123 } 124 125 impl fmt::Display for UnexpectedUniFFICallbackError { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result126 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 127 write!( 128 f, 129 "UnexpectedUniFFICallbackError(reason: {:?})", 130 self.reason 131 ) 132 } 133 } 134 135 impl std::error::Error for UnexpectedUniFFICallbackError {} 136 137 // Autoref-based specialization for converting UnexpectedUniFFICallbackError into error types. 138 // 139 // For more details, see: 140 // https://github.com/dtolnay/case-studies/blob/master/autoref-specialization/README.md 141 142 // Define two ZST types: 143 // - One implements `try_convert_unexpected_callback_error` by always returning an error value. 144 // - The specialized version implements it using `From<UnexpectedUniFFICallbackError>` 145 146 #[doc(hidden)] 147 #[derive(Debug)] 148 pub struct UnexpectedUniFFICallbackErrorConverterGeneric; 149 150 impl UnexpectedUniFFICallbackErrorConverterGeneric { try_convert_unexpected_callback_error<E>( &self, e: UnexpectedUniFFICallbackError, ) -> anyhow::Result<E>151 pub fn try_convert_unexpected_callback_error<E>( 152 &self, 153 e: UnexpectedUniFFICallbackError, 154 ) -> anyhow::Result<E> { 155 Err(e.into()) 156 } 157 } 158 159 #[doc(hidden)] 160 #[derive(Debug)] 161 pub struct UnexpectedUniFFICallbackErrorConverterSpecialized; 162 163 impl UnexpectedUniFFICallbackErrorConverterSpecialized { try_convert_unexpected_callback_error<E>( &self, e: UnexpectedUniFFICallbackError, ) -> anyhow::Result<E> where E: From<UnexpectedUniFFICallbackError>,164 pub fn try_convert_unexpected_callback_error<E>( 165 &self, 166 e: UnexpectedUniFFICallbackError, 167 ) -> anyhow::Result<E> 168 where 169 E: From<UnexpectedUniFFICallbackError>, 170 { 171 Ok(E::from(e)) 172 } 173 } 174 175 // Macro to convert an UnexpectedUniFFICallbackError value for a particular type. This is used in 176 // the `ConvertError` implementation. 177 #[doc(hidden)] 178 #[macro_export] 179 macro_rules! convert_unexpected_error { 180 ($error:ident, $ty:ty) => {{ 181 // Trait for generic conversion, implemented for all &T. 182 pub trait GetConverterGeneric { 183 fn get_converter(&self) -> $crate::UnexpectedUniFFICallbackErrorConverterGeneric; 184 } 185 186 impl<T> GetConverterGeneric for &T { 187 fn get_converter(&self) -> $crate::UnexpectedUniFFICallbackErrorConverterGeneric { 188 $crate::UnexpectedUniFFICallbackErrorConverterGeneric 189 } 190 } 191 // Trait for specialized conversion, implemented for all T that implements 192 // `Into<ErrorType>`. I.e. it's implemented for UnexpectedUniFFICallbackError when 193 // ErrorType implements From<UnexpectedUniFFICallbackError>. 194 pub trait GetConverterSpecialized { 195 fn get_converter(&self) -> $crate::UnexpectedUniFFICallbackErrorConverterSpecialized; 196 } 197 198 impl<T: Into<$ty>> GetConverterSpecialized for T { 199 fn get_converter(&self) -> $crate::UnexpectedUniFFICallbackErrorConverterSpecialized { 200 $crate::UnexpectedUniFFICallbackErrorConverterSpecialized 201 } 202 } 203 // Here's the hack. Because of the auto-ref rules, this will use `GetConverterSpecialized` 204 // if it's implemented and `GetConverterGeneric` if not. 205 (&$error) 206 .get_converter() 207 .try_convert_unexpected_callback_error($error) 208 }}; 209 } 210