Added notifications

This commit is contained in:
Thomas Forgione 2018-09-25 14:51:32 +02:00
parent 0aa7822621
commit a47ee3c814
No known key found for this signature in database
GPG Key ID: 203DAEA747F48F41
4 changed files with 136 additions and 37 deletions

View File

@ -4,6 +4,8 @@ version = "0.1.0"
authors = ["Thomas Forgione <thomas@forgione.fr>"] authors = ["Thomas Forgione <thomas@forgione.fr>"]
[dependencies] [dependencies]
dirs = "1.0.4"
notify-rust = "3.4.2"
colored = "1.6.1" colored = "1.6.1"
hyper = "0.12.10" hyper = "0.12.10"

BIN
assets/failure.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
assets/success.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

@ -1,20 +1,30 @@
extern crate dirs;
extern crate notify_rust;
extern crate hyper; extern crate hyper;
use std::io; use std::io;
use std::process::{Command, ExitStatus}; use std::process::{Command, ExitStatus};
use std::path::PathBuf; use std::path::PathBuf;
use std::ffi::OsStr; use std::fs::{File, create_dir_all};
use std::fs::create_dir_all;
macro_rules! destroy { use notify_rust::Notification;
($e: expr) => {
match $e { /// The different types of error that can occur.
Err(_) => { #[derive(Debug)]
let err: Result<(), ()> = Err(()); pub enum Error {
return err; /// No builder can build this project.
}, NoBuilderFound,
_ => (),
/// An io::Error happened while running a build command.
IoError(io::Error),
/// A command exited with non-zero exit code.
CommandError(String, Vec<String>, ExitStatus),
} }
impl From<io::Error> for Error {
fn from(e: io::Error) -> Error {
Error::IoError(e)
} }
} }
@ -26,32 +36,31 @@ pub fn contains_file(path: &str, file: &str) -> bool {
} }
/// Tries to build a certain directory using the specified builders. /// Tries to build a certain directory using the specified builders.
pub fn run_command(command: &str, path: &str) -> Result<ExitStatus, io::Error> { pub fn run_command(command: &str, path: &str) -> Result<(), Error> {
let mut child = match Command::new(command) run_command_with_args(command, path, vec![])
.current_dir(path)
.spawn() {
Err(x) => return Err(x),
Ok(child) => child,
};
child.wait()
} }
/// 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<I, S>(command: &str, path: &str, args: I) -> Result<ExitStatus, io::Error> where pub fn run_command_with_args(command: &str, path: &str, args: Vec<&str>) -> Result<(), Error> {
I: IntoIterator<Item=S>,
S: AsRef<OsStr> { let new_args: Vec<String> = args.iter().map(|x| String::from(*x)).collect();
let mut child = Command::new(command) let mut child = Command::new(command)
.current_dir(path) .current_dir(path)
.args(args) .args(args)
.spawn()?; .spawn()?;
child.wait() let exit = child.wait()?;
if exit.success() {
Ok(())
} else {
Err(Error::CommandError(command.to_owned(), new_args, exit))
}
} }
/// 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, builders: &Vec<Box<Builder>>) -> Result<(), ()> { pub fn build(path: &PathBuf, builders: &Vec<Box<Builder>>) -> Result<(), Error> {
let mut path = path.clone(); let mut path = path.clone();
@ -59,7 +68,7 @@ pub fn build(path: &PathBuf, builders: &Vec<Box<Builder>>) -> Result<(), ()> {
if path.to_str().unwrap() == "/" { if path.to_str().unwrap() == "/" {
// Couldn't find a buildable directory // Couldn't find a buildable directory
return Err(()); return Err(Error::NoBuilderFound);
} }
for builder in builders { for builder in builders {
@ -86,6 +95,12 @@ pub fn destroy<T, E>(result: Result<T, E>) -> Result<(), ()> {
/// A general builders that contains many builders and can build any type of code. /// A general builders that contains many builders and can build any type of code.
pub struct GeneralBuilder { pub struct GeneralBuilder {
/// The path to the success icon.
success: PathBuf,
/// The path to the failure icon.
failure: PathBuf,
/// The builders contained. /// The builders contained.
builders: Vec<Box<Builder>>, builders: Vec<Box<Builder>>,
} }
@ -93,7 +108,41 @@ 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());
config.push("mars");
let mut success = config.clone();
success.push("success.png");
let mut failure = config.clone();
failure.push("failure.png");
if ! success.exists() || ! failure.exists() {
create_dir_all(&config).unwrap();
const SUCCESS_BYTES: &[u8] = include_bytes!("../assets/success.png");
const FAILURE_BYTES: &[u8] = include_bytes!("../assets/failure.png");
use std::io::Write;
let mut path = config.clone();
path.push("success.png");
let mut success_file = File::create(&success).unwrap();
success_file.write_all(SUCCESS_BYTES).unwrap();
let mut path = config.clone();
path.push("success.png");
let mut failure_file = File::create(&failure).unwrap();
failure_file.write_all(FAILURE_BYTES).unwrap();
}
GeneralBuilder { GeneralBuilder {
success: success,
failure: failure,
builders: vec![ builders: vec![
Box::new(MakeBuilder::new()), Box::new(MakeBuilder::new()),
Box::new(CMakeBuilder::new()), Box::new(CMakeBuilder::new()),
@ -103,9 +152,57 @@ impl GeneralBuilder {
} }
/// Triggers a build. /// Triggers a build.
pub fn build(&self, path: &str) -> Result<(), ()> { pub fn build(&self, path: &str) -> Result<(), Error> {
build(&PathBuf::from(path), &self.builders) let result = build(&PathBuf::from(path), &self.builders);
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(&self) {
println!("{}", self.success.to_str().unwrap());
let _ = Notification::new()
.appname("Mars")
.summary("Success")
.body("Mars finished successfully")
.icon(self.success.to_str().unwrap())
.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(),
Error::CommandError(ref command, ref args, ref status) => {
let status = match status.code() {
None => "None".to_owned(),
Some(e) => e.to_string(),
};
format!("Command \"{} {}\" failed: {}",
command, args.join(" "), status).to_string()
},
};
let _ = Notification::new()
.appname("Mars")
.summary("Failure")
.body(&body)
.icon(self.failure.to_str().unwrap())
.show();
}
} }
/// A generic builder. It can build some projects. /// A generic builder. It can build some projects.
@ -115,7 +212,7 @@ pub trait Builder {
fn is_buildable(&self, path: &str) -> bool; fn is_buildable(&self, path: &str) -> bool;
/// Trigger all the commands to build the project. /// Trigger all the commands to build the project.
fn build(&self, path: &str) -> Result<(), ()>; fn build(&self, path: &str) -> Result<(), Error>;
} }
/// The builder that looks for makefiles. /// The builder that looks for makefiles.
@ -133,8 +230,8 @@ impl Builder for MakeBuilder {
contains_file(path, "Makefile") contains_file(path, "Makefile")
} }
fn build(&self, path: &str) -> Result<(), ()> { fn build(&self, path: &str) -> Result<(), Error> {
destroy!(run_command("make", path)); run_command("make", path)?;
Ok(()) Ok(())
} }
} }
@ -154,7 +251,7 @@ impl Builder for CMakeBuilder {
contains_file(path, "CMakeLists.txt") contains_file(path, "CMakeLists.txt")
} }
fn build(&self, path: &str) -> Result<(), ()> { fn build(&self, path: &str) -> Result<(), Error> {
let mut build = PathBuf::from(path); let mut build = PathBuf::from(path);
build.push("build"); build.push("build");
@ -163,14 +260,14 @@ impl Builder for CMakeBuilder {
if ! contains_file(path, "build/Makefile") { if ! contains_file(path, "build/Makefile") {
// Create build directory // Create build directory
destroy!(create_dir_all(&build)); create_dir_all(&build)?;
// Run cmake .. in build directory // Run cmake .. in build directory
destroy!(run_command_with_args("cmake", build.to_str().unwrap(), [".."].iter())); run_command_with_args("cmake", build.to_str().unwrap(), vec![".."])?;
} }
// Run make in build directory // Run make in build directory
destroy!(run_command("make", build.to_str().unwrap())); run_command("make", build.to_str().unwrap())?;
Ok(()) Ok(())
} }
@ -191,7 +288,7 @@ impl Builder for CargoBuilder {
contains_file(path, "Cargo.toml") contains_file(path, "Cargo.toml")
} }
fn build(&self, path: &str) -> Result<(), ()> { fn build(&self, path: &str) -> Result<(), Error> {
destroy(run_command_with_args("cargo", path, ["build"].iter())) run_command_with_args("cargo", path, vec!["build"])
} }
} }