mod text;
mod verdict;
mod decided;
mod answer;
mod enroll;
mod start;
pub use decided::{Asking, Decided};
pub use enroll::{AT, INTO};
pub use text::{usage, version};
pub use verdict::{Stream, Verdict};
use crate::render::Form;
pub const JSON: &str = "--json";
pub fn run(args: Vec<String>) -> Decided {
let (form, args) = form(args);
let words: Vec<&str> = args.iter().map(String::as_str).collect();
match words.as_slice() {
["entries"] => Decided::Entries,
["ask", envelope] => ask(envelope, form),
["help" | "--help" | "-h"] => Decided::Say(Verdict::ok(usage())),
["help", verb] => Decided::Say(match crate::verbs::help::page(verb) {
Ok(page) => Verdict::ok(page),
Err(refusal) => Verdict::refused(refusal),
}),
["--version" | "-V"] => Decided::Say(Verdict::ok(version())),
["start", address, goal, tail @ ..] => start::start(address, goal, tail, form),
["follow", workspace, agent] => Decided::Follow {
workspace: (*workspace).to_owned(),
agent: (*agent).to_owned(),
form,
},
["model", workspace, role, provider, model] => Decided::Model {
workspace: (*workspace).to_owned(),
role: (*role).to_owned(),
provider: (*provider).to_owned(),
model: (*model).to_owned(),
form,
},
["ops"] => Decided::Fanned(crate::verbs::ops(crate::verbs::DEPTH), form),
["ops", depth] => trail(depth, form),
["answer", workspace, agent, verdict] => {
answer::answer(workspace, agent, verdict, None, form)
}
["answer", workspace, agent, verdict, scope] => {
answer::answer(workspace, agent, verdict, Some(scope), form)
}
["enroll", workspace, name, grade, tail @ ..] => {
enroll::enroll(workspace, name, grade, tail, form)
}
[] => Decided::Window,
[word, arguments @ ..] => typed(word, arguments, form),
}
}
fn trail(depth: &str, form: Form) -> Decided {
let Ok(max) = depth.parse::<u64>() else {
return Decided::Say(Verdict::refused(misplaced(
format!(
"`lernie ops` takes a depth in rows and got {depth:?} — usage: {}",
crate::verbs::doors::OPS.usage()
),
&[depth],
)));
};
Decided::Fanned(crate::verbs::ops(max), form)
}
fn form(args: Vec<String>) -> (Form, Vec<String>) {
match args.split_first() {
Some((first, rest)) if first == JSON => (Form::Json, rest.to_vec()),
_ => (Form::Rendered, args),
}
}
fn misplaced(refusal: String, arguments: &[&str]) -> String {
if arguments.contains(&JSON) {
return format!("{refusal} — `{JSON}` goes BEFORE the word: `lernie {JSON} …`");
}
refusal
}
fn ask(text: &str, form: Form) -> Decided {
match crate::envelope::parse(text) {
Ok(envelope) => Decided::Ask(envelope, form),
Err(refusal) => Decided::Say(Verdict::refused(refusal)),
}
}
fn typed(word: &str, arguments: &[&str], form: Form) -> Decided {
if let Some(verb) = crate::verbs::find(word) {
return match verb.envelope(arguments.iter().map(|a| (*a).to_owned()).collect()) {
Ok(envelope) if verb.addresses_a_workspace() => Decided::Ask(envelope, form),
Ok(envelope) => Decided::Fanned(envelope, form),
Err(refusal) => Decided::Say(Verdict::refused(misplaced(refusal, arguments))),
};
}
if let Some(door) = crate::verbs::doors::find(word) {
return Decided::Say(Verdict::refused(misplaced(
door.refused(arguments.len()),
arguments,
)));
}
Decided::Say(Verdict::refused(format!(
"unrecognised argument: {}",
std::iter::once(word)
.chain(arguments.iter().copied())
.collect::<Vec<&str>>()
.join(" ")
)))
}
#[cfg(test)]
mod tests;