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 //! # Basic typesystem for defining a component interface. 6 //! 7 //! This module provides the "API-level" typesystem of a UniFFI Rust Component, that is, 8 //! the types provided by the Rust implementation and consumed callers of the foreign language 9 //! bindings. Think "objects" and "enums" and "records". 10 //! 11 //! The [`Type`] enum represents high-level types that would appear in the public API of 12 //! a component, such as enums and records as well as primitives like ints and strings. 13 //! The Rust code that implements a component, and the foreign language bindings that consume it, 14 //! will both typically deal with such types as their core concern. 15 //! 16 //! As a developer working on UniFFI itself, you're likely to spend a fair bit of time thinking 17 //! about how these API-level types map into the lower-level types of the FFI layer as represented 18 //! by the [`ffi::FfiType`](super::ffi::FfiType) enum, but that's a detail that is invisible to end users. 19 20 use crate::Checksum; 21 22 #[derive(Debug, Copy, Clone, Eq, PartialEq, Checksum, Ord, PartialOrd)] 23 pub enum ObjectImpl { 24 // A single Rust type 25 Struct, 26 // A trait that's can be implemented by Rust types 27 Trait, 28 // A trait + a callback interface -- can be implemented by both Rust and foreign types. 29 CallbackTrait, 30 } 31 32 impl ObjectImpl { 33 /// Return the fully qualified name which should be used by Rust code for 34 /// an object with the given name. 35 /// Includes `r#`, traits get a leading `dyn`. If we ever supported associated types, then 36 /// this would also include them. rust_name_for(&self, name: &str) -> String37 pub fn rust_name_for(&self, name: &str) -> String { 38 if self.is_trait_interface() { 39 format!("dyn r#{name}") 40 } else { 41 format!("r#{name}") 42 } 43 } 44 is_trait_interface(&self) -> bool45 pub fn is_trait_interface(&self) -> bool { 46 matches!(self, Self::Trait | Self::CallbackTrait) 47 } 48 has_callback_interface(&self) -> bool49 pub fn has_callback_interface(&self) -> bool { 50 matches!(self, Self::CallbackTrait) 51 } 52 } 53 54 #[derive(Debug, Clone, Copy, Eq, PartialEq, Checksum, Ord, PartialOrd)] 55 pub enum ExternalKind { 56 Interface, 57 Trait, 58 // Either a record or enum 59 DataClass, 60 } 61 62 /// Represents all the different high-level types that can be used in a component interface. 63 /// At this level we identify user-defined types by name, without knowing any details 64 /// of their internal structure apart from what type of thing they are (record, enum, etc). 65 #[derive(Debug, Clone, Eq, PartialEq, Checksum, Ord, PartialOrd)] 66 pub enum Type { 67 // Primitive types. 68 UInt8, 69 Int8, 70 UInt16, 71 Int16, 72 UInt32, 73 Int32, 74 UInt64, 75 Int64, 76 Float32, 77 Float64, 78 Boolean, 79 String, 80 Bytes, 81 Timestamp, 82 Duration, 83 Object { 84 // The module path to the object 85 module_path: String, 86 // The name in the "type universe" 87 name: String, 88 // How the object is implemented. 89 imp: ObjectImpl, 90 }, 91 // Types defined in the component API, each of which has a string name. 92 Record { 93 module_path: String, 94 name: String, 95 }, 96 Enum { 97 module_path: String, 98 name: String, 99 }, 100 CallbackInterface { 101 module_path: String, 102 name: String, 103 }, 104 // Structurally recursive types. 105 Optional { 106 inner_type: Box<Type>, 107 }, 108 Sequence { 109 inner_type: Box<Type>, 110 }, 111 Map { 112 key_type: Box<Type>, 113 value_type: Box<Type>, 114 }, 115 // An FfiConverter we `use` from an external crate 116 External { 117 module_path: String, 118 name: String, 119 #[checksum_ignore] // The namespace is not known generating scaffolding. 120 namespace: String, 121 kind: ExternalKind, 122 tagged: bool, // does its FfiConverter use <UniFFITag>? 123 }, 124 // Custom type on the scaffolding side 125 Custom { 126 module_path: String, 127 name: String, 128 builtin: Box<Type>, 129 }, 130 } 131 132 impl Type { iter_types(&self) -> TypeIterator<'_>133 pub fn iter_types(&self) -> TypeIterator<'_> { 134 let nested_types = match self { 135 Type::Optional { inner_type } | Type::Sequence { inner_type } => { 136 inner_type.iter_types() 137 } 138 Type::Map { 139 key_type, 140 value_type, 141 } => Box::new(key_type.iter_types().chain(value_type.iter_types())), 142 _ => Box::new(std::iter::empty()), 143 }; 144 Box::new(std::iter::once(self).chain(nested_types)) 145 } 146 } 147 148 // A trait so various things can turn into a type. 149 pub trait AsType: core::fmt::Debug { as_type(&self) -> Type150 fn as_type(&self) -> Type; 151 } 152 153 impl AsType for Type { as_type(&self) -> Type154 fn as_type(&self) -> Type { 155 self.clone() 156 } 157 } 158 159 // Needed to handle &&Type and &&&Type values, which we sometimes end up with in the template code 160 impl<T, C> AsType for T 161 where 162 T: std::ops::Deref<Target = C> + std::fmt::Debug, 163 C: AsType, 164 { as_type(&self) -> Type165 fn as_type(&self) -> Type { 166 self.deref().as_type() 167 } 168 } 169 170 /// An abstract type for an iterator over &Type references. 171 /// 172 /// Ideally we would not need to name this type explicitly, and could just 173 /// use an `impl Iterator<Item = &Type>` on any method that yields types. 174 pub type TypeIterator<'a> = Box<dyn Iterator<Item = &'a Type> + 'a>; 175