Basic functionality works.
TODO: * Allow calling scripts from other directories. * Don't use OS-dependent path processing.
This commit is contained in:
commit
a13cd20685
|
@ -0,0 +1,2 @@
|
|||
target/
|
||||
Cargo.lock
|
|
@ -0,0 +1,10 @@
|
|||
[package]
|
||||
name = "rush"
|
||||
version = "0.1.0"
|
||||
authors = ["Sage Vaillancourt <sagev9000@tutanota.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
|
@ -0,0 +1,21 @@
|
|||
#!/bin/rush
|
||||
|
||||
let x = %cat example.rsh%;
|
||||
|
||||
if x.contains("example") {
|
||||
%echo It\\'s a script!%
|
||||
}
|
||||
|
||||
if %echo some words%.contains("me wo") {
|
||||
println!("But it's also Rust!");
|
||||
}
|
||||
|
||||
let mem = %free -h%.lines().nth(1).unwrap()
|
||||
.split_whitespace().nth(3).unwrap().to_owned();
|
||||
|
||||
// for i in 0..10000 {
|
||||
// println!("x contains %% example? [{}]", x.contains("example"));
|
||||
// }
|
||||
|
||||
println!("There are {} of memory free", &mem);
|
||||
%echo hello%
|
|
@ -0,0 +1,181 @@
|
|||
#![feature(option_result_contains)]
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::time::{Instant, SystemTime};
|
||||
|
||||
use std::iter;
|
||||
use std::str::Chars;
|
||||
|
||||
use std::str;
|
||||
use std::process::Command;
|
||||
|
||||
#[derive(PartialEq, Clone)]
|
||||
enum PrintType {
|
||||
Out, Err, All
|
||||
}
|
||||
|
||||
fn com(print: &PrintType, command_string: &str) -> String {
|
||||
let command_output = Command::new("bash").arg("-c")
|
||||
.arg(command_string)
|
||||
.output().unwrap();
|
||||
|
||||
let to_string = |bytes| str::from_utf8(bytes).unwrap().to_string();
|
||||
let out = to_string(&command_output.stdout);
|
||||
let err = to_string(&command_output.stderr);
|
||||
|
||||
match print {
|
||||
PrintType::Out => { print!("{}", out); out },
|
||||
PrintType::Err => { print!("{}", err); err },
|
||||
PrintType::All => {
|
||||
print!("{}", out);
|
||||
if err.trim().is_empty() {
|
||||
print!("{}", err);
|
||||
}
|
||||
out
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn earlier_than(earlier: SystemTime, later: SystemTime) -> bool {
|
||||
earlier.duration_since(later).is_ok()
|
||||
}
|
||||
|
||||
fn needs_compilation(script_name: &str) -> bool {
|
||||
if cfg!(debug_assertions) {
|
||||
return true;
|
||||
}
|
||||
let script_attr = fs::metadata(&script_name);
|
||||
let compiled_attr = fs::metadata(&output_filename(script_name));
|
||||
if let (Ok(sa), Ok(ca)) = (script_attr, compiled_attr) {
|
||||
let sm = sa.modified();
|
||||
let cm = ca.modified();
|
||||
if let (Ok(script_mod_time), Ok(compiled_mod_time)) = (sm, cm) {
|
||||
if earlier_than(compiled_mod_time, script_mod_time) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
fn output_filename(script_name: &str) -> String {
|
||||
format!(".{}.compiled", script_name.replace("./", ""))
|
||||
}
|
||||
|
||||
fn run_compiled(script_name: &str) {
|
||||
com(&PrintType::All, &output_filename(script_name));
|
||||
}
|
||||
|
||||
fn parse_bash_command(
|
||||
iter: &mut iter::Peekable<Chars>,
|
||||
code: &mut String,
|
||||
is_expr: bool
|
||||
) -> Result<()> {
|
||||
code.push_str(&format!("com({}, \"", !is_expr));
|
||||
loop {
|
||||
if let Some(c) = iter.next() {
|
||||
if c == '%' {
|
||||
break;
|
||||
}
|
||||
code.push(c);
|
||||
} else {
|
||||
bail!("Unexpected end-of-file while parsing bash command");
|
||||
}
|
||||
}
|
||||
code.push_str("\")");
|
||||
let peek = iter.peek();
|
||||
if peek.is_none() || peek.contains(&&' ') || peek.contains(&&'\n') {
|
||||
code.push(';');
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn collect_code(script_name: &str) -> Result<String> {
|
||||
let script = fs::read_to_string(&script_name)?;
|
||||
|
||||
let mut code = "
|
||||
use std::str;
|
||||
use std::process::Command;
|
||||
|
||||
fn com(print: bool, comcom: &str) -> String {
|
||||
let s = str::from_utf8(&Command::new(\"bash\").arg(\"-c\")
|
||||
.arg(comcom).output().unwrap().stdout).unwrap().to_string();
|
||||
if print {
|
||||
print!(\"{}\", s)
|
||||
};
|
||||
return s;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
".to_string();
|
||||
|
||||
let mut is_expr = false;
|
||||
|
||||
let mut iter = script.chars().peekable();
|
||||
if script.starts_with("#!/") {
|
||||
while let Some(c) = iter.next() {
|
||||
if c == '\n' {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while let Some(c) = iter.next() {
|
||||
if c == '%' {
|
||||
// Double percents count as a single literal percent
|
||||
if iter.peek().contains(&&'%') {
|
||||
code.push('%');
|
||||
iter.next();
|
||||
} else {
|
||||
parse_bash_command(&mut iter, &mut code, is_expr)?;
|
||||
}
|
||||
} else {
|
||||
code.push(c);
|
||||
if (c == 'i' && iter.peek().contains(&&'f'))
|
||||
|| (c == '=' && !iter.peek().contains(&&'=')) {
|
||||
is_expr = true;
|
||||
} else if (c == '{' || c == ';') && is_expr {
|
||||
is_expr = false;
|
||||
} else if c == '\n' && iter.peek().is_some() {
|
||||
code.push_str(" ");
|
||||
}
|
||||
}
|
||||
}
|
||||
code.push('}');
|
||||
return Ok(code);
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let now = Instant::now();
|
||||
if let Some(script_name) = env::args().nth(1) {
|
||||
if !needs_compilation(&script_name) {
|
||||
run_compiled(&script_name);
|
||||
return Ok(());
|
||||
}
|
||||
let code = collect_code(&script_name)?;
|
||||
let code_filename = format!("/tmp/{}-rush.rs",
|
||||
script_name.replace("./", "").replace(".", "-"));
|
||||
let mut file = File::create(&code_filename)?;
|
||||
file.write_all(code.as_bytes())?;
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
println!("[Start Rust]");
|
||||
println!("{}", code);
|
||||
println!("[End Rust]");
|
||||
}
|
||||
|
||||
let compile_command = format!(
|
||||
"rustc {} -o {}",
|
||||
code_filename,
|
||||
output_filename(&script_name)
|
||||
);
|
||||
com(&PrintType::Err, &compile_command);
|
||||
println!("[32m[Compiled in {}ms][0m\n", now.elapsed().as_millis());
|
||||
run_compiled(&script_name);
|
||||
}
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in New Issue