formatted

This commit is contained in:
matthieu.jolimaitre 2022-09-25 10:30:30 +02:00
parent 37e5b5e555
commit 7f694cd6c2
9 changed files with 333 additions and 332 deletions

1
rustfmt.toml Normal file
View file

@ -0,0 +1 @@
"hard_tabs" = true

View file

@ -1,3 +1,3 @@
pub fn main(_files: Vec<String>) {
todo!()
todo!()
}

View file

@ -1,6 +1,6 @@
use std::{
env, fs,
path::{Path, PathBuf},
env, fs,
path::{Path, PathBuf},
};
use ron::ser::PrettyConfig;
@ -10,71 +10,71 @@ use crate::utils::Apply;
#[derive(Debug, Serialize, Deserialize)]
pub struct Config {
name: String,
main_file: String,
test_file: String,
includes: Vec<String>,
name: String,
main_file: String,
test_file: String,
includes: Vec<String>,
}
impl Config {
const CONFIG_FILE_NAME: &'static str = "pi.ron";
pub fn new(name: String) -> Self {
Self {
name,
main_file: "main.c".into(),
test_file: "test.c".into(),
includes: vec![],
}
}
const CONFIG_FILE_NAME: &'static str = "pi.ron";
pub fn new(name: String) -> Self {
Self {
name,
main_file: "main.c".into(),
test_file: "test.c".into(),
includes: vec![],
}
}
pub fn write(&self, mut path: PathBuf) {
path.extend([Self::CONFIG_FILE_NAME]);
let content =
ron::ser::to_string_pretty(self, PrettyConfig::default().struct_names(true)).unwrap();
fs::write(path, content).unwrap();
}
pub fn write(&self, mut path: PathBuf) {
path.extend([Self::CONFIG_FILE_NAME]);
let content =
ron::ser::to_string_pretty(self, PrettyConfig::default().struct_names(true)).unwrap();
fs::write(path, content).unwrap();
}
pub fn get_current() -> Self {
let path = env::current_dir().unwrap();
Self::get(&path)
.unwrap_or_else(|| Self::new(path.file_name().unwrap().to_str().unwrap().to_string()))
}
pub fn get_current() -> Self {
let path = env::current_dir().unwrap();
Self::get(&path)
.unwrap_or_else(|| Self::new(path.file_name().unwrap().to_str().unwrap().to_string()))
}
pub fn get(path: &Path) -> Option<Self> {
let path = path.to_path_buf().canonicalize().unwrap();
Self::try_get(&path).or_else(|| path.parent().and_then(Self::get))
}
pub fn get(path: &Path) -> Option<Self> {
let path = path.to_path_buf().canonicalize().unwrap();
Self::try_get(&path).or_else(|| path.parent().and_then(Self::get))
}
pub fn name(&self) -> &str {
&self.name
}
pub fn name(&self) -> &str {
&self.name
}
pub fn main_file(&self) -> &str {
&self.main_file
}
pub fn main_file(&self) -> &str {
&self.main_file
}
pub fn test_file(&self) -> &str {
&self.test_file
}
pub fn test_file(&self) -> &str {
&self.test_file
}
pub fn includes(&self) -> &Vec<String> {
&self.includes
}
pub fn includes(&self) -> &Vec<String> {
&self.includes
}
fn try_get(path: &Path) -> Option<Self> {
let path = path.to_path_buf().apply(|p| p.push(Self::CONFIG_FILE_NAME));
fs::read_to_string(path)
.ok()
.and_then(|content| ron::from_str(&content).ok())
}
fn try_get(path: &Path) -> Option<Self> {
let path = path.to_path_buf().apply(|p| p.push(Self::CONFIG_FILE_NAME));
fs::read_to_string(path)
.ok()
.and_then(|content| ron::from_str(&content).ok())
}
}
pub fn create(path: String) {
let absolute = fs::canonicalize(&path).unwrap();
if !absolute.is_dir() {
panic!("not a directory");
}
let name = absolute.file_name().unwrap();
let config = Config::new(name.to_str().unwrap().to_string());
config.write(absolute);
let absolute = fs::canonicalize(&path).unwrap();
if !absolute.is_dir() {
panic!("not a directory");
}
let name = absolute.file_name().unwrap();
let config = Config::new(name.to_str().unwrap().to_string());
config.write(absolute);
}

View file

@ -11,80 +11,80 @@ pub mod watch;
#[derive(Parser)]
pub struct Arguments {
#[clap(subcommand)]
command: Commands,
#[clap(subcommand)]
command: Commands,
}
#[allow(non_camel_case_types)]
#[derive(Subcommand)]
pub enum Commands {
/// Checks a source file for conformance with piscine limitations.
check {
/// File to check.
files: Vec<String>,
},
/// Checks a source file for conformance with piscine limitations.
check {
/// File to check.
files: Vec<String>,
},
/// Runs a set of files or the default target.
run {
/// Files to run.
files: Vec<String>,
},
/// Runs a set of files or the default target.
run {
/// Files to run.
files: Vec<String>,
},
/// Runs tests contained within a particular test file or
test {
/// Wether to capture standard output or not.
#[clap(short, long)]
capture: bool,
/// 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<String>,
/// Files to run tests from.
files: Vec<String>,
/// Specific tests to run.
#[clap(short, long)]
tests: Vec<String>,
},
/// Specific tests to run.
#[clap(short, long)]
tests: Vec<String>,
},
/// Watches changes to source files and re run them
watch {
/// Files to run.
files: Vec<String>,
},
/// Watches changes to source files and re run them
watch {
/// Files to run.
files: Vec<String>,
},
///
init { path: String },
///
init { path: String },
}
fn append_includes(list: &mut Vec<String>) {
list.extend(
Config::get_current()
.includes()
.iter()
.map(|f| f.to_string()),
);
list.extend(
Config::get_current()
.includes()
.iter()
.map(|f| f.to_string()),
);
}
fn main() {
let args: Arguments = Parser::parse();
let args: Arguments = Parser::parse();
match args.command {
Commands::check { files } => check::main(files),
Commands::run { mut files } => {
append_includes(&mut files);
run::main(files);
}
Commands::test {
capture,
mut files,
tests,
} => {
append_includes(&mut files);
let tests = (!tests.is_empty()).then_some(tests);
test::main(capture, files, tests)
}
Commands::watch { mut files } => {
append_includes(&mut files);
watch::main(files)
}
Commands::init { path } => config::create(path),
}
match args.command {
Commands::check { files } => check::main(files),
Commands::run { mut files } => {
append_includes(&mut files);
run::main(files);
}
Commands::test {
capture,
mut files,
tests,
} => {
append_includes(&mut files);
let tests = (!tests.is_empty()).then_some(tests);
test::main(capture, files, tests)
}
Commands::watch { mut files } => {
append_includes(&mut files);
watch::main(files)
}
Commands::init { path } => config::create(path),
}
}

View file

@ -1,28 +1,28 @@
use crate::{
tasks::{CompileTask, RunTask},
utils::{log_failure, log_success},
tasks::{CompileTask, RunTask},
utils::{log_failure, log_success},
};
pub fn main(files: Vec<String>) -> Option<()> {
let source_file = files.into_iter().map(|f| f.into()).collect();
let compiled = CompileTask::new(source_file)
.with_flag("-Wall")
.with_flag("-Wextra")
.with_flag("-std=c99")
.run()
.map(Option::from)
.unwrap_or_else(|_| {
log_failure("compilation failed");
None
})?;
log_success("compilation successful");
RunTask::new(compiled)
.run()
.map(Option::from)
.unwrap_or_else(|_| {
log_failure("process failure");
None
})?;
log_success("process exited successfully");
Some(())
let source_file = files.into_iter().map(|f| f.into()).collect();
let compiled = CompileTask::new(source_file)
.with_flag("-Wall")
.with_flag("-Wextra")
.with_flag("-std=c99")
.run()
.map(Option::from)
.unwrap_or_else(|_| {
log_failure("compilation failed");
None
})?;
log_success("compilation successful");
RunTask::new(compiled)
.run()
.map(Option::from)
.unwrap_or_else(|_| {
log_failure("process failure");
None
})?;
log_success("process exited successfully");
Some(())
}

View file

@ -1,109 +1,109 @@
use std::{
fs,
path::PathBuf,
process::{Command, ExitStatus},
thread,
time::Duration,
fs,
path::PathBuf,
process::{Command, ExitStatus},
thread,
time::Duration,
};
use crate::utils::{
log_command_run, log_separator_bottom, log_separator_top, tmp_file_path, Apply,
log_command_run, log_separator_bottom, log_separator_top, tmp_file_path, Apply,
};
pub struct CompileTask {
files: Vec<PathBuf>,
addition: Vec<String>,
flags: Vec<String>,
files: Vec<PathBuf>,
addition: Vec<String>,
flags: Vec<String>,
}
// TODO: split compile & compile raw
impl CompileTask {
pub fn new(files: Vec<PathBuf>) -> Self {
Self {
files,
addition: vec![],
flags: vec![],
}
}
pub fn new(files: Vec<PathBuf>) -> Self {
Self {
files,
addition: vec![],
flags: vec![],
}
}
pub fn with_addition(mut self, code: impl ToString) -> Self {
self.addition.push(code.to_string());
self
}
pub fn with_addition(mut self, code: impl ToString) -> Self {
self.addition.push(code.to_string());
self
}
pub fn with_flag(mut self, flag: impl ToString) -> Self {
self.flags.push(flag.to_string());
self
}
pub fn with_flag(mut self, flag: impl ToString) -> Self {
self.flags.push(flag.to_string());
self
}
pub fn run(self) -> Result<PathBuf, ExitStatus> {
let proc_source = self.gen_source();
let mut sources = self.files.clone();
sources.push(proc_source);
self.compile(sources)
}
pub fn run(self) -> Result<PathBuf, ExitStatus> {
let proc_source = self.gen_source();
let mut sources = self.files.clone();
sources.push(proc_source);
self.compile(sources)
}
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
}
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
}
pub fn compile(&self, sources: Vec<PathBuf>) -> Result<PathBuf, ExitStatus> {
let output_path = tmp_file_path().apply(|o| o.set_extension("b"));
let output_path_ref = output_path.to_str().unwrap();
let mut command = Command::new("gcc");
command
.args(["-o", output_path_ref])
.args(self.flags.clone())
.args(sources.iter().map(|s| s.to_str().unwrap()));
log_command_run(&command);
log_separator_top();
let status = command.status().unwrap();
log_separator_bottom();
thread::sleep(Duration::from_millis(100));
status.success().then_some(output_path).ok_or(status)
}
pub fn compile(&self, sources: Vec<PathBuf>) -> Result<PathBuf, ExitStatus> {
let output_path = tmp_file_path().apply(|o| o.set_extension("b"));
let output_path_ref = output_path.to_str().unwrap();
let mut command = Command::new("gcc");
command
.args(["-o", output_path_ref])
.args(self.flags.clone())
.args(sources.iter().map(|s| s.to_str().unwrap()));
log_command_run(&command);
log_separator_top();
let status = command.status().unwrap();
log_separator_bottom();
thread::sleep(Duration::from_millis(100));
status.success().then_some(output_path).ok_or(status)
}
}
pub struct RunTask {
file: PathBuf,
file: PathBuf,
}
impl RunTask {
pub fn new(file: PathBuf) -> Self {
Self { file }
}
pub fn new(file: PathBuf) -> Self {
Self { file }
}
pub fn run(self) -> Result<(), ExitStatus> {
let mut command = Command::new(self.file);
log_command_run(&command);
log_separator_top();
let status = command.status().unwrap();
log_separator_bottom();
if status.success() {
Ok(())
} else {
Err(status)
}
}
pub fn run(self) -> Result<(), ExitStatus> {
let mut command = Command::new(self.file);
log_command_run(&command);
log_separator_top();
let status = command.status().unwrap();
log_separator_bottom();
if status.success() {
Ok(())
} else {
Err(status)
}
}
}
pub struct GenTask {
content: String,
content: String,
}
impl GenTask {
pub fn new(content: String) -> Self {
Self { content }
}
pub fn run(self) -> PathBuf {
let output_path = tmp_file_path().apply(|o| o.set_extension("c"));
fs::write(&output_path, &self.content).unwrap();
dbg!(fs::read_to_string(&output_path).unwrap());
output_path
}
pub fn new(content: String) -> Self {
Self { content }
}
pub fn run(self) -> PathBuf {
let output_path = tmp_file_path().apply(|o| o.set_extension("c"));
fs::write(&output_path, &self.content).unwrap();
dbg!(fs::read_to_string(&output_path).unwrap());
output_path
}
}

View file

@ -3,39 +3,39 @@ use std::fs;
use crate::tasks::{CompileTask, GenTask, RunTask};
pub fn main(_capture: bool, files: Vec<String>, _test: Option<Vec<String>>) {
// let includes = files
// .iter()
// .cloned()
// .map(|p| PathBuf::from_str(&p).unwrap())
// .collect::<Vec<_>>();
// let includes = files
// .iter()
// .cloned()
// .map(|p| PathBuf::from_str(&p).unwrap())
// .collect::<Vec<_>>();
for path in files {
let content = fs::read_to_string(&path).unwrap();
let tests = find_tests(content);
for test in tests {
let content = gen_test_main(fs::canonicalize(&path).unwrap().to_str().unwrap(), &test);
let generated_code = GenTask::new(content).run();
for path in files {
let content = fs::read_to_string(&path).unwrap();
let tests = find_tests(content);
for test in tests {
let content = gen_test_main(fs::canonicalize(&path).unwrap().to_str().unwrap(), &test);
let generated_code = GenTask::new(content).run();
// 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();
// run
RunTask::new(generated_bin).run().unwrap();
}
}
// 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();
// run
RunTask::new(generated_bin).run().unwrap();
}
}
}
pub fn find_tests(source: String) -> Vec<String> {
source
.split([' ', '(', ')', ';'])
.filter(|name| name.starts_with("test_"))
.map(String::from)
.collect()
source
.split([' ', '(', ')', ';'])
.filter(|name| name.starts_with("test_"))
.map(String::from)
.collect()
}
pub fn gen_test_main(path: &str, test: &str) -> String {
format!(
"
format!(
"
void ____test();
int main() {{
@ -49,5 +49,5 @@ void ____test() {{
{test}();
}}
"
)
)
}

View file

@ -4,90 +4,90 @@ use chrono::Utc;
use termion::color;
pub fn tmp_file_path() -> PathBuf {
let ms = Utc::now().timestamp_millis().to_string();
let mut path: PathBuf = ["/", "tmp", "epitls-pi"].iter().collect();
fs::create_dir_all(&path).unwrap();
path.push(ms);
path
let ms = Utc::now().timestamp_millis().to_string();
let mut path: PathBuf = ["/", "tmp", "epitls-pi"].iter().collect();
fs::create_dir_all(&path).unwrap();
path.push(ms);
path
}
pub trait Apply: Sized {
fn apply<F, O>(mut self, f: F) -> Self
where
F: FnOnce(&mut Self) -> O,
{
f(&mut self);
self
}
fn apply<F, O>(mut self, f: F) -> Self
where
F: FnOnce(&mut Self) -> O,
{
f(&mut self);
self
}
}
impl<T> Apply for T {}
fn log_pi_prefix() {
print!(
"{}[pi] {}",
color::Fg(color::LightBlue),
color::Fg(color::Reset)
)
print!(
"{}[pi] {}",
color::Fg(color::LightBlue),
color::Fg(color::Reset)
)
}
pub fn log_command_run(command: &Command) {
log_pi_prefix();
let prefix = format_process("running ");
let value = format_variable(&format!("{command:?}"));
let suffix = format_process(" ...");
println!("{prefix}{value}{suffix}");
log_pi_prefix();
let prefix = format_process("running ");
let value = format_variable(&format!("{command:?}"));
let suffix = format_process(" ...");
println!("{prefix}{value}{suffix}");
}
pub fn log_separator() {
println!("────────────────")
println!("────────────────")
}
pub fn log_separator_top() {
println!("───────────────┐")
println!("───────────────┐")
}
pub fn log_separator_bottom() {
println!("───────────────┘")
println!("───────────────┘")
}
pub fn log_failure(text: &str) {
log_pi_prefix();
let text = format!("{}{text}{}", color::Fg(color::Red), color::Fg(color::Reset));
println!("{text}");
log_pi_prefix();
let text = format!("{}{text}{}", color::Fg(color::Red), color::Fg(color::Reset));
println!("{text}");
}
pub fn log_success(text: &str) {
log_pi_prefix();
let text = format_success(text);
println!("{text}");
log_pi_prefix();
let text = format_success(text);
println!("{text}");
}
pub fn log_process(text: &str) {
log_pi_prefix();
let text = format_process(text);
println!("{text}");
log_pi_prefix();
let text = format_process(text);
println!("{text}");
}
fn format_process(input: &str) -> String {
format!(
"{}{input}{}",
color::Fg(color::Blue),
color::Fg(color::Reset)
)
format!(
"{}{input}{}",
color::Fg(color::Blue),
color::Fg(color::Reset)
)
}
fn format_success(input: &str) -> String {
format!(
"{}{input}{}",
color::Fg(color::Green),
color::Fg(color::Reset)
)
format!(
"{}{input}{}",
color::Fg(color::Green),
color::Fg(color::Reset)
)
}
fn format_variable(input: &str) -> String {
format!(
"{}{input}{}",
color::Fg(color::White),
color::Fg(color::Reset),
)
format!(
"{}{input}{}",
color::Fg(color::White),
color::Fg(color::Reset),
)
}

View file

@ -3,61 +3,61 @@ 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},
tasks::{CompileTask, RunTask},
utils::{log_failure, log_process, log_success},
};
pub struct Repeater {
files: Vec<String>,
files: Vec<String>,
}
impl Repeater {
pub fn new(files: Vec<String>) -> Self {
Self { files }
}
pub fn new(files: Vec<String>) -> Self {
Self { files }
}
pub fn repeat(&self) -> Option<()> {
let binary = CompileTask::new(self.files.clone().into_iter().map(|f| f.into()).collect())
.run()
.map(Option::from)
.unwrap_or_else(|_| {
log_failure("failed compilation");
None
})?;
pub fn repeat(&self) -> Option<()> {
let binary = CompileTask::new(self.files.clone().into_iter().map(|f| f.into()).collect())
.run()
.map(Option::from)
.unwrap_or_else(|_| {
log_failure("failed compilation");
None
})?;
log_success("compilation successful");
RunTask::new(binary)
.run()
.map(Option::from)
.unwrap_or_else(|_| {
log_failure("task failure");
None
})?;
log_success("compilation successful");
RunTask::new(binary)
.run()
.map(Option::from)
.unwrap_or_else(|_| {
log_failure("task failure");
None
})?;
log_success("task successful");
log_process("waiting for changes before re run");
Some(())
}
log_success("task successful");
log_process("waiting for changes before re run");
Some(())
}
}
pub fn main(files: Vec<String>) {
log_process(&format!("watching files '{files:?}'"));
let repeater = Repeater::new(files.clone());
repeater.repeat();
log_process(&format!("watching files '{files:?}'"));
let repeater = Repeater::new(files.clone());
repeater.repeat();
let (send, rec) = mpsc::channel();
let mut debouncer = new_debouncer(Duration::from_millis(100), None, send).unwrap();
let (send, rec) = mpsc::channel();
let mut debouncer = new_debouncer(Duration::from_millis(100), None, send).unwrap();
for file in files {
debouncer
.watcher()
.watch(Path::new(&file), notify::RecursiveMode::Recursive)
.unwrap();
}
for file in files {
debouncer
.watcher()
.watch(Path::new(&file), notify::RecursiveMode::Recursive)
.unwrap();
}
for events in rec {
for _ in events.unwrap() {
repeater.repeat();
}
}
for events in rec {
for _ in events.unwrap() {
repeater.repeat();
}
}
}