use std::fs;
use json::{JsonValue, object};
#[cfg(any(feature = "server"))]
use crate::server::{Handler, Server};
#[cfg(any(feature = "server"))]
pub mod server;
#[cfg(any(feature = "server"))]
pub mod request;
#[cfg(any(feature = "server"))]
pub mod response;
#[cfg(any(feature = "server"))]
pub fn listen(path: &str, factory: fn() -> Box<dyn Handler>) {
loop {
fn default(path: &str) -> JsonValue {
let _ = fs::create_dir(path);
let conf = Config::default();
let _ = fs::write(&*path, &*conf.clone().to_json().to_string());
conf.to_json()
}
let config = match fs::read_to_string(path) {
Ok(content) => {
match json::parse(content.as_str()) {
Ok(e) => e,
Err(_) => default(path)
}
}
Err(_) => default(path)
};
let config = Config::load(config);
let mut web = Server::new(config.clone(), factory);
web.listen();
}
}
#[derive(Clone, Debug)]
pub struct Config {
pub domain: String,
pub url: String,
pub ip: String,
pub port: String,
pub public: String,
pub runtime: String,
pub cors: Cors,
pub ssl: bool,
pub ssl_pkey: String,
pub ssl_certs: String,
pub debug: bool,
}
impl Config {
pub fn default() -> Self {
Self {
domain: "".to_string(),
url: "".to_string(),
ip: "0.0.0.0".to_string(),
port: "3535".to_string(),
public: "./public".to_string(),
runtime: "./runtime".to_string(),
cors: Cors::default(),
ssl: false,
ssl_pkey: "".to_string(),
ssl_certs: "".to_string(),
debug: false,
}
}
pub fn load(data: JsonValue) -> Self {
Self {
domain: data["domain"].to_string(),
url: "".to_string(),
ip: data["ip"].to_string(),
port: data["port"].to_string(),
public: data["public"].to_string(),
runtime: data["runtime"].to_string(),
cors: Cors::load(data["cors"].clone()),
ssl: data["ssl"].as_bool().unwrap_or(false),
ssl_pkey: data["ssl_pkey"].to_string(),
ssl_certs: data["ssl_certs"].to_string(),
debug: data["debug"].as_bool().unwrap_or(false),
}
}
pub fn to_json(self) -> JsonValue {
let mut data = object! {};
data["domain"] = self.domain.into();
data["ip"] = self.ip.into();
data["port"] = self.port.into();
data["public"] = self.public.into();
data["runtime"] = self.runtime.into();
data["ssl"] = self.ssl.into();
data["ssl_pkey"] = self.ssl_pkey.into();
data["ssl_certs"] = self.ssl_certs.into();
data["cors"] = self.cors.to_json();
data["debug"] = self.debug.into();
data
}
}
#[derive(Clone, Debug)]
pub struct Cors {
pub allow_origin: Vec<String>,
pub allow_methods: String,
pub(crate) allow_headers: String,
pub allow_credentials: bool,
pub expose_headers: String,
pub max_age: i32,
}
impl Cors {
pub fn default() -> Self {
Self {
allow_origin: vec![],
allow_methods: "GET,POST".to_string(),
allow_headers: "content-type".to_string(),
allow_credentials: true,
expose_headers: "content-disposition".to_string(),
max_age: 1800,
}
}
pub fn to_json(self) -> JsonValue {
let mut data = object! {};
data["allow_origin"] = self.allow_origin.into();
data["allow_methods"] = self.allow_methods.into();
data["allow_headers"] = self.allow_headers.into();
data["allow_credentials"] = self.allow_credentials.into();
data["expose_headers"] = self.expose_headers.into();
data["max_age"] = self.max_age.into();
data
}
pub fn load(data: JsonValue) -> Self {
Self {
allow_origin: data["allow_origin"].members().map(|x| x.to_string()).collect(),
allow_methods: data["allow_methods"].to_string(),
allow_headers: data["allow_headers"].to_string(),
allow_credentials: data["allow_credentials"].as_bool().unwrap_or(true),
expose_headers: data["expose_headers"].to_string(),
max_age: data["max_age"].as_i32().unwrap_or(1800),
}
}
}
#[derive(Clone, Debug)]
pub enum ContentType {
Xml,
Html,
Json,
FormData,
XWwwFormUrlencoded,
Plain,
JavaScript,
None,
}
impl ContentType {
pub fn form(text: &str) -> Self {
match text {
"text/html" => ContentType::Html,
"text/xml" | "application/xml" => ContentType::Xml,
"text/plain" => ContentType::Plain,
"application/json" => ContentType::Json,
"application/javascript" => ContentType::JavaScript,
"multipart/form-data" => ContentType::FormData,
"application/x-www-form-urlencoded" => ContentType::XWwwFormUrlencoded,
_ => ContentType::None
}
}
pub fn to_str(self) -> &'static str {
match self {
ContentType::Html => "text/html",
ContentType::Plain => "text/plain",
ContentType::Xml => "application/xml",
ContentType::Json => "application/json",
ContentType::JavaScript => "application/javascript",
ContentType::FormData => "multipart/form-data",
ContentType::XWwwFormUrlencoded => "application/x-www-form-urlencoded",
ContentType::None => ""
}
}
}