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

View File

@ -1,28 +1,86 @@
#[macro_use] #[macro_use]
extern crate log; extern crate log;
use std::{thread, env}; use std::process::exit;
use std::sync::{Mutex, Arc, mpsc}; use std::sync::{mpsc, Arc, Mutex};
use std::{env, thread};
use colored::*; use colored::*;
use percent_encoding::percent_decode; use percent_encoding::percent_decode;
use hyper::{Body, Response, Server};
use hyper::service::service_fn_ok;
use hyper::rt::Future; 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() { fn main() {
beautylog::init(log::LevelFilter::Info).ok(); 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" { let mut watching = None;
if let Err(e) = watch(&args[2]) { 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); error!("{}", e);
exit(1);
} }
} }
@ -32,7 +90,6 @@ fn main() {
let clone = tasks.clone(); let clone = tasks.clone();
thread::spawn(move || { thread::spawn(move || {
let addr = ([127, 0, 0, 1], 1500).into(); let addr = ([127, 0, 0, 1], 1500).into();
let server = Server::bind(&addr) let server = Server::bind(&addr)
@ -50,37 +107,37 @@ fn main() {
info!("mars server listening on {}...", addr); info!("mars server listening on {}...", addr);
hyper::rt::run(server); hyper::rt::run(server);
}); });
let mut builder = GeneralBuilder::new(); let mut builder = GeneralBuilder::new();
for _ in rx { for _ in rx {
let uri = { let uri = {
let mut tasks = tasks.lock().unwrap(); let mut tasks = tasks.lock().unwrap();
tasks.pop() tasks.pop()
}; };
if let Some(uri) = uri { if let Some(uri) = uri {
let uri = percent_decode(uri.path().as_bytes()).decode_utf8().unwrap(); let uri = percent_decode(uri.path().as_bytes()).decode_utf8().unwrap();
let (path, args) = builder_arguments_from_string(&*uri); let (path, args) = builder_arguments_from_string(&*uri);
let path_string = path.to_str().unwrap(); 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()); println!("{}", start_string.bold().green());
match builder.build(&path, &args) { match builder.build(&path, &args) {
Err(_) => { Err(_) => {
println!("{}", "--------- FAIL ---------".bold().red()); println!("{}", "--------- FAIL ---------".bold().red());
}, }
Ok(_) => { Ok(_) => {
println!("{}", "----- SUCCESSFUL -----".bold().green()); println!("{}", "----- SUCCESSFUL -----".bold().green());
}, }
}; };
println!(); println!();
} }
} }
} }