Skip to main content

sandogasa_cli/
lib.rs

1// SPDX-License-Identifier: MPL-2.0
2
3//! Shared CLI utilities for sandogasa tools.
4
5use std::process::{Command, Stdio};
6
7/// Check that an external tool is available in `$PATH`.
8///
9/// Runs `<name> <version_arg>` and returns `Ok(())` if it exits
10/// successfully, or an error message with the install hint.
11/// Most tools use `--version`; see [`require_tool`] for a
12/// convenience wrapper.
13///
14/// # Example
15///
16/// ```no_run
17/// // koji uses `version` subcommand instead of `--version`
18/// sandogasa_cli::require_tool_with_arg("koji", "version", "sudo dnf install koji").unwrap();
19/// ```
20pub fn require_tool_with_arg(
21    name: &str,
22    version_arg: &str,
23    install_hint: &str,
24) -> Result<(), String> {
25    match Command::new(name)
26        .arg(version_arg)
27        .stdout(Stdio::null())
28        .stderr(Stdio::null())
29        .status()
30    {
31        Ok(s) if s.success() => Ok(()),
32        Ok(s) => Err(format!(
33            "{name} exited with {s}; is it installed correctly? \
34             Install it with: {install_hint}"
35        )),
36        Err(_) => Err(format!("{name} not found. Install it with: {install_hint}")),
37    }
38}
39
40/// Check that an external tool is available in `$PATH`.
41///
42/// Runs `<name> --version` and returns `Ok(())` if it exits
43/// successfully, or an error message with the install hint.
44///
45/// For tools that use a different version probe (e.g. `koji version`
46/// instead of `koji --version`), use [`require_tool_with_arg`].
47///
48/// # Example
49///
50/// ```no_run
51/// sandogasa_cli::require_tool("fedrq", "sudo dnf install fedrq").unwrap();
52/// ```
53pub fn require_tool(name: &str, install_hint: &str) -> Result<(), String> {
54    require_tool_with_arg(name, "--version", install_hint)
55}
56
57#[cfg(test)]
58mod tests {
59    use super::*;
60
61    #[test]
62    fn require_missing_tool() {
63        let result = require_tool("nonexistent_tool_xyz_123", "magic install");
64        assert!(result.is_err());
65        let msg = result.unwrap_err();
66        assert!(msg.contains("nonexistent_tool_xyz_123"));
67        assert!(msg.contains("magic install"));
68    }
69
70    #[test]
71    fn require_available_tool() {
72        // `true` is a standard Unix utility that always succeeds.
73        // It doesn't support --version but some impls exit 0 anyway.
74        // Use `sh` which reliably exists and handles --version.
75        let result = require_tool("sh", "should already be installed");
76        // sh --version may or may not succeed depending on implementation,
77        // so just verify it doesn't panic.
78        let _ = result;
79    }
80}