mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-07-07 20:06:36 +02:00
131 lines
4.1 KiB
Rust
131 lines
4.1 KiB
Rust
use std::collections::HashMap;
|
|
use uuid::Uuid;
|
|
|
|
/// A WorkingSet represents a snapshot of the working set from a replica.
|
|
///
|
|
/// A replica's working set is a mapping from small integers to task uuids for all pending tasks.
|
|
/// The small integers are meant to be stable, easily-typed identifiers for users to interact with
|
|
/// important tasks.
|
|
///
|
|
/// IMPORTANT: the content of the working set may change at any time that a DB transaction is not
|
|
/// in progress, and the data in this type will not be updated automatically. It is up to the
|
|
/// caller to decide how long to keep this value, and how much to trust the accuracy of its
|
|
/// contents. In practice, the answers are usually "a few milliseconds" and treating unexpected
|
|
/// results as non-fatal.
|
|
pub struct WorkingSet {
|
|
by_index: Vec<Option<Uuid>>,
|
|
by_uuid: HashMap<Uuid, usize>,
|
|
}
|
|
|
|
impl WorkingSet {
|
|
/// Create a new WorkingSet. Typically this is acquired via `replica.working_set()`
|
|
pub(crate) fn new(by_index: Vec<Option<Uuid>>) -> Self {
|
|
let mut by_uuid = HashMap::new();
|
|
|
|
// working sets are 1-indexed, so element 0 should always be None
|
|
assert!(by_index.is_empty() || by_index[0].is_none());
|
|
|
|
for (index, uuid) in by_index.iter().enumerate() {
|
|
if let Some(uuid) = uuid {
|
|
by_uuid.insert(*uuid, index);
|
|
}
|
|
}
|
|
Self { by_index, by_uuid }
|
|
}
|
|
|
|
/// Get the "length" of the working set: the total number of uuids in the set.
|
|
pub fn len(&self) -> usize {
|
|
self.by_index.iter().filter(|e| e.is_some()).count()
|
|
}
|
|
|
|
/// True if the length is zero
|
|
pub fn is_empty(&self) -> bool {
|
|
self.by_index.iter().all(|e| e.is_none())
|
|
}
|
|
|
|
/// Get the uuid with the given index, if any exists.
|
|
pub fn by_index(&self, index: usize) -> Option<Uuid> {
|
|
if let Some(Some(uuid)) = self.by_index.get(index) {
|
|
Some(*uuid)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
/// Get the index for the given uuid, if any
|
|
pub fn by_uuid(&self, uuid: Uuid) -> Option<usize> {
|
|
self.by_uuid.get(&uuid).copied()
|
|
}
|
|
|
|
/// Iterate over pairs (index, uuid), in order by index.
|
|
pub fn iter(&self) -> impl Iterator<Item = (usize, Uuid)> + '_ {
|
|
self.by_index
|
|
.iter()
|
|
.enumerate()
|
|
.filter_map(|(index, uuid)| uuid.as_ref().map(|uuid| (index, *uuid)))
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::*;
|
|
use pretty_assertions::assert_eq;
|
|
|
|
fn make() -> (Uuid, Uuid, WorkingSet) {
|
|
let uuid1 = Uuid::new_v4();
|
|
let uuid2 = Uuid::new_v4();
|
|
(
|
|
uuid1,
|
|
uuid2,
|
|
WorkingSet::new(vec![None, Some(uuid1), None, Some(uuid2), None]),
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn test_new() {
|
|
let (_, uuid2, ws) = make();
|
|
assert_eq!(ws.by_index[3], Some(uuid2));
|
|
assert_eq!(ws.by_uuid.get(&uuid2), Some(&3));
|
|
}
|
|
|
|
#[test]
|
|
fn test_len_and_is_empty() {
|
|
let (_, _, ws) = make();
|
|
assert_eq!(ws.len(), 2);
|
|
assert_eq!(ws.is_empty(), false);
|
|
|
|
let ws = WorkingSet::new(vec![]);
|
|
assert_eq!(ws.len(), 0);
|
|
assert_eq!(ws.is_empty(), true);
|
|
|
|
let ws = WorkingSet::new(vec![None, None, None]);
|
|
assert_eq!(ws.len(), 0);
|
|
assert_eq!(ws.is_empty(), true);
|
|
}
|
|
|
|
#[test]
|
|
fn test_by_index() {
|
|
let (uuid1, uuid2, ws) = make();
|
|
assert_eq!(ws.by_index(0), None);
|
|
assert_eq!(ws.by_index(1), Some(uuid1));
|
|
assert_eq!(ws.by_index(2), None);
|
|
assert_eq!(ws.by_index(3), Some(uuid2));
|
|
assert_eq!(ws.by_index(4), None);
|
|
assert_eq!(ws.by_index(100), None); // past the end of the vector
|
|
}
|
|
|
|
#[test]
|
|
fn test_by_uuid() {
|
|
let (uuid1, uuid2, ws) = make();
|
|
let nosuch = Uuid::new_v4();
|
|
assert_eq!(ws.by_uuid(uuid1), Some(1));
|
|
assert_eq!(ws.by_uuid(uuid2), Some(3));
|
|
assert_eq!(ws.by_uuid(nosuch), None);
|
|
}
|
|
|
|
#[test]
|
|
fn test_iter() {
|
|
let (uuid1, uuid2, ws) = make();
|
|
assert_eq!(ws.iter().collect::<Vec<_>>(), vec![(1, uuid1), (3, uuid2),]);
|
|
}
|
|
}
|