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