use serde_json::{Map, Value, json};
mod bodies;
use super::board::{board_row, fleet_facts};
use super::rows::{conv_row, join_row, lineage_row, provider_row, role_row, ws_row};
use super::{Reply, prepared_value};
use crate::registry::mailbox::{capture_value, invocation_value};
use bodies::{delivered, enrolled_reply, governing, outcome_reply};
pub(super) const FOLLOW: &str = "follow";
pub(super) const LOGIN: &str = "login";
pub(super) const ENROLLED: &str = "enrolled";
pub fn encode(reply: &Reply) -> Value {
match reply {
Reply::Outcome(outcome) => outcome_reply(outcome),
Reply::Prepared(prepared) => {
json!({ "ok": true, "kind": "prepared", "prepared": prepared_value(prepared) })
}
Reply::Fanned(candidates) => rows_reply("fanned", candidates.iter().map(prepared_value)),
Reply::Retired { discarded } => {
json!({ "ok": true, "kind": "retired", "discarded": discarded })
}
Reply::Delivered(delivery) => delivered(delivery),
Reply::Started { conversation } => {
json!({ "ok": true, "kind": "started", "conversation": conversation })
}
Reply::Deleted => json!({ "ok": true, "kind": "deleted" }),
Reply::Armed { armed } => json!({ "ok": true, "kind": "armed", "armed": armed }),
Reply::Flagged => json!({ "ok": true, "kind": "flagged" }),
Reply::Answered(answered) => json!({ "ok": true, "kind": "answered",
"tool_use": answered.tool_use, "tool": answered.tool,
"verdict": answered.answer.ruling.word(),
"scope": answered.answer.scope.word(),
"advanced": answered.advanced }),
Reply::Floored { standing } => {
json!({ "ok": true, "kind": "floored", "standing": standing })
}
Reply::Nudged => json!({ "ok": true, "kind": "nudged" }),
Reply::Acked => json!({ "ok": true, "kind": "acked" }),
Reply::TrailCleared => json!({ "ok": true, "kind": "trail-cleared" }),
Reply::Applied => json!({ "ok": true, "kind": "applied" }),
Reply::Advertised { wrote } => json!({ "ok": true, "kind": "advertised", "wrote": wrote }),
Reply::Enrolled(enrolled) => enrolled_reply(enrolled),
Reply::Routed {
invocation,
capture,
} => routed_reply(invocation, capture.as_ref()),
Reply::Invocations(rows) => rows_reply("invocations", rows.iter().map(invocation_value)),
Reply::Marks { branch } => json!({ "ok": true, "kind": "marks", "branch": branch }),
Reply::Workspaces(view) => {
let mut map = rows_map("workspaces", view.rows.iter().map(ws_row));
for (key, note) in [("stale", &view.stale), ("growth", &view.growth)] {
if let Some(note) = note {
map.insert(key.to_owned(), json!(note));
}
}
Value::Object(map)
}
Reply::WorkspaceBalls(rows) => {
rows_reply("workspace-balls", rows.iter().map(super::balls::bound_ball))
}
Reply::Conversations(rows) => rows_reply("conversations", rows.iter().map(conv_row)),
Reply::Balls(rows) => rows_reply("balls", rows.iter().map(join_row)),
Reply::Board(board) => {
let mut map = rows_map("board", board.rows.iter().map(board_row));
if !board.fleet.is_empty() {
map.insert(
"fleet".to_owned(),
json!(board.fleet.iter().map(fleet_facts).collect::<Vec<Value>>()),
);
}
Value::Object(map)
}
Reply::Attention(rows) => super::queue::attention(rows),
Reply::Acknowledged(ack) => super::queue::acknowledged(ack),
Reply::Ops(rows) => rows_reply("ops", rows.iter().map(super::op_row::op_row)),
Reply::Help(rows) => rows_reply("help", rows.iter().map(help_row)),
Reply::WorkDiff { attempts, patch } => {
crate::workdiff::wire::reply(attempts, patch.as_ref())
}
Reply::Science(rows) => crate::science::wire::reply(rows),
Reply::Transcript(transcript) => crate::transcript::wire::reply(transcript),
Reply::Follow(frame) => super::follow_frame::reply(frame),
Reply::Login(view) => bodies::login_reply(view),
Reply::Steps(view) => crate::steps_view::wire::steps(view),
Reply::Step(detail) => crate::steps_view::wire::detail(detail),
Reply::Files {
view,
preview,
working_dir,
} => crate::files_view::wire::reply(view, preview.as_ref(), working_dir.as_deref()),
Reply::Rail(rail) => crate::rail::wire::reply(rail),
Reply::Governing {
config,
workflow_mark,
} => governing(config, workflow_mark.as_ref()),
Reply::Inbox(entries) => crate::inboxview::wire::reply(entries),
Reply::Agent(view) => super::agent::reply(view),
Reply::Search(found) => super::search::reply(found),
Reply::Config(answer) => config_answer(answer),
Reply::Clients(rows) => rows_reply("clients", rows.iter().map(client_row)),
Reply::Doctor(report) => super::doctor::reply(report),
Reply::Prices(view) => super::prices::reply(view),
}
}
fn config_answer(answer: &super::ConfigAnswer) -> Value {
use super::ConfigAnswer as A;
match answer {
A::File(view) => super::config_view::config(view),
A::Providers(rows) => rows_reply("providers", rows.iter().map(provider_row)),
A::Roles(rows) => rows_reply("roles", rows.iter().map(role_row)),
A::Lineages(rows) => rows_reply("lineages", rows.iter().map(lineage_row)),
A::Models(ids) => rows_reply("models", ids.iter().map(|id| json!(id))),
A::Proposals(view) => crate::proposals::wire::reply(view),
}
}
pub fn refusal(error: &str) -> Value {
json!({ "ok": false, "error": error })
}
fn help_row(row: &crate::boundary::help::HelpRow) -> Value {
json!({ "verb": row.verb, "usage": row.usage,
"summary": row.summary, "detail": row.detail,
"surface": row.surface.word() })
}
fn client_row(row: &crate::registry::roster::ClientRow) -> Value {
let mut map = Map::new();
map.insert("client".to_owned(), json!(row.client));
map.insert("present".to_owned(), json!(row.present));
map.insert(
"tools".to_owned(),
crate::registry::tools::encode(&row.tools),
);
if let Some(last_seen) = row.last_seen {
map.insert("last_seen".to_owned(), json!(last_seen));
}
Value::Object(map)
}
fn routed_reply(invocation: &str, capture: Option<&crate::registry::mailbox::Capture>) -> Value {
let mut map = obj_reply("routed");
map.insert("invocation".to_owned(), json!(invocation));
if let Some(capture) = capture {
map.insert("capture".to_owned(), capture_value(capture));
}
Value::Object(map)
}
fn obj_reply(kind: &str) -> Map<String, Value> {
let mut map = Map::new();
map.insert("ok".to_owned(), json!(true));
map.insert("kind".to_owned(), json!(kind));
map
}
fn rows_reply(kind: &str, rows: impl Iterator<Item = Value>) -> Value {
Value::Object(rows_map(kind, rows))
}
fn rows_map(kind: &str, rows: impl Iterator<Item = Value>) -> Map<String, Value> {
let mut map = obj_reply(kind);
map.insert("rows".to_owned(), json!(rows.collect::<Vec<Value>>()));
map
}