use crate::cli_outbound::Binary;
mod probe;
pub(crate) use probe::classify;
pub use probe::{CliProbe, ToolProbe};
const BL_FIX: &str = "reinstall balls to match yog's pinned verbs: `cargo install --locked balls`";
const LERNIE_FIX: &str =
"reinstall lernie to match yog's pinned verbs: `cargo install --locked lernie`";
const BZ_FIX: &str =
"reinstall brazen to match yog's pinned probe: `cargo install --locked brazen`";
pub(crate) const PHASE1_DISCLAIMER: &str = "Phase-1 gate: yog shells to host \
tools, probing each driven verb by capability. Phase 2's exact-pinned crates make \
the verbs definitional and delete this gate (DESIGN §16.4).";
pub(crate) fn remediation(binary: Binary) -> &'static str {
match binary {
Binary::Bl => BL_FIX,
Binary::Lernie => LERNIE_FIX,
Binary::Bz => BZ_FIX,
}
}
pub(crate) fn tool_name(binary: Binary) -> &'static str {
match binary {
Binary::Bl => "bl",
Binary::Lernie => "lernie",
Binary::Bz => "bz",
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ToolState {
Ok,
Mismatch { verb: String, remediation: String },
Missing { remediation: String },
}
impl ToolState {
pub(crate) fn permits(&self) -> bool {
matches!(self, ToolState::Ok)
}
pub(crate) fn remediation(&self) -> Option<&str> {
match self {
ToolState::Ok => None,
ToolState::Mismatch { remediation, .. } | ToolState::Missing { remediation } => {
Some(remediation)
}
}
}
pub(crate) fn describe(&self) -> String {
match self {
ToolState::Ok => "ok".to_owned(),
ToolState::Mismatch { verb, remediation } => {
format!("missing verb `{verb}` — {remediation}")
}
ToolState::Missing { remediation } => format!("not found — {remediation}"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ToolchainState {
bl: ToolState,
lernie: ToolState,
bz: ToolState,
}
impl ToolchainState {
fn state_of(&self, binary: Binary) -> &ToolState {
match binary {
Binary::Bl => &self.bl,
Binary::Lernie => &self.lernie,
Binary::Bz => &self.bz,
}
}
pub(crate) fn permits(&self, binary: Binary) -> bool {
self.state_of(binary).permits()
}
pub(crate) fn require(&self, binary: Binary) -> Result<(), Refusal> {
let state = self.state_of(binary);
if state.permits() {
Ok(())
} else {
Err(Refusal {
tool: tool_name(binary),
state: state.clone(),
})
}
}
pub(crate) fn rows(&self) -> [(&'static str, &ToolState); 3] {
[("bl", &self.bl), ("lernie", &self.lernie), ("bz", &self.bz)]
}
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
#[error("{tool}: {}", .state.describe())]
pub struct Refusal {
pub tool: &'static str,
pub state: ToolState,
}
pub fn probe(runner: &dyn ToolProbe) -> ToolchainState {
ToolchainState {
bl: classify(runner, Binary::Bl),
lernie: classify(runner, Binary::Lernie),
bz: classify(runner, Binary::Bz),
}
}
#[cfg(test)]
mod tests;