use serde_json::Value;
pub(crate) const INPUT_FIELD: &str = "input";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Mode {
Command,
Agent,
}
impl Mode {
pub(crate) const fn as_str(self) -> &'static str {
match self {
Self::Command => "command",
Self::Agent => "agent",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct Spawn {
mode: Mode,
body: String,
}
impl Spawn {
pub(crate) const fn mode(&self) -> Mode {
self.mode
}
pub(crate) fn body(&self) -> &str {
&self.body
}
}
pub(crate) fn parse(input: &Value) -> Result<Spawn, String> {
let raw = input
.get(INPUT_FIELD)
.and_then(Value::as_str)
.ok_or_else(|| {
format!(
"spawn takes one string field, `{INPUT_FIELD}`: a command to run when it starts \
with `!`, otherwise a task to delegate"
)
})?;
read(raw)
}
fn read(raw: &str) -> Result<Spawn, String> {
let trimmed = raw.trim();
let Some(rest) = trimmed.strip_prefix('!') else {
return delegation(trimmed);
};
if rest.starts_with('!') {
return delegation(rest);
}
command(rest)
}
fn command(body: &str) -> Result<Spawn, String> {
let body = body.trim();
if body.is_empty() {
return Err(
"`!` on its own runs nothing; write the command after it, as `!cargo test`".to_string(),
);
}
Ok(Spawn {
mode: Mode::Command,
body: body.to_string(),
})
}
fn delegation(body: &str) -> Result<Spawn, String> {
let body = body.trim();
if body.is_empty() {
return Err(
"spawn needs something to do: a command after `!`, or a task to delegate".to_string(),
);
}
Ok(Spawn {
mode: Mode::Agent,
body: body.to_string(),
})
}