pint_cli/
lib.rs

1use anyhow::Context;
2use clap::{CommandFactory, Parser, Subcommand};
3
4pub mod build;
5pub mod new;
6mod plugin;
7
8#[derive(Parser, Debug)]
9#[command(
10    name = "pint",
11    about = "Pint's package manager and CLI plugin host",
12    version
13)]
14pub struct Pint {
15    #[command(subcommand)]
16    cmd: Cmd,
17}
18
19#[derive(Debug, Subcommand)]
20enum Cmd {
21    #[command(alias = "b")]
22    Build(build::Args),
23    New(new::Args),
24    /// Print all pint plugins found in `PATH`.
25    Plugins,
26    /// A catch-all for unknown pint plugins and their arguments.
27    ///
28    /// Checks for a plugin exe named `pint-<unknown-subcommand>` and tries to
29    /// execute it with the remaining args.
30    #[clap(external_subcommand)]
31    Plugin(Vec<String>),
32}
33
34// Manually print the help output for missing/unknown commands or plugins.
35fn print_help_output() -> anyhow::Result<()> {
36    let mut cli = Pint::command();
37    cli.print_help()?;
38    Ok(())
39}
40
41/// Run the `pint` CLI given the set of parsed commands/args.
42pub fn run(pint: Pint) -> anyhow::Result<()> {
43    match pint.cmd {
44        Cmd::New(arg) => new::cmd(arg),
45        Cmd::Build(arg) => {
46            let (_plan, _built_pkgs) = build::cmd(arg)?;
47            Ok(())
48        }
49        Cmd::Plugins => {
50            plugin::print_all();
51            Ok(())
52        }
53        Cmd::Plugin(args) if !args.is_empty() => {
54            let Some(plugin_exe_path) = plugin::find_exe(&args[0]) else {
55                // There's no plugin with the given name, so print help output.
56                print_help_output()?;
57                anyhow::bail!("no known command or plugin {:?}", &args[0]);
58            };
59            let output = plugin::exec(&plugin_exe_path, &args[1..])?;
60            std::process::exit(output.status.code().context("unknown exit status")?);
61        }
62        _ => print_help_output(),
63    }
64}