shulkerscript_cli/
cli.rs

1use crate::subcommands::{self, BuildArgs, CleanArgs, InitArgs};
2
3use anyhow::Result;
4use clap::{Parser, Subcommand, ValueEnum};
5use const_format::formatcp;
6use tracing::Level;
7use tracing_subscriber::FmtSubscriber;
8
9static VERSION: &str = formatcp!(
10    "v{cli_version}\nshulkerscript-lang v{lang_version}",
11    cli_version = env!("CARGO_PKG_VERSION"),
12    lang_version = shulkerscript::VERSION
13);
14
15#[derive(Debug, Clone, Parser)]
16#[command(version, about, long_about = None, disable_version_flag = false, version = VERSION)]
17pub struct Args {
18    #[command(subcommand)]
19    cmd: Command,
20    /// Enable tracing output
21    ///
22    /// When specified without a value, defaults to `info`.
23    #[arg(
24        long,
25        global = true,
26        default_missing_value = "info",
27        require_equals = true,
28        num_args = 0..=1,
29        value_name = "LEVEL"
30    )]
31    trace: Option<TracingLevel>,
32}
33
34#[derive(Debug, Clone, Subcommand)]
35pub enum Command {
36    /// Initialize a new project.
37    Init(InitArgs),
38    /// Build the project.
39    Build(BuildArgs),
40    /// Clean build artifacts.
41    /// This will remove the output directory.
42    Clean(CleanArgs),
43    #[cfg(feature = "lang-debug")]
44    /// Build the project and dump the intermediate state.
45    LangDebug(subcommands::LangDebugArgs),
46    #[cfg(feature = "migrate")]
47    /// Migrate a regular datapack to a Shulkerscript project.
48    Migrate(subcommands::MigrateArgs),
49    #[cfg(feature = "watch")]
50    /// Watch for changes and execute commands.
51    Watch(subcommands::WatchArgs),
52}
53
54#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, ValueEnum)]
55pub enum TracingLevel {
56    Trace,
57    Debug,
58    #[default]
59    Info,
60    Warn,
61    Error,
62}
63
64impl Args {
65    pub fn run(&self) -> Result<()> {
66        if let Some(level) = self.trace {
67            setup_tracing(level)?;
68        }
69
70        self.cmd.run()
71    }
72}
73
74impl Command {
75    pub fn run(&self) -> Result<()> {
76        match self {
77            Command::Init(args) => subcommands::init(args)?,
78            Command::Build(args) => subcommands::build(args)?,
79            Command::Clean(args) => subcommands::clean(args)?,
80            #[cfg(feature = "lang-debug")]
81            Command::LangDebug(args) => subcommands::lang_debug(args)?,
82            #[cfg(feature = "migrate")]
83            Command::Migrate(args) => subcommands::migrate(args)?,
84            #[cfg(feature = "watch")]
85            Command::Watch(args) => subcommands::watch(args)?,
86        }
87
88        Ok(())
89    }
90}
91
92impl From<TracingLevel> for Level {
93    fn from(value: TracingLevel) -> Self {
94        match value {
95            TracingLevel::Trace => Level::TRACE,
96            TracingLevel::Debug => Level::DEBUG,
97            TracingLevel::Info => Level::INFO,
98            TracingLevel::Warn => Level::WARN,
99            TracingLevel::Error => Level::ERROR,
100        }
101    }
102}
103
104fn setup_tracing(level: TracingLevel) -> Result<()> {
105    // a builder for `FmtSubscriber`.
106    let subscriber = FmtSubscriber::builder()
107        // all spans/events with a level higher than TRACE (e.g, debug, info, warn, etc.)
108        // will be written to stdout.
109        .with_max_level(Level::from(level))
110        // completes the builder.
111        .finish();
112
113    tracing::subscriber::set_global_default(subscriber)?;
114
115    Ok(())
116}
117
118#[cfg(test)]
119mod tests {
120    use clap::CommandFactory;
121
122    use super::*;
123
124    #[test]
125    fn verify_cli() {
126        Args::command().debug_assert();
127    }
128}