diff options
author | syn <isaqtm@gmail.com> | 2019-12-27 10:19:55 +0300 |
---|---|---|
committer | syn <isaqtm@gmail.com> | 2019-12-27 10:19:55 +0300 |
commit | 4b062197e6dd67311e7cfb2c05f28e20c6809daa (patch) | |
tree | 2961151b2b19b582e4d41b47fb832aa2838f5e6d | |
download | evr-4b062197e6dd67311e7cfb2c05f28e20c6809daa.tar.gz |
Something is done
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | Cargo.toml | 15 | ||||
-rw-r--r-- | readme | 1 | ||||
-rw-r--r-- | src/backends/clang.rs | 21 | ||||
-rw-r--r-- | src/backends/mod.rs | 20 | ||||
-rw-r--r-- | src/backends/python.rs | 43 | ||||
-rw-r--r-- | src/conf.rs | 110 | ||||
-rw-r--r-- | src/main.rs | 67 |
8 files changed, 280 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5c2bc0e --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +**/*.rs.bk +.vscode/
\ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..e726855 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "evr" +version = "0.1.0" +authors = ["syn <isaqtm@gmail.com>"] +edition = "2018" + + +[dependencies] +clap = "2.33" +structopt = "0.3" +serde = "1" +toml = "0.5" +serde_derive = "1" +log = "0.4" +env_logger = "0.7.1" @@ -0,0 +1 @@ +todo: smart time, clang
\ No newline at end of file diff --git a/src/backends/clang.rs b/src/backends/clang.rs new file mode 100644 index 0000000..5a0c37f --- /dev/null +++ b/src/backends/clang.rs @@ -0,0 +1,21 @@ +use serde_derive::{ Serialize, Deserialize }; +use crate::backends::Backend; +use std::path::Path; + +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct ClangBackend { + template: Option<String> +} + +impl Backend for ClangBackend { + fn get_template(&self) -> Option<&str> { + match self.template { + Some(ref t) => Some(t), + None => None + } + } + + fn run(&self, _fname: &Path) -> std::io::Result<()> { + todo!(); + } +}
\ No newline at end of file diff --git a/src/backends/mod.rs b/src/backends/mod.rs new file mode 100644 index 0000000..729daa5 --- /dev/null +++ b/src/backends/mod.rs @@ -0,0 +1,20 @@ +use std::path::{ Path, PathBuf }; + +pub mod python; +pub mod clang; + +pub use python::PythonBackend; +pub use clang::ClangBackend; + +pub trait Backend { + fn get_template(&self) -> Option<&str>; + fn run(&self, fname: &Path) -> std::io::Result<()>; + + fn try_guess_test(&self, fname: &Path) -> Option<PathBuf> { + let maybe_test = fname.with_extension("txt"); + if maybe_test.exists() { + return Some(maybe_test); + } + None + } +} diff --git a/src/backends/python.rs b/src/backends/python.rs new file mode 100644 index 0000000..2776eb2 --- /dev/null +++ b/src/backends/python.rs @@ -0,0 +1,43 @@ +use serde_derive::{ Serialize, Deserialize }; +use crate::backends::Backend; +use std::process::{ Command, Stdio }; +use std::path::Path; +use log::trace; +use std::io::{ Error, ErrorKind }; + +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct PythonBackend { + template: Option<String>, + version: Option<String>, +} + +impl Backend for PythonBackend { + fn get_template(&self) -> Option<&str> { + match self.template { + Some(ref t) => Some(t), + None => None + } + } + + fn run(&self, fname: &Path) -> std::io::Result<()> { + let interpreter = format!("python{}", self.version.as_ref().unwrap_or(&String::new())); + let stdio = match self.try_guess_test(fname) { + Some(test_file) => Stdio::from(std::fs::File::open(test_file)?), + None => Stdio::piped() + }; + let timer = std::time::SystemTime::now(); + let mut child = Command::new(interpreter) + .arg(fname.as_os_str()) + .stdin(stdio) + .spawn()?; + + let status = child.wait()?; + if !status.success() { + return Err(Error::new(ErrorKind::Other, + "Process exited with non-zero exit code")); + } + trace!("elapsed: {:#?}", timer.elapsed()); + + Ok(()) + } +}
\ No newline at end of file diff --git a/src/conf.rs b/src/conf.rs new file mode 100644 index 0000000..eec5d86 --- /dev/null +++ b/src/conf.rs @@ -0,0 +1,110 @@ +use serde_derive::{Serialize, Deserialize}; +use std::path::{ PathBuf, Path }; +use toml::de; +use log::{ error, trace }; +use std::io::prelude::*; + +type Error = std::io::Error; +use std::io::ErrorKind; + +use crate::backends::{ Backend, PythonBackend, ClangBackend }; + + +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct Conf { + #[serde(skip)] + path: Option<PathBuf>, + + #[serde(default)] + python: PythonBackend, + + #[serde(default)] + clang: ClangBackend +} + + +impl Conf { + pub fn get_template(&self, fname: &Path) -> &str { + self.get_backend(fname) + .and_then(|backend| backend.get_template()) + .unwrap_or("") + } + + pub fn get_backend(&self, fname: &Path) -> Option<Box<&dyn Backend>> { + let ext = fname.extension() + .and_then(|ext| ext.to_str()) + .unwrap_or(""); + + match ext { + "py" => Some(Box::new(&self.python)), + "cc" | "cpp" | "cxx" => Some(Box::new(&self.clang)), + _ => None + } + } + + pub fn run(&self, fname: &Path) -> std::io::Result<()> { + self.get_backend(fname) + .and_then(|backend| Some(backend.run(fname))) + .unwrap_or(Err(Error::new(ErrorKind::InvalidData, "Backend not found"))) + } + + pub fn make(&self, fname: &Path) -> std::io::Result<()> { + trace!("Template: {:?}", self.get_template(&fname)); + + std::fs::File::create(fname)? + .write_all(self.get_template(fname).as_bytes())?; + + trace!("Written some bytes to {}", fname.to_string_lossy()); + Ok(()) + } + + #[allow(unused)] + pub fn get_compiler_args(&self) -> String { + "somedef".to_string() + } +} + + + +pub fn get_conf() -> Conf { + match get_conf_maybe() { + Ok(c) => c, + Err(e) => { + match e.kind() { + ErrorKind::InvalidData => error!("parse: {}", e), + _ => error!("{}", e) + }; + Default::default() + } + } +} + + +pub fn get_conf_maybe() -> Result<Conf, Error> { + let mut current = std::env::current_dir()?; + let path = loop { + let candidate = current.join(".evr"); + if candidate.exists() { + break candidate; + } + + if !current.pop() { + error!("Not a evr subtree."); + return Err(Error::new(ErrorKind::NotFound, "Not a evr subtree")); + } + }; + + let raw_buf = std::fs::read_to_string(path.as_path())?; + + let buf_result: Result<Conf, de::Error> = de::from_str(&raw_buf); + + match buf_result { + Ok(mut buf) => { + buf.path = Some(path.clone()); + Ok(buf) + }, + Err(err) => { + Err(Error::new(ErrorKind::InvalidData, err)) + } + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..af6a547 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,67 @@ +use clap::{AppSettings}; +use structopt::StructOpt; +use env_logger; +use log::{ trace, error }; + +mod conf; +mod backends; + +#[derive(StructOpt, Debug)] +#[structopt( + name = "evr", + version = "0.1", + author = "by syn", + global_settings = &[ + AppSettings::ColoredHelp, + AppSettings::UnifiedHelpMessage + ], +)] +struct EVROpts { + /// source filename + #[structopt(long, index = 1)] + src: String, + + /// be quiet + #[structopt(short, long)] + quiet: bool, + + /// optimize with this level + #[structopt(short, long)] + opt: Option<u8>, + + /// show time (wall) + #[structopt(short, long)] + time: bool, + + /// show mem usage (rss) + #[structopt(short, long)] + mem: bool +} + +fn main() { + let opts = EVROpts::from_args(); + if !opts.quiet { + env_logger::builder() + .format_timestamp(None) + .init(); + } + + trace!("{:#?}", opts); + + let src_path: std::path::PathBuf = opts.src.into(); + let config = conf::get_conf(); + + trace!("{:#?}", config); + + let (action, result) = + if src_path.exists() { + ("run", config.run(&src_path)) + } else { + ("make", config.make(&src_path)) + }; + + match result { + Ok(_) => trace!("ok {}", action), + Err(err) => error!("{}", err) + } +} |