use serde_json::{Value, json};
use crate::invocation::{Capture, Invocation, capture_of, capture_value, invocation_of};
use crate::json::{bool_of, str_of};
use crate::tools::{self, Tool};
pub fn advertise(set: &[Tool]) -> Value {
json!({ "op": "advertise", "tools": tools::encode(set) })
}
pub fn invocations() -> Value {
json!({ "op": "invocations" })
}
pub fn complete(invocation: &str, capture: &Capture) -> Value {
json!({ "op": "complete", "invocation": invocation,
"capture": capture_value(capture) })
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Reply {
Advertised { wrote: bool },
Invocations(Vec<Invocation>),
Routed {
invocation: String,
capture: Option<Capture>,
},
}
pub fn decode(v: &Value) -> Result<Result<Reply, String>, String> {
let o = v.as_object().ok_or("reply: not a JSON object")?;
let Some(kind) = o.get("kind") else {
return refusal_of(o).map(Err);
};
let kind = kind.as_str().ok_or("reply: non-string field \"kind\"")?;
match kind {
"advertised" => bool_of(o, "wrote")
.map(|wrote| Reply::Advertised { wrote })
.map_err(|e| format!("reply: {e}")),
"invocations" => rows(o).map(Reply::Invocations),
"routed" => routed(o),
other => Err(format!("reply: unusable kind {other:?}")),
}
.map(Ok)
}
fn refusal_of(o: &serde_json::Map<String, Value>) -> Result<String, String> {
match o.get("ok") {
Some(Value::Bool(false)) => str_of(o, "error").map_err(|e| format!("reply: {e}")),
_ => Err("reply: an answer with no kind".to_owned()),
}
}
fn rows(o: &serde_json::Map<String, Value>) -> Result<Vec<Invocation>, String> {
o.get("rows")
.and_then(Value::as_array)
.ok_or("reply: invocations with no rows")?
.iter()
.map(invocation_of)
.collect()
}
fn routed(o: &serde_json::Map<String, Value>) -> Result<Reply, String> {
Ok(Reply::Routed {
invocation: str_of(o, "invocation").map_err(|e| format!("reply: {e}"))?,
capture: match o.get("capture") {
None | Some(Value::Null) => None,
Some(held) => Some(capture_of(held)?),
},
})
}
#[cfg(test)]
mod tests;