use serde_json::{Map, Value, json};
use super::board::{board_row, fleet_facts};
use super::queue::queue_row;
use super::rows::{conv_row, join_row, lineage_row, op_row, ws_row};
use super::{Reply, prepared_value};
use crate::registry::mailbox::{capture_value, invocation_value};
pub(super) const FOLLOW: &str = "follow";
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 {
tool_use,
tool,
ruling,
advanced,
} => json!({ "ok": true, "kind": "answered", "tool_use": tool_use, "tool": tool,
"verdict": ruling.word(), "advanced": 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,
} => {
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)
}
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) => rows_reply("attention", rows.iter().map(queue_row)),
Reply::Ops(rows) => rows_reply("ops", rows.iter().map(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(stream) => json!({ "ok": true, "kind": FOLLOW,
"stream": crate::git_tree::stream_wire::stream_value(stream) }),
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(gov) => governing(gov),
Reply::Inbox(entries) => crate::inboxview::wire::reply(entries),
Reply::Agent(view) => super::agent::reply(view),
Reply::Search(found) => super::search::reply(found),
Reply::Config { text } => json!({ "ok": true, "kind": "config", "text": text }),
Reply::Providers(rows) => rows_reply("providers", rows.iter().map(provider_row)),
Reply::Roles(rows) => rows_reply("roles", rows.iter().map(role_row)),
Reply::Lineages(rows) => rows_reply("lineages", rows.iter().map(lineage_row)),
Reply::Models(ids) => rows_reply("models", ids.iter().map(|id| json!(id))),
Reply::Clients(rows) => rows_reply("clients", rows.iter().map(client_row)),
}
}
pub fn refusal(error: &str) -> Value {
json!({ "ok": false, "error": error })
}
fn provider_row(row: &crate::config_edit::brazen::ProviderRowView) -> Value {
json!({ "name": row.name, "fact": row.fact, "blocked": row.blocked,
"effort": row.effort, "priority": row.priority })
}
fn outcome_reply(outcome: &crate::actions::verbs::Outcome) -> Value {
json!({
"ok": outcome.ok(), "kind": "outcome", "exit": outcome.exit,
"stdout": outcome.stdout, "stderr": outcome.stderr,
})
}
fn role_row(row: &crate::model_pick::RoleModel) -> Value {
json!({ "role": row.role, "provider": row.provider, "model": row.model,
"effort": row.effort, "priority": row.priority })
}
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 {
json!({ "client": row.client, "present": row.present,
"tools": crate::registry::tools::encode(&row.tools) })
}
fn delivered(delivery: &crate::fan::Delivery) -> Value {
let mut map = obj_reply("delivered");
map.insert("target".to_owned(), json!(delivery.target));
map.insert("base".to_owned(), json!(delivery.base));
if let Some(source) = &delivery.source {
map.insert("source".to_owned(), json!(source));
}
if let Some(commit) = &delivery.commit {
map.insert("commit".to_owned(), json!(commit));
}
Value::Object(map)
}
fn enrolled_reply(enrolled: &crate::registry::enroll::Enrolled) -> Value {
json!({
"ok": true, "kind": ENROLLED, "grade": enrolled.grade.word(),
"name": enrolled.name, "address": enrolled.address,
"ca": enrolled.ca, "cert": enrolled.cert, "key": enrolled.key,
})
}
fn governing(gov: &crate::config_edit::branch::GoverningConfig) -> Value {
json!({
"ok": true, "kind": "governing",
"oid": gov.oid, "short_oid": gov.short_oid,
"follows": gov.followed_lineage(),
"diverged_lineages": gov.diverged_lineages(),
"files": gov.files,
})
}
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
}