Skip to main content

pubhubs/
map.rs

1//! [`Map`]: look up objects by their [`Id`] or [`Handle`].
2use crate::handle::Handle;
3use crate::id::Id;
4
5/// Objects that have a unique [`Id`], and at least one [`Handle`].
6pub trait Handled {
7    fn handles(&self) -> &[Handle];
8    fn id(&self) -> &Id;
9}
10
11pub trait AsHandleOrId {
12    fn match_case<T>(
13        &self,
14        case_id: impl FnOnce(&Id) -> T,
15        case_handle: impl FnOnce(&Handle) -> T,
16    ) -> T;
17}
18
19impl AsHandleOrId for Handle {
20    fn match_case<T>(
21        &self,
22        _case_id: impl FnOnce(&Id) -> T,
23        case_handle: impl FnOnce(&Handle) -> T,
24    ) -> T {
25        case_handle(self)
26    }
27}
28
29impl AsHandleOrId for Id {
30    fn match_case<T>(
31        &self,
32        case_id: impl FnOnce(&Id) -> T,
33        _case_handle: impl FnOnce(&Handle) -> T,
34    ) -> T {
35        case_id(self)
36    }
37}
38
39#[derive(Clone)]
40pub struct Map<T: Handled> {
41    value_by_id: std::collections::HashMap<Id, T>,
42    id_by_handle: std::collections::HashMap<Handle, Id>,
43}
44
45impl<T: Handled> Map<T> {
46    pub fn get<Q>(&self, k: &Q) -> Option<&T>
47    where
48        Q: AsHandleOrId,
49    {
50        k.match_case::<Option<&T>>(
51            |id| self.value_by_id.get(id),
52            |handle| self.value_by_id.get(self.id_by_handle.get(handle)?),
53        )
54    }
55
56    /// Inserts `value` into [`Map`] unless its `id` or one of its `handle`s is already
57    /// present in the map.  In that case the `id` or one of the conflicting handles is returned.
58    #[must_use]
59    pub fn insert_new(&mut self, value: T) -> Option<HandleOrId> {
60        let id: Id = *value.id();
61
62        // before inserting anything check for duplicates
63        if self.value_by_id.contains_key(&id) {
64            return Some(id.into());
65        }
66
67        for handle in value.handles() {
68            if self.id_by_handle.contains_key(handle) {
69                return Some(handle.clone().into());
70            }
71        }
72
73        for handle in value.handles() {
74            assert!(self.id_by_handle.insert(handle.clone(), id).is_none());
75        }
76
77        assert!(self.value_by_id.insert(id, value).is_none());
78
79        None
80    }
81
82    /// Returns an [`Iterator`] over the values of this map.
83    pub fn values(&self) -> std::collections::hash_map::Values<'_, Id, T> {
84        self.value_by_id.values()
85    }
86}
87
88// This cannot be derived using 'derive(Default)' without requiring that T: Default,
89// which we neither want nor need.
90impl<T: Handled> Default for Map<T> {
91    fn default() -> Self {
92        Map {
93            value_by_id: Default::default(),
94            id_by_handle: Default::default(),
95        }
96    }
97}
98
99pub enum HandleOrId {
100    Handle(Handle),
101    Id(Id),
102}
103
104impl std::fmt::Display for HandleOrId {
105    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106        match &self {
107            HandleOrId::Handle(handle) => handle.fmt(f),
108            HandleOrId::Id(id) => id.fmt(f),
109        }
110    }
111}
112
113impl From<Id> for HandleOrId {
114    fn from(id: Id) -> Self {
115        HandleOrId::Id(id)
116    }
117}
118
119impl From<Handle> for HandleOrId {
120    fn from(handle: Handle) -> Self {
121        HandleOrId::Handle(handle)
122    }
123}