mirror of
https://github.com/zaphar/runwhen.git
synced 2025-07-22 20:39:49 -04:00
Modifies the trigger to run when the command fails instead of when it succeeds.
153 lines
5.7 KiB
Rust
153 lines
5.7 KiB
Rust
// Copyright 2017 Jeremy Wall <jeremy@marzhillstudios.com>
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
// runwhen - A utility that runs commands on user defined triggers.
|
|
#[macro_use]
|
|
extern crate clap;
|
|
extern crate humantime;
|
|
extern crate notify;
|
|
|
|
use std::process;
|
|
use std::str::FromStr;
|
|
|
|
mod traits;
|
|
mod file;
|
|
mod timer;
|
|
mod error;
|
|
mod events;
|
|
mod exec;
|
|
|
|
use traits::Process;
|
|
use file::FileProcess;
|
|
use timer::TimerProcess;
|
|
use exec::ExecProcess;
|
|
use events::WatchEventType;
|
|
|
|
fn do_flags<'a>() -> clap::ArgMatches<'a> {
|
|
clap_app!(
|
|
runwhen =>
|
|
(version: crate_version!())
|
|
(author: crate_authors!())
|
|
(about: "Runs a command on user defined triggers.")
|
|
(@arg cmd: -c --cmd +required +takes_value "Command to run on supplied triggers")
|
|
(@arg env: -e --env +takes_value ... "Command to run on supplied triggers")
|
|
(@subcommand watch =>
|
|
(about: "Trigger that fires when a file or directory changes.")
|
|
// TODO(jeremy): We need to support filters
|
|
(@arg file: -f --file +takes_value
|
|
"File/Directory to watch. (default current working directory)")
|
|
(@arg filetouch: --touch
|
|
"Watches for attribute modifications as well as content changes.")
|
|
(@arg wait: --poll +takes_value
|
|
"How frequently to poll for events (default 5s)")
|
|
)
|
|
(@subcommand timer =>
|
|
(about: "Trigger that fires on a timer.")
|
|
(@arg duration: -t --duration +required +takes_value
|
|
"Defines timer frequency.")
|
|
(@arg repeat: -n --repeat +takes_value
|
|
"Defines an optional max number times to run on repeat.")
|
|
)
|
|
(@subcommand success =>
|
|
(about: "Trigger that fires if a command runs successful.")
|
|
(@arg ifcmd: --if +required +takes_value
|
|
"The command to test for successful exit from")
|
|
(@arg not: --not
|
|
"Negate the test command so we run on failure instead of success.")
|
|
(@arg wait: --poll +takes_value
|
|
"How frequently to test command (default 5s)")
|
|
)
|
|
)
|
|
.get_matches()
|
|
}
|
|
|
|
fn main() {
|
|
let app = do_flags();
|
|
// Unwrap because this flag is required.
|
|
let cmd = app.value_of("cmd").expect("cmd flag is required");
|
|
let mut maybe_env = None;
|
|
if let Some(env_values) = app.values_of("env") {
|
|
let mut env_vec = Vec::new();
|
|
for v in env_values {
|
|
env_vec.push(v);
|
|
}
|
|
maybe_env = Some(env_vec);
|
|
}
|
|
let mut process: Option<Box<Process>> = None;
|
|
if let Some(matches) = app.subcommand_matches("watch") {
|
|
// Unwrap because this flag is required.
|
|
let file = matches.value_of("file").unwrap_or(".");
|
|
let mut method = WatchEventType::Changed;
|
|
if matches.is_present("filetouch") {
|
|
method = WatchEventType::Touched;
|
|
}
|
|
let poll = matches.value_of("poll").unwrap_or("5s");
|
|
let dur = humantime::parse_duration(poll).expect("Invalid poll value.");
|
|
process = Some(Box::new(FileProcess::new(
|
|
cmd, maybe_env, file, method, dur)));
|
|
} else if let Some(matches) = app.subcommand_matches("timer") {
|
|
// Unwrap because this flag is required.
|
|
let dur = humantime::parse_duration(matches.value_of("duration")
|
|
.expect("duration flag is required"));
|
|
match dur {
|
|
Ok(duration) => {
|
|
let max_repeat = if let Some(val) = matches.value_of("repeat") {
|
|
match u32::from_str(val) {
|
|
Ok(n) => Some(n),
|
|
Err(e) => {
|
|
println!("Invalid --repeat value {}", e);
|
|
println!("{}", matches.usage());
|
|
process::exit(1)
|
|
}
|
|
}
|
|
} else {
|
|
None
|
|
};
|
|
process = Some(Box::new(TimerProcess::new(
|
|
cmd, maybe_env, duration, max_repeat)));
|
|
}
|
|
Err(msg) => {
|
|
println!("Malformed duration {:?}", msg);
|
|
process::exit(1);
|
|
}
|
|
}
|
|
} else if let Some(matches) = app.subcommand_matches("success") {
|
|
// unwrap because this is required.
|
|
let ifcmd = matches.value_of("ifcmd").expect("ifcmd flag is required");
|
|
let negate = matches.is_present("not");
|
|
let dur = humantime::parse_duration(matches.value_of("poll").unwrap_or("5s"));
|
|
process = match dur {
|
|
Ok(duration) => Some(Box::new(ExecProcess::new(ifcmd, cmd, negate, maybe_env, duration))),
|
|
Err(msg) => {
|
|
println!("Malformed poll {:?}", msg);
|
|
process::exit(1)
|
|
}
|
|
}
|
|
}
|
|
match process {
|
|
Some(process) => {
|
|
match process.run() {
|
|
Ok(_) => return,
|
|
Err(err) => {
|
|
println!("{0}", err);
|
|
process::exit(1)
|
|
}
|
|
}
|
|
}
|
|
None => {
|
|
println!("You must specify a subcommand.");
|
|
process::exit(1)
|
|
}
|
|
}
|
|
}
|