diff --git a/Cargo.lock b/Cargo.lock index a8514e4..8a7eeb3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1067,6 +1067,7 @@ dependencies = [ "log", "nix", "serde", + "serde_json", "tokio", "toml", "uuid", diff --git a/Cargo.toml b/Cargo.toml index 2e5bd12..6088751 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ path = "src/platform/os/main.rs" required-features = [ "os-target" ] [features] -os-target = [ "clap", "home", "env_logger", "daemonize", "nix", "toml" ] +os-target = [ "clap", "home", "env_logger", "daemonize", "nix", "toml", "serde_json" ] wasm-target = [ "wasm-bindgen", "console_error_panic_hook", "wasm-bindgen-cli" ] @@ -43,6 +43,7 @@ env_logger = { version = "0.10.1", optional = true } daemonize = { version = "0.5.0", optional = true } nix = { version = "0.27.1", features = ["signal"], optional = true } toml = { version = "0.8.13", features = ["parse"], optional = true } +serde_json = { version = "1.0.117", optional = true } # bare dependencies diff --git a/src/core/instance/mod.rs b/src/core/instance/mod.rs index 2c3ac57..4a389cf 100644 --- a/src/core/instance/mod.rs +++ b/src/core/instance/mod.rs @@ -10,6 +10,7 @@ use std::path::Path; use interner::shared::VecStringPool; use crate::error::{MizeError, MizeResult, IntoMizeResult, MizeResultTrait}; +use crate::instance; use crate::instance::store::Store; use crate::instance::connection::Connection; use crate::id::{IntoMizeId, MizeId}; @@ -45,17 +46,23 @@ pub enum RealmId { */ impl Instance { - pub fn new() -> MizeResult> { - trace!("[ {} ] Instance::new()", "CALL".yellow()); - + pub fn empty() -> Instance { let store = MemStore::new(); let id_pool = VecStringPool::default(); let peers = Arc::new(Mutex::new(Vec::new())); let subs = Arc::new(Mutex::new(HashMap::new())); let mut instance = Instance {store, peers, subs, id_pool }; + return instance; + } + pub fn new() -> MizeResult> { + trace!("[ {} ] Instance::new()", "CALL".yellow()); + + let mut instance = Instance::empty(); instance.init(); + debug!("instance inited with conifg: {}", instance.get("0/config")?.as_data_full()?); + return Ok(instance); } @@ -72,13 +79,16 @@ impl Instance { } pub fn with_config(config: ItemData) -> MizeResult> { - let mut instance = Instance::new()?; + let mut instance = Instance::empty(); instance.set("0", config.clone()); instance.init()?; // set it again, so that the passed config data has presidence over anything the init would set + debug!("overwriting instance config again with the one passed to Instance::with_config()"); instance.set("0", config); + debug!("instance inited with conifg: {}", instance.get("0/config")?.as_data_full()?); + Ok(instance) } } diff --git a/src/core/instance/store.rs b/src/core/instance/store.rs index ea84b21..cf5568e 100644 --- a/src/core/instance/store.rs +++ b/src/core/instance/store.rs @@ -21,5 +21,7 @@ pub trait Store { fn new_id(&self) -> MizeResult; fn get_value_raw(&self, id: MizeId) -> MizeResult>; + + fn get_value_data_full(&self, id: MizeId) -> MizeResult; } diff --git a/src/core/item.rs b/src/core/item.rs index 5da5723..63867de 100644 --- a/src/core/item.rs +++ b/src/core/item.rs @@ -1,8 +1,13 @@ use log::{trace, debug, info, warn, error}; +use serde::Serialize; +use core::fmt; +use std::{i128, string}; use std::{collections::HashMap, path::PathBuf, io::Cursor, fmt::Display}; use std::fs::File; use tokio::sync::Mutex; use std::rc::Rc; +use colored::Colorize; +use std::io; use crate::error::{MizeError, MizeResult, IntoMizeResult}; use crate::instance::Instance; @@ -20,7 +25,8 @@ pub struct Item<'a, S: Store + Sized> { // without an Instance it is not an item, but only the "data of an item" // and this type for now is just an alias to CborValue -pub type ItemData = CborValue; +#[derive(Debug, Clone)] +pub struct ItemData ( pub CborValue ); impl Item<'_, S> { pub fn id(&self) -> MizeId { @@ -31,6 +37,9 @@ impl Item<'_, S> { // this will call from the instance which gets the value from the store self.instance.store.get_value_raw(self.id()) } + pub fn as_data_full(&self) -> MizeResult { + self.instance.store.get_value_data_full(self.id()) + } } impl Display for Item<'_, S> { @@ -46,233 +55,217 @@ impl Display for Item<'_, S> { } } +impl ItemData { + pub fn new() -> ItemData { + ItemData(CborValue::Null) + } + pub fn merge(&mut self, other: ItemData) { + item_data_merge(self, other) + } + pub fn null() -> CborValue { + CborValue::Null + } -/* -#[derive(Debug)] -pub enum Item { - Cbor(ItemCbor), - Folder(ItemFolder), - Ref(ItemRef), -} - -#[derive(Clone, Debug)] -pub struct ItemCbor { - pub inner: CborValue, - pub id: MizeId, -} - -#[derive(Debug)] -pub struct ItemFolder { - pub path: PathBuf, - pub id: MizeId, -} - -#[derive(Debug)] -pub struct ItemRef { - pub store: Mutex>, - pub id: MizeId, -} - -#[derive(Clone, Debug, Hash, PartialEq, Eq)] -pub struct MizeId { - pub main: u64, - pub path: Option> -} - -pub struct MizeType { -} - - -////////////////////////////////////////////////////////////////////////////////////////////// - - -impl Display for MizeId { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if let Some(path) = &self.path { - write!(f, "{}/{}", self.main, path.join("/")) - } else { - write!(f, "{}", self.main) + pub fn parse>(value: T) -> ItemData { + let value_str = value.into(); + if value_str == "false" { + return CborValue::Bool(false).into_item_data(); } + if value_str == "false" { + return CborValue::Bool(true).into_item_data(); + } + if let Ok(int) = value_str.parse::() { + return int.into_item_data() + } + return CborValue::Text(value_str).into_item_data(); } + + pub fn set_path(&mut self, path: P, value: D) -> MizeResult<()> { + trace!("[ {} ] ItemData::set_path()", "CALL".yellow()); + let path = path.into_path(); + let value = value.into_item_data(); + + item_data_set_path(&mut self.0, path, &value.0) + } + + pub fn get_path(&self, path: P) -> MizeResult { + let path = path.into_path(); + Ok(item_data_get_path(&self.0, path)?.to_owned().into_item_data()) + } + } -impl MizeId { - pub fn main(main: u64) -> MizeId { - MizeId { main, path: None } - } - pub fn join(self, segment: &str) -> MizeId { - let mut new = self.clone(); - match new.path { - None => { - new.path = Some(vec![segment.to_owned()]); +fn item_data_get_path(data: &CborValue, path: Vec) -> MizeResult<&CborValue> { + let mut path_iter = path.clone().into_iter(); + let path_el = match path_iter.nth(0) { + Some(val) => val, + None => return Ok(data), // our base case + }; + let mut sub_data = &CborValue::Null; + match data { + CborValue::Map(ref map) => { + for (key, val) in map { + if let CborValue::Text(key_str) = key { + if key_str == &path_el { + sub_data = val; + } + } } - Some(ref mut val) => { - val.push(segment.to_owned()); + }, + val => { + return Err(MizeError::new() + .msg(format!("Failed to get path '{:?}' from ItemData, the data at '{}' is not a map", path, path_el)) + .msg(format!("{:?} is: {:?}", path_el, val))); + }, + }; + path_iter.next(); + item_data_get_path(sub_data, path_iter.collect()) +} + +fn item_data_set_path(data: &mut CborValue, path: Vec, new_data: &CborValue) -> MizeResult<()> { + trace!("[ {} ] item_data_set_path", "CALL".yellow()); + trace!("[ {} ] data: {}", "ARG".yellow(), data.clone().into_item_data()); + trace!("[ {} ] path: {:?}", "ARG".yellow(), path); + trace!("[ {} ] new_data: {}", "ARG".yellow(), new_data.clone().into_item_data()); + + let mut path_iter = path.clone().into_iter(); + let path_el = match path_iter.nth(0) { + Some(val) => val, + None => { + // our base case + *data = new_data.to_owned(); + return Ok(()); + }, + }; + match data { + CborValue::Map(ref mut map) => { + for (key, val) in map { + if let CborValue::Text(key_str) = key { + if key_str == &path_el { + path_iter.next(); + return item_data_set_path(val, path_iter.collect(), new_data) + } + } } - } - return new; + }, + CborValue::Null => { + let map_vec = vec![(CborValue::Text(path_el), CborValue::Null)]; + *data = CborValue::Map(map_vec); + return item_data_set_path(data, path, new_data); + }, + val => { + return Err(MizeError::new() + .msg(format!("Failed to get path '{:?}' from ItemData, the data at '{}' is not a map", path, path_el)) + .msg(format!("{:?} is: {:?}", path_el, val))); + }, + }; + return Err(MizeError::new().msg("unreachable")); +} + +fn item_data_merge(first: &mut ItemData, other: ItemData){ + match first.0 { + _ => { + *first = other; + }, + CborValue::Map(ref mut map) => { + match other.0 { + _ => { + *first = other; + } + CborValue::Map(mut other_map) => { + let mut to_add = Vec::new(); + for (other_key, other_val) in other_map.clone() { + let mut key_found_in_old = false; + for (key, mut val) in map.clone() { + if key == other_key { + val = other_val.clone(); + key_found_in_old = true; + } + } + if key_found_in_old == false { + to_add.push((other_key.clone(), other_val.clone())); + } + } + other_map.extend(to_add); + }, + } + }, } } +trait IntoPath { + fn into_path(self) -> Vec; +} +pub trait IntoItemData { + fn into_item_data(self) -> ItemData; +} -////////////////////////////////////////////////////////////////////////////////////////////// - -impl ItemCbor { - fn value_as_file(&self) -> MizeResult { - /* sadly does not work like that - let cbor_c = self.inner.get("v").ok_or(MizeError::new() - .msg(format!("The ItemCbor ({}) does not have a \"c\" field ... so can't get it as a file", self.id()?)))?; - match cbor_c { - Cbor::Bytes(val) => { - return Ok(Cursor::new(val.clone()).into()); - }, - _ => { - return Err(MizeError::new().msg(format!("The value fo item ({}) is not of type Cbor::Bytes", self.id()?))); - }, - } - */ - return File::open(self.value_as_path()?).mize_result_msg("Could not open file"); - } - - fn value_as_path(&self) -> MizeResult { - todo!() - } - - /* an old way of getting the id this - fn id(self) -> MizeResult { - let cbor_item = self.inner.get("item".to_owned()).ok_or(MizeError::new() - .msg("ItemCbor had no path: \"item\""))?; - - let cbor_id = match cbor_item { - Cbor::Map(map) => { - map.get("id").ok_or(MizeError::new().msg("ItemCbor had no path \"item/id\"")) - }, - _ => { - return Err(MizeError::new().msg("ItemCbor path \"item\" was not of type Cbor::Map")); - }, - }; - - let id = match cbor_id { - Cbor::Unsigned(val) => val.into_u64(), - _ => { - return Err(MizeError::new().msg("ItemCbor path \"item/id\" was not of type Cbor::Unsigned")); - }, - - }; - - return Ok(MizeId { inner: id }); - } - // */ - - fn id(&self) -> MizeResult { - Ok(self.id.clone()) - } - fn as_cbor(&self) -> MizeResult { - Ok(self.clone()) - } - fn write_to_path(&self, path: PathBuf) -> MizeResult<()> { - todo!() - //let cbor_content = - //std::fs::write(path.join(format!("{}", self.id)), ); +impl IntoPath for &str { + fn into_path(self) -> Vec { + self.to_owned().split("/").map(|v| v.to_owned()).collect() } } -impl ItemFolder { - fn value_as_file(&self) -> MizeResult { - let file = File::open(self.path.join("v")) - .mize_result_msg(format!("Could not open the value file of item ({})", self.id()?)); - return file; +impl IntoPath for &[&str] { + fn into_path(self) -> Vec { + self.into_iter().map(|v| (*v).to_owned()).collect() } - - fn value_as_path(&self) -> MizeResult { - return Ok(self.path.clone()); +} +impl IntoPath for Vec { + fn into_path(self) -> Vec { + self } - - fn id(&self) -> MizeResult { - Ok(self.id.clone()) - } - - fn as_cbor(&self) -> MizeResult { - todo!() - } - - fn write_to_path(&self, path: PathBuf) -> MizeResult<()> { - Ok(()) +} +impl IntoPath for Vec<&str> { + fn into_path(self) -> Vec { + self.into_iter().map(|v| v.to_owned()).collect() } } -impl ItemRef { - fn value_as_file(&self) -> MizeResult { - todo!() +impl IntoItemData for &str { + fn into_item_data(self) -> ItemData { + ItemData::parse(self) } - - fn value_as_path(&self) -> MizeResult { - todo!() +} +impl IntoItemData for CborValue { + fn into_item_data(self) -> ItemData { + ItemData ( self ) } - - fn id(&self) -> MizeResult { - return Ok(self.id.clone()); +} +impl IntoItemData for ItemData { + fn into_item_data(self) -> ItemData { + self } - - fn as_cbor(&self) -> MizeResult { - todo!() - } - - async fn write_to_path(&self, path: PathBuf) -> MizeResult<()> { - let store = self.store.lock().await; - for (key, value) in self.map.iter() { - // TODO right here!!!! - } - todo!() +} +impl IntoItemData for i128 { + fn into_item_data(self) -> ItemData { + ItemData(self.into()) } } -impl Item { - pub fn value_as_file(&self) -> MizeResult { - match self { - Item::Cbor(val) => val.value_as_file(), - Item::Ref(val) => val.value_as_file(), - Item::Folder(val) => val.value_as_file(), - } - } - - pub fn value_as_path(&self) -> MizeResult { - match self { - Item::Cbor(val) => val.value_as_path(), - Item::Ref(val) => val.value_as_path(), - Item::Folder(val) => val.value_as_path(), - } - } - - pub fn id(&self) -> MizeResult { - match self { - Item::Cbor(val) => val.id(), - Item::Ref(val) => val.id(), - Item::Folder(val) => val.id(), - } - } - - pub fn as_cbor(&self) -> MizeResult { - match self { - Item::Cbor(val) => val.as_cbor(), - Item::Ref(val) => val.as_cbor(), - Item::Folder(val) => val.as_cbor(), - } - } - - pub fn write_to_path(&self, path: PathBuf) -> MizeResult<()> { - match self { - Item::Cbor(val) => val.write_to_path(path), - Item::Ref(val) => val.write_to_path(path), - Item::Folder(val) => val.write_to_path(path), - } - } - pub fn load_json(json_str: &str) -> MizeResult { - todo!() +impl fmt::Display for ItemData { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "\nItemData: "); + let display_writer = DisplayWriter (f); + self.0.serialize(&mut serde_json::Serializer::pretty(display_writer)) + .map_err(|serde_err| std::fmt::Error) } } -*/ + +// thanks to: https://stackoverflow.com/a/61768916 +struct DisplayWriter<'a, 'b>(&'a mut fmt::Formatter<'b>); + +impl<'a, 'b> io::Write for DisplayWriter<'a, 'b> { + fn write(&mut self, bytes: &[u8]) -> std::result::Result { + + self.0.write_str(&String::from_utf8_lossy(bytes)) + .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))?; + + Ok(bytes.len()) + } + fn flush(&mut self) -> std::result::Result<(), std::io::Error> { todo!() } +} diff --git a/src/core/memstore.rs b/src/core/memstore.rs index 4aa0dd8..87f4aee 100644 --- a/src/core/memstore.rs +++ b/src/core/memstore.rs @@ -19,7 +19,7 @@ pub type MemStoreId = u64; #[derive(Debug)] struct MemStoreInner { - map: HashMap, + map: HashMap, next_id: u64, } @@ -60,7 +60,13 @@ impl Store for MemStore { id_iter.next(); let id_without_first = id_iter.collect(); - Ok(get_raw_from_cbor(cbor_val, id_without_first)?.to_owned()) + Ok(get_raw_from_cbor(&cbor_val.0, id_without_first)?.to_owned()) + } + + fn get_value_data_full(&self, id: MizeId) -> MizeResult { + let inner = self.inner.lock()?; + inner.map.get(&id_to_u64(id.clone())?) + .ok_or(MizeError::new().msg(format!("Item wich id '{}' has not data stored in this MemStore", id.clone()))).cloned() } } @@ -83,7 +89,6 @@ fn get_raw_from_cbor<'a>(value: &'a CborValue, path: Vec<&String>) -> MizeResult CborValue::Text(string) => Ok(string.as_bytes()), CborValue::Map(map) => { let mut inner_val: &CborValue = &CborValue::Null; - trace!("map: {:?}", map); for (key, val) in map.into_iter() { let first_path_string = *path.iter().nth(0) .ok_or(MizeError::new() diff --git a/src/platform/os/cli/get.rs b/src/platform/os/cli/get.rs index eb701db..41219e4 100644 --- a/src/platform/os/cli/get.rs +++ b/src/platform/os/cli/get.rs @@ -5,12 +5,13 @@ use home::home_dir; use mize::error::{IntoMizeResult, MizeError, MizeResult, MizeResultTrait}; use mize::instance::Instance; +use mize::platform::os::config_from_cli_args; pub fn get(sub_matches: &ArgMatches) -> MizeResult<()> { - let instance = Instance::new()?; + let instance = Instance::with_config(config_from_cli_args(sub_matches)?)?; let id = sub_matches.get_one::("id") .ok_or(MizeError::new().msg("No id Argument specified"))?; diff --git a/src/platform/os/cli/run.rs b/src/platform/os/cli/run.rs index 13418ae..a2c1d84 100644 --- a/src/platform/os/cli/run.rs +++ b/src/platform/os/cli/run.rs @@ -13,9 +13,8 @@ use mize::instance::Instance; pub fn run(sub_matches: &ArgMatches) -> MizeResult<()> { // TODO NEXT!!! - // - add --system arg - // - add --user arg // - --config arg + // - --folder // - to set a location, where there will be a /store and a /sock // - listen for connections on that sock // - msg handeling diff --git a/src/platform/os/main.rs b/src/platform/os/main.rs index 0bc198f..1ba76f5 100644 --- a/src/platform/os/main.rs +++ b/src/platform/os/main.rs @@ -5,7 +5,8 @@ use std::path::PathBuf; use clap::{ArgAction, ArgMatches}; use clap::{Arg, crate_version, Command}; -use mize::error::MizeError; +use mize::error::{MizeError, MizeResult}; +use mize::item::ItemData; use tokio::sync::{Mutex, mpsc}; use std::sync::Arc; use log::Level; @@ -258,12 +259,9 @@ fn level_from_env(var_name: &str, log_level: LevelFilter) -> (Option clap::ArgMatches { - let folder_arg = Arg::new("folder") - .short('f') - .long("folder") - .help("The folder the Instance stores all it's data and the socket for connections"); let main = Command::new(APPNAME) .version(crate_version!()) @@ -273,18 +271,37 @@ fn cli_matches() -> clap::ArgMatches { .long("verbose") .short('v') .action(ArgAction::Count) + .global(true) ) .arg(Arg::new("log-level") .long("log-level") .value_name("LOGLEVEL") .help("set the log-level to one of OFF, ERROR, WARN, INFO, DEBUG, TRACE") + .global(true) ) .arg(Arg::new("silent") .long("silent") .action(ArgAction::SetTrue) .help("set the log-level to OFF") + .global(true) + ) + .arg(Arg::new("folder") + .short('f') + .long("folder") + .help("The folder the Instance stores all it's data and the socket for connections") + .global(true) + ) + .arg(Arg::new("config") + .short('c') + .long("config") + .help("overwrite config options") + .global(true) + ) + .arg(Arg::new("config-file") + .long("config-file") + .help("specify a config file") + .global(true) ) - .arg(&folder_arg) .subcommand( Command::new("run") .aliases(["r"]) @@ -300,25 +317,21 @@ fn cli_matches() -> clap::ArgMatches { .subcommand( Command::new("get") .aliases(["g"]) - .arg(&folder_arg) .arg(Arg::new("id").help("The id or path to get")) ) .subcommand( Command::new("set") .aliases(["s"]) - .arg(&folder_arg) .arg(Arg::new("id").help("The id or path to set")) ) .subcommand( Command::new("show") .aliases(["so"]) - .arg(&folder_arg) .arg(Arg::new("id").help("The id or path to sub to and show")) ) .subcommand( Command::new("call") .aliases(["c"]) - .arg(&folder_arg) ) .arg_required_else_help(true); diff --git a/src/platform/os/mod.rs b/src/platform/os/mod.rs index bb0a760..6a6210f 100644 --- a/src/platform/os/mod.rs +++ b/src/platform/os/mod.rs @@ -1,34 +1,109 @@ use ciborium::Value as CborValue; use serde::Deserialize; +use std::env::VarError; use std::fs; +use log::{debug, error, warn, trace}; +use clap::ArgMatches; use crate::id::MizeId; use crate::instance::store::Store; use crate::instance::Instance; use crate::error::{IntoMizeResult, MizeError, MizeResult}; +use crate::item::{ItemData, IntoItemData}; pub fn os_instance_init(instance: &mut Instance) -> MizeResult<()> { // this is the code, that runs to initialize an Instance on a system with an os present. - ////// load MIZE_CONFIG_FILE as the instance, which is item 0 - let config_file_path = std::env::var("MIZE_CONFIG_FILE") - .mize_result_msg("Could not get the MIZE_CONFIG_FILE Environment Variable")?; + let mut config = ItemData::new(); - let toml_string = fs::read_to_string(&config_file_path) - .mize_result_msg(format!("Could not read file {} as mize config", &config_file_path))?; + ////// load MIZE_CONFIG_FILE as the instance, which is item 0 + match std::env::var("MIZE_CONFIG_FILE") { + Ok(config_file_path) => { + config.merge(config_from_file(config_file_path)?); + debug!("env var MIZE_CONFIG_FILE present"); + trace!("config after MIZE_CONFIG_FILE env var: {}", config); + } + Err(var_err) => match var_err { + VarError::NotPresent => { + debug!("env var MIZE_CONFIG_FILE NOT present"); + }, + VarError::NotUnicode(_) => { + warn!("env var MIZE_CONFIG_FILE is not Unicode, so not reading it") + }, + } + }; + + ////// load config from MIZE_CONFIG env var + match std::env::var("MIZE_CONFIG") { + Ok(config_string) => { + config.merge(config_from_string(config_string)?); + debug!("env var MIZE_CONFIG present"); + trace!("config after MIZE_CONFIG env var: {}", config); + } + Err(var_err) => match var_err { + VarError::NotPresent => { + debug!("env var MIZE_CONFIG NOT present") + }, + VarError::NotUnicode(_) => { + warn!("env var MIZE_CONFIG is not Unicode, so not reading it") + }, + } + }; + + + instance.set("0", config)?; + + return Ok(()); +} + +pub fn config_from_cli_args(matches: &ArgMatches) -> MizeResult { + + let mut config = ItemData::new(); + + if let Some(config_file_path) = matches.get_one::("config-file") { + config.merge(config_from_file(config_file_path.to_string())?); + trace!("config after --config-file arg: {}", config); + } + + if let Some(config_string) = matches.get_one::("config") { + config.merge(config_from_string(config_string.to_string())?); + trace!("config after --config arg: {}", config); + } + + return Ok(config); +} + + +pub fn config_from_file(file_path: String) -> MizeResult { + + let toml_string = fs::read_to_string(&file_path) + .mize_result_msg(format!("Could not read file {} as mize config", &file_path))?; let toml_deserializer = toml::Deserializer::new(&toml_string.as_str()); let config = CborValue::deserialize(toml_deserializer) - .mize_result_msg(format!("Could not deserialize the content of MIZE_CONFIG_FILE at {}", &config_file_path))?; + .mize_result_msg(format!("Could not deserialize the content of MIZE_CONFIG_FILE at {}", &file_path))?; - instance.set("0", config)?; + return Ok(config.into_item_data()); +} - ////// load config from MIZE_CONFIG env var - //TODO - - return Ok(()); +fn config_from_string(config_string: String) -> MizeResult { + + let mut config = ItemData::new(); + + for option in config_string.split(";") { + let path = option.split("=").nth(0) + .ok_or(MizeError::new().msg(format!("Failed to parse Option: option '{}' has an empty path (thing beforee =)", option)))?; + let value = option.split("=").nth(1) + .ok_or(MizeError::new().msg(format!("Failed to parse Option: option '{}' has an empty value (thing after =)", option)))?; + let mut path_vec = vec!["config"]; + path_vec.extend(path.split(".")); + + config.set_path(path_vec, ItemData::parse(value))?; + } + + return Ok(config); }