use serde_json::Value;
use super::{
ERROR, KIND, OK, Outcome, Read, Reply, convs, enrolled, fields, roles, roster, start, stream,
transcript,
};
const OUTCOME: &str = "outcome";
const NUDGED: &str = "nudged";
pub fn read(frame: &Value) -> Read {
match decode(frame) {
Ok(read) => read,
Err(why) => Read::Unreadable(why),
}
}
fn decode(frame: &Value) -> Result<Read, String> {
let obj = frame
.as_object()
.ok_or("reply: not a JSON object".to_owned())?;
let Some(kind) = obj.get(KIND) else {
return refusal(fields::flag(obj, OK)?, fields::text(obj, ERROR));
};
let kind = kind
.as_str()
.ok_or_else(|| format!("reply: non-string field {KIND:?}"))?;
let reply = match kind {
OUTCOME => Reply::Outcome(Outcome {
exit: fields::exit(obj)?,
stdout: fields::text(obj, "stdout")?,
stderr: fields::text(obj, "stderr")?,
}),
NUDGED => Reply::Nudged,
roster::KIND => Reply::Workspaces(roster::workspaces(obj)?),
convs::KIND => Reply::Conversations(fields::rows(obj, convs::row)?),
roles::KIND => Reply::Roles(fields::rows(obj, roles::row)?),
transcript::KIND => Reply::Transcript(transcript::transcript(obj)?),
stream::KIND => Reply::Follow(stream::follow(obj)?),
enrolled::KIND => Reply::Enrolled(enrolled::enrolled(obj)?),
start::PREPARED => Reply::Prepared(start::prepared(obj)?),
start::STARTED => Reply::Started {
conversation: start::started(obj)?,
},
other => {
return Err(format!(
"reply: this seat cannot paint a {other:?} answer — the engine \
speaks a kind this build does not; upgrade the seat"
));
}
};
Ok(Read::Answer(reply))
}
fn refusal(ok: bool, error: Result<String, String>) -> Result<Read, String> {
if ok {
return Err(format!("reply: an answer with no {KIND:?}"));
}
error.map(Read::Refusal)
}