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