Core: #[doc(hidden)] engine-side constructors for Job, StreamEvent, Schedule, Wake
Task core-engine-value-constructors (wave-3 pre-work). All four value types are #[non_exhaustive] (ADR-017 §3), so downstream engine crates cannot struct-literal-construct them (E0639). Give engines a sanctioned construction path without weakening the consumer posture: pub #[doc(hidden)] full-field constructors (Job::from_row, StreamEvent:: from_row, Schedule::new, Wake::new), each doc-commented as engine- construction-only — not contract surface, not covered by ADR-017's semver-minor field-addition promise; a field addition changes the signature and is a lockstep-duty event (ADR-017 §5). Core tests now construct through the new constructors; no behavior change.
This commit is contained in:
1 parent
1269246faf
commit
5474141f2b
7 files changed
+190
-37
No files matched your search
@@ -80,6 +80,62 @@ pub struct Job {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Job {
|
impl Job {
|
||||||
|
/// Engine-construction-only constructor taking the full field set.
|
||||||
|
///
|
||||||
|
/// # Contract posture
|
||||||
|
///
|
||||||
|
/// Not consumer API and not part of the contract surface: engines
|
||||||
|
/// (in-repo, lockstep-updated with core) use it to build [`Job`]
|
||||||
|
/// values from row data, since `#[non_exhaustive]` (ADR-017 §3)
|
||||||
|
/// bans struct-literal construction downstream. Not covered by
|
||||||
|
/// ADR-017's semver-minor field-addition promise — a `Job` field
|
||||||
|
/// addition changes this signature, which is an engine breaking
|
||||||
|
/// change under a core change and a lockstep-duty event
|
||||||
|
/// (ADR-017 §5). Hidden from consumer-facing docs.
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub fn from_row(
|
||||||
|
id: i64,
|
||||||
|
queue: String,
|
||||||
|
state: JobState,
|
||||||
|
payload: Vec<u8>,
|
||||||
|
priority: i64,
|
||||||
|
run_at: i64,
|
||||||
|
attempts: i64,
|
||||||
|
max_attempts: i64,
|
||||||
|
worker_id: Option<String>,
|
||||||
|
claimed_at: Option<i64>,
|
||||||
|
claim_expires_at: Option<i64>,
|
||||||
|
created_at: i64,
|
||||||
|
expires_at: Option<i64>,
|
||||||
|
visibility_timeout_s: i64,
|
||||||
|
backoff_base_s: i64,
|
||||||
|
dead_letter_retention_s: Option<i64>,
|
||||||
|
last_error: Option<String>,
|
||||||
|
died_at: Option<i64>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
id,
|
||||||
|
queue,
|
||||||
|
state,
|
||||||
|
payload,
|
||||||
|
priority,
|
||||||
|
run_at,
|
||||||
|
attempts,
|
||||||
|
max_attempts,
|
||||||
|
worker_id,
|
||||||
|
claimed_at,
|
||||||
|
claim_expires_at,
|
||||||
|
created_at,
|
||||||
|
expires_at,
|
||||||
|
visibility_timeout_s,
|
||||||
|
backoff_base_s,
|
||||||
|
dead_letter_retention_s,
|
||||||
|
last_error,
|
||||||
|
died_at,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Decode the stored payload bytes into a concrete target type.
|
/// Decode the stored payload bytes into a concrete target type.
|
||||||
///
|
///
|
||||||
/// The stored bytes are the serde_json serialization of the
|
/// The stored bytes are the serde_json serialization of the
|
||||||
|
|||||||
@@ -20,3 +20,27 @@ pub struct Schedule {
|
|||||||
/// engine defaults at each boundary fire (ADR-020 §3).
|
/// engine defaults at each boundary fire (ADR-020 §3).
|
||||||
pub opts: ScheduleOpts,
|
pub opts: ScheduleOpts,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Schedule {
|
||||||
|
/// Engine-construction-only constructor taking the full field set.
|
||||||
|
///
|
||||||
|
/// # Contract posture
|
||||||
|
///
|
||||||
|
/// Not consumer API and not part of the contract surface: engines
|
||||||
|
/// (in-repo, lockstep-updated with core) use it to build
|
||||||
|
/// [`Schedule`] values from row data, since `#[non_exhaustive]`
|
||||||
|
/// (ADR-017 §3) bans struct-literal construction downstream. Not
|
||||||
|
/// covered by ADR-017's semver-minor field-addition promise — a
|
||||||
|
/// `Schedule` field addition changes this signature, which is an
|
||||||
|
/// engine breaking change under a core change and a lockstep-duty
|
||||||
|
/// event (ADR-017 §5). Hidden from consumer-facing docs.
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub fn new(name: String, spec: String, queue: String, opts: ScheduleOpts) -> Self {
|
||||||
|
Self {
|
||||||
|
name,
|
||||||
|
spec,
|
||||||
|
queue,
|
||||||
|
opts,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -32,6 +32,36 @@ pub struct StreamEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl StreamEvent {
|
impl StreamEvent {
|
||||||
|
/// Engine-construction-only constructor taking the full field set.
|
||||||
|
///
|
||||||
|
/// # Contract posture
|
||||||
|
///
|
||||||
|
/// Not consumer API and not part of the contract surface: engines
|
||||||
|
/// (in-repo, lockstep-updated with core) use it to build
|
||||||
|
/// [`StreamEvent`] values from row data, since
|
||||||
|
/// `#[non_exhaustive]` (ADR-017 §3) bans struct-literal
|
||||||
|
/// construction downstream. Not covered by ADR-017's
|
||||||
|
/// semver-minor field-addition promise — a `StreamEvent` field
|
||||||
|
/// addition changes this signature, which is an engine breaking
|
||||||
|
/// change under a core change and a lockstep-duty event
|
||||||
|
/// (ADR-017 §5). Hidden from consumer-facing docs.
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub fn from_row(
|
||||||
|
offset: i64,
|
||||||
|
stream: String,
|
||||||
|
key: Option<String>,
|
||||||
|
payload: Vec<u8>,
|
||||||
|
created_at: i64,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
offset,
|
||||||
|
stream,
|
||||||
|
key,
|
||||||
|
payload,
|
||||||
|
created_at,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Decode the stored payload bytes into a concrete target type.
|
/// Decode the stored payload bytes into a concrete target type.
|
||||||
///
|
///
|
||||||
/// The stored bytes are the serde_json serialization of the
|
/// The stored bytes are the serde_json serialization of the
|
||||||
|
|||||||
@@ -330,12 +330,12 @@ impl Store for ProbeStore {
|
|||||||
_payload: Value,
|
_payload: Value,
|
||||||
_opts: ScheduleOpts,
|
_opts: ScheduleOpts,
|
||||||
) -> BoxedFuture<'a, Result<Schedule>> {
|
) -> BoxedFuture<'a, Result<Schedule>> {
|
||||||
Box::pin(std::future::ready(Ok(Schedule {
|
Box::pin(std::future::ready(Ok(Schedule::new(
|
||||||
name: "probe".into(),
|
"probe".into(),
|
||||||
spec: "@every 60s".into(),
|
"@every 60s".into(),
|
||||||
queue: "q".into(),
|
"q".into(),
|
||||||
opts: ScheduleOpts::default(),
|
ScheduleOpts::default(),
|
||||||
})))
|
))))
|
||||||
}
|
}
|
||||||
fn unschedule<'a>(&'a self, _name: &str) -> BoxedFuture<'a, Result<bool>> {
|
fn unschedule<'a>(&'a self, _name: &str) -> BoxedFuture<'a, Result<bool>> {
|
||||||
Box::pin(std::future::ready(Ok(false)))
|
Box::pin(std::future::ready(Ok(false)))
|
||||||
|
|||||||
@@ -51,36 +51,36 @@ fn schedule_opts_default_matches_enqueue_opts_twin_types() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn sample_job(payload: Vec<u8>) -> Job {
|
fn sample_job(payload: Vec<u8>) -> Job {
|
||||||
Job {
|
Job::from_row(
|
||||||
id: 42,
|
42,
|
||||||
queue: "orders".to_string(),
|
"orders".to_string(),
|
||||||
state: JobState::Dead,
|
JobState::Dead,
|
||||||
payload,
|
payload,
|
||||||
priority: 7,
|
7,
|
||||||
run_at: 1000,
|
1000,
|
||||||
attempts: 3,
|
3,
|
||||||
max_attempts: 3,
|
3,
|
||||||
worker_id: Some("w1".to_string()),
|
Some("w1".to_string()),
|
||||||
claimed_at: Some(999),
|
Some(999),
|
||||||
claim_expires_at: Some(1299),
|
Some(1299),
|
||||||
created_at: 900,
|
900,
|
||||||
expires_at: Some(5000),
|
Some(5000),
|
||||||
visibility_timeout_s: 300,
|
300,
|
||||||
backoff_base_s: 5,
|
5,
|
||||||
dead_letter_retention_s: None,
|
None,
|
||||||
last_error: Some("max attempts exceeded".to_string()),
|
Some("max attempts exceeded".to_string()),
|
||||||
died_at: Some(1100),
|
Some(1100),
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sample_event(payload: Vec<u8>) -> StreamEvent {
|
fn sample_event(payload: Vec<u8>) -> StreamEvent {
|
||||||
StreamEvent {
|
StreamEvent::from_row(
|
||||||
offset: 17,
|
17,
|
||||||
stream: "events".to_string(),
|
"events".to_string(),
|
||||||
key: Some("k".to_string()),
|
Some("k".to_string()),
|
||||||
payload,
|
payload,
|
||||||
created_at: 1234,
|
1234,
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -173,9 +173,7 @@ fn stop_token_debug_shows_cancelled_state() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn wake_carries_only_the_channel() {
|
fn wake_carries_only_the_channel() {
|
||||||
let w = Wake {
|
let w = Wake::new("ch-1".to_string());
|
||||||
channel: "ch-1".to_string(),
|
|
||||||
};
|
|
||||||
assert_eq!(w.channel, "ch-1");
|
assert_eq!(w.channel, "ch-1");
|
||||||
assert_eq!(w.clone(), w);
|
assert_eq!(w.clone(), w);
|
||||||
}
|
}
|
||||||
@@ -20,3 +20,23 @@ pub struct Wake {
|
|||||||
/// The channel the wake was delivered on.
|
/// The channel the wake was delivered on.
|
||||||
pub channel: String,
|
pub channel: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Wake {
|
||||||
|
/// Engine-construction-only constructor taking the full field set.
|
||||||
|
///
|
||||||
|
/// # Contract posture
|
||||||
|
///
|
||||||
|
/// Not consumer API and not part of the contract surface: engines
|
||||||
|
/// (in-repo, lockstep-updated with core) use it to build [`Wake`]
|
||||||
|
/// values (including the Postgres engine's synthetic reconnect
|
||||||
|
/// wake), since `#[non_exhaustive]` (ADR-017 §3) bans
|
||||||
|
/// struct-literal construction downstream. Not covered by
|
||||||
|
/// ADR-017's semver-minor field-addition promise — a `Wake` field
|
||||||
|
/// addition changes this signature, which is an engine breaking
|
||||||
|
/// change under a core change and a lockstep-duty event
|
||||||
|
/// (ADR-017 §5). Hidden from consumer-facing docs.
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub fn new(channel: String) -> Self {
|
||||||
|
Self { channel }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
id: core-engine-value-constructors
|
id: core-engine-value-constructors
|
||||||
name: Core — engine-side constructors for the non_exhaustive value types
|
name: Core — engine-side constructors for the non_exhaustive value types
|
||||||
status: pending
|
status: completed
|
||||||
depends_on: []
|
depends_on: []
|
||||||
scope: narrow
|
scope: narrow
|
||||||
risk: low
|
risk: low
|
||||||
@@ -60,8 +60,33 @@ cheap to review in isolation.
|
|||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
> To be filled by implementation agent
|
- Names pinned as `Job::from_row` and `StreamEvent::from_row`
|
||||||
|
(row-shaped values) but `Schedule::new` and `Wake::new`
|
||||||
|
(not row-shaped) — matching the task's own examples.
|
||||||
|
- `Job::from_row` takes 18 args and carries an explicit
|
||||||
|
`#[allow(clippy::too_many_arguments)]` — the task pins "taking the
|
||||||
|
full field set" as the posture, so arg count is intrinsic, not a
|
||||||
|
smell to engineer around (no opts struct / builder introduced).
|
||||||
|
- Doc-comments follow the crate's `# Contract posture` section style
|
||||||
|
(see `job.rs` / `stream.rs`), naming ADR-017 §3 and §5 explicitly.
|
||||||
|
- Core tests updated to construct through the new constructors:
|
||||||
|
`value_type_tests.rs` (`sample_job`, `sample_event`, `wake_test`)
|
||||||
|
and `trait_probe_tests.rs` (`schedule()` return). All 25 core tests
|
||||||
|
still pass.
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
> To be filled on completion
|
Added `#[doc(hidden)]` full-field constructors to core's four
|
||||||
|
`#[non_exhaustive]` value types: `Job::from_row` (18 fields),
|
||||||
|
`StreamEvent::from_row` (5 fields), `Schedule::new` (4 fields),
|
||||||
|
`Wake::new` (1 field) — each `pub` with a "Contract posture" doc
|
||||||
|
comment pinning engine-construction-only status, exclusion from the
|
||||||
|
contract surface and ADR-017's semver-minor field-addition promise,
|
||||||
|
and the ADR-017 §5 lockstep duty for field additions.
|
||||||
|
`#[non_exhaustive]` left untouched on all four; consumer posture
|
||||||
|
unchanged. Verified: `cargo build`, `cargo test` (workspace; core
|
||||||
|
unchanged-behavior), `cargo clippy --all-targets -- -D warnings`,
|
||||||
|
`cargo fmt --check` all clean. `cargo doc` spot-check: none of the
|
||||||
|
four constructors appears in the generated `struct.*.html` pages;
|
||||||
|
the only rustdoc warnings are three pre-existing unresolved
|
||||||
|
intra-doc links in `validation.rs`, untouched by this task.
|
||||||
Reference in new issue
Block a user