jobq-wire: move the generic graph projection into its own crate
The wire types were in hive-host-sock, which is the host *socket* crate — so anything living there is core-shaped by construction, and the projection had quietly grown two core dependencies to match: it selected roots by matching NodeKind::Dag, and rendered payloads through free functions in hive-c0re that nothing obliged a second host to write. hive-jobq is the wrong home too. That crate is the scheduler — logic — and folding presentation in means every consumer of it carries a JSON vocabulary it may never serve. So: a new hive-jobq-wire. A host implements WireNode for its payload N and WireResource for its resource name R; GraphWire::wire_snapshot is blanket-implemented for Graph<N, R> when both hold, and for nothing else. A payload that has never said how it displays has no way onto the wire. wire_snapshot takes the roots to serve rather than reading Graph::roots itself. Nothing is ever removed from a Graph, so retention is a policy only the host can hold; hive-c0re passes visible_roots(), which is the existing MAX_HISTORY_DAGS bound selected structurally (a root is a node with no parent) instead of by node kind.
This commit is contained in:
parent
f357f98867
commit
7966d5eb66
14 changed files with 564 additions and 297 deletions
|
|
@ -283,6 +283,44 @@ pub enum NodeKind {
|
|||
},
|
||||
}
|
||||
|
||||
/// How a hive-c0re node describes itself to a generic graph viewer.
|
||||
///
|
||||
/// Every field in [`WireNode::data`] here used to be a named column on
|
||||
/// `NodeView`, meaningful for one node kind and `null` on all the others. As
|
||||
/// free-form data it costs the wire type nothing, and a generic consumer
|
||||
/// renders it without knowing what any of it means.
|
||||
impl hive_jobq_wire::WireNode for NodeKind {
|
||||
fn label(&self) -> String {
|
||||
self.as_str().to_owned()
|
||||
}
|
||||
|
||||
fn data(&self, id: hive_jobq_wire::WireId) -> serde_json::Value {
|
||||
let mut data = serde_json::Map::new();
|
||||
let agent = self.agent();
|
||||
if !agent.is_empty() {
|
||||
data.insert("agent".to_owned(), agent.into());
|
||||
}
|
||||
if let NodeKind::DeployWindow { approval_id, .. } = self {
|
||||
data.insert("approval_id".to_owned(), (*approval_id).into());
|
||||
}
|
||||
if let NodeKind::MetaLock { inputs, .. } = self
|
||||
&& !inputs.is_empty()
|
||||
{
|
||||
data.insert("inputs".to_owned(), inputs.clone().into());
|
||||
}
|
||||
// Not in the payload at all — the build log is keyed on node identity
|
||||
// in a side table, which is why `data` is handed the id.
|
||||
if let Some(log) = crate::build_logs::global().and_then(|h| h.id_for_node(id)) {
|
||||
data.insert("build_log_id".to_owned(), log.into());
|
||||
}
|
||||
if data.is_empty() {
|
||||
serde_json::Value::Null
|
||||
} else {
|
||||
serde_json::Value::Object(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NodeKind {
|
||||
/// Wire string for `NodeView.kind`.
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
|
|
|
|||
Loading…
Reference in a new issue