diff --git a/src/config/argument_tree.rs b/src/config/argument_tree.rs new file mode 100644 index 0000000..88b3a0b --- /dev/null +++ b/src/config/argument_tree.rs @@ -0,0 +1,93 @@ +use std::{collections::HashMap, slice::Iter, iter::Peekable}; + +use figment::value::{Value, Dict}; + +#[derive(Debug)] +pub enum ArgumentTreeNode { + Leaf(Value), + Branch(HashMap), +} + +impl ArgumentTreeNode { + /// Insert a value into the tree. + pub fn insert(&mut self, keys: &mut Peekable<&mut Iter>, value: Value) { + let key = keys.next().unwrap(); + + match self { + ArgumentTreeNode::Leaf(_) => panic!("Cannot insert into a leaf node"), + ArgumentTreeNode::Branch(children) => { + match children.get_mut(key) { + // If the key is already in the tree, insert into it, + // going farther until the key doesn't exist anymore which + // is where we insert the value. + Some(node) => { + node.insert(keys, value); + } + None => { + // Check if we should insert a leaf or a branch by + // `peek`ing to see if there is a next key. + if keys.peek().is_none() { + let node = ArgumentTreeNode::Leaf(value); + children.insert(key.to_owned(), node); + } else { + let mut node = ArgumentTreeNode::Branch(HashMap::new()); + node.insert(keys, value); + + children.insert(key.to_owned(), node); + } + } + } + } + } + } + + /// Convert the tree into a `Dict`. + fn to_dict(&self) -> Dict { + match self { + ArgumentTreeNode::Leaf(_) => panic!("Cannot convert a leaf node to a dict!"), + ArgumentTreeNode::Branch(children) => { + let mut dict = Dict::new(); + + // Iterate over the children and recursively convert them to `Dict`s. + // When it run into a leaf node, insert its value. + for (key, node) in children.iter() { + match node { + ArgumentTreeNode::Leaf(value) => { + dict.insert(key.to_owned(), value.clone()); + } + _ => { + dict.insert(key.to_owned(), Value::from(node.to_dict())); + } + } + } + dict + } + } + } +} + +#[derive(Debug)] +pub struct ArgumentTree { + pub root: ArgumentTreeNode +} + +impl ArgumentTree { + /// Create a new tree. + pub fn new() -> ArgumentTree { + ArgumentTree { + root: ArgumentTreeNode::Branch(HashMap::new()) + } + } + + /// Insert into the tree. + pub fn insert(&mut self, keys: &mut Iter, value: Value) { + let mut keys = keys.peekable(); + + self.root.insert(&mut keys, value); + } + + /// Convert the tree into a `Dict`. + pub fn to_dict(&mut self) -> Dict { + self.root.to_dict() + } +} \ No newline at end of file diff --git a/src/config/cli_provider.rs b/src/config/cli_provider.rs index 141e551..bc043ac 100644 --- a/src/config/cli_provider.rs +++ b/src/config/cli_provider.rs @@ -1,9 +1,7 @@ -use std::sync::Arc; -use std::slice::Iter; - use figment::{Provider, Metadata, Profile, Error}; use figment::value::{Map, Dict, Value, Tag}; -use serde::Deserialize; + +use crate::config::ArgumentTree; /// A provider that fetches its data from a given URL. pub struct CliProvider { @@ -22,21 +20,15 @@ impl CliProvider { } impl Provider for CliProvider { - /// Returns metadata with kind `Network`, custom source `self.url`, - /// and interpolator that returns a URL of `url/a/b/c` for key `a.b.c`. + /// Returns metadata with kind `Cli Flags`, custom source is the + /// command line arguments separated by spaces. fn metadata(&self) -> Metadata { let args = &self.args; - Metadata::named("CLI Flags") + Metadata::named("Cli Flags") .source(args.join(" ")) - //.source(args.map(|args| args.collect::>().join(" ")).unwrap_or(String::default())) - /* .interpolater(move |profile, keys| match profile.is_custom() { - true => format!("{}/{}/{}", url, profile, keys.join("/")), - false => format!("{}/{}", url, keys.join("/")), - }) */ } - /// Fetches the data from `self.url`. Note that `Dict`, `Map`, and - /// `Profile` are `Deserialize`, so we can deserialized to them. + /// Parses the command line arguments into a `Map` and `Value`s. fn data(&self) -> Result, Error> { // Parse a `Value` from a `String` fn parse_from_string(string: &String) -> Value { @@ -50,98 +42,36 @@ impl Provider for CliProvider { } } - fn parse_keys(keys: &mut Iter<&str>, dict: &Dict, vals: &Vec) -> Value { - let key = keys.next(); - - match key { - None => { - if vals.len() == 1 { - parse_from_string(&vals[0]) - } else { - let mut values = Vec::new(); - for val in vals.iter() { - values.push(parse_from_string(val)); - } - - Value::Array(Tag::Default, values) - } - }, - Some(key) => { - let key = key.to_string(); - println!("Key is {}", key); - - println!("Dict is {:?}", dict); - - match dict.get(&key) { - Some(val) => { - println!("Val is {:?}", val); - - match val.as_dict() { - Some(dict) => parse_keys(keys, &dict, vals), - None => panic!("Expected a `Dict`, got some other value"), - } - //parse_keys(keys, &dict, vals) - }, - None => { - let mut current_dict = Dict::new(); - let val = parse_keys(keys, ¤t_dict, vals); - - current_dict.insert(key.to_string(), val); - - Value::from(current_dict) - } - } - /* let mut current_dict = Dict::new(); - let val = parse_keys(keys, ¤t_dict, vals); - - current_dict.insert(key.to_string(), val); - - Value::from(current_dict) */ - } - } - } - fn parse_cli(args: &Vec)-> Result { - let (args, argv) = argmap::parse(args.iter()); - - let mut dict = Dict::new(); + // TODO: Parse _args as booleans + let (_args, argv) = argmap::parse(args.iter()); + let mut tree = ArgumentTree::new(); for (key, vals) in argv { let len = vals.len(); if len == 0 { continue; } - let key_vec = key.split(".").collect::>(); - if key_vec.len() > 1 { - let mut key_iter = key_vec.iter(); - - //let key = key_iter.next(); - let key = key_vec.first(); - let val = parse_keys(&mut key_iter, &dict, &vals); - - println!("Final val is {:?}", val); - - dict.insert(key.unwrap().to_string(), val); - } else { - if len == 1 { - dict.insert(key, parse_from_string(&vals[0])); - } else { - let mut values = Vec::new(); - for val in &vals { - values.push(parse_from_string(val)); + // Parse the string argument values into a `Value` + let val = match len { + 1 => parse_from_string(&vals[0]), + _ => { + let mut vec = Vec::new(); + for val in vals { + vec.push(parse_from_string(&val)); } - - dict.insert(key, Value::Array(Tag::Default, values)); - } - } + Value::from(vec) + }, + }; - println!("Dict: {:?}", dict); + // Separate the key into its parts and then insert it into the tree + let key_vec = key.split(".").map(|s| s.to_string()).collect::>(); + let mut key_iter = key_vec.iter(); + tree.insert(&mut key_iter, val); } - - - Ok(dict) + Ok(tree.to_dict()) } match &self.profile { diff --git a/src/config/mod.rs b/src/config/mod.rs index d855f52..cca2569 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,5 +1,8 @@ pub mod config; pub use config::Config; +pub mod argument_tree; +pub use argument_tree::*; + pub mod cli_provider; -pub use cli_provider::CliProvider; \ No newline at end of file +pub use cli_provider::*; \ No newline at end of file