use serde_json::Value;
use super::{
ERROR, KIND, OK, Outcome, Read, Reply, convs, enrolled, fields, files, help, login, providers,
queue, roles, roster, search, start, steps, stream, transcript,
};
const OUTCOME: &str = "outcome";
const NUDGED: &str = "nudged";
const FLAGGED: &str = "flagged";
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)?),
queue::KIND => Reply::Attention(fields::rows(obj, queue::row)?),
FLAGGED => Reply::Flagged,
transcript::KIND => Reply::Transcript(transcript::transcript(obj)?),
steps::KIND => Reply::Steps(steps::steps(obj)?),
files::KIND => Reply::Files(files::files(obj)?),
help::KIND => Reply::Help(fields::rows(obj, help::row)?),
search::KIND => Reply::Found(search::found(obj)?),
providers::KIND => Reply::Providers(fields::rows(obj, providers::row)?),
providers::MODELS => Reply::Models(fields::rows(obj, providers::offered)?),
login::KIND => Reply::Login(login::signin(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)
}