#[cfg(feature = "to_lazy_static")]
pub mod to_lazy_static;
pub fn read_env<T: Transform>(env_reader: &mut EnvReader<T>) {
let raw = String::from_utf8(env_reader.env.clone()).unwrap();
let env = raw.split('\n').collect::<Vec<_>>();
let mut comments_above_key = vec![];
for line in env {
let trimmed = line.trim();
if trimmed.is_empty() {
if env_reader
.transformer
.remove_comments_if_blank_line_occurs()
{
comments_above_key = vec![];
}
continue;
}
if trimmed.starts_with('#') {
comments_above_key.push(trimmed.replace('#', "//"));
continue;
}
let (key, value) = {
let split = trimmed
.find('=')
.unwrap_or_else(|| panic!("No '=' found in: '{}'", trimmed));
(&trimmed[0..split], &trimmed[split + 1..])
};
let env_type = if let Ok(o) = value.parse::<i32>() {
EnvType::I32(o)
} else if let Ok(o) = value.parse::<f32>() {
EnvType::F32(o)
} else if let Ok(o) = value.parse::<bool>() {
EnvType::Bool(o)
} else {
let string_value = value.to_string();
EnvType::StaticStr(string_value)
};
env_reader
.transformer
.write(comments_above_key.clone(), key, env_type);
comments_above_key = vec![];
}
}
pub struct EnvReader<'a, T: Transform> {
pub env: Vec<u8>,
pub transformer: &'a mut T,
}
impl<'a, T: Transform> EnvReader<'a, T> {
pub fn new(env: Vec<u8>, transformer: &'a mut T) -> EnvReader<'a, T> {
EnvReader { env, transformer }
}
}
pub trait CustomMap {
fn rust_type(&self) -> String;
fn raw_value(&self) -> String;
fn value(&self) -> String;
#[cfg(feature = "to_lazy_static")]
fn transform(&self) -> String;
}
pub enum EnvType {
Bool(bool),
I32(i32),
I64(i64),
I128(i128),
U8(u8),
U32(u32),
U128(u128),
F32(f32),
F64(f64),
USize(usize),
StaticStr(String),
Custom(Box<dyn CustomMap>),
}
impl EnvType {
pub fn rust_type(&self) -> String {
match self {
EnvType::Bool(_) => "bool".to_string(),
EnvType::I32(_) => "i32".to_string(),
EnvType::I64(_) => "i64".to_string(),
EnvType::I128(_) => "i128".to_string(),
EnvType::U8(_) => "u8".to_string(),
EnvType::U32(_) => "u32".to_string(),
EnvType::U128(_) => "u128".to_string(),
EnvType::F32(_) => "f32".to_string(),
EnvType::F64(_) => "f64".to_string(),
EnvType::USize(_) => "usize".to_string(),
EnvType::StaticStr(_) => "&'static str".to_string(),
EnvType::Custom(c) => c.rust_type(),
}
.replace('\"', "")
}
pub fn raw_value(&self) -> String {
match self {
EnvType::Bool(val) => val.to_string(),
EnvType::I32(val) => val.to_string(),
EnvType::I64(val) => val.to_string(),
EnvType::I128(val) => val.to_string(),
EnvType::U8(val) => val.to_string(),
EnvType::U32(val) => val.to_string(),
EnvType::U128(val) => val.to_string(),
EnvType::F32(val) => val.to_string(),
EnvType::F64(val) => val.to_string(),
EnvType::USize(val) => val.to_string(),
EnvType::StaticStr(val) => format!("\"{}\"", val),
EnvType::Custom(c) => c.raw_value(),
}
}
pub fn value(&self) -> String {
let ty = self.raw_value();
match self {
EnvType::StaticStr(_) | EnvType::Bool(_) => ty,
EnvType::Custom(c) => c.value(),
_ => ty + &self.rust_type(),
}
}
}
pub trait Transform {
fn remove_comments_if_blank_line_occurs(&self) -> bool {
false
}
fn write(&mut self, comments: Vec<String>, key: &str, inferred_type: EnvType);
}
#[cfg(test)]
mod locations {
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
pub fn src() -> PathBuf {
std::env::current_dir().unwrap().join("src")
}
pub fn env() -> Vec<u8> {
include_bytes!("../.env").to_vec()
}
pub fn temp_rs() -> PathBuf {
src().join("temp_rs.rs")
}
pub fn check_equals(to_check: &str) {
let mut assert_test = String::new();
File::open(src().join(to_check))
.unwrap()
.read_to_string(&mut assert_test)
.unwrap();
let mut temp_rs_string = String::new();
File::open(temp_rs())
.unwrap()
.read_to_string(&mut temp_rs_string)
.unwrap();
assert_eq!(assert_test.replace('\r', ""), temp_rs_string);
std::fs::remove_file(temp_rs()).unwrap();
}
}
#[test]
fn test_write() {
use crate::locations::{check_equals, env, temp_rs};
use std::fs::File;
use std::io::Write;
struct TransformerImpl {
file: File,
}
impl Transform for TransformerImpl {
fn write(&mut self, comments: Vec<String>, key: &str, inferred_type: EnvType) {
for comment in comments {
writeln!(&self.file, "{}", comment).unwrap();
}
let inferred_type = if key == "SOME_I64_VAL" {
EnvType::I64(inferred_type.raw_value().parse().unwrap())
} else {
inferred_type
};
let declaration = format!(
"pub const {}: {} = {};",
key,
inferred_type.rust_type(),
inferred_type.value()
);
writeln!(&self.file, "{}", declaration).unwrap();
}
}
read_env(&mut EnvReader::new(
env(),
&mut TransformerImpl {
file: File::create(&temp_rs()).unwrap(),
},
));
check_equals("assert_test.rs");
}