implement watch command
This commit is contained in:
parent
055206f7c8
commit
cc6cf18406
|
@ -17,9 +17,10 @@ name = "shulkerscript"
|
|||
path = "src/main.rs"
|
||||
|
||||
[features]
|
||||
default = ["zip", "lua"]
|
||||
default = ["zip", "lua", "watch"]
|
||||
lang-debug = []
|
||||
lua = ["shulkerscript/lua"]
|
||||
watch = ["dep:notify-debouncer-mini", "dep:ctrlc"]
|
||||
zip = ["shulkerbox/zip"]
|
||||
|
||||
[dependencies]
|
||||
|
@ -34,4 +35,5 @@ git2 = { version = "0.18.3", 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 }
|
||||
|
|
10
README.md
10
README.md
|
@ -57,6 +57,16 @@ Options:
|
|||
Environment variables:
|
||||
- `DATAPACK_DIR` The output directory [default: `./dist`]
|
||||
|
||||
### Watch for changes
|
||||
```bash
|
||||
shulkerscript watch [OPTIONS] [SUBCOMMAND]
|
||||
```
|
||||
Where [SUBCOMMAND] is either `build` or `package` [default: `build`]
|
||||
|
||||
Options:
|
||||
- `--no-initial` Do not run the command initially
|
||||
- `--debounce-time <DEBOUNCE_TIME>` The time to wait in ms after the last change before running the command [default: `2000`]
|
||||
|
||||
## Contributing
|
||||
|
||||
Pull requests are welcome. For major changes, please open an issue first
|
||||
|
|
21
src/cli.rs
21
src/cli.rs
|
@ -24,6 +24,9 @@ pub enum Command {
|
|||
#[cfg(feature = "zip")]
|
||||
/// Build and package the project.
|
||||
Package(subcommands::PackageArgs),
|
||||
#[cfg(feature = "watch")]
|
||||
/// Watch for changes and execute command.
|
||||
Watch(subcommands::WatchArgs),
|
||||
#[cfg(feature = "lang-debug")]
|
||||
/// Build the project and dump the intermediate state.
|
||||
LangDebug(subcommands::LangDebugArgs),
|
||||
|
@ -31,12 +34,20 @@ pub enum Command {
|
|||
|
||||
impl Args {
|
||||
pub fn run(&self) -> Result<()> {
|
||||
match &self.cmd {
|
||||
Command::Init(args) => subcommands::init(self.verbose, args)?,
|
||||
Command::Build(args) => subcommands::build(self.verbose, args)?,
|
||||
Command::Clean(args) => subcommands::clean(self.verbose, args)?,
|
||||
self.cmd.run(self.verbose)
|
||||
}
|
||||
}
|
||||
|
||||
impl Command {
|
||||
pub fn run(&self, verbose: bool) -> Result<()> {
|
||||
match self {
|
||||
Command::Init(args) => subcommands::init(verbose, args)?,
|
||||
Command::Build(args) => subcommands::build(verbose, args)?,
|
||||
Command::Clean(args) => subcommands::clean(verbose, args)?,
|
||||
#[cfg(feature = "zip")]
|
||||
Command::Package(args) => subcommands::package(self.verbose, args)?,
|
||||
Command::Package(args) => subcommands::package(verbose, args)?,
|
||||
#[cfg(feature = "watch")]
|
||||
Command::Watch(args) => subcommands::watch(verbose, args)?,
|
||||
#[cfg(feature = "lang-debug")]
|
||||
Command::LangDebug(args) => subcommands::lang_debug(args)?,
|
||||
}
|
||||
|
|
|
@ -20,26 +20,26 @@ use crate::{
|
|||
pub struct InitArgs {
|
||||
/// The path of the folder to initialize in.
|
||||
#[clap(default_value = ".")]
|
||||
path: PathBuf,
|
||||
pub path: PathBuf,
|
||||
/// The name of the project.
|
||||
#[clap(short, long)]
|
||||
name: Option<String>,
|
||||
pub name: Option<String>,
|
||||
/// The description of the project.
|
||||
#[clap(short, long)]
|
||||
description: Option<String>,
|
||||
pub description: Option<String>,
|
||||
/// The pack format version.
|
||||
#[clap(short, long)]
|
||||
pack_format: Option<u8>,
|
||||
pub pack_format: Option<u8>,
|
||||
/// Force initialization even if the directory is not empty.
|
||||
#[clap(short, long)]
|
||||
force: bool,
|
||||
pub force: bool,
|
||||
/// The version control system to initialize.
|
||||
#[clap(long, default_value = "git")]
|
||||
vcs: VersionControlSystem,
|
||||
pub vcs: VersionControlSystem,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default, ValueEnum)]
|
||||
enum VersionControlSystem {
|
||||
pub enum VersionControlSystem {
|
||||
#[default]
|
||||
Git,
|
||||
None,
|
||||
|
|
|
@ -7,13 +7,13 @@ use std::path::PathBuf;
|
|||
pub struct LangDebugArgs {
|
||||
/// The path of the project to compile.
|
||||
#[clap(default_value = ".")]
|
||||
path: PathBuf,
|
||||
pub path: PathBuf,
|
||||
/// The state to dump.
|
||||
#[clap(short, long, default_value = "ast")]
|
||||
dump: DumpState,
|
||||
pub dump: DumpState,
|
||||
/// Pretty-print the output.
|
||||
#[clap(short, long)]
|
||||
pretty: bool,
|
||||
pub pretty: bool,
|
||||
}
|
||||
|
||||
#[derive(ValueEnum, Debug, Clone, Copy, Default)]
|
||||
|
|
|
@ -12,6 +12,11 @@ mod package;
|
|||
#[cfg(feature = "zip")]
|
||||
pub use package::{package, PackageArgs};
|
||||
|
||||
#[cfg(feature = "watch")]
|
||||
mod watch;
|
||||
#[cfg(feature = "watch")]
|
||||
pub use watch::{watch, WatchArgs};
|
||||
|
||||
#[cfg(feature = "lang-debug")]
|
||||
mod lang_debug;
|
||||
#[cfg(feature = "lang-debug")]
|
||||
|
|
|
@ -14,7 +14,7 @@ use super::BuildArgs;
|
|||
#[derive(Debug, clap::Args, Clone)]
|
||||
pub struct PackageArgs {
|
||||
#[clap(flatten)]
|
||||
build_args: BuildArgs,
|
||||
pub build_args: BuildArgs,
|
||||
}
|
||||
|
||||
pub fn package(_verbose: bool, args: &PackageArgs) -> Result<()> {
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
thread,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use clap::Subcommand;
|
||||
use notify_debouncer_mini::{new_debouncer, notify::*, DebounceEventResult};
|
||||
|
||||
use super::BuildArgs;
|
||||
use crate::{
|
||||
cli::Command,
|
||||
error::Result,
|
||||
terminal_output::{print_error, print_info},
|
||||
};
|
||||
|
||||
#[derive(Debug, clap::Args, Clone)]
|
||||
pub struct WatchArgs {
|
||||
/// Do not run the command when starting, only after changes are detected.
|
||||
#[clap(short, long)]
|
||||
no_inital: bool,
|
||||
/// The time to wait in ms before running the command after changes are detected.
|
||||
#[clap(short, long, default_value = "2000")]
|
||||
debounce_time: u64,
|
||||
/// The command to run when changes are detected.
|
||||
#[command(subcommand)]
|
||||
cmd: Option<WatchSubcommand>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Subcommand)]
|
||||
pub enum WatchSubcommand {
|
||||
/// Build the project.
|
||||
Build(BuildArgs),
|
||||
#[cfg(feature = "zip")]
|
||||
/// Build and package the project.
|
||||
Package(super::PackageArgs),
|
||||
}
|
||||
|
||||
impl From<WatchSubcommand> for Command {
|
||||
fn from(value: WatchSubcommand) -> Self {
|
||||
match value {
|
||||
WatchSubcommand::Build(args) => Command::Build(args),
|
||||
#[cfg(feature = "zip")]
|
||||
WatchSubcommand::Package(args) => Command::Package(args),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn watch(verbose: bool, args: &WatchArgs) -> Result<()> {
|
||||
let cmd = Command::from(args.cmd.to_owned().unwrap_or_else(|| {
|
||||
WatchSubcommand::Build(BuildArgs {
|
||||
path: PathBuf::from("."),
|
||||
output: None,
|
||||
assets: None,
|
||||
})
|
||||
}));
|
||||
|
||||
let project_path = match &args.cmd {
|
||||
Some(WatchSubcommand::Build(args)) => args.path.as_path(),
|
||||
#[cfg(feature = "zip")]
|
||||
Some(WatchSubcommand::Package(args)) => args.build_args.path.as_path(),
|
||||
None => Path::new("."),
|
||||
};
|
||||
|
||||
#[allow(clippy::collapsible_if)]
|
||||
if !args.no_inital {
|
||||
if cmd.run(verbose).is_err() {
|
||||
print_error("Command failed to run initially");
|
||||
}
|
||||
}
|
||||
|
||||
ctrlc::set_handler(move || {
|
||||
print_info("Stopping watcher...");
|
||||
std::process::exit(0);
|
||||
})
|
||||
.expect("Error setting Ctrl-C handler");
|
||||
|
||||
let mut debouncer = new_debouncer(
|
||||
Duration::from_millis(args.debounce_time),
|
||||
move |res: DebounceEventResult| {
|
||||
if res.is_ok() {
|
||||
if cmd.run(verbose).is_err() {
|
||||
print_error("Command failed to run");
|
||||
}
|
||||
} else {
|
||||
std::process::exit(1);
|
||||
}
|
||||
},
|
||||
)
|
||||
.expect("Failed to initialize watcher");
|
||||
|
||||
let watcher = debouncer.watcher();
|
||||
watcher
|
||||
.watch(project_path.join("src").as_path(), RecursiveMode::Recursive)
|
||||
.expect("Failed to watch project src");
|
||||
watcher
|
||||
.watch(
|
||||
project_path.join("pack.png").as_path(),
|
||||
RecursiveMode::NonRecursive,
|
||||
)
|
||||
.expect("Failed to watch project pack.png");
|
||||
watcher
|
||||
.watch(
|
||||
project_path.join("pack.toml").as_path(),
|
||||
RecursiveMode::NonRecursive,
|
||||
)
|
||||
.expect("Failed to watch project pack.toml");
|
||||
|
||||
loop {
|
||||
thread::sleep(Duration::from_secs(60));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue