use super::{Context, args};
use crate::boundary::config::ConfigFile;
use crate::boundary::{Action, Gesture, Query};
use crate::config_edit::branch::edit::EditOrigin;
use crate::world::marks;
pub(super) fn config(tail: &str, ctx: &Context, verb: &str) -> Result<Gesture, String> {
let (target, rest) = args::first_word(tail);
let (file, rest) = match target.as_str() {
"brazen" => (
ConfigFile::Brazen {
workspace: args::workspace(ctx, verb)?,
},
rest,
),
"models" => (ConfigFile::LernieModels, rest),
"cadence" => (ConfigFile::Cadence, rest),
"workflow" => {
let (name, rest) = word(&rest, verb, "a workflow name")?;
(ConfigFile::LernieWorkflow { name }, rest)
}
"branch" => lineage(&rest, ctx, verb, None)?,
"orphan" => lineage(&rest, ctx, verb, Some(EditOrigin::Orphan))?,
"fork" => {
let (lineage_name, rest) = word(&rest, verb, "the lineage name")?;
let (source, rest) = word(&rest, verb, "the lineage to fork from")?;
let (path, rest) = word(&rest, verb, "the file's path in the lineage")?;
(
branch(ctx, verb, lineage_name, EditOrigin::Fork { source }, path)?,
rest,
)
}
other => Err(format!(
"/{verb}: unknown destination {other:?}; usage: {USAGE}"
))?,
};
if rest.trim().is_empty() {
return Ok(Gesture::Ask(Query::ReadConfig { file }));
}
Ok(Gesture::Act(Action::ApplyConfig {
file,
text: args::required(&rest, verb, "the file's text")?,
}))
}
pub(super) fn marks(tail: &str, ctx: &Context, verb: &str) -> Result<Gesture, String> {
let (branch, rest) = args::first_word(tail);
if branch.is_empty() {
return Ok(Gesture::Ask(Query::Marks {
workspace: args::workspace(ctx, verb)?,
}));
}
args::none(&rest, verb)?;
if !marks::lawful(&branch) {
return Err(format!("/{verb}: {}", marks::REFUSAL));
}
Ok(Gesture::Act(Action::SetMarks {
workspace: args::workspace(ctx, verb)?,
branch,
}))
}
pub(super) fn model(tail: &str, ctx: &Context, verb: &str) -> Result<Gesture, String> {
let [role, provider, model] = *tail.split_whitespace().collect::<Vec<_>>() else {
return Err(format!(
"/{verb}: usage: /model <role> <provider> <model-id>"
));
};
Ok(Gesture::Act(Action::PickModel {
workspace: args::workspace(ctx, verb)?,
role: role.to_owned(),
provider: provider.to_owned(),
model: model.to_owned(),
}))
}
pub(super) fn target_words(file: &ConfigFile) -> String {
match file {
ConfigFile::Brazen { .. } => "brazen".to_owned(),
ConfigFile::LernieModels => "models".to_owned(),
ConfigFile::Cadence => "cadence".to_owned(),
ConfigFile::LernieWorkflow { name } => format!("workflow {name}"),
ConfigFile::Branch {
lineage,
origin,
path,
..
} => match origin {
EditOrigin::Advance => format!("branch {lineage} {path}"),
EditOrigin::Fork { source } => format!("fork {lineage} {source} {path}"),
EditOrigin::Orphan => format!("orphan {lineage} {path}"),
},
}
}
pub const USAGE: &str = "/config brazen|models|cadence <text…> | /config workflow <name> <text…> | \
/config branch|orphan <lineage> <path> <text…> | /config fork <lineage> <source> <path> <text…>";
fn lineage(
rest: &str,
ctx: &Context,
verb: &str,
origin: Option<EditOrigin>,
) -> Result<(ConfigFile, String), String> {
let (name, rest) = word(rest, verb, "the lineage name")?;
let (path, rest) = word(&rest, verb, "the file's path in the lineage")?;
let origin = origin.unwrap_or(EditOrigin::Advance);
Ok((branch(ctx, verb, name, origin, path)?, rest))
}
fn branch(
ctx: &Context,
verb: &str,
lineage: String,
origin: EditOrigin,
path: String,
) -> Result<ConfigFile, String> {
Ok(ConfigFile::Branch {
workspace: args::workspace(ctx, verb)?,
lineage,
origin,
path,
})
}
fn word(rest: &str, verb: &str, what: &str) -> Result<(String, String), String> {
let (head, tail) = args::first_word(rest);
if head.is_empty() {
return Err(format!("/{verb}: {what} is required"));
}
Ok((head, tail))
}
#[cfg(test)]
mod tests;