diff --git a/src/check.rs b/src/check.rs index e0d8945..b65011c 100644 --- a/src/check.rs +++ b/src/check.rs @@ -2,74 +2,89 @@ use std::fs; use termion::color; -use crate::{tasks::FormatTask, utils::log_failure}; +use crate::{ + tasks::FormatTask, + utils::{log_failure, log_process}, +}; const FORMAT_CONFIG: &str = r#"{BasedOnStyle: llvm}"#; mod testables; +pub fn main(files: Vec) { + for file in files { + log_process(&format!("checking '{file}'")); + check_formatting(file.to_string()); + let content = fs::read_to_string(&file).unwrap(); + for test in testables::tests() { + if let Err(reason) = test.test(content.clone()) { + let name = test.name(); + log_failure(&format!("'{file}': rule '{name}' fails:")); + println!("{reason}"); + } + } + } +} + pub enum Diff { ToRemove { index: usize, content: String }, ToAdd { index: usize, content: String }, Keep { index: usize, content: String }, } -pub fn main(files: Vec) { - for file in files { - let content = fs::read_to_string(&file).unwrap(); - let formatted = FormatTask::new(file, FORMAT_CONFIG.into()).run(); - let mut line_number = 0usize; - let mut invalid = false; - let differences = diff::lines(&content, &formatted) - .into_iter() - .map(|change| { - match change { - diff::Result::Left(_) | diff::Result::Both(_, _) => { - line_number += 1; - } - _ => (), +fn check_formatting(file: String) { + let content = fs::read_to_string(&file).unwrap(); + let formatted = FormatTask::new(file.clone(), FORMAT_CONFIG.into()).run(); + let mut line_number = 0usize; + let mut invalid = false; + let differences = diff::lines(&content, &formatted) + .into_iter() + .map(|change| { + match change { + diff::Result::Left(_) | diff::Result::Both(_, _) => { + line_number += 1; } - (line_number, change) - }) - .map(|(index, d)| match d { - diff::Result::Left(content) => { - invalid = true; - Diff::ToRemove { - index, - content: content.into(), - } - } - diff::Result::Both(content, _) => Diff::Keep { + _ => (), + } + (line_number, change) + }) + .map(|(index, d)| match d { + diff::Result::Left(content) => { + invalid = true; + Diff::ToRemove { index, content: content.into(), - }, - diff::Result::Right(content) => { - invalid = true; - Diff::ToAdd { - index, - content: content.into(), - } } - }) - .collect::>(); + } + diff::Result::Both(content, _) => Diff::Keep { + index, + content: content.into(), + }, + diff::Result::Right(content) => { + invalid = true; + Diff::ToAdd { + index, + content: content.into(), + } + } + }) + .collect::>(); + if invalid { + log_failure(&format!("'{file}': invalid formatting:")); + let red = color::Fg(color::Red); + let green = color::Fg(color::Green); + let reset = color::Fg(color::Reset); - if invalid { - log_failure("invalid formatting:"); - let red = color::Fg(color::Red); - let green = color::Fg(color::Green); - let reset = color::Fg(color::Reset); - - for difference in differences { - match difference { - Diff::ToRemove { index, content } => { - println!("{red} {index} - | {content}{reset}") - } - Diff::ToAdd { index, content } => { - println!("{green} {index} + | {content}{reset}") - } - Diff::Keep { index, content } => { - println!(" {index} | {content}") - } + for difference in differences { + match difference { + Diff::ToRemove { index, content } => { + println!("{red}{index:>3} -|{content}{reset}") + } + Diff::ToAdd { index, content } => { + println!("{green}{index:>3} +|{content}{reset}") + } + Diff::Keep { index, content } => { + println!("{index:>3} |{content}") } } } diff --git a/src/check/testables.rs b/src/check/testables.rs index e85ce04..e86c857 100644 --- a/src/check/testables.rs +++ b/src/check/testables.rs @@ -1,5 +1,7 @@ +use crate::utils::log_failure; + fn ends_with_newline(source: String) -> Result<(), String> { - if !source.ends_with("\n") { + if !source.ends_with('\n') { Err("source does not end with newline".into()) } else { Ok(()) diff --git a/src/main.rs b/src/main.rs index 4178f88..ce79166 100644 --- a/src/main.rs +++ b/src/main.rs @@ -30,7 +30,7 @@ pub enum Commands { files: Vec, }, - /// Runs tests contained within a particular test file or + /// Runs tests contained within a particular test file or the default test file test { /// Wether to capture standard output or not. #[clap(short, long)] @@ -44,40 +44,18 @@ pub enum Commands { tests: Vec, }, - /// Watches changes to source files and re run them + /// Watches changes to the project included files and runs a command on changes watch { - #[clap(subcommand)] - command: WatchSubcommand, + #[clap(short)] + files: Option>, + /// command to run on changes (ex: "pi test") + command: String, }, /// init { path: String }, } -#[allow(non_camel_case_types)] -#[derive(Subcommand)] -pub enum WatchSubcommand { - /// Runs a set of files or the default target. - run { - /// Files to run. - files: Vec, - }, - - /// Runs tests contained within a particular test file or - test { - /// Wether to capture standard output or not. - #[clap(short, long)] - capture: bool, - - /// Files to run tests from. - files: Vec, - - /// Specific tests to run. - #[clap(short, long)] - tests: Vec, - }, -} - fn append_includes(list: &mut Vec) { list.extend( Config::get_current() @@ -117,34 +95,18 @@ fn main() { mut files, tests, } => { + if files.is_empty() { + files.push(Config::get_current().test_file().to_string()); + } append_includes(&mut files); let args = compilation_args(); let tests = (!tests.is_empty()).then_some(tests); test::main(capture, files, args, tests) } - Commands::watch { - command: WatchSubcommand::run { mut files }, - } => { + Commands::watch { command, files } => { + let mut files = files.unwrap_or_default(); append_includes(&mut files); - let args = compilation_args(); - watch::main(files.clone(), move || { - run::main(files.clone(), args.clone()); - }) - } - - Commands::watch { - command: WatchSubcommand::test { - mut files, - tests, - capture, - }, - } => { - append_includes(&mut files); - let args = compilation_args(); - let tests = (!tests.is_empty()).then_some(tests); - watch::main(files.clone(), move || { - test::main(capture, files.clone(), args.clone(), tests.clone()); - }) + watch::main(files, command); } Commands::init { path } => config::create(path), diff --git a/src/tasks.rs b/src/tasks.rs index 60ead31..92c9a92 100644 --- a/src/tasks.rs +++ b/src/tasks.rs @@ -143,11 +143,31 @@ impl FormatTask { command .arg(self.file) .arg(format!("-style={config}")) - .stdout(Stdio::null()) - .stderr(Stdio::null()); + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); command.status().unwrap(); let result = command.output().unwrap().stdout; String::from_utf8(result).unwrap() } } + +pub struct CmdTask { + command: String, +} + +impl CmdTask { + pub fn new(command: String) -> Self { + Self { command } + } + + pub fn run(self) -> Result<(), ()> { + Command::new("sh") + .arg("-c") + .arg(self.command) + .stdout(Stdio::inherit()) + .output() + .map(|_| ()) + .map_err(|_| ()) + } +} diff --git a/src/test.rs b/src/test.rs index 10f93c9..930d58a 100644 --- a/src/test.rs +++ b/src/test.rs @@ -2,16 +2,11 @@ use std::{fs, thread, time::Duration}; use crate::{ tasks::{CompileTask, GenTask, RunTask}, - utils::{log_failure, log_process}, + utils::{log_failure, log_process, log_success}, }; pub fn main(_capture: bool, files: Vec, args: Vec, _test: Option>) { - // let includes = files - // .iter() - // .cloned() - // .map(|p| PathBuf::from_str(&p).unwrap()) - // .collect::>(); - + log_process("running tests"); for path in files { let content = fs::read_to_string(&path).unwrap(); let tests = find_tests(content); @@ -35,6 +30,7 @@ pub fn main(_capture: bool, files: Vec, args: Vec, _test: Option } } } + log_success("finished"); } pub fn find_tests(source: String) -> Vec { @@ -50,8 +46,11 @@ pub fn gen_test_main(path: &str, test: &str) -> String { " void __pi_test(); -int main(int _argc, char** _argv) {{ - __pi_test(); +int main(int argc, char** argv) {{ + (void)argc; + (void)argv; + + __pi_test(); return 0; }} diff --git a/src/watch.rs b/src/watch.rs index b71a738..a50fd72 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -2,7 +2,7 @@ use std::{path::Path, sync::mpsc, time::Duration}; use notify_debouncer_mini::new_debouncer; -use crate::utils::log_process; +use crate::{tasks::CmdTask, utils::log_process}; pub struct Repeater { op: Box, @@ -21,13 +21,15 @@ impl Repeater { } } -pub fn main(files: Vec, op: impl Fn() + 'static) { +pub fn main(files: Vec, command: String) { log_process(&format!("watching files '{files:?}'")); - let repeater = Repeater::new(op); + let repeater = Repeater::new(move || { + CmdTask::new(command.clone()).run().unwrap(); + }); repeater.repeat(); let (send, rec) = mpsc::channel(); - let mut debouncer = new_debouncer(Duration::from_millis(100), None, send).unwrap(); + let mut debouncer = new_debouncer(Duration::from_millis(300), None, send).unwrap(); for file in files { debouncer