mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-07 18:07:47 +08:00
111 lines
3.3 KiB
Rust
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);
|
|
}
|
|
}
|