Skip to main content

sandogasa_cli/
lib.rs

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