crates/nornir-warehouse-embed/examples/room_ask.rs
//! **Ask the code room what a pane asks — from a shell.**
//!
//! Every code-graph pane of the dwarves UI draws an answer of nornir's code room
//! (`CodeRoom.Answer`). This is the shell-side twin of those panes: the SAME question
//! over the SAME client, printed as the JSON the server sent — so the number on a
//! pane can be put beside the number the server gives.
//!
//! ```text
//! # through the dwarves HOST's proxy (the path the UI takes)
//! NORNIR_SERVER=http://127.0.0.1:7900 NORNIR_WORKSPACE=nordisk \
//! cargo run -p nornir-warehouse-embed --example room_ask -- ask Viz.CrateGraph '{"universe":true}'
//!
//! # straight at nornir (no forge in between)
//! NORNIR_SERVER=http://127.0.0.1:7878 … -- ask Knowledge.TestReach
//! ```
//!
//! Other verbs, for standing a workspace up without a UI:
//!
//! | verb | does |
//! |---|---|
//! | `ask <surface> [params-json]` | `CodeRoom.Answer`; prints the body |
//! | `state <surface> [params-json]` | the same, one line: state · room · evidence |
//! | `lens <ChangeRisk\|ZombieFeatures\|GenericCost\|SimilarCode\|WeakTests\|BenchGaps\|Villa> ` | the `Mimir.*` lens; prints the envelope |
//! | `register <name> <descriptor.toml>` | `Workspaces.Register` (monitored, eager), shipping the file's content |
//! | `fetch <name>` | `Workspaces.Fetch { force }` |
//! | `get <name>` | `Workspaces.Get`: the members and the snapshot |
//! | `scan <table> [limit]` | `Warehouse.Scan`: row count and the first rows |
//!
//! `NORNIR_SERVER_TOKEN` supplies the bearer (empty ⇒ open).
use nornir_warehouse_embed::{mimir, pb, room};
fn main() {
let endpoint =
std::env::var("NORNIR_SERVER").unwrap_or_else(|_| "http://127.0.0.1:7878".into());
let token = std::env::var("NORNIR_SERVER_TOKEN").unwrap_or_default();
let workspace = std::env::var("NORNIR_WORKSPACE").unwrap_or_default();
let args: Vec<String> = std::env::args().skip(1).collect();
let arg = |i: usize| args.get(i).map(String::as_str).unwrap_or("");
let params = |i: usize| -> serde_json::Value {
match arg(i) {
"" => serde_json::json!({}),
text => serde_json::from_str(text).unwrap_or_else(|e| {
eprintln!("room_ask: params are not JSON ({e}): {text}");
std::process::exit(2);
}),
}
};
let pretty = |v: &serde_json::Value| serde_json::to_string_pretty(v).unwrap_or_default();
match arg(0) {
"ask" => {
let a = room::ask(&endpoint, &token, &workspace, arg(1), ¶ms(2));
eprintln!(
"{} — state `{}`{}",
a.via,
a.state,
if a.is_measured() {
""
} else {
" (not a measurement)"
}
);
if a.body.is_null() {
println!("{}", pretty(&a.state_json()));
} else {
println!("{}", pretty(&a.body));
}
}
"state" => {
let a = room::ask(&endpoint, &token, &workspace, arg(1), ¶ms(2));
println!(
"{} · state {} · room [{}] · {}",
a.via,
a.state,
a.room_line(),
a.evidence_text()
);
}
"lens" => {
let res = match arg(1) {
"ChangeRisk" => mimir::change_risk(&endpoint, &token, &workspace, "", 0),
"ZombieFeatures" => mimir::zombie_features(&endpoint, &token, &workspace, "", 0),
"GenericCost" => mimir::generic_cost(&endpoint, &token, &workspace, "", 0, 0),
"SimilarCode" => mimir::similar_code(&endpoint, &token, &workspace, "", 0),
"WeakTests" => mimir::weak_tests(&endpoint, &token, &workspace, "", 0),
"BenchGaps" => mimir::bench_gaps(&endpoint, &token, &workspace, "", 0),
"Villa" => mimir::villa(&endpoint, &token, &workspace, "", 0),
other => {
eprintln!("room_ask: no lens `{other}` here");
std::process::exit(2);
}
};
match res {
Ok(v) => println!("{}", pretty(&v)),
Err(e) => {
eprintln!("Mimir.{}: {e}", arg(1));
std::process::exit(1);
}
}
}
"register" => {
let content = std::fs::read_to_string(arg(2)).unwrap_or_else(|e| {
eprintln!("room_ask: read descriptor {}: {e}", arg(2));
std::process::exit(2);
});
let (name, url) = (arg(1).to_string(), endpoint.clone());
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.expect("runtime");
let rec = rt.block_on(async move {
let mut client = pb::workspaces_client::WorkspacesClient::connect(url)
.await
.map_err(|e| e.to_string())?;
client
.register(pb::RegisterWorkspaceRequest {
name,
descriptor: String::new(),
mode: "monitored".into(),
poll: String::new(),
lazy: false,
descriptor_content: content,
})
.await
.map(|r| r.into_inner())
.map_err(|s| format!("{} ({:?})", s.message(), s.code()))
});
match rec {
Ok(r) => println!(
"registered `{}` mode {} snapshot `{}` members {:?}",
r.name,
r.mode,
r.current_snapshot,
r.members
.iter()
.map(|m| format!("{}@{} [{}]", m.name, m.last_seen_sha, m.sync_state))
.collect::<Vec<_>>()
),
Err(e) => {
eprintln!("Workspaces.Register: {e}");
std::process::exit(1);
}
}
}
"fetch" => {
match nornir_warehouse_embed::populate_warehouse(&endpoint, &token, arg(1), true) {
Ok(o) => println!(
"fetched {} member(s), changed {:?}, errors {:?}, snapshot `{}`",
o.fetched, o.changed, o.errors, o.snapshot
),
Err(e) => {
eprintln!("Workspaces.Fetch: {e:#}");
std::process::exit(1);
}
}
}
"get" => match nornir_warehouse_embed::get_warehouse(&endpoint, &token, arg(1)) {
Ok(w) => println!("{}", serde_json::to_string_pretty(&w).unwrap_or_default()),
Err(e) => {
eprintln!("Workspaces.Get: {e:#}");
std::process::exit(1);
}
},
"scan" => {
let limit = arg(2).parse().unwrap_or(5);
match nornir_warehouse_embed::scan_warehouse_table(
&endpoint,
&token,
&workspace,
arg(1),
limit,
) {
Ok(s) => {
println!("{}: {} row(s) {:?}", arg(1), s.rows.len(), s.columns);
for r in s.rows.iter().take(5) {
println!(" {r:?}");
}
}
Err(e) => {
eprintln!("Warehouse.Scan {}: {e}", arg(1));
std::process::exit(1);
}
}
}
_ => {
eprintln!("usage: room_ask ask|state <surface> [params-json] | lens <Name> | register <name> <descriptor.toml> | fetch <name> | get <name> | scan <table> [limit]");
std::process::exit(2);
}
}
}