make it possible to implement another lanaguage than python
This commit is contained in:
parent
62f9981325
commit
888743f4de
|
@ -0,0 +1,30 @@
|
||||||
|
use crate::executor::python::PythonExecutor;
|
||||||
|
|
||||||
|
|
||||||
|
pub mod python;
|
||||||
|
pub mod helper;
|
||||||
|
|
||||||
|
pub fn executors() -> Vec<Box<dyn Executor>> {
|
||||||
|
vec![
|
||||||
|
Box::new(PythonExecutor {}),
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn executors_id() -> Vec<String> {
|
||||||
|
executors()
|
||||||
|
.iter()
|
||||||
|
.map(|x| x.id())
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_executor<'a, T: Into<String>>(id: T, executors: &'a Vec<Box<dyn Executor>>) -> Option<&'a Box<dyn Executor>> {
|
||||||
|
let id: String = id.into();
|
||||||
|
let found = executors.iter().find(|x| x.id() == id);
|
||||||
|
found
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Executor {
|
||||||
|
fn id(&self) -> String;
|
||||||
|
fn exec(&self, code: String) -> Result<String, String>;
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
use std::{fs::create_dir_all, process::ExitStatus};
|
||||||
|
|
||||||
|
use rand::{distributions::Alphanumeric, Rng, thread_rng};
|
||||||
|
|
||||||
|
pub fn assure_dir_exists() -> Result<(), std::io::Error> {
|
||||||
|
create_dir_all("/tmp/sandy-tmp")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_path() -> String {
|
||||||
|
let name = thread_rng()
|
||||||
|
.sample_iter(&Alphanumeric)
|
||||||
|
.take(64)
|
||||||
|
.map(char::from)
|
||||||
|
.collect::<String>();
|
||||||
|
format!("/tmp/sandy-tmp/{name}")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exit_code_msg(code: ExitStatus) -> String {
|
||||||
|
let status = code.code();
|
||||||
|
if let Some(status) = status {
|
||||||
|
if status != 0 {
|
||||||
|
format!("<span style='color:red;font-weight:bolder'>Command exited with code {status}</span>")
|
||||||
|
} else {
|
||||||
|
"Command exited with code 0".into()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
"Command exited with unknown code".into()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
use std::fs;
|
||||||
|
use std::io::Read;
|
||||||
|
use std::process::Command;
|
||||||
|
use std::process::Stdio;
|
||||||
|
|
||||||
|
use super::Executor;
|
||||||
|
|
||||||
|
use super::helper::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct PythonExecutor {}
|
||||||
|
|
||||||
|
impl Executor for PythonExecutor {
|
||||||
|
fn id(&self) -> String {
|
||||||
|
"python".into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exec(&self, code: String) -> Result<String, String> {
|
||||||
|
assure_dir_exists().map_err(|x| x.to_string())?;
|
||||||
|
|
||||||
|
let path = create_path();
|
||||||
|
|
||||||
|
// 1. Save the code
|
||||||
|
fs::write(path.clone(), code).map_err(|x| x.to_string())?;
|
||||||
|
|
||||||
|
// 2. Run
|
||||||
|
let mut out = Command::new("python")
|
||||||
|
.arg(path.clone())
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.spawn()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// 3. Grab the stuff and exit
|
||||||
|
let exit_status = out.wait().map_err(|x| x.to_string())?;
|
||||||
|
|
||||||
|
let mut stdout: String = "".into();
|
||||||
|
|
||||||
|
if let Some(mut sout) = out.stdout {
|
||||||
|
let mut buf = vec![];
|
||||||
|
sout.read_to_end(&mut buf).map_err(|x| x.to_string())?;
|
||||||
|
stdout += String::from_utf8(buf).map_err(|x| x.to_string())?.as_str();
|
||||||
|
} else {
|
||||||
|
stdout += "Couldn't get stdout from the process"
|
||||||
|
}
|
||||||
|
|
||||||
|
stdout += "\n\n";
|
||||||
|
stdout += exit_code_msg(exit_status).as_str();
|
||||||
|
|
||||||
|
Ok(stdout)
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,8 +3,10 @@
|
||||||
use std::{fs::create_dir_all, net::SocketAddr, process::Stdio};
|
use std::{fs::create_dir_all, net::SocketAddr, process::Stdio};
|
||||||
|
|
||||||
use web::state::{SharedState, TemplateState};
|
use web::state::{SharedState, TemplateState};
|
||||||
|
use executor::executors_id;
|
||||||
|
|
||||||
mod web;
|
mod web;
|
||||||
|
pub mod executor;
|
||||||
|
|
||||||
fn check() {
|
fn check() {
|
||||||
let res = std::process::Command::new("python")
|
let res = std::process::Command::new("python")
|
||||||
|
@ -26,7 +28,9 @@ async fn main() {
|
||||||
check();
|
check();
|
||||||
|
|
||||||
let state = SharedState {
|
let state = SharedState {
|
||||||
template: TemplateState {}
|
template: TemplateState {
|
||||||
|
langs: executors_id()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let routes = web::routes(state);
|
let routes = web::routes(state);
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
use std::{fs::{self, remove_file}, io::Read, process::{Command, Stdio}};
|
|
||||||
|
|
||||||
use rand::{distributions::Alphanumeric, Rng};
|
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
use warp::{reject::Rejection, reply::{json, with_status, Reply}, Filter, http::StatusCode};
|
use warp::{reject::Rejection, reply::{json, with_status, Reply}, Filter, http::StatusCode};
|
||||||
|
|
||||||
use crate::SharedState;
|
use crate::{executor::{executors, executors_id, get_executor, python::PythonExecutor, Executor}, SharedState};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
struct ExecutorData {
|
struct ExecutorData {
|
||||||
|
@ -15,49 +12,25 @@ struct ExecutorData {
|
||||||
|
|
||||||
async fn executor(_state: SharedState, data: ExecutorData) -> Result<Box<dyn Reply>, Rejection> {
|
async fn executor(_state: SharedState, data: ExecutorData) -> Result<Box<dyn Reply>, Rejection> {
|
||||||
|
|
||||||
if data.lang != "python" {
|
let lang = data.lang;
|
||||||
|
let execs = executors();
|
||||||
|
let found = get_executor(&lang, &execs);
|
||||||
|
|
||||||
|
if found.is_none() {
|
||||||
return Ok(
|
return Ok(
|
||||||
Box::new(
|
Box::new(
|
||||||
with_status(
|
with_status(
|
||||||
json(&"only python supported".to_string()),
|
json(&format!("{lang} is not supported")),
|
||||||
StatusCode::BAD_REQUEST
|
StatusCode::BAD_REQUEST
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
let name = rand::thread_rng()
|
let lang = found.unwrap();
|
||||||
.sample_iter(&Alphanumeric)
|
let out = lang.exec(data.code).unwrap();
|
||||||
.take(64)
|
|
||||||
.map(char::from)
|
|
||||||
.collect::<String>();
|
|
||||||
let path = format!("/tmp/sandy-tmp/{name}");
|
|
||||||
|
|
||||||
fs::write(path.clone(), &data.code).unwrap();
|
Ok(Box::new(warp::reply::json(&out)))
|
||||||
let mut out = Command::new("python")
|
|
||||||
.arg(path.clone())
|
|
||||||
.stdout(Stdio::piped())
|
|
||||||
.spawn()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let exit_status = out.wait().unwrap().code().unwrap();
|
|
||||||
|
|
||||||
let mut buf = vec![];
|
|
||||||
out.stdout.unwrap().read_to_end(&mut buf).unwrap();
|
|
||||||
|
|
||||||
let mut stdout = String::from_utf8(buf).unwrap();
|
|
||||||
stdout += "\n---\n";
|
|
||||||
if exit_status != 0 {
|
|
||||||
stdout += format!("<span style='color:red;font-weight:bolder'>Command exited with code {}</span>", exit_status).as_str();
|
|
||||||
} else {
|
|
||||||
stdout += "Command exited with code 0";
|
|
||||||
}
|
|
||||||
|
|
||||||
let stdout = stdout.replace('\n', "<br>");
|
|
||||||
|
|
||||||
remove_file(path).unwrap();
|
|
||||||
|
|
||||||
Ok(Box::new(warp::reply::json(&stdout)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn executor_f(state: SharedState) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
|
fn executor_f(state: SharedState) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
|
||||||
|
|
|
@ -9,7 +9,7 @@ use super::state::TemplateState;
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template( path = "index.html" )]
|
#[template( path = "index.html" )]
|
||||||
pub struct Index {
|
pub struct Index {
|
||||||
pub state: TemplateState
|
state: TemplateState
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Index {
|
impl Index {
|
||||||
|
|
|
@ -6,5 +6,5 @@ pub struct SharedState {
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct TemplateState {
|
pub struct TemplateState {
|
||||||
|
pub langs: Vec<String>
|
||||||
}
|
}
|
Loading…
Reference in New Issue