From 916901e81a1278bdf65f5e895773625ff4a9cbf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20H=C3=B6lting?= <87192362+moritz-hoelting@users.noreply.github.com> Date: Sun, 16 Jun 2024 21:59:02 +0200 Subject: [PATCH] add tracing as global option --- Cargo.toml | 8 ++-- src/cli.rs | 70 ++++++++++++++++++++++++++++++----- src/subcommands/build.rs | 8 +++- src/subcommands/clean.rs | 6 ++- src/subcommands/init.rs | 6 ++- src/subcommands/lang_debug.rs | 2 + src/subcommands/watch.rs | 14 +++++-- 7 files changed, 93 insertions(+), 21 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7db1b65..76d9425 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,11 +29,13 @@ colored = "2.1.0" serde = { version = "1.0.197", features = ["derive"] } thiserror = "1.0.58" toml = "0.8.12" -shulkerscript = { git = "https://github.com/moritz-hoelting/shulkerscript-lang", features = ["shulkerbox"], default-features = false, rev = "5336ffb91ed5dfa1b2fce3993d84a2876aa732a3" } -shulkerbox = { git = "https://github.com/moritz-hoelting/shulkerbox", default-features = false, rev = "b79c9ecd6d45f9319c9083a8103ef0186839b0c0" } -git2 = { version = "0.18.3", default-features = false } +shulkerscript = { git = "https://github.com/moritz-hoelting/shulkerscript-lang", features = ["shulkerbox"], default-features = false, rev = "dd79541ae914140df4141fe90f71e74fb961f3a3" } +shulkerbox = { git = "https://github.com/moritz-hoelting/shulkerbox", default-features = false, rev = "e31f9f904a5f5905e912907d988c189ffc8cf313" } +git2 = { version = "0.19.0", default-features = false } path-absolutize = "3.1.1" color-eyre = "0.6.3" dotenvy = "0.15.7" notify-debouncer-mini = { version = "0.4.1", default-features = false, optional = true } ctrlc = { version = "3.4.4", optional = true } +tracing = "0.1.40" +tracing-subscriber = "0.3.18" diff --git a/src/cli.rs b/src/cli.rs index 9d3decd..902b54f 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,15 +1,27 @@ use crate::subcommands::{self, BuildArgs, CleanArgs, InitArgs}; -use clap::{Parser, Subcommand}; + +use clap::{Parser, Subcommand, ValueEnum}; use color_eyre::eyre::Result; +use tracing::Level; +use tracing_subscriber::FmtSubscriber; #[derive(Debug, Clone, Parser)] #[command(version, about, long_about = None)] pub struct Args { #[command(subcommand)] cmd: Command, - /// Enable verbose output. - #[clap(short, long)] - verbose: bool, + /// Enable tracing output + /// + /// When specified without a value, defaults to `info`. + #[clap( + long, + global = true, + default_missing_value = "info", + require_equals = true, + num_args = 0..=1, + value_name = "LEVEL" + )] + trace: Option, } #[derive(Debug, Clone, Subcommand)] @@ -29,20 +41,34 @@ pub enum Command { LangDebug(subcommands::LangDebugArgs), } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, ValueEnum)] +pub enum TracingLevel { + Trace, + Debug, + #[default] + Info, + Warn, + Error, +} + impl Args { pub fn run(&self) -> Result<()> { - self.cmd.run(self.verbose) + if let Some(level) = self.trace { + setup_tracing(level); + } + + self.cmd.run() } } impl Command { - pub fn run(&self, verbose: bool) -> Result<()> { + pub fn run(&self) -> Result<()> { match self { - Command::Init(args) => subcommands::init(verbose, args)?, - Command::Build(args) => subcommands::build(verbose, args)?, - Command::Clean(args) => subcommands::clean(verbose, args)?, + Command::Init(args) => subcommands::init(args)?, + Command::Build(args) => subcommands::build(args)?, + Command::Clean(args) => subcommands::clean(args)?, #[cfg(feature = "watch")] - Command::Watch(args) => subcommands::watch(verbose, args)?, + Command::Watch(args) => subcommands::watch(args)?, #[cfg(feature = "lang-debug")] Command::LangDebug(args) => subcommands::lang_debug(args)?, } @@ -51,6 +77,30 @@ impl Command { } } +impl From for Level { + fn from(value: TracingLevel) -> Self { + match value { + TracingLevel::Trace => Level::TRACE, + TracingLevel::Debug => Level::DEBUG, + TracingLevel::Info => Level::INFO, + TracingLevel::Warn => Level::WARN, + TracingLevel::Error => Level::ERROR, + } + } +} + +fn setup_tracing(level: TracingLevel) { + // a builder for `FmtSubscriber`. + let subscriber = FmtSubscriber::builder() + // all spans/events with a level higher than TRACE (e.g, debug, info, warn, etc.) + // will be written to stdout. + .with_max_level(Level::from(level)) + // completes the builder. + .finish(); + + tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed"); +} + #[cfg(test)] mod tests { use clap::CommandFactory; diff --git a/src/subcommands/build.rs b/src/subcommands/build.rs index 6c79896..976750d 100644 --- a/src/subcommands/build.rs +++ b/src/subcommands/build.rs @@ -18,14 +18,18 @@ pub struct BuildArgs { /// The path of the project to build. #[clap(default_value = ".")] pub path: PathBuf, + /// Path of output directory + /// /// The path of the directory to place the compiled datapack. #[clap(short, long, env = "DATAPACK_DIR")] pub output: Option, + /// Path of the assets folder + /// /// The path of a folder which files and subfolders will be copied to the root of the datapack. /// Overrides the `assets` field in the pack.toml file. #[clap(short, long)] pub assets: Option, - /// Whether to package the project to a zip file. + /// Package the project to a zip file. #[clap(short, long)] pub zip: bool, } @@ -41,7 +45,7 @@ impl Default for BuildArgs { } } -pub fn build(_verbose: bool, args: &BuildArgs) -> Result<()> { +pub fn build(args: &BuildArgs) -> Result<()> { if args.zip && !cfg!(feature = "zip") { print_error("The zip feature is not enabled. Please install with the `zip` feature enabled to use the `--zip` option."); return Err(Report::from(Error::FeatureNotEnabledError( diff --git a/src/subcommands/clean.rs b/src/subcommands/clean.rs index f1452fa..cb01f9e 100644 --- a/src/subcommands/clean.rs +++ b/src/subcommands/clean.rs @@ -19,9 +19,13 @@ pub struct CleanArgs { /// Force clean #[clap(short, long)] pub force: bool, + /// Enable verbose output. + #[clap(short, long)] + verbose: bool, } -pub fn clean(verbose: bool, args: &CleanArgs) -> Result<()> { +pub fn clean(args: &CleanArgs) -> Result<()> { + let verbose = args.verbose; let path = args.path.as_path(); let dist_path = args .output diff --git a/src/subcommands/init.rs b/src/subcommands/init.rs index 332bdd8..b684485 100644 --- a/src/subcommands/init.rs +++ b/src/subcommands/init.rs @@ -36,6 +36,9 @@ pub struct InitArgs { /// The version control system to initialize. #[clap(long, default_value = "git")] pub vcs: VersionControlSystem, + /// Enable verbose output. + #[clap(short, long)] + verbose: bool, } #[derive(Debug, Clone, Copy, Default, ValueEnum)] @@ -45,7 +48,8 @@ pub enum VersionControlSystem { None, } -pub fn init(verbose: bool, args: &InitArgs) -> Result<()> { +pub fn init(args: &InitArgs) -> Result<()> { + let verbose = args.verbose; let path = args.path.as_path(); let description = args.description.as_deref(); let pack_format = args.pack_format; diff --git a/src/subcommands/lang_debug.rs b/src/subcommands/lang_debug.rs index 9088fb4..e676d64 100644 --- a/src/subcommands/lang_debug.rs +++ b/src/subcommands/lang_debug.rs @@ -9,6 +9,8 @@ pub struct LangDebugArgs { #[clap(default_value = ".")] pub path: PathBuf, /// The state to dump. + /// + /// Output can be the raw tokens, the abstract syntax tree, or the transpiled datapack. #[clap(short, long, value_name = "STATE", default_value = "ast")] pub dump: DumpState, /// Pretty-print the output. diff --git a/src/subcommands/watch.rs b/src/subcommands/watch.rs index bd83f11..9baf84f 100644 --- a/src/subcommands/watch.rs +++ b/src/subcommands/watch.rs @@ -20,17 +20,23 @@ pub struct WatchArgs { /// The path of the project to watch. #[clap(default_value = ".")] pub path: PathBuf, - /// Do not run the command when starting, only after changes are detected. + /// Only run after changes are detected. + /// + /// Skips the initial run of the commands. #[clap(short, long)] pub no_inital: bool, /// The time to wait in ms before running the command after changes are detected. #[clap(short, long, value_name = "TIME_IN_MS", default_value = "2000")] pub debounce_time: u64, /// Additional paths to watch for changes. - /// By default, the `src` directory, `pack.png`, and `pack.toml` as well as the defined assets directory in the config are watched. + /// + /// By default, the `src` directory, `pack.png`, and `pack.toml` as well as the defined + /// assets directory in the config are watched. #[clap(short, long, value_name = "PATH")] pub watch: Vec, - /// The commands to run in the project directory when changes are detected. Use multiple times to run multiple commands. + /// The commands to run in the project directory when changes are detected. + /// + /// Use multiple times to run multiple commands. #[clap(short = 'x', long, value_name = "COMMAND", default_value = "build .")] pub execute: Vec, } @@ -41,7 +47,7 @@ enum WatchCommand { External(String), } -pub fn watch(_verbose: bool, args: &WatchArgs) -> Result<()> { +pub fn watch(args: &WatchArgs) -> Result<()> { print_info(format!("Watching project at {}", args.path.display())); let commands = args