mod tests;
mod cache;
use core::fmt;
use std::fmt::Debug;
use serde::{Deserialize, Serialize};
use crate::cache::CompilerCache;
use std::sync::{RwLock, Arc};
use std::error::Error;
use std::collections::HashSet;
pub struct Wandbox {
cache : Arc<RwLock<CompilerCache>>,
}
impl Wandbox {
pub async fn new(comps : Option<HashSet<String>>, langs : Option<HashSet<String>>) -> Result<Wandbox, Box<dyn Error>> {
let mut cache : CompilerCache = cache::load().await?;
if let Some(langs) = langs {
cache = cache.into_iter().filter(|(_x, v)| !langs.contains(&v.name)).collect();
}
if let Some(comps) = comps {
for (_k, v) in cache.iter_mut() {
for str in &comps {
v.remove_compiler(str);
}
}
}
for (_k, v) in cache.iter_mut() {
for mut c in v.compilers.iter_mut() {
c.language = c.language.to_ascii_lowercase();
}
}
Ok(Wandbox {
cache: Arc::new(RwLock::new(cache))
})
}
pub fn get_compilers(&self, lang : &str) -> Option<Vec<Compiler>> {
let lock = self.cache.read().unwrap();
let language_option = lock.get(lang);
let lang = match language_option {
Some(l) => l,
None => return None
};
Some(lang.compilers.clone())
}
pub fn get_languages(&self) -> Vec<Language> {
let lock = self.cache.read().unwrap();
let mut vec : Vec<Language> = Vec::new();
for (_k, v) in lock.iter() {
vec.push(v.clone());
}
vec
}
pub fn is_valid_compiler_str(&self, c : &str) -> bool {
let lock = self.cache.read().unwrap();
for (_l, k) in lock.iter() {
for v in k.compilers.iter() {
if v.name == c {
return true;
}
}
}
return false;
}
pub fn get_compiler_language_str(&self, c : &str) -> Option<String> {
let lock = self.cache.read().unwrap();
for (_k, v) in lock.iter() {
for comp in &v.compilers {
if comp.name == c {
return Some(v.name.clone());
}
}
}
return None;
}
pub fn is_valid_language(&self, l : &str) -> bool {
let lock = self.cache.read().unwrap();
return lock.get(l).is_some();
}
pub fn get_default_compiler(&self, l : &str) -> Option<String> {
let lock = self.cache.read().unwrap();
if let Some(lang) = lock.get(l) {
Some(lang.compilers.get(0).expect("awd").name.clone())
}
else {
None
}
}
}
#[derive(Hash, Eq, PartialEq, Serialize, Deserialize)]
pub struct Compiler {
#[serde(rename = "compiler-option-raw")]
pub compiler_option_raw : bool,
#[serde(rename = "display-compile-command")]
pub display_compile_command : String,
#[serde(rename = "runtime-option-raw")]
pub runtime_option_raw : bool,
pub version : String,
pub language : String,
pub name : String,
pub provider : i32,
}
impl Clone for Compiler {
fn clone(&self) -> Self {
Compiler {
compiler_option_raw : self.compiler_option_raw,
display_compile_command : self.display_compile_command.clone(),
runtime_option_raw : self.runtime_option_raw,
version : self.version.clone(),
language : self.language.clone(),
name : self.name.clone(),
provider : self.provider,
}
}
}
impl fmt::Debug for Compiler {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[{} {}] {}: {}", self.name, self.version, self.provider, self.language)
}
}
#[derive(Default, Serialize)]
pub struct CompilationBuilder {
#[serde(skip)]
target : String,
lang : String,
compiler : String,
code : String,
stdin : String,
#[serde(skip)]
options : Vec<String>,
#[serde(rename = "compiler-option-raw")]
compiler_options_raw : String,
save : bool
}
impl CompilationBuilder {
pub fn new() -> CompilationBuilder {
return CompilationBuilder { ..Default::default()}
}
pub fn target(&mut self, target : &str) -> () {
self.target = target.trim().to_string();
}
pub fn code(&mut self, code : &str) -> () {
self.code = code.trim().to_string();
}
pub fn stdin(&mut self, stdin : &str) -> () {
self.stdin = stdin.trim().to_string();
}
pub fn save(&mut self, save : bool) -> () {
self.save = save;
}
pub fn options(&mut self, options : Vec<String>) -> () {
self.options = options;
}
pub fn options_str(&mut self, options : Vec<&str>) -> () {
self.options = options.into_iter().map(|f| f.to_owned()).collect();
}
pub fn build(&mut self, wb : &Wandbox) -> Result<(), WandboxError> {
self.compiler_options_raw = self.options.join("\n");
if wb.is_valid_language(&self.target) {
let comp = match wb.get_default_compiler(&self.target) {
Some(def) => def,
None => return Err(WandboxError::new("Unable to determine default compiler for input language"))
};
self.compiler = comp;
self.lang = self.target.clone();
}
else if wb.is_valid_compiler_str(&self.target) {
let lang = match wb.get_compiler_language_str(&self.target) {
Some(lang) => lang,
None => return Err(WandboxError::new("Unable to determine language for compiler}"))
};
self.lang = lang;
self.compiler = self.target.clone();
}
else {
return Err(WandboxError::new("Unable to find compiler or language for target"));
}
Ok(())
}
pub async fn dispatch(&self) -> Result<CompilationResult, WandboxError> {
let client = reqwest::Client::new();
let result = client.post("https://wandbox.org/api/compile.json")
.json(&self)
.header("Content-Type", "application/json; charset=utf-8")
.send().await;
let response = match result {
Ok(r) => r,
Err(e) => return Err(WandboxError::new(&format!("{}", e)))
};
let status_code = response.status().clone();
let res : CompilationResult = match response.json().await {
Ok(res) => res,
Err(_e) => return Err(WandboxError::new(&format!("Wandbox replied with: {}\n\
This could mean WandBox is experiencing an outage, or a network connection error has occured", status_code)))
};
return Ok(res);
}
}
#[derive(Default, Deserialize)]
pub struct CompilationResult {
#[serde(default)]
pub status : String,
#[serde(default)]
pub signal : String,
#[serde(rename = "compiler_output", default)]
pub compiler_stdout : String,
#[serde(rename = "compiler_error", default)]
pub compiler_stderr : String,
#[serde(rename = "compiler_message", default)]
pub compiler_all : String,
#[serde(rename = "program_output", default)]
pub program_stdout : String,
#[serde(rename = "program_error", default)]
pub program_stderr : String,
#[serde(rename = "program_message", default)]
pub program_all : String,
#[serde(default)]
pub permlink : String,
#[serde(default)]
pub url : String,
}
impl fmt::Debug for CompilationResult {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[{} {}] {}: {}", self.status, self.signal, self.compiler_all, self.program_all)
}
}
#[derive(Hash, Eq, PartialEq, Debug, Clone)]
pub struct Language {
pub name : String,
pub compilers : Vec<Compiler>
}
impl Language {
fn remove_compiler(&mut self, str : &str) {
let mut copy = self.compilers.clone();
copy = copy.into_iter().filter(|v| v.name != str).collect();
self.compilers = copy;
}
}
#[derive(Debug)]
pub struct WandboxError {
details: String
}
impl WandboxError {
fn new(msg: &str) -> WandboxError {
WandboxError{details: msg.to_string()}
}
}
impl fmt::Display for WandboxError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f,"{}",self.details)
}
}
impl std::error::Error for WandboxError {
fn description(&self) -> &str {
&self.details
}
}