created singleton config manager using once pattern.

This commit is contained in:
michael-bailey 2022-09-06 18:05:04 +01:00
parent 31d67889ff
commit 4bfaf1a813
2 changed files with 81 additions and 99 deletions

View File

@ -10,6 +10,7 @@ use toml::Value;
use crate::{
config_manager::{
get_args,
messages::{
ConfigManagerDataMessage, ConfigManagerDataResponse,
ConfigManagerOutput,
@ -23,10 +24,10 @@ static mut SHARED: Option<Addr<ConfigManager>> = None;
static INIT: Once = Once::new();
pub(crate) struct ConfigManager {
_file: Option<File>,
_stored: ConfigValue,
file: File,
stored: ConfigValue,
root: ConfigValue,
_subscribers: Vec<Recipient<ObservableMessage<ConfigManagerOutput>>>,
subscribers: Vec<Recipient<ObservableMessage<ConfigManagerOutput>>>,
}
impl ConfigManager {
@ -34,112 +35,83 @@ impl ConfigManager {
INIT.call_once(|| {
// Since this access is inside a call_once, before any other accesses, it is safe
unsafe {
// todo: add proper error handling
let file = OpenOptions::new()
.write(true)
.read(true)
.open(file.unwrap_or("./config_file.toml".into()))
.ok();
let mut file = Self::get_file();
let shared = Self::new(file);
let mut output = String::new();
file.as_ref().map(|mut v| {
v.read_to_string(&mut output)
.expect("failed to read from file")
});
let stored = output
.parse::<Value>()
.map(|v| v.into())
.ok()
.unwrap_or_else(|| ConfigValue::Dict(BTreeMap::new()));
let root = stored.clone();
let shared = Self {
_file: file,
root,
_stored: stored,
_subscribers: Vec::default(),
}
.start();
SHARED = Some(shared);
}
});
unsafe { SHARED.clone().unwrap() }
}
fn new(mut file: File) -> Addr<Self> {
let mut output = String::new();
file.read_to_string(&mut output)
.expect("failed to read from file");
let stored = output
.parse::<Value>()
.map(|v| v.into())
.ok()
.unwrap_or_else(|| ConfigValue::Dict(BTreeMap::new()));
let root = stored.clone();
Self {
file,
root,
stored,
subscribers: Vec::default(),
}
.start()
}
fn get_file() -> File {
let default = "./config_file.toml".to_owned();
let args = get_args();
let file_path =
args.get_one::<String>("config file").unwrap_or(&default);
OpenOptions::new()
.write(true)
.read(true)
.open(file_path)
.ok()
.unwrap()
}
}
impl ConfigManager {
pub fn get_value(
&self,
val_path: String,
) -> Result<ConfigValue, &'static str> {
use ConfigValue::{Array, Dict};
pub fn get_value(&self, key: String) -> Result<ConfigValue, &'static str> {
use ConfigValue::Dict;
let path: Vec<String> = val_path.split('.').map(|v| v.into()).collect();
let mut current_node: &ConfigValue = &self.root;
for i in path {
match current_node {
Dict(v) => match v.get(&i) {
Some(v) => current_node = v,
None => return Err("path does not exist"),
},
Array(v) => {
if let Ok(index) = i.parse::<usize>() {
current_node = &v[index];
}
}
_ => return Err("invalid path"),
}
if let Dict(dict) = &self.root {
let opt_value = dict.get(&key);
return if let Some(value) = opt_value {
Ok(value.clone())
} else {
Err("[ConfigManager] get_value: Value does not exist")
};
}
Ok(current_node.clone())
Err("[ConfigManager] get_value: Key does not exist")
}
// this doesn't work for now
pub fn set_value(&self, val_path: String) -> Result<(), &'static str> {
use ConfigValue::{Array, Dict};
pub fn set_value(
&mut self,
key: String,
value: ConfigValue,
) -> Result<ConfigManagerDataResponse, &'static str> {
use ConfigManagerDataResponse::SetValue;
use ConfigValue::Dict;
let path: Vec<String> = val_path.split('.').map(|v| v.into()).collect();
let mut current_node: &ConfigValue = &self.root;
let mut next_node: Option<&ConfigValue> = None;
// check that the current
for i in path {
match current_node {
Dict(v) => {
if v.contains_key(&i) {
next_node = v.get(&i);
} else {
return Err("invalid path");
}
}
Array(v) => {
if let Ok(index) = i.parse::<usize>() {
if v.len() > index {
next_node = v.get(index)
} else {
return Err("invalid path");
}
}
}
_ => return Err("invalid path"),
}
match next_node {
Some(a @ Dict(_) | a @ Array(_)) => {
current_node = a;
continue;
}
_ => return Err("invalid path"),
}
if let Dict(dict) = &mut self.root {
dict.insert(key, value);
Ok(SetValue)
} else {
Err("[ConfigManager] set_value: What the hell did ou do wrong")
}
if let Dict(v) = current_node {
} else if let Dict(v) = current_node {
}
Ok(())
}
}
@ -175,7 +147,9 @@ impl Handler<ConfigManagerDataMessage> for ConfigManager {
ConfigManagerDataMessage::GetValue(val) => {
Ok(GotValue(self.get_value(val)?))
}
ConfigManagerDataMessage::SetValue(_, _) => Ok(SetValue),
ConfigManagerDataMessage::SetValue(key, value) => {
self.set_value(key, value)
}
ConfigManagerDataMessage::SoftSetValue(_, _) => Ok(SetValue),
}
}

View File

@ -1,7 +1,15 @@
mod config_manager;
mod types;
mod messages;
//! # config_manager
//! This module contains all the code that deals with server configuration.
//! It tries to implement a singleton actor, that will be fetchable globaly.
pub(crate) use messages::{ConfigManagerDataResponse, ConfigManagerDataMessage, ConfigManagerOutput};
mod arg_fetcher;
mod config_manager;
mod messages;
mod types;
pub(crate) use arg_fetcher::get_args;
pub(crate) use config_manager::ConfigManager;
pub(crate) use types::ConfigValue;
pub(crate) use messages::{
ConfigManagerDataMessage, ConfigManagerDataResponse, ConfigManagerOutput,
};
pub(crate) use types::ConfigValue;