2021-04-10 15:33:40 +02:00
use std ::{
2021-08-24 10:23:37 +02:00
env ,
ffi ::OsString ,
fs ::File ,
io ::{ BufRead , BufReader } ,
path ::Path ,
2021-04-10 15:33:40 +02:00
} ;
2021-08-24 10:23:37 +02:00
use clap ::{ crate_version , App , Arg , ArgMatches } ;
2021-10-09 07:43:51 +02:00
use miette ::{ Context , IntoDiagnostic , Result } ;
2021-07-30 18:24:05 +02:00
2021-11-22 09:23:29 +01:00
trait Clap3Compat {
/// Does nothing for clap2, but remove this trait for clap3, and get cool new option groups!
fn help_heading ( self , _heading : impl Into < Option < & 'static str > > ) -> Self
where
Self : Sized ,
{
self
}
}
impl Clap3Compat for Arg < '_ , '_ > { }
const OPTSET_FILTERING : & str = " Filtering options: " ;
const OPTSET_COMMAND : & str = " Command options: " ;
const OPTSET_DEBUGGING : & str = " Debugging options: " ;
const OPTSET_OUTPUT : & str = " Output options: " ;
const OPTSET_BEHAVIOUR : & str = " Behaviour options: " ;
2021-12-23 15:56:03 +01:00
pub fn get_args ( tagged_filterer : bool ) -> Result < ArgMatches < 'static > > {
2021-08-24 10:23:37 +02:00
let app = App ::new ( " watchexec " )
2021-08-24 12:45:31 +02:00
. version ( crate_version! ( ) )
. about ( " Execute commands when watched files change " )
. after_help ( " Use @argfile as first argument to load arguments from the file `argfile` (one argument per line) which will be inserted in place of the @argfile (further arguments on the CLI will override or add onto those in the file). " )
. arg ( Arg ::with_name ( " command " )
2021-11-22 09:23:29 +01:00
. help_heading ( Some ( OPTSET_COMMAND ) )
2021-08-24 12:45:31 +02:00
. help ( " Command to execute " )
. multiple ( true )
. required ( true ) )
. arg ( Arg ::with_name ( " paths " )
2021-11-22 09:23:29 +01:00
. help_heading ( Some ( OPTSET_FILTERING ) )
2021-08-24 12:45:31 +02:00
. help ( " Watch a specific file or directory " )
. short ( " w " )
. long ( " watch " )
2021-08-24 12:53:44 +02:00
. value_name ( " path " )
2021-08-24 12:45:31 +02:00
. number_of_values ( 1 )
. multiple ( true )
. takes_value ( true ) )
. arg ( Arg ::with_name ( " clear " )
2021-11-22 09:23:29 +01:00
. help_heading ( Some ( OPTSET_BEHAVIOUR ) )
2021-08-24 12:45:31 +02:00
. help ( " Clear screen before executing command " )
. short ( " c " )
. long ( " clear " ) )
. arg ( Arg ::with_name ( " on-busy-update " )
2021-11-22 09:23:29 +01:00
. help_heading ( Some ( OPTSET_BEHAVIOUR ) )
2021-08-24 12:45:31 +02:00
. help ( " Select the behaviour to use when receiving events while the command is running. Current default is queue, will change to do-nothing in 2.0. " )
. takes_value ( true )
. possible_values ( & [ " do-nothing " , " queue " , " restart " , " signal " ] )
. long ( " on-busy-update " ) )
. arg ( Arg ::with_name ( " restart " )
2021-11-22 09:23:29 +01:00
. help_heading ( Some ( OPTSET_BEHAVIOUR ) )
2021-08-24 12:45:31 +02:00
. help ( " Restart the process if it's still running. Shorthand for --on-busy-update=restart " )
. short ( " r " )
. long ( " restart " ) )
. arg ( Arg ::with_name ( " signal " )
2021-11-22 09:23:29 +01:00
. help_heading ( Some ( OPTSET_BEHAVIOUR ) )
2021-08-24 12:45:31 +02:00
. help ( " Specify the signal to send when using --on-busy-update=signal " )
. short ( " s " )
. long ( " signal " )
. takes_value ( true )
. value_name ( " signal " )
. default_value ( " SIGTERM " )
. hidden ( cfg! ( windows ) ) )
. arg ( Arg ::with_name ( " kill " )
2021-11-22 09:23:29 +01:00
. help_heading ( Some ( OPTSET_BEHAVIOUR ) )
2021-08-24 12:45:31 +02:00
. hidden ( true )
. short ( " k " )
. long ( " kill " ) )
. arg ( Arg ::with_name ( " debounce " )
2021-11-22 09:23:29 +01:00
. help_heading ( Some ( OPTSET_BEHAVIOUR ) )
2021-08-24 12:45:31 +02:00
. help ( " Set the timeout between detected change and command execution, defaults to 100ms " )
. takes_value ( true )
. value_name ( " milliseconds " )
. short ( " d " )
. long ( " debounce " ) )
. arg ( Arg ::with_name ( " verbose " )
2021-11-22 09:23:29 +01:00
. help_heading ( Some ( OPTSET_DEBUGGING ) )
2022-01-15 03:14:25 +01:00
. help ( " Print debugging messages (-v, -vv, -vvv, -vvvv; use -vvv for bug reports) " )
2021-08-24 12:45:31 +02:00
. multiple ( true )
. short ( " v " )
. long ( " verbose " ) )
. arg ( Arg ::with_name ( " print-events " )
2021-11-22 09:23:29 +01:00
. help_heading ( Some ( OPTSET_DEBUGGING ) )
2021-08-24 12:45:31 +02:00
. help ( " Print events that trigger actions " )
. long ( " print-events " )
. alias ( " changes-only " ) ) // --changes-only is deprecated (remove at v2)
2021-12-29 07:29:45 +01:00
. arg ( Arg ::with_name ( " no-vcs-ignore " )
2021-11-22 09:23:29 +01:00
. help_heading ( Some ( OPTSET_FILTERING ) )
2021-12-29 07:10:08 +01:00
. help ( " Skip auto-loading of VCS (Git, etc) ignore files " )
2021-08-24 12:45:31 +02:00
. long ( " no-vcs-ignore " ) )
2021-12-29 07:29:45 +01:00
. arg ( Arg ::with_name ( " no-project-ignore " )
2021-11-22 09:23:29 +01:00
. help_heading ( Some ( OPTSET_FILTERING ) )
2021-12-29 07:10:08 +01:00
. help ( " Skip auto-loading of project ignore files (.gitignore, .ignore, etc) " )
2021-08-24 12:45:31 +02:00
. long ( " no-ignore " ) )
2021-12-29 08:14:29 +01:00
. arg ( Arg ::with_name ( " no-default-ignore " )
2021-11-22 09:23:29 +01:00
. help_heading ( Some ( OPTSET_FILTERING ) )
2021-08-24 12:45:31 +02:00
. help ( " Skip auto-ignoring of commonly ignored globs " )
. long ( " no-default-ignore " ) )
2021-12-29 07:29:45 +01:00
. arg ( Arg ::with_name ( " no-global-ignore " )
2021-12-29 06:39:31 +01:00
. help_heading ( Some ( OPTSET_FILTERING ) )
. help ( " Skip auto-loading of global or environment-wide ignore files " )
2021-12-29 06:53:47 +01:00
. long ( " no-global-ignore " ) )
2021-08-24 12:45:31 +02:00
. arg ( Arg ::with_name ( " postpone " )
2021-11-22 09:23:29 +01:00
. help_heading ( Some ( OPTSET_BEHAVIOUR ) )
2021-08-24 12:45:31 +02:00
. help ( " Wait until first change to execute command " )
. short ( " p " )
. long ( " postpone " ) )
. arg ( Arg ::with_name ( " poll " )
2021-11-22 09:23:29 +01:00
. help_heading ( Some ( OPTSET_BEHAVIOUR ) )
2021-08-24 12:45:31 +02:00
. help ( " Force polling mode (interval in milliseconds) " )
. long ( " force-poll " )
. value_name ( " interval " ) )
. arg ( Arg ::with_name ( " shell " )
2021-11-22 09:23:29 +01:00
. help_heading ( Some ( OPTSET_COMMAND ) )
2021-08-24 12:45:31 +02:00
. help ( if cfg! ( windows ) {
" Use a different shell, or `none`. Try --shell=powershell, which will become the default in 2.0. "
} else {
" Use a different shell, or `none`. E.g. --shell=bash "
} )
. takes_value ( true )
. long ( " shell " ) )
// -n short form will not be removed, and instead become a shorthand for --shell=none
. arg ( Arg ::with_name ( " no-shell " )
2021-11-22 09:23:29 +01:00
. help_heading ( Some ( OPTSET_COMMAND ) )
2021-08-24 12:45:31 +02:00
. help ( " Do not wrap command in a shell. Deprecated: use --shell=none instead. " )
. short ( " n " )
. long ( " no-shell " ) )
2021-12-29 08:55:09 +01:00
. arg ( Arg ::with_name ( " no-environment " )
2021-11-22 09:23:29 +01:00
. help_heading ( Some ( OPTSET_OUTPUT ) )
2021-12-29 09:36:42 +01:00
. help ( " Do not set WATCHEXEC_*_PATH environment variables for the command " )
2021-08-24 12:45:31 +02:00
. long ( " no-environment " ) )
2021-12-23 15:47:15 +01:00
. arg ( Arg ::with_name ( " no-process-group " )
2021-11-22 09:23:29 +01:00
. help_heading ( Some ( OPTSET_COMMAND ) )
2021-08-24 12:45:31 +02:00
. help ( " Do not use a process group when running the command " )
. long ( " no-process-group " ) )
. arg ( Arg ::with_name ( " once " ) . short ( " 1 " ) . hidden ( true ) )
. arg ( Arg ::with_name ( " watch-when-idle " )
2021-11-22 09:23:29 +01:00
. help_heading ( Some ( OPTSET_BEHAVIOUR ) )
2021-08-24 12:45:31 +02:00
. help ( " Deprecated alias for --on-busy-update=do-nothing, which will become the default in 2.0. " )
. short ( " W " )
2021-08-24 12:56:15 +02:00
. hidden ( true )
2021-08-24 12:45:31 +02:00
. long ( " watch-when-idle " ) )
2021-12-24 07:06:01 +01:00
. arg ( Arg ::with_name ( " notif " )
2021-11-22 09:23:29 +01:00
. help_heading ( Some ( OPTSET_OUTPUT ) )
2021-12-24 07:06:01 +01:00
. help ( " Send a desktop notification when the command ends " )
2021-08-24 12:45:31 +02:00
. short ( " N " )
. long ( " notify " ) ) ;
2021-04-10 15:33:40 +02:00
2021-12-23 15:56:03 +01:00
let app = if tagged_filterer {
app . arg (
Arg ::with_name ( " filter " )
. help_heading ( Some ( OPTSET_FILTERING ) )
. help ( " Add tagged filter (e.g. 'path=foo*', 'type=dir', 'kind=Create(*)') " )
. short ( " f " )
. long ( " filter " )
. number_of_values ( 1 )
. multiple ( true )
. takes_value ( true )
. value_name ( " tagged filter " ) ,
)
. arg (
2021-12-29 06:39:31 +01:00
Arg ::with_name ( " filter-files " )
2021-12-23 15:56:03 +01:00
. help_heading ( Some ( OPTSET_FILTERING ) )
. help ( " Load tagged filters from a file " )
. short ( " F " )
. long ( " filter-file " )
. number_of_values ( 1 )
. multiple ( true )
. takes_value ( true )
. value_name ( " path " ) ,
)
2021-12-29 06:39:31 +01:00
. arg (
Arg ::with_name ( " no-global-filters " )
. help_heading ( Some ( OPTSET_FILTERING ) )
. help ( " Skip auto-loading of global or environment-wide ignore files " )
. long ( " no-default-filters " ) ,
)
2021-12-23 15:56:03 +01:00
. arg (
2021-12-29 07:29:45 +01:00
Arg ::with_name ( " no-meta " )
2021-12-23 15:56:03 +01:00
. help_heading ( Some ( OPTSET_FILTERING ) )
. help ( " Ignore metadata changes (equivalent of `-f 'kind*!Modify(Metadata(*))'`) " )
. long ( " no-meta " ) ,
)
} else {
app . arg (
Arg ::with_name ( " extensions " )
. help_heading ( Some ( OPTSET_FILTERING ) )
. help ( " Comma-separated list of file extensions to watch (e.g. js,css,html) " )
. short ( " e " )
. long ( " exts " )
. takes_value ( true ) ,
)
. arg (
2021-12-29 08:55:09 +01:00
Arg ::with_name ( " filter " )
2021-12-23 15:56:03 +01:00
. help_heading ( Some ( OPTSET_FILTERING ) )
. help ( " Ignore all modifications except those matching the pattern " )
. short ( " f " )
. long ( " filter " )
. number_of_values ( 1 )
. multiple ( true )
. takes_value ( true )
. value_name ( " pattern " ) ,
)
. arg (
2021-12-29 08:55:09 +01:00
Arg ::with_name ( " ignore " )
2021-12-23 15:56:03 +01:00
. help_heading ( Some ( OPTSET_FILTERING ) )
. help ( " Ignore modifications to paths matching the pattern " )
. short ( " i " )
. long ( " ignore " )
. number_of_values ( 1 )
. multiple ( true )
. takes_value ( true )
. value_name ( " pattern " ) ,
)
. arg (
2021-12-29 07:29:45 +01:00
Arg ::with_name ( " no-meta " )
2021-12-23 15:56:03 +01:00
. help_heading ( Some ( OPTSET_FILTERING ) )
. help ( " Ignore metadata changes " )
. long ( " no-meta " ) ,
)
} ;
2021-08-24 10:23:37 +02:00
let mut raw_args : Vec < OsString > = env ::args_os ( ) . collect ( ) ;
2021-04-10 15:33:40 +02:00
2021-08-24 10:23:37 +02:00
if let Some ( first ) = raw_args . get ( 1 ) . and_then ( | s | s . to_str ( ) ) {
if let Some ( arg_path ) = first . strip_prefix ( '@' ) . map ( Path ::new ) {
let arg_file = BufReader ::new (
File ::open ( arg_path )
2021-10-09 07:43:51 +02:00
. into_diagnostic ( )
2021-08-24 10:23:37 +02:00
. wrap_err_with ( | | format! ( " Failed to open argument file {:?} " , arg_path ) ) ? ,
) ;
2021-04-10 15:33:40 +02:00
2021-08-24 10:23:37 +02:00
let mut more_args : Vec < OsString > = arg_file
. lines ( )
2021-10-09 07:43:51 +02:00
. map ( | l | l . map ( OsString ::from ) . into_diagnostic ( ) )
2021-08-24 10:23:37 +02:00
. collect ::< Result < _ > > ( ) ? ;
2021-04-10 15:33:40 +02:00
2021-08-24 10:23:37 +02:00
more_args . insert ( 0 , raw_args . remove ( 0 ) ) ;
more_args . extend ( raw_args . into_iter ( ) . skip ( 1 ) ) ;
raw_args = more_args ;
}
}
2021-04-10 16:36:10 +02:00
2021-08-24 10:23:37 +02:00
Ok ( app . get_matches_from ( raw_args ) )
2021-04-10 16:36:10 +02:00
}