v0.3.6-pre.003
This commit is contained in:
@@ -1,9 +1,12 @@
|
||||
// file: crates/ksp-job-api/src/error.rs
|
||||
// version: 1
|
||||
// version: 2
|
||||
|
||||
/// Error code used when a Job identifier violates its bounded safe-code contract.
|
||||
pub const ERROR_CODE_JOB_ID_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("job_api", "job_id_invalid");
|
||||
/// Error code used when a Job kind code violates its bounded safe-code contract.
|
||||
pub const ERROR_CODE_JOB_KIND_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("job_api", "job_kind_invalid");
|
||||
/// Error code used when a Job notification sequence cannot advance without wrapping.
|
||||
pub const ERROR_CODE_JOB_NOTIFICATION_SEQUENCE_EXHAUSTED: ksp_core_lib::ErrorCode =
|
||||
ksp_core_lib::ErrorCode::new("job_api", "job_notification_sequence_exhausted");
|
||||
/// Error code used when a requested Job lifecycle transition is not allowed.
|
||||
pub const ERROR_CODE_JOB_TRANSITION_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("job_api", "job_transition_invalid");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-job-api/src/lib.rs
|
||||
// version: 1
|
||||
// version: 2
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![deny(unreachable_pub)]
|
||||
@@ -8,14 +8,15 @@
|
||||
//! Passive runtime-neutral lifecycle contracts for bounded KSP Jobs.
|
||||
//!
|
||||
//! This foundation owns validated Job identity, explicit lifecycle transitions
|
||||
//! and cooperative cancellation intent. Notification delivery, concrete Job
|
||||
//! behavior, runtime spawning, Transport, Store and Worker contracts remain
|
||||
//! outside this crate.
|
||||
//! cooperative cancellation intent and runtime-neutral latest-value observation.
|
||||
//! Concrete Job behavior, runtime spawning, Transport, Store and Worker contracts
|
||||
//! remain outside this crate.
|
||||
|
||||
mod cancellation;
|
||||
mod error;
|
||||
mod identity;
|
||||
mod lifecycle;
|
||||
mod notification;
|
||||
|
||||
/// Runtime-neutral cloneable token carrying cooperative cancellation intent.
|
||||
pub use self::cancellation::JobCancellationToken;
|
||||
@@ -23,6 +24,8 @@ pub use self::cancellation::JobCancellationToken;
|
||||
pub use self::error::ERROR_CODE_JOB_ID_INVALID;
|
||||
/// Error code used when a Job kind code violates its bounded safe-code contract.
|
||||
pub use self::error::ERROR_CODE_JOB_KIND_INVALID;
|
||||
/// Error code used when a Job notification sequence cannot advance without wrapping.
|
||||
pub use self::error::ERROR_CODE_JOB_NOTIFICATION_SEQUENCE_EXHAUSTED;
|
||||
/// Error code used when a requested Job lifecycle transition is not allowed.
|
||||
pub use self::error::ERROR_CODE_JOB_TRANSITION_INVALID;
|
||||
/// Bounded caller-supplied identity of one logical Job and its controlled resumptions.
|
||||
@@ -39,6 +42,14 @@ pub use self::lifecycle::JobCompletion;
|
||||
pub use self::lifecycle::JobLifecycle;
|
||||
/// Current lifecycle state of one bounded Job.
|
||||
pub use self::lifecycle::JobState;
|
||||
/// Latest complete observable value for one Job at a monotone sequence position.
|
||||
pub use self::notification::JobNotification;
|
||||
/// Monotone sequence attached to one latest-value Job notification stream.
|
||||
pub use self::notification::JobNotificationSequence;
|
||||
/// Runtime-neutral future returned while observing a latest-value Job snapshot source.
|
||||
pub use self::notification::JobSnapshotFuture;
|
||||
/// Runtime-neutral read and change-wait contract for one latest-value Job snapshot stream.
|
||||
pub use self::notification::JobSnapshotSource;
|
||||
/// Common KSP error type used by Job-facing contracts.
|
||||
pub use ksp_core_lib::Error;
|
||||
/// Stable structured code identifying a KSP error category and condition.
|
||||
|
||||
126
crates/ksp-job-api/src/notification.rs
Normal file
126
crates/ksp-job-api/src/notification.rs
Normal file
@@ -0,0 +1,126 @@
|
||||
// file: crates/ksp-job-api/src/notification.rs
|
||||
// version: 1
|
||||
|
||||
/// Monotone sequence attached to one latest-value Job notification stream.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub struct JobNotificationSequence(u64);
|
||||
|
||||
impl JobNotificationSequence {
|
||||
/// Creates the initial sequence position for one Job snapshot stream.
|
||||
#[must_use]
|
||||
pub const fn initial() -> Self {
|
||||
return Self(0);
|
||||
}
|
||||
|
||||
/// Returns the opaque numeric position carried by this sequence.
|
||||
#[must_use]
|
||||
pub const fn value(&self) -> u64 {
|
||||
return self.0;
|
||||
}
|
||||
|
||||
/// Advances the sequence exactly once or reports exhaustion without wrapping.
|
||||
pub fn next(&self) -> crate::Result<Self> {
|
||||
let next = match self.0.checked_add(1) {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => {
|
||||
return std::result::Result::Err(
|
||||
crate::Error::new(crate::ERROR_CODE_JOB_NOTIFICATION_SEQUENCE_EXHAUSTED, "Job notification sequence exhausted")
|
||||
.with_context("sequence", self.0.to_string()),
|
||||
);
|
||||
},
|
||||
};
|
||||
return std::result::Result::Ok(Self(next));
|
||||
}
|
||||
|
||||
/// Reports whether this sequence is strictly newer than an observed sequence.
|
||||
#[must_use]
|
||||
pub const fn is_after(&self, observed: Self) -> bool {
|
||||
return self.0 > observed.0;
|
||||
}
|
||||
}
|
||||
|
||||
/// Latest complete observable value for one Job at a monotone sequence position.
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct JobNotification<S> {
|
||||
id: crate::JobId,
|
||||
kind: crate::JobKindCode,
|
||||
sequence: crate::JobNotificationSequence,
|
||||
snapshot: S,
|
||||
state: crate::JobState,
|
||||
}
|
||||
|
||||
impl<S> JobNotification<S> {
|
||||
/// Creates one immutable latest-value notification from an already validated Job identity and snapshot.
|
||||
#[must_use]
|
||||
pub const fn new(id: crate::JobId, kind: crate::JobKindCode, sequence: crate::JobNotificationSequence, state: crate::JobState, snapshot: S) -> Self {
|
||||
return Self { id, kind, sequence, snapshot, state };
|
||||
}
|
||||
|
||||
/// Returns the logical Job identity.
|
||||
#[must_use]
|
||||
pub const fn id(&self) -> &crate::JobId {
|
||||
return &self.id;
|
||||
}
|
||||
|
||||
/// Returns the stable Job family code.
|
||||
#[must_use]
|
||||
pub const fn kind(&self) -> &crate::JobKindCode {
|
||||
return &self.kind;
|
||||
}
|
||||
|
||||
/// Returns the monotone sequence of this latest value.
|
||||
#[must_use]
|
||||
pub const fn sequence(&self) -> crate::JobNotificationSequence {
|
||||
return self.sequence;
|
||||
}
|
||||
|
||||
/// Returns the complete safe snapshot owned by the concrete Job contract.
|
||||
#[must_use]
|
||||
pub const fn snapshot(&self) -> &S {
|
||||
return &self.snapshot;
|
||||
}
|
||||
|
||||
/// Returns the lifecycle state represented by this snapshot.
|
||||
#[must_use]
|
||||
pub const fn state(&self) -> crate::JobState {
|
||||
return self.state;
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> std::fmt::Debug for JobNotification<S> {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
return formatter
|
||||
.debug_struct("JobNotification")
|
||||
.field("id", &self.id)
|
||||
.field("kind", &self.kind)
|
||||
.field("sequence", &self.sequence)
|
||||
.field("state", &self.state)
|
||||
.field("snapshot", &"<redacted>")
|
||||
.finish();
|
||||
}
|
||||
}
|
||||
|
||||
/// Runtime-neutral future returned while observing a latest-value Job snapshot source.
|
||||
pub type JobSnapshotFuture<'a, S> = std::pin::Pin<std::boxed::Box<dyn std::future::Future<Output = crate::JobNotification<S>> + std::marker::Send + 'a>>;
|
||||
|
||||
/// Runtime-neutral read and change-wait contract for one latest-value Job snapshot stream.
|
||||
pub trait JobSnapshotSource: std::marker::Send + std::marker::Sync {
|
||||
/// Complete snapshot type retained by the concrete source.
|
||||
type Snapshot: std::clone::Clone + std::marker::Send + std::marker::Sync + 'static;
|
||||
|
||||
/// Returns the complete current value without requiring replay of prior notifications.
|
||||
#[must_use]
|
||||
fn current(&self) -> crate::JobNotification<Self::Snapshot>;
|
||||
|
||||
/// Waits for a value newer than `observed`, returning the complete current snapshot after coalescing any intermediate updates.
|
||||
fn wait_for_change(&self, observed: crate::JobNotificationSequence) -> crate::JobSnapshotFuture<'_, Self::Snapshot>;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn exhausted_notification_sequence() -> crate::JobNotificationSequence {
|
||||
return crate::JobNotificationSequence(u64::MAX);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "../unit_tests/notification.rs"]
|
||||
mod tests;
|
||||
Reference in New Issue
Block a user