Files
Aether/crates/aether-dispatch-core/src/sequence.rs
T

111 lines
3.3 KiB
Rust

#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct DispatchSequenceItem<Candidate> {
pub candidate_index: u32,
pub retry_index: u32,
pub candidate: Candidate,
pub mark: DispatchSequenceMark,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum DispatchSequenceMark {
Pending,
Failed,
Succeeded,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DispatchSequence<Candidate> {
items: Vec<DispatchSequenceItem<Candidate>>,
cursor: usize,
}
impl<Candidate> DispatchSequence<Candidate> {
pub fn new(items: Vec<DispatchSequenceItem<Candidate>>) -> Self {
Self { items, cursor: 0 }
}
pub fn from_candidates(candidates: Vec<Candidate>) -> Self {
let items = candidates
.into_iter()
.enumerate()
.map(|(index, candidate)| DispatchSequenceItem {
candidate_index: u32::try_from(index).unwrap_or(u32::MAX),
retry_index: 0,
candidate,
mark: DispatchSequenceMark::Pending,
})
.collect();
Self::new(items)
}
pub fn peek_current(&self) -> Option<&DispatchSequenceItem<Candidate>> {
self.items.get(self.cursor)
}
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> Option<&DispatchSequenceItem<Candidate>> {
while self
.items
.get(self.cursor)
.is_some_and(|item| item.mark != DispatchSequenceMark::Pending)
{
self.cursor = self.cursor.saturating_add(1);
}
self.items.get(self.cursor)
}
pub fn mark_failed(&mut self) -> Option<&DispatchSequenceItem<Candidate>> {
self.mark_current(DispatchSequenceMark::Failed)
}
pub fn mark_succeeded(&mut self) -> Option<&DispatchSequenceItem<Candidate>> {
self.mark_current(DispatchSequenceMark::Succeeded)
}
pub fn len(&self) -> usize {
self.items.len()
}
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
pub fn cursor(&self) -> usize {
self.cursor
}
pub fn items(&self) -> &[DispatchSequenceItem<Candidate>] {
&self.items
}
fn mark_current(
&mut self,
mark: DispatchSequenceMark,
) -> Option<&DispatchSequenceItem<Candidate>> {
let item = self.items.get_mut(self.cursor)?;
item.mark = mark;
self.cursor = self.cursor.saturating_add(1);
self.items.get(self.cursor)
}
}
#[cfg(test)]
mod tests {
use super::{DispatchSequence, DispatchSequenceMark};
#[test]
fn mark_failed_advances_without_reordering() {
let mut sequence = DispatchSequence::from_candidates(vec!["a", "b", "c"]);
assert_eq!(sequence.next().map(|item| item.candidate), Some("a"));
assert_eq!(sequence.mark_failed().map(|item| item.candidate), Some("b"));
assert_eq!(sequence.next().map(|item| item.candidate), Some("b"));
assert_eq!(sequence.mark_failed().map(|item| item.candidate), Some("c"));
assert_eq!(sequence.next().map(|item| item.candidate), Some("c"));
assert_eq!(sequence.items()[0].mark, DispatchSequenceMark::Failed);
assert_eq!(sequence.items()[1].mark, DispatchSequenceMark::Failed);
assert_eq!(sequence.items()[2].mark, DispatchSequenceMark::Pending);
}
}