v0.3.9-pre.002
This commit is contained in:
199
crates/ksp-worker-api/src/lifecycle.rs
Normal file
199
crates/ksp-worker-api/src/lifecycle.rs
Normal file
@@ -0,0 +1,199 @@
|
||||
// file: crates/ksp-worker-api/src/lifecycle.rs
|
||||
// version: 1
|
||||
|
||||
/// Current lifecycle state of one continuous Worker.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
pub enum WorkerState {
|
||||
/// The Worker exists but has not started initialization.
|
||||
Created,
|
||||
/// The Worker is initializing resources before entering steady service.
|
||||
Starting,
|
||||
/// The Worker is actively providing its continuous service.
|
||||
Running,
|
||||
/// The Worker observed stop intent and is draining or releasing resources.
|
||||
Stopping,
|
||||
/// The Worker reached its normal terminal stopped state.
|
||||
Stopped,
|
||||
/// The Worker reached a terminal fault classified by one stable KSP error code.
|
||||
Faulted(crate::ErrorCode),
|
||||
}
|
||||
|
||||
impl WorkerState {
|
||||
/// Returns the stable safe lifecycle code without rendering Worker data.
|
||||
#[must_use]
|
||||
pub const fn code(&self) -> &'static str {
|
||||
return match self {
|
||||
Self::Created => "created",
|
||||
Self::Starting => "starting",
|
||||
Self::Running => "running",
|
||||
Self::Stopping => "stopping",
|
||||
Self::Stopped => "stopped",
|
||||
Self::Faulted(_) => "faulted",
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns the terminal fault code when the state is [`Self::Faulted`].
|
||||
#[must_use]
|
||||
pub const fn fault_code(&self) -> std::option::Option<crate::ErrorCode> {
|
||||
return match self {
|
||||
Self::Faulted(code) => std::option::Option::Some(*code),
|
||||
_ => std::option::Option::None,
|
||||
};
|
||||
}
|
||||
|
||||
/// Reports whether no later lifecycle transition is permitted.
|
||||
#[must_use]
|
||||
pub const fn is_terminal(&self) -> bool {
|
||||
return matches!(self, Self::Stopped | Self::Faulted(_));
|
||||
}
|
||||
}
|
||||
|
||||
/// Operational health classification independent from Worker lifecycle phase.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
pub enum WorkerHealth {
|
||||
/// Health has not yet been established or cannot currently be classified.
|
||||
Unknown,
|
||||
/// The Worker is operating within its expected healthy envelope.
|
||||
Healthy,
|
||||
/// The Worker is operating with a known degradation while service remains available.
|
||||
Degraded,
|
||||
/// The Worker is currently unable to satisfy its expected service health contract.
|
||||
Unhealthy,
|
||||
}
|
||||
|
||||
impl WorkerHealth {
|
||||
/// Returns the stable safe code for this health classification.
|
||||
#[must_use]
|
||||
pub const fn code(&self) -> &'static str {
|
||||
return match self {
|
||||
Self::Unknown => "unknown",
|
||||
Self::Healthy => "healthy",
|
||||
Self::Degraded => "degraded",
|
||||
Self::Unhealthy => "unhealthy",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Minimal generic activity classification for a continuous Worker.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
pub enum WorkerActivity {
|
||||
/// Activity has not yet been established or cannot currently be classified.
|
||||
Unknown,
|
||||
/// The Worker is alive but not currently processing concrete work.
|
||||
Idle,
|
||||
/// The Worker is currently processing concrete work.
|
||||
Active,
|
||||
}
|
||||
|
||||
impl WorkerActivity {
|
||||
/// Returns the stable safe code for this activity classification.
|
||||
#[must_use]
|
||||
pub const fn code(&self) -> &'static str {
|
||||
return match self {
|
||||
Self::Unknown => "unknown",
|
||||
Self::Idle => "idle",
|
||||
Self::Active => "active",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Passive owner of one Worker identity and its validated lifecycle state.
|
||||
#[derive(Eq, PartialEq)]
|
||||
pub struct WorkerLifecycle {
|
||||
id: crate::WorkerId,
|
||||
kind: crate::WorkerKindCode,
|
||||
state: crate::WorkerState,
|
||||
}
|
||||
|
||||
impl WorkerLifecycle {
|
||||
/// Creates one lifecycle in [`WorkerState::Created`] state.
|
||||
#[must_use]
|
||||
pub const fn new(id: crate::WorkerId, kind: crate::WorkerKindCode) -> Self {
|
||||
return Self { id, kind, state: crate::WorkerState::Created };
|
||||
}
|
||||
|
||||
/// Returns the logical Worker identity.
|
||||
#[must_use]
|
||||
pub const fn id(&self) -> &crate::WorkerId {
|
||||
return &self.id;
|
||||
}
|
||||
|
||||
/// Returns the stable Worker family code.
|
||||
#[must_use]
|
||||
pub const fn kind(&self) -> &crate::WorkerKindCode {
|
||||
return &self.kind;
|
||||
}
|
||||
|
||||
/// Returns the current lifecycle state.
|
||||
#[must_use]
|
||||
pub const fn state(&self) -> crate::WorkerState {
|
||||
return self.state;
|
||||
}
|
||||
|
||||
/// Begins initialization of a newly created Worker.
|
||||
pub fn start(&mut self) -> crate::Result<()> {
|
||||
return self.transition(crate::WorkerState::Starting);
|
||||
}
|
||||
|
||||
/// Records that initialization completed and steady service is running.
|
||||
pub fn mark_running(&mut self) -> crate::Result<()> {
|
||||
return self.transition(crate::WorkerState::Running);
|
||||
}
|
||||
|
||||
/// Records that a starting or running Worker observed cooperative stop intent.
|
||||
pub fn mark_stopping(&mut self) -> crate::Result<()> {
|
||||
return self.transition(crate::WorkerState::Stopping);
|
||||
}
|
||||
|
||||
/// Records normal terminal stop after draining, or before initialization began.
|
||||
pub fn mark_stopped(&mut self) -> crate::Result<()> {
|
||||
return self.transition(crate::WorkerState::Stopped);
|
||||
}
|
||||
|
||||
/// Records a terminal fault using only a stable KSP error code.
|
||||
pub fn fault(&mut self, code: crate::ErrorCode) -> crate::Result<()> {
|
||||
return self.transition(crate::WorkerState::Faulted(code));
|
||||
}
|
||||
|
||||
fn transition(&mut self, target: crate::WorkerState) -> crate::Result<()> {
|
||||
if !allowed_transition(self.state, target) {
|
||||
return std::result::Result::Err(transition_error(self.state, target));
|
||||
}
|
||||
self.state = target;
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for WorkerLifecycle {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
return formatter.debug_struct("WorkerLifecycle").field("id", &self.id).field("kind", &self.kind).field("state", &self.state).finish();
|
||||
}
|
||||
}
|
||||
|
||||
fn allowed_transition(source: crate::WorkerState, target: crate::WorkerState) -> bool {
|
||||
return matches!(
|
||||
(source, target),
|
||||
(crate::WorkerState::Created, crate::WorkerState::Starting)
|
||||
| (crate::WorkerState::Created, crate::WorkerState::Stopped)
|
||||
| (crate::WorkerState::Starting, crate::WorkerState::Running)
|
||||
| (crate::WorkerState::Starting, crate::WorkerState::Stopping)
|
||||
| (crate::WorkerState::Starting, crate::WorkerState::Faulted(_))
|
||||
| (crate::WorkerState::Running, crate::WorkerState::Stopping)
|
||||
| (crate::WorkerState::Running, crate::WorkerState::Faulted(_))
|
||||
| (crate::WorkerState::Stopping, crate::WorkerState::Stopped)
|
||||
| (crate::WorkerState::Stopping, crate::WorkerState::Faulted(_))
|
||||
);
|
||||
}
|
||||
|
||||
fn transition_error(source: crate::WorkerState, target: crate::WorkerState) -> crate::Error {
|
||||
return crate::Error::new(crate::ERROR_CODE_WORKER_TRANSITION_INVALID, "invalid Worker lifecycle transition")
|
||||
.with_context("source_state", source.code())
|
||||
.with_context("target_state", target.code());
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "../unit_tests/lifecycle.rs"]
|
||||
mod tests;
|
||||
Reference in New Issue
Block a user