use serde::{Deserialize, Serialize};
use std::fs;
use std::path::{Path, PathBuf};
mod client;
mod executor;
pub use client::Client;
pub use executor::ExecResponse;
pub use executor::ExecResult;
pub use executor::Executor;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Runtime {
pub language: String,
pub version: String,
pub aliases: Vec<String>,
}
type LoadResult<T> = Result<T, LoadError>;
#[derive(Debug, Clone)]
pub struct LoadError {
pub details: String,
}
impl LoadError {
pub fn new(details: &str) -> Self {
Self {
details: details.into(),
}
}
}
impl std::fmt::Display for LoadError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.details)
}
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct File {
pub name: String,
pub content: String,
pub encoding: String,
}
impl Default for File {
fn default() -> Self {
Self {
name: String::new(),
content: String::new(),
encoding: String::from("utf8"),
}
}
}
impl File {
pub fn new(name: &str, content: &str, encoding: &str) -> Self {
Self {
name: name.to_string(),
content: content.to_string(),
encoding: encoding.to_string(),
}
}
pub fn load_from(path: &str) -> LoadResult<Self> {
let path = PathBuf::from(path);
if !path.is_file() {
return Err(LoadError::new("File does not exist, or is a directory"));
}
let name = match path.file_name() {
Some(n) => n.to_string_lossy(),
None => {
return Err(LoadError::new("Unable to parse file name"));
}
};
Ok(Self {
name: name.to_string(),
content: File::load_contents(&path)?,
encoding: String::from("utf8"),
})
}
fn load_contents(path: &Path) -> LoadResult<String> {
match fs::read_to_string(path) {
Ok(content) => Ok(content),
Err(e) => Err(LoadError::new(&e.to_string())),
}
}
#[must_use]
pub fn set_content(mut self, content: &str) -> Self {
self.content = content.to_string();
self
}
pub fn load_content_from(mut self, path: &str) -> LoadResult<Self> {
let path = PathBuf::from(path);
self.content = File::load_contents(&path)?;
Ok(self)
}
#[must_use]
pub fn set_name(mut self, name: &str) -> Self {
self.name = name.to_string();
self
}
#[must_use]
pub fn set_encoding(mut self, encoding: &str) -> Self {
self.encoding = encoding.to_string();
self
}
}
#[cfg(test)]
mod test_file_private {
use super::File;
use super::Runtime;
use std::path::PathBuf;
#[test]
fn test_load_contents() {
let path = PathBuf::from(file!());
let contents = File::load_contents(&path).unwrap();
assert!(contents.contains("mod test_file_private {"));
}
#[test]
fn test_load_contents_non_existent() {
let path = PathBuf::from("/path/doesnt/exist");
let contents = File::load_contents(&path);
assert!(contents.is_err());
let err = contents.unwrap_err();
let expd_err = match cfg!(windows) {
true => String::from("The system cannot find the path specified. (os error 3)"),
false => String::from("No such file or directory (os error 2)"),
};
assert_eq!(err.details, expd_err);
assert_eq!(format!("{}", err), expd_err);
let err2 = err.clone();
assert_eq!(err.details, err2.details);
}
#[test]
fn test_runtime_creation() {
let rt = Runtime {
language: "clojure".to_string(),
version: "9000".to_string(),
aliases: vec![],
};
let rt2 = rt.clone();
assert_eq!(rt.language, rt2.language);
assert_eq!(rt.version, rt2.version);
assert_eq!(rt.aliases, rt2.aliases);
assert_eq!(rt.language, "clojure".to_string());
assert_eq!(rt.version, "9000".to_string());
assert!(rt.aliases.is_empty());
}
}