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>, by_uuid: HashMap, } impl WorkingSet { /// Create a new WorkingSet. Typically this is acquired via `replica.working_set()` pub(crate) fn new(by_index: Vec>) -> Self { let mut by_uuid = HashMap::new(); 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 { 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 { self.by_uuid.get(&uuid).copied() } /// Iterate over pairs (index, uuid), in order by index. pub fn iter(&self) -> impl Iterator + '_ { self.by_index .iter() .enumerate() .filter_map(|(index, uuid)| { if let Some(uuid) = uuid { Some((index, *uuid)) } else { None } }) } } #[cfg(test)] mod test { use super::*; 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![(1, uuid1), (3, uuid2),]); } }