From 95b2ab46622d77cb46977a22dee3eb35b7d7c7c3 Mon Sep 17 00:00:00 2001 From: JOLIMAITRE Matthieu Date: Sun, 25 Sep 2022 14:40:56 +0200 Subject: [PATCH] improved check: formatting system --- Cargo.lock | 7 ++++ Cargo.toml | 1 + example/simple/main.c | 4 +-- src/check.rs | 78 ++++++++++++++++++++++++++++++++++++++++-- src/check/testables.rs | 38 ++++++++++++++++++++ src/main.rs | 11 +++--- src/tasks.rs | 32 ++++++++++++++--- src/test.rs | 18 ++++++---- src/watch.rs | 7 ++-- 9 files changed, 170 insertions(+), 26 deletions(-) create mode 100644 src/check/testables.rs diff --git a/Cargo.lock b/Cargo.lock index 0c49be0..1001cee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -147,12 +147,19 @@ dependencies = [ "once_cell", ] +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + [[package]] name = "epitls-pi" version = "0.1.0" dependencies = [ "chrono", "clap", + "diff", "glob", "notify", "notify-debouncer-mini", diff --git a/Cargo.toml b/Cargo.toml index b0c2980..5e0341b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ path = "src/main.rs" [dependencies] chrono = "0.4.22" clap = { version = "3.2.22", features = ["clap_derive", "derive"] } +diff = "0.1.13" glob = "0.3.0" notify = "5.0.0" notify-debouncer-mini = "0.2.1" diff --git a/example/simple/main.c b/example/simple/main.c index 6308544..bfd922e 100644 --- a/example/simple/main.c +++ b/example/simple/main.c @@ -1,6 +1,6 @@ #include int main() { - int a; - printf("hello, world!! %d\n", a); + int a; + printf("hello, world!! %d\n", a); } diff --git a/src/check.rs b/src/check.rs index 0d57e31..e0d8945 100644 --- a/src/check.rs +++ b/src/check.rs @@ -1,3 +1,77 @@ -pub fn main(_files: Vec) { - todo!() +use std::fs; + +use termion::color; + +use crate::{tasks::FormatTask, utils::log_failure}; + +const FORMAT_CONFIG: &str = r#"{BasedOnStyle: llvm}"#; + +mod testables; + +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; + } + _ => (), + } + (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 { + index, + content: content.into(), + }, + diff::Result::Right(content) => { + invalid = true; + Diff::ToAdd { + index, + content: content.into(), + } + } + }) + .collect::>(); + + 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}") + } + } + } + } + } } diff --git a/src/check/testables.rs b/src/check/testables.rs new file mode 100644 index 0000000..e85ce04 --- /dev/null +++ b/src/check/testables.rs @@ -0,0 +1,38 @@ +fn ends_with_newline(source: String) -> Result<(), String> { + if !source.ends_with("\n") { + Err("source does not end with newline".into()) + } else { + Ok(()) + } +} + +pub struct Rule { + name: String, + test: Box Result<(), String>>, +} + +impl Rule { + pub fn new<'a>( + name: impl ToString, + test: impl 'static + Fn(String) -> Result<(), String>, + ) -> Self { + let name = name.to_string(); + let test = Box::new(test); + Self { name, test } + } + + pub fn name(&self) -> &str { + &self.name + } + + pub fn test(&self, source: String) -> Result<(), String> { + (self.test)(source) + } +} + +pub fn tests() -> Vec { + vec![ + // rules + Rule::new("ends_with_newline", ends_with_newline), + ] +} diff --git a/src/main.rs b/src/main.rs index c95a5c9..4178f88 100644 --- a/src/main.rs +++ b/src/main.rs @@ -120,18 +120,18 @@ fn main() { append_includes(&mut files); let args = compilation_args(); let tests = (!tests.is_empty()).then_some(tests); - test::main(capture, files, tests) + test::main(capture, files, args, tests) } Commands::watch { command: WatchSubcommand::run { mut files }, } => { append_includes(&mut files); let args = compilation_args(); - let files = files.clone(); - watch::main(files, move || { + watch::main(files.clone(), move || { run::main(files.clone(), args.clone()); }) } + Commands::watch { command: WatchSubcommand::test { mut files, @@ -141,8 +141,9 @@ fn main() { } => { append_includes(&mut files); let args = compilation_args(); - watch::main(files, move || { - run::main(files.clone(), args.clone()); + let tests = (!tests.is_empty()).then_some(tests); + watch::main(files.clone(), move || { + test::main(capture, files.clone(), args.clone(), tests.clone()); }) } diff --git a/src/tasks.rs b/src/tasks.rs index c55a440..60ead31 100644 --- a/src/tasks.rs +++ b/src/tasks.rs @@ -1,7 +1,7 @@ use std::{ fs, path::PathBuf, - process::{Command, ExitStatus}, + process::{Command, ExitStatus, Stdio}, }; use crate::utils::{ @@ -15,8 +15,6 @@ pub struct CompileTask { verbose: bool, } -// TODO: split compile & compile raw - impl CompileTask { pub fn new(files: Vec) -> Self { Self { @@ -51,7 +49,6 @@ impl CompileTask { pub fn gen_source(&self) -> PathBuf { let mut output_path = tmp_file_path(); - // TODO: make use of supplement output_path.set_extension("c"); fs::write(&output_path, "").unwrap(); output_path @@ -124,8 +121,33 @@ impl GenTask { pub fn run(self) -> PathBuf { let output_path = tmp_file_path().apply(|o| o.set_extension("c")); - let content = self.content.clone(); + let content = self.content; fs::write(&output_path, &content).unwrap(); output_path } } + +pub struct FormatTask { + file: String, + config: String, +} + +impl FormatTask { + pub fn new(file: String, config: String) -> Self { + Self { config, file } + } + + pub fn run(self) -> String { + let config = self.config; + let mut command = Command::new("clang-format"); + command + .arg(self.file) + .arg(format!("-style={config}")) + .stdout(Stdio::null()) + .stderr(Stdio::null()); + command.status().unwrap(); + + let result = command.output().unwrap().stdout; + String::from_utf8(result).unwrap() + } +} diff --git a/src/test.rs b/src/test.rs index 9349132..10f93c9 100644 --- a/src/test.rs +++ b/src/test.rs @@ -5,7 +5,7 @@ use crate::{ utils::{log_failure, log_process}, }; -pub fn main(_capture: bool, files: Vec, _test: Option>) { +pub fn main(_capture: bool, files: Vec, args: Vec, _test: Option>) { // let includes = files // .iter() // .cloned() @@ -23,10 +23,14 @@ pub fn main(_capture: bool, files: Vec, _test: Option>) { thread::sleep(Duration::from_millis(100)); // compile with all files - //let files = includes.clone().apply(|v| v.insert(0, generated_code)); - let generated_bin = CompileTask::new(vec![generated_code]).run().unwrap(); + let mut task = CompileTask::new(vec![generated_code]); + for flag in args.clone() { + task = task.with_flag(flag); + } + let generated_bin = task.run().unwrap(); + // run - if let Err(_) = RunTask::new(generated_bin).run() { + if RunTask::new(generated_bin).run().is_err() { log_failure("test failed"); } } @@ -44,16 +48,16 @@ pub fn find_tests(source: String) -> Vec { pub fn gen_test_main(path: &str, test: &str) -> String { format!( " -void ____test(); +void __pi_test(); int main(int _argc, char** _argv) {{ - ____test(); + __pi_test(); return 0; }} #include \"{path}\" -void ____test() {{ +void __pi_test() {{ {test}(); }} " diff --git a/src/watch.rs b/src/watch.rs index 61489b9..b71a738 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -2,10 +2,7 @@ use std::{path::Path, sync::mpsc, time::Duration}; use notify_debouncer_mini::new_debouncer; -use crate::{ - tasks::{CompileTask, RunTask}, - utils::{log_failure, log_process, log_success}, -}; +use crate::utils::log_process; pub struct Repeater { op: Box, @@ -40,7 +37,7 @@ pub fn main(files: Vec, op: impl Fn() + 'static) { } for events in rec { - for _ in events.unwrap() { + for _event in events.unwrap() { repeater.repeat(); } }