diff options
author | Matthias Beyer <mail@beyermatthias.de> | 2016-01-24 20:20:04 +0100 |
---|---|---|
committer | Matthias Beyer <mail@beyermatthias.de> | 2016-02-09 13:45:13 +0100 |
commit | dbd6388946abe429568bd6fd3cb9ecce13ab622e (patch) | |
tree | 0ca849e8ffa7ad324eb6bcdfa667fa20666d0108 /imag-store/src | |
parent | bd0d44d4b2ca193b98a70793b2f68f378f17e4d0 (diff) |
Implement Store::create() interface
Diffstat (limited to 'imag-store/src')
-rw-r--r-- | imag-store/src/create.rs | 181 |
1 files changed, 181 insertions, 0 deletions
diff --git a/imag-store/src/create.rs b/imag-store/src/create.rs index 16b26948..8752c84a 100644 --- a/imag-store/src/create.rs +++ b/imag-store/src/create.rs @@ -1,5 +1,186 @@ +use std::collections::BTreeMap; +use std::path::PathBuf; +use std::io::stdin; +use std::fs::OpenOptions; +use std::result::Result as RResult; +use std::io::Read; +use std::ops::DerefMut; +use std::str::Split; + +use clap::ArgMatches; +use toml::Table; +use toml::Value; + use libimagrt::runtime::Runtime; +use libimagstore::store::Entry; +use libimagstore::store::EntryHeader; +use libimagutil::key_value_split::IntoKeyValue; + +use error::StoreError; +use error::StoreErrorKind; +use util::build_entry_path; + +type Result<T> = RResult<T, StoreError>; pub fn create(rt: &Runtime) { + rt.cli() + .subcommand_matches("create") + .map(|scmd| { + debug!("Found 'create' subcommand..."); + + // unwrap is safe as value is required + let path = build_entry_path(rt, scmd.value_of("path").unwrap()); + debug!("path = {:?}", path); + + scmd.subcommand_matches("entry") + .map(|entry| create_from_cli_spec(rt, scmd, &path)) + .ok_or(()) // hackythehackhack + .map_err(|_| { + create_from_source(rt, scmd, &path) + .unwrap_or_else(|e| debug!("Error building Entry: {:?}", e)) + }); + }); +} + +fn create_from_cli_spec(rt: &Runtime, matches: &ArgMatches, path: &PathBuf) -> Result<()> { + let content = matches.subcommand_matches("entry") + .map(|entry_subcommand| { + debug!("Found entry subcommand, parsing content"); + entry_subcommand + .value_of("content") + .map(String::from) + .unwrap_or_else(|| { + entry_subcommand + .value_of("content-from") + .map(|src| entry_from_raw(src)) + .unwrap_or(String::new()) + }) + }) + .unwrap_or_else(|| { + debug!("Didn't find entry subcommand, getting raw content"); + matches.value_of("from-raw") + .map(|raw_src| entry_from_raw(raw_src)) + .unwrap_or(String::new()) + }); + + debug!("Got content with len = {}", content.len()); + + rt.store() + .create(PathBuf::from(path)) + .map(|mut element| { + { + let mut e_content = element.get_content_mut(); + *e_content = content; + debug!("New content set"); + } + { + let mut e_header = element.get_header_mut(); + matches.subcommand_matches("entry") + .map(|entry_matches| { + *e_header = build_toml_header(entry_matches, EntryHeader::new()); + debug!("New header set"); + }); + } + }) + .map_err(|e| StoreError::new(StoreErrorKind::BackendError, Some(Box::new(e)))) +} + +fn create_from_source(rt: &Runtime, matches: &ArgMatches, path: &PathBuf) -> Result<()> { + let content = matches + .value_of("from-raw") + .ok_or(StoreError::new(StoreErrorKind::NoCommandlineCall, None)) + .map(|raw_src| entry_from_raw(raw_src)); + + if content.is_err() { + return content.map(|_| ()); + } + let content = content.unwrap(); + debug!("Content with len = {}", content.len()); + + Entry::from_str(path.clone(), &content[..]) + .map(|mut new_e| { + rt.store() + .create(path.clone()) + .map(|mut old_e| { + *old_e.deref_mut() = new_e; + }); + + debug!("Entry build"); + }) + .map_err(|serr| StoreError::new(StoreErrorKind::BackendError, Some(Box::new(serr)))) +} + +fn entry_from_raw(raw_src: &str) -> String { + let mut content = String::new(); + if raw_src == "-" { + debug!("Reading entry from stdin"); + let res = stdin().read_to_string(&mut content); + debug!("Read {:?} bytes", res); + } else { + debug!("Reading entry from file at {:?}", raw_src); + OpenOptions::new() + .read(true) + .write(false) + .create(false) + .open(raw_src) + .and_then(|mut f| f.read_to_string(&mut content)); + } + content +} + +fn build_toml_header(matches: &ArgMatches, header: EntryHeader) -> EntryHeader { + if let Some(headerspecs) = matches.values_of("header") { + let mut main = BTreeMap::new(); + for tpl in headerspecs.into_iter().filter_map(|hs| String::from(hs).into_kv()) { + let (key, value) = tpl.into(); + let mut split = key.split("."); + let current = split.next(); + if current.is_some() { + insert_key_into(String::from(current.unwrap()), &mut split, value, &mut main); + } + } + } + header +} + +fn insert_key_into(current: String, + rest_path: &mut Split<&str>, + value: String, + map: &mut BTreeMap<String, Value>) { + let next = rest_path.next(); + + if next.is_none() { + map.insert(current, parse_value(value)); + } else { + if map.contains_key(¤t) { + match map.get_mut(¤t).unwrap() { + &mut Value::Table(ref mut t) => { + insert_key_into(String::from(next.unwrap()), rest_path, value, t); + }, + _ => unreachable!(), + } + } else { + let mut submap = BTreeMap::new(); + insert_key_into(String::from(next.unwrap()), rest_path, value, &mut submap); + map.insert(current, Value::Table(submap)); + } + } +} + +fn parse_value(value: String) -> Value { + fn is_ary(v: &String) -> bool { + v.chars().next() == Some('[') && v.chars().last() == Some(']') && v.len() >= 3 + } + + if value == "true" { + Value::Boolean(true) + } else if value == "false" { + Value::Boolean(false) + } else if is_ary(&value) { + let sub = &value[1..(value.len()-1)]; + Value::Array(sub.split(",").map(|v| parse_value(String::from(v))).collect()) + } else { + Value::String(value) + } } |