use super::{BallSpec, Payload, PlanInputs, StartInputs, Target};
use crate::binding::work_worktree_path;
use crate::names::Rng;
use std::path::{Path, PathBuf};
pub fn identity_preview(name: &str) -> String {
format!("You are {name}.")
}
pub fn identity_preamble(name: &str) -> String {
format!("You are {name}. Stamp every bl verb you run with --as {name}.")
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct Composer {
pub preview: String,
pub prefill: String,
}
pub fn preview(inputs: &StartInputs, rng: &mut dyn Rng) -> Composer {
let preview = mint_name_of(
&inputs.target,
&inputs.yog_data_root,
&inputs.claimants,
rng,
)
.map(|name| identity_preview(&name))
.unwrap_or_default();
let worktree = canonical_worktree(&inputs.payload, &inputs.balls_state_root);
Composer {
preview,
prefill: prefill(&inputs.payload, worktree.as_deref()),
}
}
pub(super) fn prefill(payload: &Payload, worktree: Option<&Path>) -> String {
match payload {
Payload::Bare => String::new(),
Payload::Path { dir } => path_preamble(dir),
Payload::Ball {
project,
ball: BallSpec::Existing {
id, title, body, ..
},
} => ball_preamble(id, title, body, worktree.unwrap_or(project), project),
Payload::Ball {
ball: BallSpec::New { title, body },
..
} => format!("Ball (new): {title}\n\n{body}"),
}
}
fn path_preamble(dir: &Path) -> String {
format!(
"The working directory for this conversation is:\n{}\nDo all work there, by absolute path. Do not rely on the current directory.",
dir.display(),
)
}
pub(super) fn canonical_worktree(payload: &Payload, balls_state_root: &Path) -> Option<PathBuf> {
match payload {
Payload::Ball {
project,
ball: BallSpec::Existing { id, .. },
} => Some(work_worktree_path(balls_state_root, project, id, None)),
_ => None,
}
}
pub(super) fn leaf_name(workspace: &Path) -> String {
workspace
.file_name()
.map(|s| s.to_string_lossy().into_owned())
.unwrap_or_default()
}
fn ball_preamble(id: &str, title: &str, body: &str, worktree: &Path, project: &Path) -> String {
format!(
"Ball {id}: {title}\n\n{body}\n\nThe project repository checkout for this work is the git worktree at:\n{worktree} (branch work/{id} of {project})\nDo all repository work there, by absolute path. Do not rely on the current directory.",
worktree = worktree.display(),
project = project.display(),
)
}
pub fn parse_ball_stamp(goal: &str) -> Option<String> {
goal.lines().find_map(stamp_id)
}
fn stamp_id(line: &str) -> Option<String> {
let (id, _title) = line.strip_prefix("Ball ")?.split_once(": ")?;
(!id.is_empty() && !id.contains(char::is_whitespace)).then(|| id.to_owned())
}
pub(super) fn driver_cwd(payload: &Payload, home: &Path, worktree: Option<&Path>) -> PathBuf {
match payload {
Payload::Path { dir } => dir.clone(),
Payload::Ball {
ball: BallSpec::Existing { .. },
..
} => worktree.unwrap_or(home).to_path_buf(),
Payload::Bare
| Payload::Ball {
ball: BallSpec::New { .. },
..
} => home.to_path_buf(),
}
}
pub(super) fn compose_prepared(inputs: &PlanInputs, worktree: Option<&Path>) -> super::Prepared {
super::Prepared {
name: inputs.name.clone(),
workspace: inputs.workspace.clone(),
cwd: driver_cwd(&inputs.payload, &inputs.home, worktree),
goal: prefill(&inputs.payload, worktree),
}
}
pub(super) fn mint_name_of(
target: &Target,
yog_data_root: &Path,
claimants: &[String],
rng: &mut dyn Rng,
) -> Result<String, crate::names::MintError> {
match target {
Target::Existing { workspace } => Ok(leaf_name(workspace)),
Target::Mint => {
let root = crate::binding::names_root(yog_data_root);
let occupied = crate::names::occupied(&root, claimants);
crate::names::mint(rng, &occupied)
}
}
}