feat(jobq): hand the builder to a closure, and make a handle name its job
Two review findings from the operator, both about the builder being more
reachable than the design said.
**The builder must not leave the crate.** The module doc claimed "an
insertion API, not a spec factory — a builder is only ever handed to a
closure by the queue's insertion entry point", and then `new()` and
`insert_into` were public, so a caller could build one, carry it around
and insert it later. That is a spec factory with a builder's name on it.
`insert_job(root_parent, |b| …)` is now the whole API: the builder is
created inside the call, handed to the closure, and consumed there.
`new` / `insert_into` / `insert_with` are crate-private.
**A handle names the job that issued it.** `NodeGuid` was a per-builder
counter, so two jobs' first handles compared equal. `NodeRef` converts
into a bare `NodeGuid` — dropping the borrow that ties it to its
builder — so a handle carried into a second job (an inner closure
capturing an outer handle) would silently resolve to whatever that job's
first node happened to be. It is now `{ job, seq }` with a random `job`
half, so a foreign handle is a miss and the insert fails naming it. The
randomness comes from `RandomState`, which is collision-avoidance rather
than cryptography and needs no new dependency.
Tests moved onto the closure API rather than keeping their in-crate
access to the private constructor — a test that only passes because it
lives inside the crate is not testing the API a caller has. The two
forward-reference tests stopped asserting literal guid values (a random
half cannot be written down) and compare against the handles instead,
and a new test carries a handle between two jobs to pin the behaviour
that motivated the change.
This commit is contained in:
parent
ec16b80415
commit
f7548e4535
3 changed files with 195 additions and 75 deletions
|
|
@ -458,6 +458,28 @@ impl<N, R> Graph<N, R> {
|
|||
Ok(id)
|
||||
}
|
||||
|
||||
/// Insert a whole job under `root_parent`, returning the id each handle's
|
||||
/// node was minted as.
|
||||
///
|
||||
/// `declare` receives a fresh [`JobBuilder`] and names the job's nodes on
|
||||
/// it; the builder never leaves this call, so a job cannot be built in one
|
||||
/// place and inserted in another. The graph-only counterpart of
|
||||
/// [`crate::scheduler::Scheduler::insert_job`] — prefer that one when a
|
||||
/// scheduler owns the graph, since it also records what it started.
|
||||
///
|
||||
/// # Errors
|
||||
/// Propagates [`BuildError`] — a forward reference in the job's own
|
||||
/// declarations, a handle from a different job, or a graph rejection.
|
||||
pub fn insert_job(
|
||||
&mut self,
|
||||
root_parent: Option<NodeId>,
|
||||
declare: impl FnOnce(&JobBuilder<N, R>),
|
||||
) -> Result<std::collections::HashMap<NodeGuid, NodeId>, BuildError> {
|
||||
let job = JobBuilder::new();
|
||||
declare(&job);
|
||||
job.insert_into(self, root_parent)
|
||||
}
|
||||
|
||||
/// Borrow a node by id.
|
||||
#[must_use]
|
||||
pub fn node(&self, id: NodeId) -> Option<&Node<N, R>> {
|
||||
|
|
|
|||
Loading…
Reference in a new issue