use tokio_util::sync::CancellationToken;
use uuid::Uuid;
pub enum GenState {
Idle,
Generating { id: Uuid, cancel: CancellationToken },
Cancelling { id: Uuid },
}
impl GenState {
pub fn current_id(&self) -> Option<Uuid> {
match self {
GenState::Idle => None,
GenState::Generating { id, .. } | GenState::Cancelling { id, .. } => Some(*id),
}
}
pub fn is_idle(&self) -> bool {
matches!(self, GenState::Idle)
}
pub fn begin(&mut self, id: Uuid, cancel: CancellationToken) -> bool {
if !self.is_idle() {
return false;
}
*self = GenState::Generating { id, cancel };
true
}
pub fn request_cancel(&mut self) -> Option<CancellationToken> {
if let GenState::Generating { id, cancel } = self {
let id = *id;
let token = cancel.clone();
*self = GenState::Cancelling { id };
Some(token)
} else {
None
}
}
pub fn active_cancel(&self) -> Option<&CancellationToken> {
match self {
GenState::Generating { cancel, .. } => Some(cancel),
GenState::Idle | GenState::Cancelling { .. } => None,
}
}
pub fn finish(&mut self, id: Uuid) -> bool {
if self.current_id() == Some(id) {
*self = GenState::Idle;
true
} else {
false
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn begin_only_from_idle() {
let mut s = GenState::Idle;
let id = Uuid::new_v4();
assert!(s.begin(id, CancellationToken::new()));
assert_eq!(s.current_id(), Some(id));
assert!(!s.is_idle());
let other = Uuid::new_v4();
assert!(!s.begin(other, CancellationToken::new()));
assert_eq!(s.current_id(), Some(id));
}
#[test]
fn request_cancel_transitions_and_returns_token() {
let mut s = GenState::Idle;
assert!(s.request_cancel().is_none());
let id = Uuid::new_v4();
let token = CancellationToken::new();
s.begin(id, token.clone());
let returned = s
.request_cancel()
.expect("a cancellation token from Generating");
returned.cancel();
assert!(
token.is_cancelled(),
"the returned token is exactly the live generation token"
);
assert!(matches!(s, GenState::Cancelling { .. }));
assert_eq!(s.current_id(), Some(id));
assert!(s.request_cancel().is_none());
}
#[test]
fn finish_matches_current_id() {
let mut s = GenState::Idle;
let id = Uuid::new_v4();
s.begin(id, CancellationToken::new());
assert!(!s.finish(Uuid::new_v4()));
assert!(!s.is_idle());
assert!(s.finish(id));
assert!(s.is_idle());
}
#[test]
fn finish_works_from_cancelling() {
let mut s = GenState::Idle;
let id = Uuid::new_v4();
s.begin(id, CancellationToken::new());
s.request_cancel();
assert!(s.finish(id));
assert!(s.is_idle());
}
#[test]
fn active_cancel_only_in_generating() {
let mut s = GenState::Idle;
assert!(s.active_cancel().is_none());
let token = CancellationToken::new();
s.begin(Uuid::new_v4(), token.clone());
assert!(s.active_cancel().is_some());
s.active_cancel().unwrap().cancel();
assert!(token.is_cancelled());
s.request_cancel();
assert!(
s.active_cancel().is_none(),
"no token is returned in Cancelling"
);
}
}