mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-08 02:17:46 +08:00
refactor: isolate dispatch scheduling core
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
#[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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user