Added notifications
This commit is contained in:
parent
0aa7822621
commit
a47ee3c814
|
@ -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"
|
||||||
|
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 3.4 KiB |
Binary file not shown.
After Width: | Height: | Size: 5.8 KiB |
171
src/lib.rs
171
src/lib.rs
|
@ -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"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue