use std::{fmt::{Display, Debug}, env, fs::read_to_string};
use regex::Regex;
use serde_json::Value;
pub struct Loader {
json: Value
}
pub struct HiddenLoader<'a> {
loader: &'a Loader
}
impl <'a> HiddenLoader<'a> {
pub fn i64(&self, name: &str) -> Hidden<i64> {
Hidden(self.loader.i64(name))
}
pub fn u64(&self, name: &str) -> Hidden<u64> {
Hidden(self.loader.u64(name))
}
pub fn f64(&self, name: &str) -> Hidden<f64> {
Hidden(self.loader.f64(name))
}
pub fn str(&self, name: &str) -> Hidden<String> {
Hidden(self.loader.str(name))
}
pub fn bool(&self, name: &str) -> Hidden<bool> {
Hidden(self.loader.bool(name))
}
pub fn json(&self, name: &str) -> Hidden<Value> {
Hidden(self.loader.json(name))
}
pub fn custom<T, F>(&self, name: &str, f: F) -> Hidden<T> where F: Fn(String) -> T {
Hidden(self.loader.custom(name, f))
}
}
pub struct Hidden<T>(T);
impl <T> Hidden<T> {
pub fn val(self) -> T {
self.0
}
}
impl <T>Display for Hidden<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "<redacted>")
}
}
impl <T> Debug for Hidden<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("<redacted>").finish()
}
}
impl Loader {
pub fn i64(&self, name: &str) -> i64 {
match env::var_os(name) {
Some(v) => v.into_string().unwrap().parse::<i64>().unwrap(),
None => {
match self.json.get(name) {
Some(_v) => self.json[name].as_i64().unwrap(),
None => panic!("Variable not found in provided configuration file."),
}
},
}
}
pub fn u64(&self, name: &str) -> u64 {
match env::var_os(name) {
Some(v) => v.into_string().unwrap().parse::<u64>().unwrap(),
None => {
match self.json.get(name) {
Some(_v) => self.json[name].as_u64().unwrap(),
None => panic!("Variable not found in provided configuration file."),
}
},
}
}
pub fn f64(&self, name: &str) -> f64 {
match env::var_os(name) {
Some(v) => v.into_string().unwrap().parse::<f64>().unwrap(),
None => {
match self.json.get(name) {
Some(_v) => self.json[name].as_f64().unwrap(),
None => panic!("Variable not found in provided configuration file."),
}
},
}
}
pub fn str(&self, name: &str) -> String {
match env::var_os(name) {
Some(v) => v.into_string().unwrap(),
None => {
match self.json.get(name) {
Some(_v) => self.json[name].as_str().unwrap().to_owned(),
None => panic!("Variable not found in provided configuration file."),
}
},
}
}
pub fn bool(&self, name: &str) -> bool {
match env::var_os(name) {
Some(v) => v.into_string().unwrap().parse::<bool>().unwrap(),
None => {
match self.json.get(name) {
Some(_v) => self.json[name].as_bool().unwrap(),
None => panic!("Variable not found in provided configuration file."),
}
},
}
}
pub fn json(&self, name: &str) -> Value {
let s = match env::var_os(name) {
Some(v) => v.into_string().unwrap(),
None => {
match self.json.get(name) {
Some(_v) => self.json[name].as_str().unwrap().to_owned(),
None => panic!("Variable not found in provided configuration file."),
}
},
};
serde_json::from_str(s.as_str()).unwrap()
}
pub fn custom<T, F>(&self, name: &str, f: F) -> T where F: Fn(String) -> T {
f(self.str(name))
}
pub fn hidden(&self) -> HiddenLoader {
HiddenLoader { loader: self }
}
}
impl From<Value> for Loader {
fn from(value: Value) -> Self {
Loader {
json: value,
}
}
}
fn remove_comments(jsonc_content: &str) -> String {
let regex = Regex::new(r"//[^\n]*").unwrap();
regex.replace_all(jsonc_content, "").to_string()
}
pub fn configuru<T>(default_path: &str) -> T where T: From<Loader> {
let path = match env::var_os("CFG_JSON_PATH") {
Some(v) => v.into_string().unwrap(),
None => default_path.to_owned(),
};
let root_dir = env::current_dir().expect("Failed to get current directory");
let json_file_path = root_dir.join(path);
let contents = if let Some(extension) = json_file_path.extension() {
let file_contents = read_to_string(&json_file_path)
.expect(format!("File {} does not exist", json_file_path.to_str().unwrap()).as_str());
match extension.to_str() {
Some("json") => file_contents,
Some("jsonc") => remove_comments(&file_contents),
_ => panic!("Unsupported file extension"),
}
} else {
panic!("File extension not found");
};
let x: serde_json::Value = serde_json::from_str(contents.as_str()).unwrap();
return T::from(Loader::from(x))
}