formatted
This commit is contained in:
parent
37e5b5e555
commit
7f694cd6c2
9 changed files with 333 additions and 332 deletions
1
rustfmt.toml
Normal file
1
rustfmt.toml
Normal file
|
@ -0,0 +1 @@
|
||||||
|
"hard_tabs" = true
|
|
@ -1,3 +1,3 @@
|
||||||
pub fn main(_files: Vec<String>) {
|
pub fn main(_files: Vec<String>) {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
110
src/config.rs
110
src/config.rs
|
@ -1,6 +1,6 @@
|
||||||
use std::{
|
use std::{
|
||||||
env, fs,
|
env, fs,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
use ron::ser::PrettyConfig;
|
use ron::ser::PrettyConfig;
|
||||||
|
@ -10,71 +10,71 @@ use crate::utils::Apply;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
name: String,
|
name: String,
|
||||||
main_file: String,
|
main_file: String,
|
||||||
test_file: String,
|
test_file: String,
|
||||||
includes: Vec<String>,
|
includes: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
const CONFIG_FILE_NAME: &'static str = "pi.ron";
|
const CONFIG_FILE_NAME: &'static str = "pi.ron";
|
||||||
pub fn new(name: String) -> Self {
|
pub fn new(name: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name,
|
name,
|
||||||
main_file: "main.c".into(),
|
main_file: "main.c".into(),
|
||||||
test_file: "test.c".into(),
|
test_file: "test.c".into(),
|
||||||
includes: vec![],
|
includes: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write(&self, mut path: PathBuf) {
|
pub fn write(&self, mut path: PathBuf) {
|
||||||
path.extend([Self::CONFIG_FILE_NAME]);
|
path.extend([Self::CONFIG_FILE_NAME]);
|
||||||
let content =
|
let content =
|
||||||
ron::ser::to_string_pretty(self, PrettyConfig::default().struct_names(true)).unwrap();
|
ron::ser::to_string_pretty(self, PrettyConfig::default().struct_names(true)).unwrap();
|
||||||
fs::write(path, content).unwrap();
|
fs::write(path, content).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_current() -> Self {
|
pub fn get_current() -> Self {
|
||||||
let path = env::current_dir().unwrap();
|
let path = env::current_dir().unwrap();
|
||||||
Self::get(&path)
|
Self::get(&path)
|
||||||
.unwrap_or_else(|| Self::new(path.file_name().unwrap().to_str().unwrap().to_string()))
|
.unwrap_or_else(|| Self::new(path.file_name().unwrap().to_str().unwrap().to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(path: &Path) -> Option<Self> {
|
pub fn get(path: &Path) -> Option<Self> {
|
||||||
let path = path.to_path_buf().canonicalize().unwrap();
|
let path = path.to_path_buf().canonicalize().unwrap();
|
||||||
Self::try_get(&path).or_else(|| path.parent().and_then(Self::get))
|
Self::try_get(&path).or_else(|| path.parent().and_then(Self::get))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn name(&self) -> &str {
|
pub fn name(&self) -> &str {
|
||||||
&self.name
|
&self.name
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main_file(&self) -> &str {
|
pub fn main_file(&self) -> &str {
|
||||||
&self.main_file
|
&self.main_file
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn test_file(&self) -> &str {
|
pub fn test_file(&self) -> &str {
|
||||||
&self.test_file
|
&self.test_file
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn includes(&self) -> &Vec<String> {
|
pub fn includes(&self) -> &Vec<String> {
|
||||||
&self.includes
|
&self.includes
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_get(path: &Path) -> Option<Self> {
|
fn try_get(path: &Path) -> Option<Self> {
|
||||||
let path = path.to_path_buf().apply(|p| p.push(Self::CONFIG_FILE_NAME));
|
let path = path.to_path_buf().apply(|p| p.push(Self::CONFIG_FILE_NAME));
|
||||||
fs::read_to_string(path)
|
fs::read_to_string(path)
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|content| ron::from_str(&content).ok())
|
.and_then(|content| ron::from_str(&content).ok())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create(path: String) {
|
pub fn create(path: String) {
|
||||||
let absolute = fs::canonicalize(&path).unwrap();
|
let absolute = fs::canonicalize(&path).unwrap();
|
||||||
if !absolute.is_dir() {
|
if !absolute.is_dir() {
|
||||||
panic!("not a directory");
|
panic!("not a directory");
|
||||||
}
|
}
|
||||||
let name = absolute.file_name().unwrap();
|
let name = absolute.file_name().unwrap();
|
||||||
let config = Config::new(name.to_str().unwrap().to_string());
|
let config = Config::new(name.to_str().unwrap().to_string());
|
||||||
config.write(absolute);
|
config.write(absolute);
|
||||||
}
|
}
|
||||||
|
|
116
src/main.rs
116
src/main.rs
|
@ -11,80 +11,80 @@ pub mod watch;
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
pub struct Arguments {
|
pub struct Arguments {
|
||||||
#[clap(subcommand)]
|
#[clap(subcommand)]
|
||||||
command: Commands,
|
command: Commands,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
pub enum Commands {
|
pub enum Commands {
|
||||||
/// Checks a source file for conformance with piscine limitations.
|
/// Checks a source file for conformance with piscine limitations.
|
||||||
check {
|
check {
|
||||||
/// File to check.
|
/// File to check.
|
||||||
files: Vec<String>,
|
files: Vec<String>,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Runs a set of files or the default target.
|
/// Runs a set of files or the default target.
|
||||||
run {
|
run {
|
||||||
/// Files to run.
|
/// Files to run.
|
||||||
files: Vec<String>,
|
files: Vec<String>,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Runs tests contained within a particular test file or
|
/// Runs tests contained within a particular test file or
|
||||||
test {
|
test {
|
||||||
/// Wether to capture standard output or not.
|
/// Wether to capture standard output or not.
|
||||||
#[clap(short, long)]
|
#[clap(short, long)]
|
||||||
capture: bool,
|
capture: bool,
|
||||||
|
|
||||||
/// Files to run tests from.
|
/// Files to run tests from.
|
||||||
files: Vec<String>,
|
files: Vec<String>,
|
||||||
|
|
||||||
/// Specific tests to run.
|
/// Specific tests to run.
|
||||||
#[clap(short, long)]
|
#[clap(short, long)]
|
||||||
tests: Vec<String>,
|
tests: Vec<String>,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Watches changes to source files and re run them
|
/// Watches changes to source files and re run them
|
||||||
watch {
|
watch {
|
||||||
/// Files to run.
|
/// Files to run.
|
||||||
files: Vec<String>,
|
files: Vec<String>,
|
||||||
},
|
},
|
||||||
|
|
||||||
///
|
///
|
||||||
init { path: String },
|
init { path: String },
|
||||||
}
|
}
|
||||||
|
|
||||||
fn append_includes(list: &mut Vec<String>) {
|
fn append_includes(list: &mut Vec<String>) {
|
||||||
list.extend(
|
list.extend(
|
||||||
Config::get_current()
|
Config::get_current()
|
||||||
.includes()
|
.includes()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|f| f.to_string()),
|
.map(|f| f.to_string()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let args: Arguments = Parser::parse();
|
let args: Arguments = Parser::parse();
|
||||||
|
|
||||||
match args.command {
|
match args.command {
|
||||||
Commands::check { files } => check::main(files),
|
Commands::check { files } => check::main(files),
|
||||||
Commands::run { mut files } => {
|
Commands::run { mut files } => {
|
||||||
append_includes(&mut files);
|
append_includes(&mut files);
|
||||||
run::main(files);
|
run::main(files);
|
||||||
}
|
}
|
||||||
Commands::test {
|
Commands::test {
|
||||||
capture,
|
capture,
|
||||||
mut files,
|
mut files,
|
||||||
tests,
|
tests,
|
||||||
} => {
|
} => {
|
||||||
append_includes(&mut files);
|
append_includes(&mut files);
|
||||||
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, tests)
|
||||||
}
|
}
|
||||||
Commands::watch { mut files } => {
|
Commands::watch { mut files } => {
|
||||||
append_includes(&mut files);
|
append_includes(&mut files);
|
||||||
watch::main(files)
|
watch::main(files)
|
||||||
}
|
}
|
||||||
Commands::init { path } => config::create(path),
|
Commands::init { path } => config::create(path),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
46
src/run.rs
46
src/run.rs
|
@ -1,28 +1,28 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
tasks::{CompileTask, RunTask},
|
tasks::{CompileTask, RunTask},
|
||||||
utils::{log_failure, log_success},
|
utils::{log_failure, log_success},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn main(files: Vec<String>) -> Option<()> {
|
pub fn main(files: Vec<String>) -> Option<()> {
|
||||||
let source_file = files.into_iter().map(|f| f.into()).collect();
|
let source_file = files.into_iter().map(|f| f.into()).collect();
|
||||||
let compiled = CompileTask::new(source_file)
|
let compiled = CompileTask::new(source_file)
|
||||||
.with_flag("-Wall")
|
.with_flag("-Wall")
|
||||||
.with_flag("-Wextra")
|
.with_flag("-Wextra")
|
||||||
.with_flag("-std=c99")
|
.with_flag("-std=c99")
|
||||||
.run()
|
.run()
|
||||||
.map(Option::from)
|
.map(Option::from)
|
||||||
.unwrap_or_else(|_| {
|
.unwrap_or_else(|_| {
|
||||||
log_failure("compilation failed");
|
log_failure("compilation failed");
|
||||||
None
|
None
|
||||||
})?;
|
})?;
|
||||||
log_success("compilation successful");
|
log_success("compilation successful");
|
||||||
RunTask::new(compiled)
|
RunTask::new(compiled)
|
||||||
.run()
|
.run()
|
||||||
.map(Option::from)
|
.map(Option::from)
|
||||||
.unwrap_or_else(|_| {
|
.unwrap_or_else(|_| {
|
||||||
log_failure("process failure");
|
log_failure("process failure");
|
||||||
None
|
None
|
||||||
})?;
|
})?;
|
||||||
log_success("process exited successfully");
|
log_success("process exited successfully");
|
||||||
Some(())
|
Some(())
|
||||||
}
|
}
|
||||||
|
|
156
src/tasks.rs
156
src/tasks.rs
|
@ -1,109 +1,109 @@
|
||||||
use std::{
|
use std::{
|
||||||
fs,
|
fs,
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
process::{Command, ExitStatus},
|
process::{Command, ExitStatus},
|
||||||
thread,
|
thread,
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::utils::{
|
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 {
|
pub struct CompileTask {
|
||||||
files: Vec<PathBuf>,
|
files: Vec<PathBuf>,
|
||||||
addition: Vec<String>,
|
addition: Vec<String>,
|
||||||
flags: Vec<String>,
|
flags: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: split compile & compile raw
|
// 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 {
|
||||||
files,
|
files,
|
||||||
addition: vec![],
|
addition: vec![],
|
||||||
flags: vec![],
|
flags: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_addition(mut self, code: impl ToString) -> Self {
|
pub fn with_addition(mut self, code: impl ToString) -> Self {
|
||||||
self.addition.push(code.to_string());
|
self.addition.push(code.to_string());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_flag(mut self, flag: impl ToString) -> Self {
|
pub fn with_flag(mut self, flag: impl ToString) -> Self {
|
||||||
self.flags.push(flag.to_string());
|
self.flags.push(flag.to_string());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(self) -> Result<PathBuf, ExitStatus> {
|
pub fn run(self) -> Result<PathBuf, ExitStatus> {
|
||||||
let proc_source = self.gen_source();
|
let proc_source = self.gen_source();
|
||||||
let mut sources = self.files.clone();
|
let mut sources = self.files.clone();
|
||||||
sources.push(proc_source);
|
sources.push(proc_source);
|
||||||
self.compile(sources)
|
self.compile(sources)
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
// 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
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile(&self, sources: Vec<PathBuf>) -> Result<PathBuf, ExitStatus> {
|
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 = tmp_file_path().apply(|o| o.set_extension("b"));
|
||||||
let output_path_ref = output_path.to_str().unwrap();
|
let output_path_ref = output_path.to_str().unwrap();
|
||||||
let mut command = Command::new("gcc");
|
let mut command = Command::new("gcc");
|
||||||
command
|
command
|
||||||
.args(["-o", output_path_ref])
|
.args(["-o", output_path_ref])
|
||||||
.args(self.flags.clone())
|
.args(self.flags.clone())
|
||||||
.args(sources.iter().map(|s| s.to_str().unwrap()));
|
.args(sources.iter().map(|s| s.to_str().unwrap()));
|
||||||
log_command_run(&command);
|
log_command_run(&command);
|
||||||
log_separator_top();
|
log_separator_top();
|
||||||
let status = command.status().unwrap();
|
let status = command.status().unwrap();
|
||||||
log_separator_bottom();
|
log_separator_bottom();
|
||||||
thread::sleep(Duration::from_millis(100));
|
thread::sleep(Duration::from_millis(100));
|
||||||
status.success().then_some(output_path).ok_or(status)
|
status.success().then_some(output_path).ok_or(status)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RunTask {
|
pub struct RunTask {
|
||||||
file: PathBuf,
|
file: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RunTask {
|
impl RunTask {
|
||||||
pub fn new(file: PathBuf) -> Self {
|
pub fn new(file: PathBuf) -> Self {
|
||||||
Self { file }
|
Self { file }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(self) -> Result<(), ExitStatus> {
|
pub fn run(self) -> Result<(), ExitStatus> {
|
||||||
let mut command = Command::new(self.file);
|
let mut command = Command::new(self.file);
|
||||||
log_command_run(&command);
|
log_command_run(&command);
|
||||||
log_separator_top();
|
log_separator_top();
|
||||||
let status = command.status().unwrap();
|
let status = command.status().unwrap();
|
||||||
log_separator_bottom();
|
log_separator_bottom();
|
||||||
if status.success() {
|
if status.success() {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(status)
|
Err(status)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GenTask {
|
pub struct GenTask {
|
||||||
content: String,
|
content: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GenTask {
|
impl GenTask {
|
||||||
pub fn new(content: String) -> Self {
|
pub fn new(content: String) -> Self {
|
||||||
Self { content }
|
Self { content }
|
||||||
}
|
}
|
||||||
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"));
|
||||||
fs::write(&output_path, &self.content).unwrap();
|
fs::write(&output_path, &self.content).unwrap();
|
||||||
dbg!(fs::read_to_string(&output_path).unwrap());
|
dbg!(fs::read_to_string(&output_path).unwrap());
|
||||||
output_path
|
output_path
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
52
src/test.rs
52
src/test.rs
|
@ -3,39 +3,39 @@ use std::fs;
|
||||||
use crate::tasks::{CompileTask, GenTask, RunTask};
|
use crate::tasks::{CompileTask, GenTask, RunTask};
|
||||||
|
|
||||||
pub fn main(_capture: bool, files: Vec<String>, _test: Option<Vec<String>>) {
|
pub fn main(_capture: bool, files: Vec<String>, _test: Option<Vec<String>>) {
|
||||||
// let includes = files
|
// let includes = files
|
||||||
// .iter()
|
// .iter()
|
||||||
// .cloned()
|
// .cloned()
|
||||||
// .map(|p| PathBuf::from_str(&p).unwrap())
|
// .map(|p| PathBuf::from_str(&p).unwrap())
|
||||||
// .collect::<Vec<_>>();
|
// .collect::<Vec<_>>();
|
||||||
|
|
||||||
for path in files {
|
for path in files {
|
||||||
let content = fs::read_to_string(&path).unwrap();
|
let content = fs::read_to_string(&path).unwrap();
|
||||||
let tests = find_tests(content);
|
let tests = find_tests(content);
|
||||||
for test in tests {
|
for test in tests {
|
||||||
let content = gen_test_main(fs::canonicalize(&path).unwrap().to_str().unwrap(), &test);
|
let content = gen_test_main(fs::canonicalize(&path).unwrap().to_str().unwrap(), &test);
|
||||||
let generated_code = GenTask::new(content).run();
|
let generated_code = GenTask::new(content).run();
|
||||||
|
|
||||||
// compile with all files
|
// compile with all files
|
||||||
//let files = includes.clone().apply(|v| v.insert(0, generated_code));
|
//let files = includes.clone().apply(|v| v.insert(0, generated_code));
|
||||||
let generated_bin = CompileTask::new(vec![generated_code]).run().unwrap();
|
let generated_bin = CompileTask::new(vec![generated_code]).run().unwrap();
|
||||||
// run
|
// run
|
||||||
RunTask::new(generated_bin).run().unwrap();
|
RunTask::new(generated_bin).run().unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_tests(source: String) -> Vec<String> {
|
pub fn find_tests(source: String) -> Vec<String> {
|
||||||
source
|
source
|
||||||
.split([' ', '(', ')', ';'])
|
.split([' ', '(', ')', ';'])
|
||||||
.filter(|name| name.starts_with("test_"))
|
.filter(|name| name.starts_with("test_"))
|
||||||
.map(String::from)
|
.map(String::from)
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
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 ____test();
|
||||||
|
|
||||||
int main() {{
|
int main() {{
|
||||||
|
@ -49,5 +49,5 @@ void ____test() {{
|
||||||
{test}();
|
{test}();
|
||||||
}}
|
}}
|
||||||
"
|
"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
98
src/utils.rs
98
src/utils.rs
|
@ -4,90 +4,90 @@ use chrono::Utc;
|
||||||
use termion::color;
|
use termion::color;
|
||||||
|
|
||||||
pub fn tmp_file_path() -> PathBuf {
|
pub fn tmp_file_path() -> PathBuf {
|
||||||
let ms = Utc::now().timestamp_millis().to_string();
|
let ms = Utc::now().timestamp_millis().to_string();
|
||||||
let mut path: PathBuf = ["/", "tmp", "epitls-pi"].iter().collect();
|
let mut path: PathBuf = ["/", "tmp", "epitls-pi"].iter().collect();
|
||||||
fs::create_dir_all(&path).unwrap();
|
fs::create_dir_all(&path).unwrap();
|
||||||
path.push(ms);
|
path.push(ms);
|
||||||
path
|
path
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Apply: Sized {
|
pub trait Apply: Sized {
|
||||||
fn apply<F, O>(mut self, f: F) -> Self
|
fn apply<F, O>(mut self, f: F) -> Self
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut Self) -> O,
|
F: FnOnce(&mut Self) -> O,
|
||||||
{
|
{
|
||||||
f(&mut self);
|
f(&mut self);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T> Apply for T {}
|
impl<T> Apply for T {}
|
||||||
|
|
||||||
fn log_pi_prefix() {
|
fn log_pi_prefix() {
|
||||||
print!(
|
print!(
|
||||||
"{}[pi] {}",
|
"{}[pi] {}",
|
||||||
color::Fg(color::LightBlue),
|
color::Fg(color::LightBlue),
|
||||||
color::Fg(color::Reset)
|
color::Fg(color::Reset)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn log_command_run(command: &Command) {
|
pub fn log_command_run(command: &Command) {
|
||||||
log_pi_prefix();
|
log_pi_prefix();
|
||||||
let prefix = format_process("running ");
|
let prefix = format_process("running ");
|
||||||
let value = format_variable(&format!("{command:?}"));
|
let value = format_variable(&format!("{command:?}"));
|
||||||
let suffix = format_process(" ...");
|
let suffix = format_process(" ...");
|
||||||
println!("{prefix}{value}{suffix}");
|
println!("{prefix}{value}{suffix}");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn log_separator() {
|
pub fn log_separator() {
|
||||||
println!("────────────────")
|
println!("────────────────")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn log_separator_top() {
|
pub fn log_separator_top() {
|
||||||
println!("───────────────┐")
|
println!("───────────────┐")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn log_separator_bottom() {
|
pub fn log_separator_bottom() {
|
||||||
println!("───────────────┘")
|
println!("───────────────┘")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn log_failure(text: &str) {
|
pub fn log_failure(text: &str) {
|
||||||
log_pi_prefix();
|
log_pi_prefix();
|
||||||
let text = format!("{}{text}{}", color::Fg(color::Red), color::Fg(color::Reset));
|
let text = format!("{}{text}{}", color::Fg(color::Red), color::Fg(color::Reset));
|
||||||
println!("{text}");
|
println!("{text}");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn log_success(text: &str) {
|
pub fn log_success(text: &str) {
|
||||||
log_pi_prefix();
|
log_pi_prefix();
|
||||||
let text = format_success(text);
|
let text = format_success(text);
|
||||||
println!("{text}");
|
println!("{text}");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn log_process(text: &str) {
|
pub fn log_process(text: &str) {
|
||||||
log_pi_prefix();
|
log_pi_prefix();
|
||||||
let text = format_process(text);
|
let text = format_process(text);
|
||||||
println!("{text}");
|
println!("{text}");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_process(input: &str) -> String {
|
fn format_process(input: &str) -> String {
|
||||||
format!(
|
format!(
|
||||||
"{}{input}{}",
|
"{}{input}{}",
|
||||||
color::Fg(color::Blue),
|
color::Fg(color::Blue),
|
||||||
color::Fg(color::Reset)
|
color::Fg(color::Reset)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_success(input: &str) -> String {
|
fn format_success(input: &str) -> String {
|
||||||
format!(
|
format!(
|
||||||
"{}{input}{}",
|
"{}{input}{}",
|
||||||
color::Fg(color::Green),
|
color::Fg(color::Green),
|
||||||
color::Fg(color::Reset)
|
color::Fg(color::Reset)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_variable(input: &str) -> String {
|
fn format_variable(input: &str) -> String {
|
||||||
format!(
|
format!(
|
||||||
"{}{input}{}",
|
"{}{input}{}",
|
||||||
color::Fg(color::White),
|
color::Fg(color::White),
|
||||||
color::Fg(color::Reset),
|
color::Fg(color::Reset),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
84
src/watch.rs
84
src/watch.rs
|
@ -3,61 +3,61 @@ use std::{path::Path, sync::mpsc, time::Duration};
|
||||||
use notify_debouncer_mini::new_debouncer;
|
use notify_debouncer_mini::new_debouncer;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
tasks::{CompileTask, RunTask},
|
tasks::{CompileTask, RunTask},
|
||||||
utils::{log_failure, log_process, log_success},
|
utils::{log_failure, log_process, log_success},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Repeater {
|
pub struct Repeater {
|
||||||
files: Vec<String>,
|
files: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Repeater {
|
impl Repeater {
|
||||||
pub fn new(files: Vec<String>) -> Self {
|
pub fn new(files: Vec<String>) -> Self {
|
||||||
Self { files }
|
Self { files }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn repeat(&self) -> Option<()> {
|
pub fn repeat(&self) -> Option<()> {
|
||||||
let binary = CompileTask::new(self.files.clone().into_iter().map(|f| f.into()).collect())
|
let binary = CompileTask::new(self.files.clone().into_iter().map(|f| f.into()).collect())
|
||||||
.run()
|
.run()
|
||||||
.map(Option::from)
|
.map(Option::from)
|
||||||
.unwrap_or_else(|_| {
|
.unwrap_or_else(|_| {
|
||||||
log_failure("failed compilation");
|
log_failure("failed compilation");
|
||||||
None
|
None
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
log_success("compilation successful");
|
log_success("compilation successful");
|
||||||
RunTask::new(binary)
|
RunTask::new(binary)
|
||||||
.run()
|
.run()
|
||||||
.map(Option::from)
|
.map(Option::from)
|
||||||
.unwrap_or_else(|_| {
|
.unwrap_or_else(|_| {
|
||||||
log_failure("task failure");
|
log_failure("task failure");
|
||||||
None
|
None
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
log_success("task successful");
|
log_success("task successful");
|
||||||
log_process("waiting for changes before re run");
|
log_process("waiting for changes before re run");
|
||||||
Some(())
|
Some(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main(files: Vec<String>) {
|
pub fn main(files: Vec<String>) {
|
||||||
log_process(&format!("watching files '{files:?}'"));
|
log_process(&format!("watching files '{files:?}'"));
|
||||||
let repeater = Repeater::new(files.clone());
|
let repeater = Repeater::new(files.clone());
|
||||||
repeater.repeat();
|
repeater.repeat();
|
||||||
|
|
||||||
let (send, rec) = mpsc::channel();
|
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(100), None, send).unwrap();
|
||||||
|
|
||||||
for file in files {
|
for file in files {
|
||||||
debouncer
|
debouncer
|
||||||
.watcher()
|
.watcher()
|
||||||
.watch(Path::new(&file), notify::RecursiveMode::Recursive)
|
.watch(Path::new(&file), notify::RecursiveMode::Recursive)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
for events in rec {
|
for events in rec {
|
||||||
for _ in events.unwrap() {
|
for _ in events.unwrap() {
|
||||||
repeater.repeat();
|
repeater.repeat();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue