use serde_json::{Map, Value, json};
use super::{Doc, StepDetail, StepSummary, StepsView, ToolIo, Wound};
use crate::budgets::BudgetSpend;
use crate::git_tree::Framing;
pub(crate) mod decode;
pub(crate) fn steps(view: &StepsView) -> Value {
json!({
"ok": true, "kind": "steps",
"rows": Value::Array(view.steps.iter().map(step_row).collect()),
})
}
fn step_row(step: &StepSummary) -> Value {
let mut map = Map::new();
map.insert("seq".to_owned(), json!(step.seq));
map.insert("framing".to_owned(), json!(framing_token(step.framing)));
map.insert("attempts".to_owned(), json!(step.attempts));
map.insert("tokens".to_owned(), spend_value(&step.tokens));
for (key, value) in [
("commit", step.commit.as_ref()),
("started_at", step.started_at.as_ref()),
("ended_at", step.ended_at.as_ref()),
] {
if let Some(value) = value {
map.insert(key.to_owned(), json!(value));
}
}
map.insert("auth_failed".to_owned(), json!(step.auth_failed.offered()));
if let Some(row) = step.auth_failed.row() {
map.insert("auth_row".to_owned(), json!(row));
}
map.insert("wounded".to_owned(), json!(step.wound.wounded()));
if let Wound::Spoke(reason) = &step.wound {
map.insert("wound_reason".to_owned(), json!(reason));
}
Value::Object(map)
}
fn framing_token(framing: Framing) -> &'static str {
match framing {
Framing::Complete => "complete",
Framing::Failed => "failed",
Framing::Killed => "killed",
}
}
pub(crate) fn spend_value(spend: &BudgetSpend) -> Value {
json!({
"input": spend.input_tokens, "output": spend.output_tokens,
"cache_read": spend.cache_read_tokens, "cache_write": spend.cache_write_tokens,
"total": spend.total_tokens(),
})
}
pub(crate) fn detail(detail: &StepDetail) -> Value {
json!({
"ok": true, "kind": "step", "seq": detail.seq,
"meta": doc_value(&detail.meta),
"request": doc_value(&detail.request),
"staging": doc_value(&detail.staging),
"response": Value::Array(detail.response.iter().map(doc_value).collect()),
"tools": Value::Array(detail.tools.iter().map(tool_value).collect()),
})
}
fn doc_value(doc: &Doc) -> Value {
match doc {
Doc::Json { value, raw } => json!({
"kind": "json", "value": value,
"raw": String::from_utf8_lossy(raw),
}),
Doc::Absent => json!({ "kind": "absent" }),
Doc::Unparsed(raw) => json!({
"kind": "unparsed", "note": super::UNPARSED,
"raw": String::from_utf8_lossy(raw),
}),
}
}
fn tool_value(tool: &ToolIo) -> Value {
json!({
"tool_id": tool.tool_id, "is_error": tool.is_error,
"input": doc_value(&tool.input), "output": doc_value(&tool.output),
})
}