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
use std::{
borrow::Borrow,
collections::{btree_map, BTreeMap},
fmt,
};
use crate::{datatype::NamedDataType, NamedType, SpectaID};
/// Define a set of types which can be exported together.
///
/// While exporting a type will add all of the types it depends on to the collection.
/// You can also construct your own collection to easily export a set of types together.
#[derive(Default, Clone, PartialEq)]
pub struct TypeCollection {
// `None` indicates that the entry is a placeholder. It was reference and we are currently working out it's definition.
pub(crate) map: BTreeMap<SpectaID, Option<NamedDataType>>,
// A stack of types that are currently being flattened. This is used to detect cycles.
pub(crate) flatten_stack: Vec<SpectaID>,
}
impl fmt::Debug for TypeCollection {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("TypeCollection").field(&self.map).finish()
}
}
impl TypeCollection {
/// Register a type with the collection.
pub fn register<T: NamedType>(&mut self) -> &mut Self {
let def = T::definition_named_data_type(self);
self.map.insert(T::sid(), Some(def));
self
}
/// Insert a type into the collection.
/// You should prefer to use `TypeCollection::register` as it ensures all invariants are met.
///
/// When using this method it's the responsibility of the caller to:
/// - Ensure the `SpectaID` and `NamedDataType` are correctly matched.
/// - Ensure the same `TypeCollection` was used when calling `NamedType::definition_named_data_type`.
/// Not honoring these rules will result in a broken collection.
pub fn insert(&mut self, sid: SpectaID, def: NamedDataType) -> &mut Self {
self.map.insert(sid, Some(def));
self
}
/// Join another type collection into this one.
pub fn extend(&mut self, collection: impl Borrow<Self>) -> &mut Self {
self.map
.extend(collection.borrow().map.iter().map(|(k, v)| (*k, v.clone())));
self
}
#[track_caller]
pub fn get(&self, sid: SpectaID) -> Option<&NamedDataType> {
#[allow(clippy::bind_instead_of_map)]
self.map.get(&sid).as_ref().and_then(|v| match v {
Some(ndt) => Some(ndt),
// If this method is used during type construction this case could be hit when it's actually valid
// but all references are managed within `specta` so we can bypass this method and use `map` directly because we have `pub(crate)` access.
None => {
#[cfg(debug_assertions)]
unreachable!("specta: `TypeCollection::get` found a type placeholder!");
#[cfg(not(debug_assertions))]
None
}
})
}
}
impl<'a> IntoIterator for &'a TypeCollection {
type Item = (SpectaID, &'a NamedDataType);
type IntoIter = TypeCollectionInterator<'a>;
fn into_iter(self) -> Self::IntoIter {
TypeCollectionInterator(self.map.iter())
}
}
// Sealed
pub struct TypeCollectionInterator<'a>(btree_map::Iter<'a, SpectaID, Option<NamedDataType>>);
impl<'a> ExactSizeIterator for TypeCollectionInterator<'a> {}
impl<'a> Iterator for TypeCollectionInterator<'a> {
type Item = (SpectaID, &'a NamedDataType);
fn next(&mut self) -> Option<Self::Item> {
loop {
let (sid, ndt) = self.0.next()?;
if let Some(ndt) = ndt {
return Some((*sid, ndt));
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(0, Some(self.0.clone().filter(|(_, t)| t.is_none()).count()))
}
}