/*
* environment.rs: Linking all rair configuration parts together into place.
* Copyright (C) 2019 Oddcoder
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*/
use super::err::*;
use super::metadata::*;
use std::collections::HashMap;
use std::mem;
#[derive(PartialEq, Debug)]
pub enum EnvData<'a> {
Str(&'a str),
U64(u64),
I64(i64),
Bool(bool),
Color(u8, u8, u8),
}
impl<'a, T> From<&'a EnvMetaData> for EnvData<'a> {
fn from(meta: &'a EnvMetaData) -> Self {
match meta {
EnvMetaData::Str(s) => EnvData::Str(&s.data),
EnvMetaData::I64(i) => EnvData::I64(i.data),
EnvMetaData::U64(u) => EnvData::U64(u.data),
EnvMetaData::Bool(u) => EnvData::Bool(u.data),
EnvMetaData::Color(c) => EnvData::Color(c.data.0, c.data.1, c.data.2),
}
}
}
#[derive(Default)]
pub struct Environment {
data: HashMap>,
}
impl Environment {
pub fn new() -> Self {
Environment { data: HashMap::new() }
}
// All exec_*_cb function are guaranteed to be running on the correct type
fn exec_str_cb(&self, key: &str, data: &mut T) -> bool {
let meta = self.data.get(key).unwrap().as_str().unwrap();
let val = &meta.data;
if let Some(cb) = meta.cb {
return cb(key, val, self, data);
}
return true;
}
pub fn add_str_with_cb(&mut self, key: &str, val: &str, help: &str, data: &mut T, cb: StrFn) -> Result<(), EnvErr> {
if self.data.contains_key(key) {
return Err(EnvErr::AlreadyExist);
}
let meta = EnvStr {
data: val.to_string(),
default: val.to_string(),
help: help.to_string(),
cb: Some(cb),
};
self.data.insert(key.to_string(), EnvMetaData::Str(meta));
if !self.exec_str_cb(key, data) {
self.data.remove(key).unwrap();
return Err(EnvErr::CbFailed);
}
return Ok(());
}
pub fn add_str(&mut self, key: &str, val: &str, help: &str) -> Result<(), EnvErr> {
if self.data.contains_key(key) {
return Err(EnvErr::AlreadyExist);
}
let meta = EnvStr {
data: val.to_string(),
default: val.to_string(),
help: help.to_string(),
cb: None,
};
self.data.insert(key.to_string(), EnvMetaData::Str(meta));
return Ok(());
}
pub fn get_str(&self, key: &str) -> Result<&str, EnvErr> {
let meta = match self.data.get(key) {
Some(meta) => meta,
None => return Err(EnvErr::NotFound),
};
match meta.as_str() {
Some(s) => return Ok(&s.data),
None => return Err(EnvErr::DifferentType),
};
}
pub fn set_str(&mut self, key: &str, value: &str, data: &mut T) -> Result<(), EnvErr> {
let meta = match self.data.get_mut(key) {
Some(meta) => meta,
None => return Err(EnvErr::NotFound),
};
let mut tmp = value.to_string();
if let Some(s) = meta.mut_str() {
mem::swap(&mut tmp, &mut s.data);
if !self.exec_str_cb(key, data) {
//restore old data
let meta = self.data.get_mut(key).unwrap();
let s = meta.mut_str().unwrap();
mem::swap(&mut s.data, &mut tmp);
return Err(EnvErr::CbFailed);
}
return Ok(());
} else {
return Err(EnvErr::DifferentType);
}
}
pub fn is_str(&self, key: &str) -> bool {
let meta = match self.data.get(key) {
Some(meta) => meta,
None => return false,
};
return meta.as_str().is_some();
}
fn exec_u64_cb(&self, key: &str, data: &mut T) -> bool {
let meta = self.data.get(key).unwrap().as_u64().unwrap();
if let Some(cb) = meta.cb {
return cb(key, meta.data, self, data);
}
return true;
}
pub fn add_u64_with_cb(&mut self, key: &str, val: u64, help: &str, data: &mut T, cb: U64Fn) -> Result<(), EnvErr> {
if self.data.contains_key(key) {
return Err(EnvErr::AlreadyExist);
}
let meta = EnvU64 {
data: val,
default: val,
help: help.to_string(),
cb: Some(cb),
};
self.data.insert(key.to_string(), EnvMetaData::U64(meta));
if !self.exec_u64_cb(key, data) {
self.data.remove(key).unwrap();
return Err(EnvErr::CbFailed);
}
return Ok(());
}
pub fn add_u64(&mut self, key: &str, val: u64, help: &str) -> Result<(), EnvErr> {
if self.data.contains_key(key) {
return Err(EnvErr::AlreadyExist);
}
let meta = EnvU64 {
data: val,
default: val,
help: help.to_string(),
cb: None,
};
self.data.insert(key.to_string(), EnvMetaData::U64(meta));
return Ok(());
}
pub fn get_u64(&self, key: &str) -> Result {
let meta = match self.data.get(key) {
Some(meta) => meta,
None => return Err(EnvErr::NotFound),
};
match meta.as_u64() {
Some(s) => return Ok(s.data),
None => return Err(EnvErr::DifferentType),
};
}
pub fn set_u64(&mut self, key: &str, value: u64, data: &mut T) -> Result<(), EnvErr> {
let meta = match self.data.get_mut(key) {
Some(meta) => meta,
None => return Err(EnvErr::NotFound),
};
let mut tmp = value;
if let Some(s) = meta.mut_u64() {
mem::swap(&mut tmp, &mut s.data);
if !self.exec_u64_cb(key, data) {
//restore old data
let meta = self.data.get_mut(key).unwrap();
let s = meta.mut_u64().unwrap();
mem::swap(&mut s.data, &mut tmp);
return Err(EnvErr::CbFailed);
}
return Ok(());
} else {
return Err(EnvErr::DifferentType);
}
}
pub fn is_u64(&self, key: &str) -> bool {
let meta = match self.data.get(key) {
Some(meta) => meta,
None => return false,
};
return meta.as_u64().is_some();
}
fn exec_i64_cb(&self, key: &str, data: &mut T) -> bool {
let meta = self.data.get(key).unwrap().as_i64().unwrap();
if let Some(cb) = meta.cb {
return cb(key, meta.data, self, data);
}
return true;
}
pub fn add_i64_with_cb(&mut self, key: &str, val: i64, help: &str, data: &mut T, cb: I64Fn) -> Result<(), EnvErr> {
if self.data.contains_key(key) {
return Err(EnvErr::AlreadyExist);
}
let meta = EnvI64 {
data: val,
default: val,
help: help.to_string(),
cb: Some(cb),
};
self.data.insert(key.to_string(), EnvMetaData::I64(meta));
if !self.exec_i64_cb(key, data) {
self.data.remove(key).unwrap();
return Err(EnvErr::CbFailed);
}
return Ok(());
}
pub fn add_i64(&mut self, key: &str, val: i64, help: &str) -> Result<(), EnvErr> {
if self.data.contains_key(key) {
return Err(EnvErr::AlreadyExist);
}
let meta = EnvI64 {
data: val,
default: val,
help: help.to_string(),
cb: None,
};
self.data.insert(key.to_string(), EnvMetaData::I64(meta));
return Ok(());
}
pub fn get_i64(&self, key: &str) -> Result {
let meta = match self.data.get(key) {
Some(meta) => meta,
None => return Err(EnvErr::NotFound),
};
match meta.as_i64() {
Some(s) => return Ok(s.data),
None => return Err(EnvErr::DifferentType),
};
}
pub fn set_i64(&mut self, key: &str, value: i64, data: &mut T) -> Result<(), EnvErr> {
let meta = match self.data.get_mut(key) {
Some(meta) => meta,
None => return Err(EnvErr::NotFound),
};
let mut tmp = value;
if let Some(s) = meta.mut_i64() {
mem::swap(&mut tmp, &mut s.data);
if !self.exec_i64_cb(key, data) {
//restore old data
let meta = self.data.get_mut(key).unwrap();
let s = meta.mut_i64().unwrap();
mem::swap(&mut s.data, &mut tmp);
return Err(EnvErr::CbFailed);
}
return Ok(());
} else {
return Err(EnvErr::DifferentType);
}
}
pub fn is_i64(&self, key: &str) -> bool {
let meta = match self.data.get(key) {
Some(meta) => meta,
None => return false,
};
return meta.as_i64().is_some();
}
fn exec_bool_cb(&self, key: &str, data: &mut T) -> bool {
let meta = self.data.get(key).unwrap().as_bool().unwrap();
if let Some(cb) = meta.cb {
return cb(key, meta.data, self, data);
}
return true;
}
pub fn add_bool_with_cb(&mut self, key: &str, val: bool, help: &str, data: &mut T, cb: BoolFn) -> Result<(), EnvErr> {
if self.data.contains_key(key) {
return Err(EnvErr::AlreadyExist);
}
let meta = EnvBool {
data: val,
default: val,
help: help.to_string(),
cb: Some(cb),
};
self.data.insert(key.to_string(), EnvMetaData::Bool(meta));
if !self.exec_bool_cb(key, data) {
self.data.remove(key).unwrap();
return Err(EnvErr::CbFailed);
}
return Ok(());
}
pub fn add_bool(&mut self, key: &str, val: bool, help: &str) -> Result<(), EnvErr> {
if self.data.contains_key(key) {
return Err(EnvErr::AlreadyExist);
}
let meta = EnvBool {
data: val,
default: val,
help: help.to_string(),
cb: None,
};
self.data.insert(key.to_string(), EnvMetaData::Bool(meta));
return Ok(());
}
pub fn get_bool(&self, key: &str) -> Result {
let meta = match self.data.get(key) {
Some(meta) => meta,
None => return Err(EnvErr::NotFound),
};
match meta.as_bool() {
Some(s) => return Ok(s.data),
None => return Err(EnvErr::DifferentType),
};
}
pub fn set_bool(&mut self, key: &str, value: bool, data: &mut T) -> Result<(), EnvErr> {
let meta = match self.data.get_mut(key) {
Some(meta) => meta,
None => return Err(EnvErr::NotFound),
};
let mut tmp = value;
if let Some(s) = meta.mut_bool() {
mem::swap(&mut tmp, &mut s.data);
if !self.exec_bool_cb(key, data) {
//restore old data
let meta = self.data.get_mut(key).unwrap();
let s = meta.mut_bool().unwrap();
mem::swap(&mut s.data, &mut tmp);
return Err(EnvErr::CbFailed);
}
return Ok(());
} else {
return Err(EnvErr::DifferentType);
}
}
pub fn is_bool(&self, key: &str) -> bool {
let meta = match self.data.get(key) {
Some(meta) => meta,
None => return false,
};
return meta.as_bool().is_some();
}
fn exec_color_cb(&self, key: &str, data: &mut T) -> bool {
let meta = self.data.get(key).unwrap().as_color().unwrap();
if let Some(cb) = meta.cb {
return cb(key, meta.data, self, data);
}
return true;
}
pub fn add_color_with_cb(&mut self, key: &str, val: (u8, u8, u8), help: &str, data: &mut T, cb: ColorFn) -> Result<(), EnvErr> {
if self.data.contains_key(key) {
return Err(EnvErr::AlreadyExist);
}
let meta = EnvColor {
data: val,
default: val,
help: help.to_string(),
cb: Some(cb),
};
self.data.insert(key.to_string(), EnvMetaData::Color(meta));
if !self.exec_color_cb(key, data) {
self.data.remove(key).unwrap();
return Err(EnvErr::CbFailed);
}
return Ok(());
}
pub fn add_color(&mut self, key: &str, val: (u8, u8, u8), help: &str) -> Result<(), EnvErr> {
if self.data.contains_key(key) {
return Err(EnvErr::AlreadyExist);
}
let meta = EnvColor {
data: val,
default: val,
help: help.to_string(),
cb: None,
};
self.data.insert(key.to_string(), EnvMetaData::Color(meta));
return Ok(());
}
pub fn get_color(&self, key: &str) -> Result<(u8, u8, u8), EnvErr> {
let meta = match self.data.get(key) {
Some(meta) => meta,
None => return Err(EnvErr::NotFound),
};
match meta.as_color() {
Some(s) => return Ok(s.data),
None => return Err(EnvErr::DifferentType),
};
}
pub fn set_color(&mut self, key: &str, value: (u8, u8, u8), data: &mut T) -> Result<(), EnvErr> {
let meta = match self.data.get_mut(key) {
Some(meta) => meta,
None => return Err(EnvErr::NotFound),
};
let mut tmp = value;
if let Some(s) = meta.mut_color() {
mem::swap(&mut tmp, &mut s.data);
if !self.exec_color_cb(key, data) {
//restore old data
let meta = self.data.get_mut(key).unwrap();
let s = meta.mut_color().unwrap();
mem::swap(&mut s.data, &mut tmp);
return Err(EnvErr::CbFailed);
}
return Ok(());
} else {
return Err(EnvErr::DifferentType);
}
}
pub fn is_color(&self, key: &str) -> bool {
let meta = match self.data.get(key) {
Some(meta) => meta,
None => return false,
};
return meta.as_color().is_some();
}
pub fn reset(&mut self, key: &str, data: &mut T) -> Result<(), EnvErr> {
let meta = match self.data.get_mut(key) {
Some(meta) => meta,
None => return Err(EnvErr::NotFound),
};
match meta {
EnvMetaData::Bool(b) => {
b.data = b.default;
self.exec_bool_cb(key, data);
}
EnvMetaData::Color(c) => {
c.data = c.default;
self.exec_color_cb(key, data);
}
EnvMetaData::I64(i) => {
i.data = i.default;
self.exec_i64_cb(key, data);
}
EnvMetaData::U64(u) => {
u.data = u.default;
self.exec_u64_cb(key, data);
}
EnvMetaData::Str(s) => {
s.data = s.default.clone();
self.exec_str_cb(key, data);
}
}
return Ok(());
}
pub fn get(&self, key: &str) -> Option {
let meta = self.data.get(key)?;
return match meta {
EnvMetaData::Bool(b) => Some(EnvData::Bool(b.data)),
EnvMetaData::I64(i) => Some(EnvData::I64(i.data)),
EnvMetaData::U64(u) => Some(EnvData::U64(u.data)),
EnvMetaData::Str(s) => Some(EnvData::Str(&s.data)),
EnvMetaData::Color(c) => Some(EnvData::Color(c.data.0, c.data.1, c.data.2)),
};
}
pub fn get_help(&self, key: &str) -> Option<&str> {
let meta = self.data.get(key)?;
return match meta {
EnvMetaData::Bool(b) => Some(&b.help),
EnvMetaData::I64(i) => Some(&i.help),
EnvMetaData::U64(u) => Some(&u.help),
EnvMetaData::Str(s) => Some(&s.help),
EnvMetaData::Color(c) => Some(&c.help),
};
}
pub fn iter<'a>(&'a self) -> Box + 'a> {
return Box::new(self.data.iter().map(|(k, v)| (k.as_str(), EnvData::from(v))));
}
}
#[cfg(test)]
mod test_environment {
use super::*;
fn even_str(_: &str, value: &str, _: &Environment