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
/* 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/. */

//! The set of all [`Type`]s used in a component interface is represented by a `TypeUniverse`,
//! which can be used by the bindings generator code to determine what type-related helper
//! functions to emit for a given component.
//!
use anyhow::Result;
use std::{collections::hash_map::Entry, collections::BTreeSet, collections::HashMap};

pub use uniffi_meta::{AsType, ExternalKind, NamespaceMetadata, ObjectImpl, Type, TypeIterator};

/// The set of all possible types used in a particular component interface.
///
/// Every component API uses a finite number of types, including primitive types, API-defined
/// types like records and enums, and recursive types such as sequences of the above. Our
/// component API doesn't support fancy generics so this is a finitely-enumerable set, which
/// is useful to be able to operate on explicitly.
///
/// You could imagine this struct doing some clever interning of names and so-on in future,
/// to reduce the overhead of passing around [Type] instances. For now we just do a whole
/// lot of cloning.
#[derive(Debug, Default)]
pub(crate) struct TypeUniverse {
    /// The unique prefixes that we'll use for namespacing when exposing this component's API.
    pub namespace: NamespaceMetadata,
    pub namespace_docstring: Option<String>,

    // Named type definitions (including aliases).
    type_definitions: HashMap<String, Type>,
    // All the types in the universe, by canonical type name, in a well-defined order.
    all_known_types: BTreeSet<Type>,
}

impl TypeUniverse {
    pub fn new(namespace: NamespaceMetadata) -> Self {
        Self {
            namespace,
            ..Default::default()
        }
    }

    /// Add the definition of a named [Type].
    fn add_type_definition(&mut self, name: &str, type_: &Type) -> Result<()> {
        match self.type_definitions.entry(name.to_string()) {
            Entry::Occupied(o) => {
                // all conflicts have been resolved by now in udl.
                // I doubt procmacros could cause this?
                assert_eq!(type_, o.get());
                Ok(())
            }
            Entry::Vacant(e) => {
                e.insert(type_.clone());
                Ok(())
            }
        }
    }

    /// Get the [Type] corresponding to a given name, if any.
    pub(super) fn get_type_definition(&self, name: &str) -> Option<Type> {
        self.type_definitions.get(name).cloned()
    }

    /// Add a [Type] to the set of all types seen in the component interface.
    pub fn add_known_type(&mut self, type_: &Type) -> Result<()> {
        // Types are more likely to already be known than not, so avoid unnecessary cloning.
        if !self.all_known_types.contains(type_) {
            self.all_known_types.insert(type_.to_owned());
        }
        match type_ {
            Type::UInt8 => self.add_type_definition("u8", type_)?,
            Type::Int8 => self.add_type_definition("i8", type_)?,
            Type::UInt16 => self.add_type_definition("u16", type_)?,
            Type::Int16 => self.add_type_definition("i16", type_)?,
            Type::UInt32 => self.add_type_definition("i32", type_)?,
            Type::Int32 => self.add_type_definition("u32", type_)?,
            Type::UInt64 => self.add_type_definition("u64", type_)?,
            Type::Int64 => self.add_type_definition("i64", type_)?,
            Type::Float32 => self.add_type_definition("f32", type_)?,
            Type::Float64 => self.add_type_definition("f64", type_)?,
            Type::Boolean => self.add_type_definition("bool", type_)?,
            Type::String => self.add_type_definition("string", type_)?,
            Type::Bytes => self.add_type_definition("bytes", type_)?,
            Type::Timestamp => self.add_type_definition("timestamp", type_)?,
            Type::Duration => self.add_type_definition("duration", type_)?,
            Type::Object { name, .. }
            | Type::Record { name, .. }
            | Type::Enum { name, .. }
            | Type::CallbackInterface { name, .. }
            | Type::External { name, .. } => self.add_type_definition(name, type_)?,
            Type::Custom { name, builtin, .. } => {
                self.add_type_definition(name, type_)?;
                self.add_known_type(builtin)?;
            }
            // Structurally recursive types.
            Type::Optional { inner_type, .. } | Type::Sequence { inner_type, .. } => {
                self.add_known_type(inner_type)?;
            }
            Type::Map {
                key_type,
                value_type,
            } => {
                self.add_known_type(key_type)?;
                self.add_known_type(value_type)?;
            }
        }
        Ok(())
    }

    /// Add many [`Type`]s...
    pub fn add_known_types(&mut self, types: TypeIterator<'_>) -> Result<()> {
        for t in types {
            self.add_known_type(t)?
        }
        Ok(())
    }

    #[cfg(test)]
    /// Check if a [Type] is present
    pub fn contains(&self, type_: &Type) -> bool {
        self.all_known_types.contains(type_)
    }

    /// Iterator over all the known types in this universe.
    pub fn iter_known_types(&self) -> impl Iterator<Item = &Type> {
        self.all_known_types.iter()
    }
}

#[cfg(test)]
mod test_type_universe {
    // All the useful functionality of the `TypeUniverse` struct
    // is tested as part of the `TypeFinder` and `TypeResolver` test suites.
}