implement watch command
This commit is contained in:
parent
055206f7c8
commit
cc6cf18406
|
@ -17,9 +17,10 @@ name = "shulkerscript"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["zip", "lua"]
|
default = ["zip", "lua", "watch"]
|
||||||
lang-debug = []
|
lang-debug = []
|
||||||
lua = ["shulkerscript/lua"]
|
lua = ["shulkerscript/lua"]
|
||||||
|
watch = ["dep:notify-debouncer-mini", "dep:ctrlc"]
|
||||||
zip = ["shulkerbox/zip"]
|
zip = ["shulkerbox/zip"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
@ -34,4 +35,5 @@ git2 = { version = "0.18.3", default-features = false }
|
||||||
path-absolutize = "3.1.1"
|
path-absolutize = "3.1.1"
|
||||||
color-eyre = "0.6.3"
|
color-eyre = "0.6.3"
|
||||||
dotenvy = "0.15.7"
|
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:
|
Environment variables:
|
||||||
- `DATAPACK_DIR` The output directory [default: `./dist`]
|
- `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
|
## Contributing
|
||||||
|
|
||||||
Pull requests are welcome. For major changes, please open an issue first
|
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")]
|
#[cfg(feature = "zip")]
|
||||||
/// Build and package the project.
|
/// Build and package the project.
|
||||||
Package(subcommands::PackageArgs),
|
Package(subcommands::PackageArgs),
|
||||||
|
#[cfg(feature = "watch")]
|
||||||
|
/// Watch for changes and execute command.
|
||||||
|
Watch(subcommands::WatchArgs),
|
||||||
#[cfg(feature = "lang-debug")]
|
#[cfg(feature = "lang-debug")]
|
||||||
/// Build the project and dump the intermediate state.
|
/// Build the project and dump the intermediate state.
|
||||||
LangDebug(subcommands::LangDebugArgs),
|
LangDebug(subcommands::LangDebugArgs),
|
||||||
|
@ -31,12 +34,20 @@ pub enum Command {
|
||||||
|
|
||||||
impl Args {
|
impl Args {
|
||||||
pub fn run(&self) -> Result<()> {
|
pub fn run(&self) -> Result<()> {
|
||||||
match &self.cmd {
|
self.cmd.run(self.verbose)
|
||||||
Command::Init(args) => subcommands::init(self.verbose, args)?,
|
}
|
||||||
Command::Build(args) => subcommands::build(self.verbose, args)?,
|
}
|
||||||
Command::Clean(args) => subcommands::clean(self.verbose, args)?,
|
|
||||||
|
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")]
|
#[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")]
|
#[cfg(feature = "lang-debug")]
|
||||||
Command::LangDebug(args) => subcommands::lang_debug(args)?,
|
Command::LangDebug(args) => subcommands::lang_debug(args)?,
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,26 +20,26 @@ use crate::{
|
||||||
pub struct InitArgs {
|
pub struct InitArgs {
|
||||||
/// The path of the folder to initialize in.
|
/// The path of the folder to initialize in.
|
||||||
#[clap(default_value = ".")]
|
#[clap(default_value = ".")]
|
||||||
path: PathBuf,
|
pub path: PathBuf,
|
||||||
/// The name of the project.
|
/// The name of the project.
|
||||||
#[clap(short, long)]
|
#[clap(short, long)]
|
||||||
name: Option<String>,
|
pub name: Option<String>,
|
||||||
/// The description of the project.
|
/// The description of the project.
|
||||||
#[clap(short, long)]
|
#[clap(short, long)]
|
||||||
description: Option<String>,
|
pub description: Option<String>,
|
||||||
/// The pack format version.
|
/// The pack format version.
|
||||||
#[clap(short, long)]
|
#[clap(short, long)]
|
||||||
pack_format: Option<u8>,
|
pub pack_format: Option<u8>,
|
||||||
/// Force initialization even if the directory is not empty.
|
/// Force initialization even if the directory is not empty.
|
||||||
#[clap(short, long)]
|
#[clap(short, long)]
|
||||||
force: bool,
|
pub force: bool,
|
||||||
/// The version control system to initialize.
|
/// The version control system to initialize.
|
||||||
#[clap(long, default_value = "git")]
|
#[clap(long, default_value = "git")]
|
||||||
vcs: VersionControlSystem,
|
pub vcs: VersionControlSystem,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Default, ValueEnum)]
|
#[derive(Debug, Clone, Copy, Default, ValueEnum)]
|
||||||
enum VersionControlSystem {
|
pub enum VersionControlSystem {
|
||||||
#[default]
|
#[default]
|
||||||
Git,
|
Git,
|
||||||
None,
|
None,
|
||||||
|
|
|
@ -7,13 +7,13 @@ use std::path::PathBuf;
|
||||||
pub struct LangDebugArgs {
|
pub struct LangDebugArgs {
|
||||||
/// The path of the project to compile.
|
/// The path of the project to compile.
|
||||||
#[clap(default_value = ".")]
|
#[clap(default_value = ".")]
|
||||||
path: PathBuf,
|
pub path: PathBuf,
|
||||||
/// The state to dump.
|
/// The state to dump.
|
||||||
#[clap(short, long, default_value = "ast")]
|
#[clap(short, long, default_value = "ast")]
|
||||||
dump: DumpState,
|
pub dump: DumpState,
|
||||||
/// Pretty-print the output.
|
/// Pretty-print the output.
|
||||||
#[clap(short, long)]
|
#[clap(short, long)]
|
||||||
pretty: bool,
|
pub pretty: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(ValueEnum, Debug, Clone, Copy, Default)]
|
#[derive(ValueEnum, Debug, Clone, Copy, Default)]
|
||||||
|
|
|
@ -12,6 +12,11 @@ mod package;
|
||||||
#[cfg(feature = "zip")]
|
#[cfg(feature = "zip")]
|
||||||
pub use package::{package, PackageArgs};
|
pub use package::{package, PackageArgs};
|
||||||
|
|
||||||
|
#[cfg(feature = "watch")]
|
||||||
|
mod watch;
|
||||||
|
#[cfg(feature = "watch")]
|
||||||
|
pub use watch::{watch, WatchArgs};
|
||||||
|
|
||||||
#[cfg(feature = "lang-debug")]
|
#[cfg(feature = "lang-debug")]
|
||||||
mod lang_debug;
|
mod lang_debug;
|
||||||
#[cfg(feature = "lang-debug")]
|
#[cfg(feature = "lang-debug")]
|
||||||
|
|
|
@ -14,7 +14,7 @@ use super::BuildArgs;
|
||||||
#[derive(Debug, clap::Args, Clone)]
|
#[derive(Debug, clap::Args, Clone)]
|
||||||
pub struct PackageArgs {
|
pub struct PackageArgs {
|
||||||
#[clap(flatten)]
|
#[clap(flatten)]
|
||||||
build_args: BuildArgs,
|
pub build_args: BuildArgs,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn package(_verbose: bool, args: &PackageArgs) -> Result<()> {
|
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