summaryrefslogtreecommitdiffstats
path: root/third_party/rust/uniffi_meta/src/types.rs
blob: 24f8a6f2a872b321f7da44628cb2a45b8ee38415 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
/* 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 {
    Struct,
    Trait,
}

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 == &ObjectImpl::Trait {
            format!("dyn r#{name}")
        } else {
            format!("r#{name}")
        }
    }

    // uniffi_meta and procmacro support tend to carry around `is_trait` bools. This makes that
    // mildly less painful
    pub fn from_is_trait(is_trait: bool) -> Self {
        if is_trait {
            ObjectImpl::Trait
        } else {
            ObjectImpl::Struct
        }
    }
}

#[derive(Debug, Clone, Copy, Eq, PartialEq, Checksum, Ord, PartialOrd)]
pub enum ExternalKind {
    Interface,
    // 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,
    },
    ForeignExecutor,
    // 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<Type>,
    },
    Sequence {
        inner_type: Box<Type>,
    },
    Map {
        key_type: Box<Type>,
        value_type: Box<Type>,
    },
    // 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 <UniFFITag>?
    },
    // Custom type on the scaffolding side
    Custom {
        module_path: String,
        name: String,
        builtin: Box<Type>,
    },
}

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<T, C> AsType for T
where
    T: std::ops::Deref<Target = C> + 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<Item = &Type>` on any method that yields types.
pub type TypeIterator<'a> = Box<dyn Iterator<Item = &'a Type> + 'a>;