Adds support for ignore

This commit is contained in:
Thomas Forgione 2023-10-12 15:49:26 +02:00
parent 8be3cb4a88
commit 5797a9a80b
2 changed files with 124 additions and 67 deletions

View File

@ -1,17 +1,17 @@
#[macro_use]
extern crate log;
use std::{io, thread, fmt};
use std::env::current_dir;
use std::process::{Command, ExitStatus};
use std::fs::{create_dir_all, File};
use std::path::{Path, PathBuf};
use std::fs::{File, create_dir_all};
use std::process::{Command, ExitStatus};
use std::sync::mpsc::channel;
use std::time::Duration;
use std::{fmt, io, thread};
use colored::*;
use notify::{Watcher, RecursiveMode, watcher, DebouncedEvent};
use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher};
use notify_rust::{Notification, NotificationHandle};
@ -39,7 +39,11 @@ impl fmt::Display for Error {
match self {
Error::NoBuilderFound => write!(fmt, "no builder found"),
Error::IoError(e) => write!(fmt, "i/o error occured: {}", e),
Error::CommandError(command, _, status) => write!(fmt, "command {} exited with status code {}", command, status),
Error::CommandError(command, _, status) => write!(
fmt,
"command {} exited with status code {}",
command, status
),
}
}
}
@ -58,11 +62,7 @@ pub fn run_command(command: &str, path: &str) -> Result<(), Error> {
/// Run a build commands, and wait untils its finished, returning its result.
pub fn run_command_with_args(command: &str, path: &str, args: &Vec<String>) -> Result<(), Error> {
let mut child = Command::new(command)
.current_dir(path)
.args(args)
.spawn()?;
let mut child = Command::new(command).current_dir(path).args(args).spawn()?;
let exit = child.wait()?;
@ -74,29 +74,28 @@ pub fn run_command_with_args(command: &str, path: &str, args: &Vec<String>) -> R
}
/// Tries to build a certain directory using the specified builders.
pub fn build(path: &PathBuf, args: &Vec<String>, builders: &Vec<Box<Builder>>) -> Result<(), Error> {
pub fn build(
path: &PathBuf,
args: &Vec<String>,
builders: &Vec<Box<dyn Builder>>,
) -> Result<(), Error> {
let mut path = path.clone();
loop {
if path.to_str().unwrap() == "/" {
// Couldn't find a buildable directory
return Err(Error::NoBuilderFound);
}
for builder in builders {
if builder.is_buildable(path.to_str().unwrap()) {
builder.build(path.to_str().unwrap(), &args)?;
return Ok(());
}
}
path.pop();
};
}
}
/// Destucture a Result to return a Result<(), ()>
@ -121,9 +120,13 @@ pub fn builder_arguments_from_string(uri: &str) -> (PathBuf, Vec<String>) {
}
/// Watches a directory and builds it when a modification occurs.
pub fn watch<P: AsRef<Path>>(p: P) -> Result<(), Error> {
let mut path = current_dir()?;
path.push(p.as_ref());
pub fn watch<P: AsRef<Path>>(p: P, ignore: Vec<String>) -> Result<(), Error> {
let path = PathBuf::from(p.as_ref()).canonicalize()?;
let ignore = ignore
.into_iter()
.map(|x| PathBuf::from(x).canonicalize())
.collect::<Result<Vec<_>, _>>()?;
thread::spawn(move || {
let mut builder = GeneralBuilder::new();
@ -134,7 +137,7 @@ pub fn watch<P: AsRef<Path>>(p: P) -> Result<(), Error> {
Err(e) => {
warn!("couldn't watch directory {}: {}", path.display(), e);
return;
},
}
};
if let Err(e) = watcher.watch(&path, RecursiveMode::Recursive) {
@ -146,11 +149,14 @@ pub fn watch<P: AsRef<Path>>(p: P) -> Result<(), Error> {
loop {
match rx.recv() {
Ok(DebouncedEvent::NoticeWrite(_)) |
Ok(DebouncedEvent::Write(_)) |
Ok(DebouncedEvent::Create(_)) |
Ok(DebouncedEvent::Rename(_, _)) |
Ok(DebouncedEvent::Chmod(_)) => {
Ok(DebouncedEvent::NoticeWrite(x))
| Ok(DebouncedEvent::Write(x))
| Ok(DebouncedEvent::Create(x))
| Ok(DebouncedEvent::Rename(_, x))
| Ok(DebouncedEvent::Chmod(x)) => {
if ignore.iter().any(|y| x.starts_with(y)) {
continue;
}
let start_string = format!("---- STARTING BUILD ---- from {}", path.display());
println!("{}", start_string.bold().green());
@ -158,13 +164,13 @@ pub fn watch<P: AsRef<Path>>(p: P) -> Result<(), Error> {
match builder.build(&path, &vec![]) {
Err(_) => {
println!("{}", "--------- FAIL ---------".bold().red());
},
}
Ok(_) => {
println!("{}", "----- SUCCESSFUL -----".bold().green());
},
}
};
println!();
},
}
Err(e) => error!("watch error: {:?}", e),
_ => (),
}
@ -183,7 +189,7 @@ pub struct GeneralBuilder {
failure: PathBuf,
/// The builders contained.
builders: Vec<Box<Builder>>,
builders: Vec<Box<dyn Builder>>,
/// The id of the notification to update a notification if possible.
notification_handle: Option<NotificationHandle>,
@ -192,7 +198,6 @@ pub struct GeneralBuilder {
impl GeneralBuilder {
/// Creates a new general builder with the defaults builders.
pub fn new() -> GeneralBuilder {
let mut config = PathBuf::from(dirs::config_dir().unwrap());
config.push("mars");
@ -202,8 +207,7 @@ impl GeneralBuilder {
let mut failure = config.clone();
failure.push("failure.png");
if ! success.exists() || ! failure.exists() {
if !success.exists() || !failure.exists() {
create_dir_all(&config).unwrap();
const SUCCESS_BYTES: &[u8] = include_bytes!("../assets/success.png");
@ -243,14 +247,13 @@ impl GeneralBuilder {
match result {
Ok(_) => self.notify_success(),
Err(ref e) => self.notify_error(&e),
}
}
result
}
/// Sends a notification of a successful build.
pub fn notify_success(&mut self) {
match self.notification_handle.as_mut() {
Some(handle) => {
handle
@ -260,7 +263,7 @@ impl GeneralBuilder {
.icon(self.success.to_str().unwrap());
handle.update();
},
}
None => {
let handle = Notification::new()
@ -271,17 +274,14 @@ impl GeneralBuilder {
.show();
self.notification_handle = handle.ok();
},
}
}
let _ = Notification::new()
.show();
let _ = Notification::new().show();
}
/// Sends a notification of an error.
pub fn notify_error(&self, e: &Error) {
let body = match e {
Error::NoBuilderFound => "No builder was found for this directory".to_string(),
Error::IoError(ref e) => format!("Error while running command: {:?}", e).to_string(),
@ -291,9 +291,14 @@ impl GeneralBuilder {
Some(e) => e.to_string(),
};
format!("Command \"{} {}\" failed: {}",
command, args.join(" "), status).to_string()
},
format!(
"Command \"{} {}\" failed: {}",
command,
args.join(" "),
status
)
.to_string()
}
};
let _ = Notification::new()
@ -302,9 +307,7 @@ impl GeneralBuilder {
.body(&body)
.icon(self.failure.to_str().unwrap())
.show();
}
}
/// A generic builder. It can build some projects.
@ -354,13 +357,11 @@ impl Builder for CMakeBuilder {
}
fn build(&self, path: &str, args: &Vec<String>) -> Result<(), Error> {
let mut build = PathBuf::from(path);
build.push("build");
// If there's a build/Makefile, we'll only make
if ! contains_file(path, "build/Makefile") {
if !contains_file(path, "build/Makefile") {
// Create build directory
create_dir_all(&build)?;
@ -372,7 +373,6 @@ impl Builder for CMakeBuilder {
run_command_with_args("make", build.to_str().unwrap(), args)?;
Ok(())
}
}
/// The builder that looks for Cargo.toml.

View File

@ -1,28 +1,86 @@
#[macro_use]
extern crate log;
use std::{thread, env};
use std::sync::{Mutex, Arc, mpsc};
use std::process::exit;
use std::sync::{mpsc, Arc, Mutex};
use std::{env, thread};
use colored::*;
use percent_encoding::percent_decode;
use hyper::{Body, Response, Server};
use hyper::service::service_fn_ok;
use hyper::rt::Future;
use hyper::service::service_fn_ok;
use hyper::{Body, Response, Server};
use mars::{GeneralBuilder, builder_arguments_from_string, watch};
use mars::{builder_arguments_from_string, watch, GeneralBuilder};
fn main() {
beautylog::init(log::LevelFilter::Info).ok();
let args = env::args().collect::<Vec<_>>();
let mut args = env::args()
.skip(1)
.collect::<Vec<_>>()
.into_iter()
.peekable();
if args.len() == 3 && args[1] == "-w" {
if let Err(e) = watch(&args[2]) {
let mut watching = None;
let mut ignore = vec![];
let exit_code = loop {
let arg = args.next();
let arg = arg.as_deref();
let next = args.peek();
let next = next.as_deref();
let watch_is_some = watching.is_some();
match (arg, next, watch_is_some) {
(None, _, _) => {
break None;
}
(Some("-w") | Some("--watch"), Some(value), false) => {
watching = Some(value.to_string());
}
(Some("-w") | Some("--watch"), Some(_), true) => {
error!("argument watch present multiple times");
break Some(1);
}
(Some("-w") | Some("--watch"), None, _) => {
error!("argument watch without value");
break Some(1);
}
(Some("-i") | Some("--ignore"), Some(value), _) => {
ignore.push(value.to_string());
}
(Some("-i") | Some("--ignore"), None, _) => {
error!("argument ignore without value");
break Some(1);
}
(Some(value), _, _) => {
error!("argument \"{}\" unknown", value);
break Some(1);
}
}
args.next();
};
if let Some(exit_code) = exit_code {
exit(exit_code);
}
if let Some(watching) = watching {
if let Err(e) = watch(&watching, ignore) {
error!("{}", e);
exit(1);
}
}
@ -32,7 +90,6 @@ fn main() {
let clone = tasks.clone();
thread::spawn(move || {
let addr = ([127, 0, 0, 1], 1500).into();
let server = Server::bind(&addr)
@ -50,37 +107,37 @@ fn main() {
info!("mars server listening on {}...", addr);
hyper::rt::run(server);
});
let mut builder = GeneralBuilder::new();
for _ in rx {
let uri = {
let mut tasks = tasks.lock().unwrap();
tasks.pop()
};
if let Some(uri) = uri {
let uri = percent_decode(uri.path().as_bytes()).decode_utf8().unwrap();
let (path, args) = builder_arguments_from_string(&*uri);
let path_string = path.to_str().unwrap();
let start_string = format!("---- STARTING BUILD ---- from {} {}", path_string, args.join(" "));
let start_string = format!(
"---- STARTING BUILD ---- from {} {}",
path_string,
args.join(" ")
);
println!("{}", start_string.bold().green());
match builder.build(&path, &args) {
Err(_) => {
println!("{}", "--------- FAIL ---------".bold().red());
},
}
Ok(_) => {
println!("{}", "----- SUCCESSFUL -----".bold().green());
},
}
};
println!();
}
}
}