use crate::storage::warp_table::{
Capacity, WarpId, WarpTable, WarpTableCreationError, WarpTableInsertOutcome,
WarpTableRemoveOutcome, WarpTableRow,
};
use crate::{Pipe, TimerToken};
use super::{
CancelledContinuationCompletion, CancelledContinuationWork, ContinuationAdmission,
ContinuationArmRefusal, ContinuationCompletion, ContinuationFailure, ContinuationId,
ContinuationResult, ContinuationUnavailable, Owed,
};
#[derive(WarpTableRow)]
struct PendingContinuation<Address> {
return_to: Address,
}
type PendingContinuationCreationError<Address> = WarpTableCreationError<
<PendingContinuation<Address> as WarpTableRow>::ColumnsCreationError,
<PendingContinuation<Address> as WarpTableRow>::IndexesCreationError,
>;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ContinuationLedgerCreationFailure {
StorageUnavailable,
}
#[derive(Debug)]
pub struct ContinuationLedgerCreationError<Address> {
failure: ContinuationLedgerCreationFailure,
_source: PendingContinuationCreationError<Address>,
}
impl<Address> ContinuationLedgerCreationError<Address> {
pub const fn failure(&self) -> ContinuationLedgerCreationFailure {
self.failure
}
}
pub struct ContinuationLedger<Address> {
pending: WarpTable<PendingContinuation<Address>>,
}
impl<Address> ContinuationLedger<Address> {
pub fn try_new(capacity: Capacity) -> Result<Self, ContinuationLedgerCreationError<Address>> {
let pending =
WarpTable::try_new(capacity).map_err(|source| ContinuationLedgerCreationError {
failure: ContinuationLedgerCreationFailure::StorageUnavailable,
_source: source,
})?;
Ok(Self { pending })
}
pub const fn capacity(&self) -> Capacity {
self.pending.capacity()
}
pub const fn len(&self) -> usize {
self.pending.len()
}
pub const fn is_empty(&self) -> bool {
self.pending.is_empty()
}
pub fn admit<'message, Worker>(
&mut self,
return_to: Address,
owed: Owed<Worker::In<'message>>,
) -> ContinuationAdmissionOutcome<'message, Address, Worker>
where
Worker: Pipe,
{
match self.pending.insert(PendingContinuation { return_to }) {
WarpTableInsertOutcome::Inserted { id, .. } => ContinuationAdmissionOutcome::Admitted {
admission: ContinuationAdmission {
id: ContinuationId::new(id.value()),
owed,
},
},
WarpTableInsertOutcome::Full {
rejected: PendingContinuation { return_to },
} => ContinuationAdmissionOutcome::Refused {
return_to,
result: Err(ContinuationFailure::Unavailable {
reason: ContinuationUnavailable::AtCapacity {
maximum_pending: self.capacity().value().get(),
},
rejected: owed,
}),
},
WarpTableInsertOutcome::IdentifiersExhausted {
rejected: PendingContinuation { return_to },
} => ContinuationAdmissionOutcome::Refused {
return_to,
result: Err(ContinuationFailure::Unavailable {
reason: ContinuationUnavailable::IdentifiersExhausted,
rejected: owed,
}),
},
WarpTableInsertOutcome::UniqueConflict {
conflict,
rejected: _,
} => match conflict {},
}
}
pub fn resolve<'message, Worker>(
&mut self,
completion: CancelledContinuationCompletion<'message, Worker>,
) -> ContinuationResolutionOutcome<'message, Address, Worker>
where
Worker: Pipe,
{
let completion = completion.0;
let id = WarpId::new(completion.id.value());
match self.pending.remove(id) {
WarpTableRemoveOutcome::Removed {
removed: PendingContinuation { return_to },
..
} => ContinuationResolutionOutcome::Resolved {
return_to,
result: Ok(completion.output),
},
WarpTableRemoveOutcome::Absent { .. } => {
ContinuationResolutionOutcome::Stale { completion }
}
}
}
pub fn refuse<'message, Worker>(
&mut self,
refusal: ContinuationArmRefusal<'message, Worker>,
) -> ContinuationRefusalOutcome<'message, Address, Worker>
where
Worker: Pipe,
{
let id = WarpId::new(refusal.id.value());
match self.pending.remove(id) {
WarpTableRemoveOutcome::Removed {
removed: PendingContinuation { return_to },
..
} => ContinuationRefusalOutcome::Refused {
return_to,
result: Err(ContinuationFailure::Unavailable {
reason: ContinuationUnavailable::TimerRefused {
reason: refusal.reason,
},
rejected: refusal.owed,
}),
},
WarpTableRemoveOutcome::Absent { .. } => ContinuationRefusalOutcome::Stale { refusal },
}
}
pub fn retract<'message, Worker>(
&mut self,
work: CancelledContinuationWork<'message, Worker>,
) -> ContinuationRetractionOutcome<'message, Address, Worker>
where
Worker: Pipe,
{
let id = WarpId::new(work.id().value());
match self.pending.remove(id) {
WarpTableRemoveOutcome::Removed {
removed: PendingContinuation { return_to },
..
} => ContinuationRetractionOutcome::Retracted {
return_to,
rejected: work.0.owed,
},
WarpTableRemoveOutcome::Absent { .. } => ContinuationRetractionOutcome::Stale { work },
}
}
pub fn expire(&mut self, token: TimerToken) -> ContinuationExpirationOutcome<Address> {
let id = WarpId::new(token.value());
match self.pending.remove(id) {
WarpTableRemoveOutcome::Removed {
removed: PendingContinuation { return_to },
..
} => ContinuationExpirationOutcome::Expired {
return_to,
expiration: ContinuationExpiration { token },
},
WarpTableRemoveOutcome::Absent { .. } => ContinuationExpirationOutcome::Stale { token },
}
}
}
#[must_use = "admitted work or immediate refusal must be handled by the fitting"]
pub enum ContinuationAdmissionOutcome<'message, Address, Worker>
where
Worker: Pipe,
{
Admitted {
admission: ContinuationAdmission<'message, Worker>,
},
Refused {
return_to: Address,
result: ContinuationResult<'message, Worker>,
},
}
#[must_use = "resolved or stale completion must be handled by the fitting"]
pub enum ContinuationResolutionOutcome<'message, Address, Worker>
where
Worker: Pipe,
{
Resolved {
return_to: Address,
result: ContinuationResult<'message, Worker>,
},
Stale {
completion: ContinuationCompletion<'message, Worker>,
},
}
#[must_use = "resolved or stale timer refusal must be handled by the fitting"]
pub enum ContinuationRefusalOutcome<'message, Address, Worker>
where
Worker: Pipe,
{
Refused {
return_to: Address,
result: ContinuationResult<'message, Worker>,
},
Stale {
refusal: ContinuationArmRefusal<'message, Worker>,
},
}
#[must_use = "retracted or stale unstarted work must be handled by the fitting"]
pub enum ContinuationRetractionOutcome<'message, Address, Worker>
where
Worker: Pipe,
{
Retracted {
return_to: Address,
rejected: Owed<Worker::In<'message>>,
},
Stale {
work: CancelledContinuationWork<'message, Worker>,
},
}
#[must_use = "expired or stale timer tokens must be handled by the fitting"]
pub enum ContinuationExpirationOutcome<Address> {
Expired {
return_to: Address,
expiration: ContinuationExpiration,
},
Stale {
token: TimerToken,
},
}
#[must_use = "expiration must become the continued pipe result"]
pub struct ContinuationExpiration {
token: TimerToken,
}
impl ContinuationExpiration {
pub const fn token(&self) -> TimerToken {
self.token
}
pub const fn into_result<'message, Worker>(self) -> ContinuationResult<'message, Worker>
where
Worker: Pipe,
{
Err(ContinuationFailure::Expired)
}
}