/
entry.rs
139 lines (125 loc) · 4.18 KB
/
entry.rs
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
use super::chunk_storage::ChunkStorage;
use crate::traits::record::Record;
use crate::traits::valid_key::{BorrowedKey, ValidKey};
use std::borrow::Cow;
use std::fmt::Debug;
/// An `Entry` for an element. This is intended to work in the same way as the `Entries` from
/// rust's standard collections API. An `Entry` refers to an element that we have tried to
/// look up and might or might not have found.
///
/// # Type Parameters
///
/// * `R`: The key used to lookup this `Entry`
/// * `ChunkKey`: The chunk key type of the backing `Storage`
/// * `ItemKey`: The item key type of the backing `Storage`
/// * `Element`: The element type of the backing `Storage`, and also the `Element` represented
/// by this `Entry`.
pub struct Entry<'a, R, ChunkKey: ?Sized, ItemKey: ?Sized, Element>
where
R: Record<ChunkKey, ItemKey> + 'a,
ChunkKey: BorrowedKey,
ChunkKey::Owned: ValidKey,
ItemKey: BorrowedKey,
ItemKey::Owned: ValidKey,
{
id: R,
idx: Option<usize>,
storage: &'a mut ChunkStorage<ChunkKey, ItemKey, Element>,
}
impl<'a, R, ChunkKey, ItemKey, Element> Entry<'a, R, ChunkKey, ItemKey, Element>
where
R: Record<ChunkKey, ItemKey> + 'a,
ChunkKey: BorrowedKey + ?Sized,
ChunkKey::Owned: ValidKey,
ItemKey: BorrowedKey + ?Sized,
ItemKey::Owned: ValidKey,
Element: Record<ChunkKey, ItemKey>,
{
pub(super) fn new(
id: R,
idx: Option<usize>,
storage: &'a mut ChunkStorage<ChunkKey, ItemKey, Element>,
) -> Self {
Entry { id, idx, storage }
}
/// Returns the value whose `ChunkKey` and `ItemKey` is used to look up this `Entry`.
pub fn id(&self) -> &R {
&self.id
}
/// Insert a record at this entry if it does not already exist.
pub fn or_insert_with<F>(mut self, f: F) -> &'a mut Element
where
F: FnOnce() -> Element,
{
if let Some(idx) = self.idx {
self.storage.get_idx_mut(idx)
} else {
let new_value: Element = f();
let new_chunk_key: Cow<ChunkKey> = new_value.chunk_key();
let old_chunk_key: Cow<ChunkKey> = Record::<ChunkKey, ItemKey>::chunk_key(&self.id);
let new_item_key: Cow<ItemKey> = new_value.item_key();
let old_item_key: Cow<ItemKey> = Record::<ChunkKey, ItemKey>::item_key(&self.id);
assert_eq!(
new_chunk_key, old_chunk_key,
"entry: inserted chunk key does not match entry chunk key"
);
assert_eq!(
new_item_key, old_item_key,
"entry: inserted item key does not match entry item key"
);
let idx = self.storage.add(new_value);
self.idx = Some(idx);
self.storage.get_idx_mut(idx)
}
}
/// Modify this element if it exists. If the element does not exist, nothing happens.
pub fn and_modify<F>(mut self, f: F) -> Self
where
F: FnOnce(&mut Element),
{
if let Some(element) = self.get_mut() {
f(element);
}
self
}
/// Panic if this element doesn't exist in `Storage`.
pub fn or_panic(self) -> Self
where
R: Debug,
{
self.get().or_else(|| {
panic!("retriever: Entry::or_panic(): {:?} doesn't exist",&self.id)
});
self
}
/// Get a reference to the element.
pub fn get(&self) -> Option<&Element> {
self.idx.map(|idx| self.storage.get_idx(idx))
}
/// Get a mutable reference to the element.
pub fn get_mut(&mut self) -> Option<&mut Element> {
self.idx.map(move |idx| self.storage.get_idx_mut(idx))
}
/// Remove and return the element.
pub fn remove(mut self) -> Option<Element> {
let idx = self.idx?;
self.idx = None;
Some(self.storage.remove_idx(idx))
}
/// Remove and return the element, only if the predicate is true.
pub fn remove_if<F>(self, mut f: F) -> Option<Element>
where
F: FnMut(&Element) -> bool,
{
let should_remove = if let Some(e) = self.get() {
f(e)
} else {
false
};
if should_remove {
self.remove()
} else {
None
}
}
}