use super::{Context, args};
use crate::boundary::{Action, Gesture};
use crate::monitor::Verb;
use crate::start::{BallSpec, Payload};
use std::path::PathBuf;
pub(super) fn seen(tail: &str, ctx: &Context, verb: &str) -> Result<Gesture, String> {
args::none(tail, verb)?;
Ok(act(Action::MarkSeen {
workspace: args::workspace(ctx, verb)?,
agent: args::agent(ctx, verb)?,
}))
}
pub(super) fn retarget(tail: &str, ctx: &Context, verb: &str) -> Result<Gesture, String> {
args::none(tail, verb)?;
Ok(act(Action::Retarget {
workspace: args::workspace(ctx, verb)?,
agent: args::agent(ctx, verb)?,
}))
}
pub(super) fn prompt(tail: &str, ctx: &Context, verb: &str) -> Result<Gesture, String> {
Ok(act(Action::Prompt {
prepared: ctx.prepared.clone().ok_or_else(|| {
format!("/{verb}: nothing is prepared — /prepare first, then say the goal")
})?,
goal: args::required(tail, verb, "the goal")?,
seed: None,
}))
}
pub(super) fn answer(tail: &str, ctx: &Context, verb: &str) -> Result<Gesture, String> {
let word = args::required(tail, verb, "pass, hold or refuse")?;
let ruling = crate::control::judge::Ruling::of(word.trim())
.ok_or_else(|| format!("/{verb}: unknown verdict {word:?}; usage: {ANSWER_USAGE}"))?;
Ok(act(Action::AnswerHold {
workspace: args::workspace(ctx, verb)?,
agent: args::agent(ctx, verb)?,
ruling,
}))
}
pub const ANSWER_USAGE: &str = "/answer pass | hold | refuse";
pub(super) fn floor(verb: &str, tail: &str, ctx: &Context) -> Result<Gesture, String> {
args::none(tail, verb)?;
Ok(act(Action::Floor {
workspace: args::workspace(ctx, verb)?,
agent: args::agent(ctx, verb)?,
raised: verb == "revoke",
}))
}
const OPS_DEFAULT: usize = 50;
fn act(action: Action) -> Gesture {
Gesture::Act(action)
}
pub(super) fn children(tail: &str) -> Result<bool, String> {
match tail.trim() {
"" => Ok(false),
"children" => Ok(true),
other => Err(format!(
"/stop: unexpected {other:?}; usage: /stop [children]"
)),
}
}
pub(super) fn id(tail: &str, ctx: &Context, verb: &str) -> Result<String, String> {
args::ball_id(args::optional_word(tail, verb)?, ctx, verb)
}
pub(super) fn moved(tail: &str, ctx: &Context, verb: &str) -> Result<Gesture, String> {
let (ball, to) = match tail.split_whitespace().collect::<Vec<_>>().as_slice() {
[to] => (args::ball_id(None, ctx, verb)?, (*to).to_owned()),
[id, to] => ((*id).to_owned(), (*to).to_owned()),
_ => return Err(format!("/{verb}: usage: /move [id] <to>")),
};
Ok(act(Action::Move {
project: args::project(ctx, verb)?,
id: ball,
from: args::name(ctx, verb)?,
to,
}))
}
pub(super) fn create(tail: &str, ctx: &Context, verb: &str) -> Result<Gesture, String> {
let (title, flags) = args::split_flags(tail);
args::only(&flags, &["body"], verb)?;
Ok(act(Action::Create {
project: args::project(ctx, verb)?,
title: args::required(&title, verb, "a title")?,
name: args::name(ctx, verb)?,
body: args::flag(&flags, "body", verb)?,
}))
}
pub(super) fn update(tail: &str, ctx: &Context, verb: &str) -> Result<Gesture, String> {
let (positional, flags) = args::split_flags(tail);
args::only(&flags, &["title", "body", "note"], verb)?;
let (title, body, note) = (
args::flag(&flags, "title", verb)?,
args::flag(&flags, "body", verb)?,
args::flag(&flags, "note", verb)?,
);
if title.is_none() && body.is_none() && note.is_none() {
return Err(format!(
"/{verb}: nothing to change; usage: /update [id] [--title T] [--body B] [--note N]"
));
}
Ok(act(Action::Update {
project: args::project(ctx, verb)?,
id: id(&positional, ctx, verb)?,
name: args::name(ctx, verb)?,
title,
body,
note,
}))
}
pub(super) fn payload(tail: &str, ctx: &Context, verb: &str) -> Result<Payload, String> {
let (rung, rest) = args::first_word(tail);
match rung.as_str() {
"" => Ok(Payload::Bare),
"dir" => Ok(Payload::Path {
dir: PathBuf::from(args::required(&rest, verb, "a work directory")?),
}),
"ball" => Ok(Payload::Ball {
project: args::project(ctx, verb)?,
ball: spec(&rest, ctx, verb)?,
}),
other => Err(format!(
"/{verb}: unknown rung {other:?}; usage: /prepare | /prepare dir <path> | /prepare ball [--new <title…>]"
)),
}
}
fn spec(rest: &str, ctx: &Context, verb: &str) -> Result<BallSpec, String> {
let (positional, flags) = args::split_flags(rest);
args::only(&flags, &["new", "body"], verb)?;
args::none(&positional, verb)?;
match args::flag(&flags, "new", verb)? {
Some(title) => Ok(BallSpec::New {
title,
body: args::flag(&flags, "body", verb)?.unwrap_or_default(),
}),
None => ctx.ball.clone().ok_or_else(|| {
format!("/{verb}: no ball is selected — select one, or say --new <title…>")
}),
}
}
pub(super) fn work_file(
tail: &str,
verb: &str,
) -> Result<Option<crate::workdiff::WorkFile>, String> {
match tail.split_whitespace().collect::<Vec<_>>().as_slice() {
[] => Ok(None),
[ball, path] => Ok(Some(crate::workdiff::WorkFile {
ball: (*ball).to_owned(),
handle: None,
path: (*path).to_owned(),
})),
[ball, handle, path] => Ok(Some(crate::workdiff::WorkFile {
ball: (*ball).to_owned(),
handle: Some((*handle).to_owned()),
path: (*path).to_owned(),
})),
_ => Err(format!(
"/{verb}: usage: /work-diff [<ball> [<handle>] <path>]"
)),
}
}
pub(super) fn max(tail: &str) -> Result<usize, String> {
match args::optional_word(tail, "ops")? {
None => Ok(OPS_DEFAULT),
Some(word) => word
.parse()
.map_err(|_| format!("/ops: {word:?} is not a row count")),
}
}
pub(super) fn monitor(verb: &str, tail: &str, ctx: &Context) -> Result<Gesture, String> {
let workspace = args::workspace(ctx, verb)?;
let gesture = match verb {
"arm" => Verb::Arm {
workspace,
model: args::required(tail, verb, "the model to check with")?,
},
"disarm" => {
args::none(tail, verb)?;
Verb::Disarm { workspace }
}
_ => Verb::Flag {
workspace,
agent: args::agent(ctx, verb)?,
reason: args::required(tail, verb, "why")?,
},
};
Ok(act(Action::Monitor(gesture)))
}
pub(super) fn fleet(verb: &str, tail: &str, ctx: &Context) -> Result<Gesture, String> {
let workspace = args::workspace(ctx, verb)?;
let gesture = if verb == crate::boundary::codec::FLEET_ARM {
let word = args::required(tail, verb, "the cap — how many balls at once")?;
crate::fleet::Verb::Arm {
workspace,
project: args::project(ctx, verb)?,
cap: word
.parse()
.map_err(|_| format!("/{verb}: {word:?} is not a cap"))?,
}
} else {
args::none(tail, verb)?;
crate::fleet::Verb::Disarm { workspace }
};
Ok(act(Action::Fleet(gesture)))
}