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/. */
5 //! # Function definitions for a `ComponentInterface`.
7 //! This module converts function definitions from UDL into structures that
8 //! can be added to a `ComponentInterface`. A declaration in the UDL like this:
11 //! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
12 //! namespace example {
15 //! # "##, "crate_name")?;
16 //! # Ok::<(), anyhow::Error>(())
19 //! Will result in a [`Function`] member being added to the resulting [`crate::ComponentInterface`]:
22 //! # use uniffi_bindgen::interface::Type;
23 //! # let ci = uniffi_bindgen::interface::ComponentInterface::from_webidl(r##"
24 //! # namespace example {
27 //! # "##, "crate_name")?;
28 //! let func = ci.get_function_definition("hello").unwrap();
29 //! assert_eq!(func.name(), "hello");
30 //! assert!(matches!(func.return_type(), Some(Type::String)));
31 //! assert_eq!(func.arguments().len(), 0);
32 //! # Ok::<(), anyhow::Error>(())
37 use super::ffi::{FfiArgument, FfiFunction, FfiType};
38 use super::{AsType, ComponentInterface, Literal, ObjectImpl, Type, TypeIterator};
39 use uniffi_meta::Checksum;
41 /// Represents a standalone function.
43 /// Each `Function` corresponds to a standalone function in the rust module,
44 /// and has a corresponding standalone function in the foreign language bindings.
46 /// In the FFI, this will be a standalone function with appropriately lowered types.
47 #[derive(Debug, Clone, Checksum)]
49 pub(super) name: String,
50 pub(super) module_path: String,
51 pub(super) is_async: bool,
52 pub(super) arguments: Vec<Argument>,
53 pub(super) return_type: Option<Type>,
54 // We don't include the FFIFunc in the hash calculation, because:
55 // - it is entirely determined by the other fields,
56 // so excluding it is safe.
57 // - its `name` property includes a checksum derived from the very
58 // hash value we're trying to calculate here, so excluding it
59 // avoids a weird circular dependency in the calculation.
61 pub(super) ffi_func: FfiFunction,
62 pub(super) throws: Option<Type>,
63 pub(super) checksum_fn_name: String,
64 // Force a checksum value, or we'll fallback to the trait.
66 pub(super) checksum: Option<u16>,
70 pub fn name(&self) -> &str {
74 pub fn is_async(&self) -> bool {
78 pub fn arguments(&self) -> Vec<&Argument> {
79 self.arguments.iter().collect()
82 pub fn full_arguments(&self) -> Vec<Argument> {
83 self.arguments.to_vec()
86 pub fn return_type(&self) -> Option<&Type> {
87 self.return_type.as_ref()
90 pub fn ffi_func(&self) -> &FfiFunction {
94 pub fn checksum_fn_name(&self) -> &str {
95 &self.checksum_fn_name
98 pub fn checksum(&self) -> u16 {
99 self.checksum.unwrap_or_else(|| uniffi_meta::checksum(self))
102 pub fn throws(&self) -> bool {
103 self.throws.is_some()
106 pub fn throws_name(&self) -> Option<&str> {
107 super::throws_name(&self.throws)
110 pub fn throws_type(&self) -> Option<&Type> {
114 pub fn derive_ffi_func(&mut self) -> Result<()> {
115 assert!(!self.ffi_func.name.is_empty());
117 self.return_type.as_ref().map(Into::into),
118 self.arguments.iter().map(Into::into),
123 pub fn iter_types(&self) -> TypeIterator<'_> {
127 .flat_map(Argument::iter_types)
128 .chain(self.return_type.iter().flat_map(Type::iter_types)),
133 impl From<uniffi_meta::FnParamMetadata> for Argument {
134 fn from(meta: uniffi_meta::FnParamMetadata) -> Self {
139 optional: meta.optional,
140 default: meta.default,
145 impl From<uniffi_meta::FnMetadata> for Function {
146 fn from(meta: uniffi_meta::FnMetadata) -> Self {
147 let ffi_name = meta.ffi_symbol_name();
148 let checksum_fn_name = meta.checksum_symbol_name();
149 let is_async = meta.is_async;
150 let return_type = meta.return_type.map(Into::into);
151 let arguments = meta.inputs.into_iter().map(Into::into).collect();
153 let ffi_func = FfiFunction {
156 ..FfiFunction::default()
161 module_path: meta.module_path,
168 checksum: meta.checksum,
173 /// Represents an argument to a function/constructor/method call.
175 /// Each argument has a name and a type, along with some optional metadata.
176 #[derive(Debug, Clone, Checksum)]
177 pub struct Argument {
178 pub(super) name: String,
179 pub(super) type_: Type,
180 pub(super) by_ref: bool,
181 pub(super) optional: bool,
182 pub(super) default: Option<Literal>,
186 pub fn name(&self) -> &str {
190 pub fn by_ref(&self) -> bool {
194 pub fn is_trait_ref(&self) -> bool {
195 matches!(&self.type_, Type::Object { imp, .. } if *imp == ObjectImpl::Trait)
198 pub fn default_value(&self) -> Option<&Literal> {
199 self.default.as_ref()
202 pub fn iter_types(&self) -> TypeIterator<'_> {
203 self.type_.iter_types()
207 impl AsType for Argument {
208 fn as_type(&self) -> Type {
213 impl From<&Argument> for FfiArgument {
214 fn from(a: &Argument) -> FfiArgument {
216 name: a.name.clone(),
217 type_: (&a.type_).into(),
222 /// Combines the return and throws type of a function/method
223 #[derive(Debug, PartialOrd, Ord, PartialEq, Eq)]
224 pub struct ResultType {
225 pub return_type: Option<Type>,
226 pub throws_type: Option<Type>,
230 /// Get the `T` parameters for the `FutureCallback<T>` for this ResultType
231 pub fn future_callback_param(&self) -> FfiType {
232 match &self.return_type {
234 None => FfiType::UInt8,
239 /// Implemented by function-like types (Function, Method, Constructor)
241 fn arguments(&self) -> Vec<&Argument>;
242 fn return_type(&self) -> Option<Type>;
243 fn throws_type(&self) -> Option<Type>;
244 fn is_async(&self) -> bool;
245 fn result_type(&self) -> ResultType {
247 return_type: self.return_type(),
248 throws_type: self.throws_type(),
252 // Quick way to get the rust future scaffolding function that corresponds to our return type.
254 fn ffi_rust_future_poll(&self, ci: &ComponentInterface) -> String {
255 ci.ffi_rust_future_poll(self.return_type().map(Into::into))
260 fn ffi_rust_future_cancel(&self, ci: &ComponentInterface) -> String {
261 ci.ffi_rust_future_cancel(self.return_type().map(Into::into))
266 fn ffi_rust_future_complete(&self, ci: &ComponentInterface) -> String {
267 ci.ffi_rust_future_complete(self.return_type().map(Into::into))
272 fn ffi_rust_future_free(&self, ci: &ComponentInterface) -> String {
273 ci.ffi_rust_future_free(self.return_type().map(Into::into))
279 impl Callable for Function {
280 fn arguments(&self) -> Vec<&Argument> {
284 fn return_type(&self) -> Option<Type> {
285 self.return_type().cloned()
288 fn throws_type(&self) -> Option<Type> {
289 self.throws_type().cloned()
292 fn is_async(&self) -> bool {
297 // Needed because Askama likes to add extra refs to variables
298 impl<T: Callable> Callable for &T {
299 fn arguments(&self) -> Vec<&Argument> {
303 fn return_type(&self) -> Option<Type> {
304 (*self).return_type()
307 fn throws_type(&self) -> Option<Type> {
308 (*self).throws_type()
311 fn is_async(&self) -> bool {
318 use super::super::ComponentInterface;
322 fn test_minimal_and_rich_function() -> Result<()> {
323 let ci = ComponentInterface::from_webidl(
328 sequence<string?> rich(u32 arg1, TestDict arg2);
331 enum TestError { "err" };
332 dictionary TestDict {
339 let func1 = ci.get_function_definition("minimal").unwrap();
340 assert_eq!(func1.name(), "minimal");
341 assert!(func1.return_type().is_none());
342 assert!(func1.throws_type().is_none());
343 assert_eq!(func1.arguments().len(), 0);
345 let func2 = ci.get_function_definition("rich").unwrap();
346 assert_eq!(func2.name(), "rich");
348 func2.return_type().unwrap(),
350 inner_type: Box::new(Type::Optional {
351 inner_type: Box::new(Type::String)
356 matches!(func2.throws_type(), Some(Type::Enum { name, .. }) if name == "TestError" && ci.is_name_used_as_error(name))
358 assert_eq!(func2.arguments().len(), 2);
359 assert_eq!(func2.arguments()[0].name(), "arg1");
360 assert_eq!(func2.arguments()[0].as_type(), Type::UInt32);
361 assert_eq!(func2.arguments()[1].name(), "arg2");
363 matches!(func2.arguments()[1].as_type(), Type::Record { name, .. } if name == "TestDict")