improved check: formatting system

This commit is contained in:
JOLIMAITRE Matthieu 2022-09-25 14:40:56 +02:00
parent c6baef23c2
commit 95b2ab4662
9 changed files with 170 additions and 26 deletions

7
Cargo.lock generated
View file

@ -147,12 +147,19 @@ dependencies = [
"once_cell", "once_cell",
] ]
[[package]]
name = "diff"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
[[package]] [[package]]
name = "epitls-pi" name = "epitls-pi"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"chrono", "chrono",
"clap", "clap",
"diff",
"glob", "glob",
"notify", "notify",
"notify-debouncer-mini", "notify-debouncer-mini",

View file

@ -10,6 +10,7 @@ path = "src/main.rs"
[dependencies] [dependencies]
chrono = "0.4.22" chrono = "0.4.22"
clap = { version = "3.2.22", features = ["clap_derive", "derive"] } clap = { version = "3.2.22", features = ["clap_derive", "derive"] }
diff = "0.1.13"
glob = "0.3.0" glob = "0.3.0"
notify = "5.0.0" notify = "5.0.0"
notify-debouncer-mini = "0.2.1" notify-debouncer-mini = "0.2.1"

View file

@ -1,6 +1,6 @@
#include <stdio.h> #include <stdio.h>
int main() { int main() {
int a; int a;
printf("hello, world!! %d\n", a); printf("hello, world!! %d\n", a);
} }

View file

@ -1,3 +1,77 @@
pub fn main(_files: Vec<String>) { use std::fs;
todo!()
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<String>) {
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::<Vec<_>>();
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}")
}
}
}
}
}
} }

38
src/check/testables.rs Normal file
View file

@ -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<dyn Fn(String) -> 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<Rule> {
vec![
// rules
Rule::new("ends_with_newline", ends_with_newline),
]
}

View file

@ -120,18 +120,18 @@ fn main() {
append_includes(&mut files); append_includes(&mut files);
let args = compilation_args(); let args = compilation_args();
let tests = (!tests.is_empty()).then_some(tests); let tests = (!tests.is_empty()).then_some(tests);
test::main(capture, files, tests) test::main(capture, files, args, tests)
} }
Commands::watch { Commands::watch {
command: WatchSubcommand::run { mut files }, command: WatchSubcommand::run { mut files },
} => { } => {
append_includes(&mut files); append_includes(&mut files);
let args = compilation_args(); let args = compilation_args();
let files = files.clone(); watch::main(files.clone(), move || {
watch::main(files, move || {
run::main(files.clone(), args.clone()); run::main(files.clone(), args.clone());
}) })
} }
Commands::watch { Commands::watch {
command: WatchSubcommand::test { command: WatchSubcommand::test {
mut files, mut files,
@ -141,8 +141,9 @@ fn main() {
} => { } => {
append_includes(&mut files); append_includes(&mut files);
let args = compilation_args(); let args = compilation_args();
watch::main(files, move || { let tests = (!tests.is_empty()).then_some(tests);
run::main(files.clone(), args.clone()); watch::main(files.clone(), move || {
test::main(capture, files.clone(), args.clone(), tests.clone());
}) })
} }

View file

@ -1,7 +1,7 @@
use std::{ use std::{
fs, fs,
path::PathBuf, path::PathBuf,
process::{Command, ExitStatus}, process::{Command, ExitStatus, Stdio},
}; };
use crate::utils::{ use crate::utils::{
@ -15,8 +15,6 @@ pub struct CompileTask {
verbose: bool, verbose: bool,
} }
// TODO: split compile & compile raw
impl CompileTask { impl CompileTask {
pub fn new(files: Vec<PathBuf>) -> Self { pub fn new(files: Vec<PathBuf>) -> Self {
Self { Self {
@ -51,7 +49,6 @@ impl CompileTask {
pub fn gen_source(&self) -> PathBuf { pub fn gen_source(&self) -> PathBuf {
let mut output_path = tmp_file_path(); let mut output_path = tmp_file_path();
// TODO: make use of supplement
output_path.set_extension("c"); output_path.set_extension("c");
fs::write(&output_path, "").unwrap(); fs::write(&output_path, "").unwrap();
output_path output_path
@ -124,8 +121,33 @@ impl GenTask {
pub fn run(self) -> PathBuf { pub fn run(self) -> PathBuf {
let output_path = tmp_file_path().apply(|o| o.set_extension("c")); 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(); fs::write(&output_path, &content).unwrap();
output_path 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()
}
}

View file

@ -5,7 +5,7 @@ use crate::{
utils::{log_failure, log_process}, utils::{log_failure, log_process},
}; };
pub fn main(_capture: bool, files: Vec<String>, _test: Option<Vec<String>>) { pub fn main(_capture: bool, files: Vec<String>, args: Vec<String>, _test: Option<Vec<String>>) {
// let includes = files // let includes = files
// .iter() // .iter()
// .cloned() // .cloned()
@ -23,10 +23,14 @@ pub fn main(_capture: bool, files: Vec<String>, _test: Option<Vec<String>>) {
thread::sleep(Duration::from_millis(100)); thread::sleep(Duration::from_millis(100));
// compile with all files // compile with all files
//let files = includes.clone().apply(|v| v.insert(0, generated_code)); let mut task = CompileTask::new(vec![generated_code]);
let generated_bin = CompileTask::new(vec![generated_code]).run().unwrap(); for flag in args.clone() {
task = task.with_flag(flag);
}
let generated_bin = task.run().unwrap();
// run // run
if let Err(_) = RunTask::new(generated_bin).run() { if RunTask::new(generated_bin).run().is_err() {
log_failure("test failed"); log_failure("test failed");
} }
} }
@ -44,16 +48,16 @@ pub fn find_tests(source: String) -> Vec<String> {
pub fn gen_test_main(path: &str, test: &str) -> String { pub fn gen_test_main(path: &str, test: &str) -> String {
format!( format!(
" "
void ____test(); void __pi_test();
int main(int _argc, char** _argv) {{ int main(int _argc, char** _argv) {{
____test(); __pi_test();
return 0; return 0;
}} }}
#include \"{path}\" #include \"{path}\"
void ____test() {{ void __pi_test() {{
{test}(); {test}();
}} }}
" "

View file

@ -2,10 +2,7 @@ use std::{path::Path, sync::mpsc, time::Duration};
use notify_debouncer_mini::new_debouncer; use notify_debouncer_mini::new_debouncer;
use crate::{ use crate::utils::log_process;
tasks::{CompileTask, RunTask},
utils::{log_failure, log_process, log_success},
};
pub struct Repeater { pub struct Repeater {
op: Box<dyn Fn()>, op: Box<dyn Fn()>,
@ -40,7 +37,7 @@ pub fn main(files: Vec<String>, op: impl Fn() + 'static) {
} }
for events in rec { for events in rec {
for _ in events.unwrap() { for _event in events.unwrap() {
repeater.repeat(); repeater.repeat();
} }
} }