use crate::boundary::Gesture;
use crate::boundary::codec::{self, prepared_from_value};
use crate::boundary::line::{self, Context};
use serde_json::Value;
struct Invocation {
context: Context,
payload: String,
}
pub(crate) fn read_gesture(verb: &str, args: &[String]) -> Result<(Gesture, Value), String> {
read(args)
.and_then(|invocation| envelope(&invocation))
.map_err(|why| format!("{why}; {}", usage(verb)))
}
fn envelope(invocation: &Invocation) -> Result<(Gesture, Value), String> {
if line::is_command(&invocation.payload) {
let gesture = line::parse(&invocation.payload, &invocation.context)?;
let value = codec::encode(&gesture);
return Ok((gesture, value));
}
let value: Value =
serde_json::from_str(&invocation.payload).map_err(|e| format!("not JSON: {e}"))?;
Ok((codec::decode(&value)?, value))
}
fn read(args: &[String]) -> Result<Invocation, String> {
let mut context = Context::default();
let mut payload: Option<String> = None;
let mut asked_help = false;
let mut rest = args.iter();
while let Some(arg) = rest.next() {
if arg == "--help" || arg == "-h" {
asked_help = true;
continue;
}
let Some(flag) = arg.strip_prefix("--") else {
if payload.replace(arg.clone()).is_some() {
return Err(format!("expected one gesture, got another: {arg:?}"));
}
continue;
};
let value = rest
.next()
.ok_or_else(|| format!("--{flag} needs a value"))?
.clone();
match flag {
"ws" => context.workspace = Some(value),
"agent" => context.agent = Some(value),
"project" => context.project = Some(value),
"as" => context.name = Some(value),
"prepared" => {
let v = serde_json::from_str(&value)
.map_err(|e| format!("--prepared: not JSON: {e}"))?;
context.prepared = Some(prepared_from_value(&v)?);
}
other => return Err(format!("unknown flag --{other}")),
}
}
if asked_help {
let about = payload.unwrap_or_default();
let about = about.trim().trim_start_matches('/');
return Ok(Invocation {
context,
payload: format!("/help {about}").trim_end().to_owned(),
});
}
let payload = payload.ok_or_else(|| "nothing to do".to_owned())?;
Ok(Invocation { context, payload })
}
pub(crate) fn usage(verb: &str) -> String {
format!(
"usage: yog {verb} [--ws NAME] [--agent ID|NAME] [--project NAME] [--as NAME] \
[--prepared JSON] '<json>' | '/line'\n yog {verb} --help [command] — every \
gesture, or one command's page"
)
}