mirror of
https://github.com/zaphar/runwhen.git
synced 2025-07-22 20:39:49 -04:00
123 lines
5.0 KiB
Rust
123 lines
5.0 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::{path::PathBuf, process, str::FromStr};
|
|
|
|
mod error;
|
|
mod events;
|
|
mod exec;
|
|
mod file;
|
|
mod timer;
|
|
mod traits;
|
|
|
|
use events::WatchEventType;
|
|
use exec::ExecProcess;
|
|
use file::FileProcess;
|
|
use timer::TimerProcess;
|
|
use traits::Process;
|
|
|
|
#[rustfmt::skip]
|
|
fn do_flags() -> clap::ArgMatches {
|
|
clap::command!()
|
|
.version(crate_version!())
|
|
.author(crate_authors!())
|
|
.about("Runs a command on user defined triggers.")
|
|
.arg(arg!(-c --cmd).takes_value(true).help("The command to run on the trigger"))
|
|
.arg(arg!(-e --env ...).takes_value(true).help("Set of environment variables to set for the command"))
|
|
.subcommand(
|
|
clap::Command::new("watch")
|
|
.about("Trigger that fires when a file or directory changes.")
|
|
.arg(
|
|
arg!(-f --file)
|
|
.takes_value(true).value_parser(value_parser!(PathBuf)).help("File or directory to watch for changes"),
|
|
)
|
|
.arg(arg!(--touch).name("filetouch").help("Use file or directory timestamps to monitor for changes."))
|
|
.arg(arg!(--poll).value_parser(value_parser!(humantime::Duration)).help("Duration of time between polls")))
|
|
.subcommand(
|
|
clap::Command::new("timer")
|
|
.about("Run command on a timer")
|
|
.arg(arg!(-t --duration).takes_value(true).value_parser(value_parser!(humantime::Duration)).help("Duration between runs"))
|
|
.arg(arg!(-n --repeat).value_parser(value_parser!(u32))).about("Number of times to run before finishing"))
|
|
.subcommand(
|
|
clap::Command::new("success")
|
|
.about("Run a command when a test command succeeds")
|
|
.arg(arg!(--if).value_parser(value_parser!(String)).help("The command to run and check for success on"))
|
|
.arg(arg!(--not).help("Negate the success of the command"))
|
|
.arg(arg!(--poll).value_parser(value_parser!(humantime::Duration)).help("Duration of time between poll")))
|
|
.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.to_string());
|
|
}
|
|
maybe_env = Some(env_vec);
|
|
}
|
|
let mut proc: Box<dyn Process> = if let Some(matches) = app.subcommand_matches("watch") {
|
|
let file = match matches.values_of("file") {
|
|
Some(v) => v.collect(),
|
|
// The default is our current directory
|
|
None => vec!["."],
|
|
};
|
|
let mut method = WatchEventType::Changed;
|
|
if matches.is_present("filetouch") {
|
|
method = WatchEventType::Touched;
|
|
}
|
|
let duration = *matches
|
|
.get_one::<humantime::Duration>("poll")
|
|
.cloned()
|
|
.unwrap_or(humantime::Duration::from_str("5s").unwrap());
|
|
Box::new(FileProcess::new(cmd, maybe_env, file, method, duration))
|
|
} else if let Some(matches) = app.subcommand_matches("timer") {
|
|
// TODO(jwall): This should use cancelable commands.
|
|
// Unwrap because this flag is required.
|
|
let duration = matches
|
|
.get_one::<humantime::Duration>("duration")
|
|
.expect("duration flag is required")
|
|
.clone();
|
|
let max_repeat = matches.get_one::<u32>("repeat").cloned();
|
|
Box::new(TimerProcess::new(cmd, maybe_env, *duration, max_repeat))
|
|
} 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 duration = *matches
|
|
.get_one::<humantime::Duration>("poll")
|
|
.cloned()
|
|
.unwrap_or(humantime::Duration::from_str("5s").unwrap());
|
|
Box::new(ExecProcess::new(ifcmd, cmd, negate, maybe_env, duration))
|
|
} else {
|
|
println!("You must specify a subcommand.");
|
|
process::exit(1)
|
|
};
|
|
match proc.run() {
|
|
Ok(_) => return,
|
|
Err(err) => {
|
|
println!("{0}", err);
|
|
process::exit(1)
|
|
}
|
|
}
|
|
}
|