/* 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/. */ //! # Basic typesystem for defining a component interface. //! //! This module provides the "API-level" typesystem of a UniFFI Rust Component, that is, //! the types provided by the Rust implementation and consumed callers of the foreign language //! bindings. Think "objects" and "enums" and "records". //! //! The [`Type`] enum represents high-level types that would appear in the public API of //! a component, such as enums and records as well as primitives like ints and strings. //! The Rust code that implements a component, and the foreign language bindings that consume it, //! will both typically deal with such types as their core concern. //! //! As a developer working on UniFFI itself, you're likely to spend a fair bit of time thinking //! about how these API-level types map into the lower-level types of the FFI layer as represented //! by the [`ffi::FfiType`](super::ffi::FfiType) enum, but that's a detail that is invisible to end users. use crate::Checksum; #[derive(Debug, Copy, Clone, Eq, PartialEq, Checksum, Ord, PartialOrd)] pub enum ObjectImpl { // A single Rust type Struct, // A trait that's can be implemented by Rust types Trait, // A trait + a callback interface -- can be implemented by both Rust and foreign types. CallbackTrait, } impl ObjectImpl { /// Return the fully qualified name which should be used by Rust code for /// an object with the given name. /// Includes `r#`, traits get a leading `dyn`. If we ever supported associated types, then /// this would also include them. pub fn rust_name_for(&self, name: &str) -> String { if self.is_trait_interface() { format!("dyn r#{name}") } else { format!("r#{name}") } } pub fn is_trait_interface(&self) -> bool { matches!(self, Self::Trait | Self::CallbackTrait) } pub fn has_callback_interface(&self) -> bool { matches!(self, Self::CallbackTrait) } } #[derive(Debug, Clone, Copy, Eq, PartialEq, Checksum, Ord, PartialOrd)] pub enum ExternalKind { Interface, Trait, // Either a record or enum DataClass, } /// Represents all the different high-level types that can be used in a component interface. /// At this level we identify user-defined types by name, without knowing any details /// of their internal structure apart from what type of thing they are (record, enum, etc). #[derive(Debug, Clone, Eq, PartialEq, Checksum, Ord, PartialOrd)] pub enum Type { // Primitive types. UInt8, Int8, UInt16, Int16, UInt32, Int32, UInt64, Int64, Float32, Float64, Boolean, String, Bytes, Timestamp, Duration, Object { // The module path to the object module_path: String, // The name in the "type universe" name: String, // How the object is implemented. imp: ObjectImpl, }, // Types defined in the component API, each of which has a string name. Record { module_path: String, name: String, }, Enum { module_path: String, name: String, }, CallbackInterface { module_path: String, name: String, }, // Structurally recursive types. Optional { inner_type: Box, }, Sequence { inner_type: Box, }, Map { key_type: Box, value_type: Box, }, // An FfiConverter we `use` from an external crate External { module_path: String, name: String, #[checksum_ignore] // The namespace is not known generating scaffolding. namespace: String, kind: ExternalKind, tagged: bool, // does its FfiConverter use ? }, // Custom type on the scaffolding side Custom { module_path: String, name: String, builtin: Box, }, } impl Type { pub fn iter_types(&self) -> TypeIterator<'_> { let nested_types = match self { Type::Optional { inner_type } | Type::Sequence { inner_type } => { inner_type.iter_types() } Type::Map { key_type, value_type, } => Box::new(key_type.iter_types().chain(value_type.iter_types())), _ => Box::new(std::iter::empty()), }; Box::new(std::iter::once(self).chain(nested_types)) } } // A trait so various things can turn into a type. pub trait AsType: core::fmt::Debug { fn as_type(&self) -> Type; } impl AsType for Type { fn as_type(&self) -> Type { self.clone() } } // Needed to handle &&Type and &&&Type values, which we sometimes end up with in the template code impl AsType for T where T: std::ops::Deref + std::fmt::Debug, C: AsType, { fn as_type(&self) -> Type { self.deref().as_type() } } /// An abstract type for an iterator over &Type references. /// /// Ideally we would not need to name this type explicitly, and could just /// use an `impl Iterator` on any method that yields types. pub type TypeIterator<'a> = Box + 'a>;