summaryrefslogtreecommitdiffstats
path: root/src/file
diff options
context:
space:
mode:
authorRyan Leckey <leckey.ryan@gmail.com>2017-03-08 11:09:37 -0800
committerRyan Leckey <leckey.ryan@gmail.com>2017-03-08 11:09:37 -0800
commit2dc6a74b84825f65142c1fa7d3e67cd4f35ee3cb (patch)
tree23b21f732efbb215498db6debf6dbaee3af7e94f /src/file
parentc9ee1568fe212e4c352ec1afc52db44b34348fcd (diff)
Initial work on deep serde integration
Diffstat (limited to 'src/file')
-rw-r--r--src/file/json.rs87
-rw-r--r--src/file/mod.rs219
-rw-r--r--src/file/nil.rs13
-rw-r--r--src/file/toml.rs74
-rw-r--r--src/file/yaml.rs101
5 files changed, 0 insertions, 494 deletions
diff --git a/src/file/json.rs b/src/file/json.rs
deleted file mode 100644
index e6cc2f4..0000000
--- a/src/file/json.rs
+++ /dev/null
@@ -1,87 +0,0 @@
-use serde_json;
-
-use source::Source;
-use std::error::Error;
-use std::collections::HashMap;
-use value::Value;
-
-pub struct Content {
- // Root table of the TOML document
- root: serde_json::Value,
-}
-
-impl Content {
- pub fn parse(text: &str, namespace: Option<&String>) -> Result<Box<Source + Send + Sync>, Box<Error>> {
- // Parse
- let mut root: serde_json::Value = serde_json::from_str(text)?;
-
- // Limit to namespace
- if let Some(namespace) = namespace {
- if let serde_json::Value::Object(mut root_map) = root {
- if let Some(value) = root_map.remove(namespace) {
- root = value;
- } else {
- // TODO: Warn?
- root = serde_json::Value::Object(serde_json::Map::new());
- }
- }
- }
-
- Ok(Box::new(Content { root: root }))
- }
-}
-
-fn from_json_value(value: &serde_json::Value) -> Value {
- match *value {
- serde_json::Value::String(ref value) => Value::String(value.clone()),
-
- serde_json::Value::Number(ref value) => {
- if let Some(value) = value.as_i64() {
- Value::Integer(value)
- } else if let Some(value) = value.as_f64() {
- Value::Float(value)
- } else {
- unreachable!();
- }
- }
-
- serde_json::Value::Bool(value) => Value::Boolean(value),
-
- serde_json::Value::Object(ref table) => {
- let mut m = HashMap::new();
-
- for (key, value) in table {
- m.insert(key.clone(), from_json_value(value));
- }
-
- Value::Table(m)
- }
-
- serde_json::Value::Array(ref array) => {
- let mut l = Vec::new();
-
- for value in array {
- l.push(from_json_value(value));
- }
-
- Value::Array(l)
- }
-
- // TODO: What's left is JSON Null; how should we handle that?
- _ => {
- unimplemented!();
- }
- }
-}
-
-impl Source for Content {
- fn collect(&self) -> HashMap<String, Value> {
- if let Value::Table(table) = from_json_value(&self.root) {
- table
- } else {
- // TODO: Better handle a non-object at root
- // NOTE: I never want to support that but a panic is bad
- panic!("expected object at JSON root");
- }
- }
-}
diff --git a/src/file/mod.rs b/src/file/mod.rs
deleted file mode 100644
index 7f7c0fb..0000000
--- a/src/file/mod.rs
+++ /dev/null
@@ -1,219 +0,0 @@
-use std::env;
-use std::error::Error;
-use std::io::{self, Read};
-use std::fs;
-use std::path::PathBuf;
-
-use source::{Source, SourceBuilder};
-
-mod nil;
-
-#[cfg(feature = "toml")]
-mod toml;
-
-#[cfg(feature = "json")]
-mod json;
-
-#[cfg(feature = "yaml")]
-mod yaml;
-
-#[derive(Clone, Copy)]
-pub enum FileFormat {
- /// TOML (parsed with toml)
- #[cfg(feature = "toml")]
- Toml,
-
- /// JSON (parsed with serde_json)
- #[cfg(feature = "json")]
- Json,
-
- /// YAML (parsed with yaml_rust)
- #[cfg(feature = "yaml")]
- Yaml,
-}
-
-impl FileFormat {
- fn extensions(&self) -> Vec<&'static str> {
- match *self {
- #[cfg(feature = "toml")]
- FileFormat::Toml => vec!["toml"],
-
- #[cfg(feature = "json")]
- FileFormat::Json => vec!["json"],
-
- #[cfg(feature = "yaml")]
- FileFormat::Yaml => vec!["yaml", "yml"],
- }
- }
-
- #[allow(unused_variables)]
- fn parse(&self, text: &str, namespace: Option<&String>) -> Result<Box<Source + Send + Sync>, Box<Error>> {
- match *self {
- #[cfg(feature = "toml")]
- FileFormat::Toml => toml::Content::parse(text, namespace),
-
- #[cfg(feature = "json")]
- FileFormat::Json => json::Content::parse(text, namespace),
-
- #[cfg(feature = "yaml")]
- FileFormat::Yaml => yaml::Content::parse(text, namespace),
- }
- }
-}
-
-pub trait FileSource {
- fn try_build(&self,
- format: FileFormat,
- namespace: Option<&String>)
- -> Result<Box<Source + Send + Sync>, Box<Error>>;
-}
-
-pub struct FileSourceString(String);
-
-impl FileSource for FileSourceString {
- fn try_build(&self,
- format: FileFormat,
- namespace: Option<&String>)
- -> Result<Box<Source + Send + Sync>, Box<Error>> {
- format.parse(&self.0, namespace)
- }
-}
-
-pub struct FileSourceFile {
- /// Basename of configuration file
- name: String,
-
- /// Directory where configuration file is found
- /// When not specified, the current working directory (CWD) is considered
- path: Option<String>,
-}
-
-impl FileSourceFile {
- // Find configuration file
- // Use algorithm similar to .git detection by git
- fn find_file(&self, format: FileFormat) -> Result<PathBuf, Box<Error>> {
- // Build expected configuration file
- let mut basename = PathBuf::new();
- let extensions = format.extensions();
-
- if let Some(ref path) = self.path {
- basename.push(path.clone());
- }
-
- basename.push(self.name.clone());
-
- // Find configuration file (algorithm similar to .git detection by git)
- let mut dir = env::current_dir()?;
-
- loop {
- let mut filename = dir.as_path().join(basename.clone());
- for ext in &extensions {
- filename.set_extension(ext);
-
- if filename.is_file() {
- // File exists and is a file
- return Ok(filename);
- }
- }
-
- // Not found.. travse up via the dir
- if !dir.pop() {
- // Failed to find the configuration file
- return Err(io::Error::new(io::ErrorKind::NotFound,
- format!("configuration file \"{}\" not found",
- basename.to_string_lossy()))
- .into());
- }
- }
- }
-}
-
-impl FileSource for FileSourceFile {
- fn try_build(&self,
- format: FileFormat,
- namespace: Option<&String>)
- -> Result<Box<Source + Send + Sync>, Box<Error>> {
- // Find file
- let filename = self.find_file(format)?;
-
- // Read contents from file
- let mut file = fs::File::open(filename)?;
- let mut text = String::new();
- file.read_to_string(&mut text)?;
-
- // Parse the file
- format.parse(&text, namespace)
- }
-}
-
-pub struct File<T: FileSource> {
- /// Source of the file
- source: T,
-
- /// Namespace to restrict configuration from the file
- namespace: Option<String>,
-
- /// Format of file (which dictates what driver to use); Defauts to TOML.
- format: FileFormat,
-
- /// A required File will error if it cannot be found
- required: bool,
-}
-
-impl File<FileSourceString> {
- pub fn from_str(s: &str, format: FileFormat) -> File<FileSourceString> {
- File {
- format: format,
- required: true,
- namespace: None,
- source: FileSourceString(s.into()),
- }
- }
-}
-
-impl File<FileSourceFile> {
- pub fn new(name: &str, format: FileFormat) -> File<FileSourceFile> {
- File {
- format: format,
- required: true,
- namespace: None,
- source: FileSourceFile {
- name: name.into(),
- path: None,
- },
- }
- }
-}
-
-impl<T: FileSource> File<T> {
- pub fn required(self, required: bool) -> File<T> {
- File { required: required, ..self }
- }
-
- pub fn namespace(self, namespace: &str) -> Self {
- File { namespace: Some(namespace.into()), ..self }
- }
-
- // Build normally and return error on failure
- fn try_build(&self) -> Result<Box<Source + Send + Sync>, Box<Error>> {
- self.source.try_build(self.format, self.namespace.as_ref())
- }
-}
-
-impl File<FileSourceFile> {
- pub fn path(self, path: &str) -> Self {
- File { source: FileSourceFile { path: Some(path.into()), ..self.source }, ..self }
- }
-}
-
-impl<T: FileSource> SourceBuilder for File<T> {
- // Use try_build but only pass an error through if this source
- // is required
- fn build(&self) -> Result<Box<Source + Send + Sync>, Box<Error>> {
- if self.required {
- self.try_build()
- } else {
- self.try_build().or_else(|_| Ok(Box::new(nil::Nil {})))
- }
- }
-}
diff --git a/src/file/nil.rs b/src/file/nil.rs
deleted file mode 100644
index f6d801a..0000000
--- a/src/file/nil.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-use std::collections::HashMap;
-
-use source::Source;
-use value::Value;
-
-// Nil source that does nothing to easily allow for optional files
-pub struct Nil {}
-
-impl Source for Nil {
- fn collect(&self) -> HashMap<String, Value> {
- HashMap::new()
- }
-}
diff --git a/src/file/toml.rs b/src/file/toml.rs
deleted file mode 100644
index 9b07cf0..0000000
--- a/src/file/toml.rs
+++ /dev/null
@@ -1,74 +0,0 @@
-use toml;
-use source::Source;
-use std::collections::{HashMap, BTreeMap};
-use std::error::Error;
-use value::Value;
-
-pub struct Content {
- // Root table of the TOML document
- root: toml::Value,
-}
-
-impl Content {
- pub fn parse(text: &str, namespace: Option<&String>) -> Result<Box<Source + Send + Sync>, Box<Error>> {
- // Parse
- let mut parser = toml::Parser::new(text);
- // TODO: Get a solution to make this return an Error-able
- let mut root = parser.parse().unwrap();
-
- // Limit to namespace
- if let Some(namespace) = namespace {
- if let Some(toml::Value::Table(table)) = root.remove(namespace) {
- root = table;
- } else {
- // TODO: Warn?
- root = BTreeMap::new();
- }
- }
-
- Ok(Box::new(Content { root: toml::Value::Table(root) }))
- }
-}
-
-fn from_toml_value(value: &toml::Value) -> Value {
- match *value {
- toml::Value::String(ref value) => Value::String(value.clone()),
- toml::Value::Float(value) => Value::Float(value),
- toml::Value::Integer(value) => Value::Integer(value),
- toml::Value::Boolean(value) => Value::Boolean(value),
-
- toml::Value::Table(ref table) => {
- let mut m = HashMap::new();
-
- for (key, value) in table {
- m.insert(key.clone(), from_toml_value(value));
- }
-
- Value::Table(m)
- }
-
- toml::Value::Array(ref array) => {
- let mut l = Vec::new();
-
- for value in array {
- l.push(from_toml_value(value));
- }
-
- Value::Array(l)
- }
-
- _ => {
- unimplemented!();
- }
- }
-}
-
-impl Source for Content {
- fn collect(&self) -> HashMap<String, Value> {
- if let Value::Table(table) = from_toml_value(&self.root) {
- table
- } else {
- unreachable!();
- }
- }
-}
diff --git a/src/file/yaml.rs b/src/file/yaml.rs
deleted file mode 100644
index 95a64b4..0000000
--- a/src/file/yaml.rs
+++ /dev/null
@@ -1,101 +0,0 @@
-use yaml_rust as yaml;
-
-use source::Source;
-use std::error::Error;
-use std::fmt;
-use std::collections::{BTreeMap, HashMap};
-use std::mem;
-use value::Value;
-
-pub struct Content {
- // Root table of the YAML document
- root: yaml::Yaml,
-}
-
-impl Content {
- pub fn parse(text: &str, namespace: Option<&String>) -> Result<Box<Source + Send + Sync>, Box<Error>> {
- let mut docs = yaml::YamlLoader::load_from_str(text)?;
-
- // Designate root
- let mut root = match docs.len() {
- 0 => yaml::Yaml::Hash(BTreeMap::new()),
- 1 => mem::replace(&mut docs[0], yaml::Yaml::Null),
- n => {
- return Err(Box::new(MultipleDocumentsError(n)));
- }
- };
-
- // Limit to namespace
- if let Some(namespace) = namespace {
- if let yaml::Yaml::Hash(mut root_map) = root {
- if let Some(value) = root_map.remove(&yaml::Yaml::String(namespace.clone())) {
- root = value;
- } else {
- // TODO: Warn?
- root = yaml::Yaml::Hash(BTreeMap::new());
- }
- }
- }
-
- Ok(Box::new(Content { root: root }))
- }
-
- pub fn from_yaml(doc: yaml::Yaml) -> Content {
- Content { root: doc }
- }
-}
-
-fn from_yaml_value<'a>(value: &yaml::Yaml) -> Value {
- match *value {
- yaml::Yaml::String(ref value) => Value::String(value.clone()),
- yaml::Yaml::Real(ref value) => Value::Float(value.parse::<f64>().unwrap()),
- yaml::Yaml::Integer(value) => Value::Integer(value),
- yaml::Yaml::Boolean(value) => Value::Boolean(value),
- yaml::Yaml::Hash(ref table) => {
- let mut m = HashMap::new();
- for (key, value) in table {
- if let Some(k) = key.as_str() {
- m.insert(k.to_owned(), from_yaml_value(value));
- }
- // TODO: should we do anything for non-string keys?
- }
- Value::Table(m)
- }
- yaml::Yaml::Array(ref array) => {
- let l: Vec<Value> = array.iter().map(from_yaml_value).collect();
- Value::Array(l)
- }
- // TODO: how should we handle Null and BadValue?
- _ => {
- unimplemented!();
- }
-
- }
-}
-
-impl Source for Content {
- fn collect(&self) -> HashMap<String, Value> {
- if let Value::Table(table) = from_yaml_value(&self.root) {
- table
- } else {
- // TODO: Better handle a non-object at root
- // NOTE: I never want to support that but a panic is bad
- panic!("expected object at YAML root");
- }
- }
-}
-
-#[derive(Debug, Copy, Clone)]
-struct MultipleDocumentsError(usize);
-
-impl fmt::Display for MultipleDocumentsError {
- fn fmt(&self, format: &mut fmt::Formatter) -> fmt::Result {
- write!(format, "Got {} YAML documents, expected 1", self.0)
- }
-}
-
-impl Error for MultipleDocumentsError {
- fn description(&self) -> &str {
- "More than one YAML document provided"
- }
-}