summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRyan Leckey <ryan@launchbadge.com>2017-06-14 00:28:43 -0700
committerRyan Leckey <ryan@launchbadge.com>2017-06-14 00:28:43 -0700
commit1716c2fe7571b03215a5ae09c91f508d03335c2a (patch)
treef2b3144044f907c8eb1c594460d6cd74eed92b1f
parent9a2e756075dce24e764c8c89e2d71703a69fd80a (diff)
parent736738a2a798d58b1a73b0da146924a7e92ceba3 (diff)
Merge branch 'feature/with_name' of https://github.com/JordiPolo/config-rs into JordiPolo-feature/with_name
-rw-r--r--Cargo.toml1
-rw-r--r--src/file/format/mod.rs33
-rw-r--r--src/file/mod.rs18
-rw-r--r--src/file/source/file.rs66
-rw-r--r--src/file/source/mod.rs2
-rw-r--r--src/file/source/string.rs4
-rw-r--r--src/lib.rs3
-rw-r--r--tests/file.rs20
8 files changed, 108 insertions, 39 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 4c57803..f35e15e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -15,6 +15,7 @@ json = ["serde_json"]
yaml = ["yaml-rust"]
[dependencies]
+lazy_static = "0.2"
serde = "^1.0.8"
nom = "^3.0.0"
diff --git a/src/file/format/mod.rs b/src/file/format/mod.rs
index 02cafe7..287b9b4 100644
--- a/src/file/format/mod.rs
+++ b/src/file/format/mod.rs
@@ -12,7 +12,7 @@ mod json;
#[cfg(feature = "yaml")]
mod yaml;
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum FileFormat {
/// TOML (parsed with toml)
#[cfg(feature = "toml")]
@@ -27,20 +27,29 @@ pub enum FileFormat {
Yaml,
}
-impl FileFormat {
- // TODO: pub(crate)
+lazy_static! {
#[doc(hidden)]
- pub fn extensions(&self) -> Vec<&'static str> {
- match *self {
- #[cfg(feature = "toml")]
- FileFormat::Toml => vec!["toml"],
+ pub static ref ALL_EXTENSIONS: HashMap<FileFormat, Vec<&'static str>> = {
+ let mut formats: HashMap<FileFormat, Vec<_>> = HashMap::new();
- #[cfg(feature = "json")]
- FileFormat::Json => vec!["json"],
+ #[cfg(feature = "toml")]
+ formats.insert(FileFormat::Toml, vec!["toml"]);
- #[cfg(feature = "yaml")]
- FileFormat::Yaml => vec!["yaml", "yml"],
- }
+ #[cfg(feature = "json")]
+ formats.insert(FileFormat::Json, vec!["json"]);
+
+ #[cfg(feature = "yaml")]
+ formats.insert(FileFormat::Yaml, vec!["yaml", "yml"]);
+
+ formats
+ };
+}
+
+impl FileFormat {
+ // TODO: pub(crate)
+ #[doc(hidden)]
+ pub fn extensions(&self) -> &'static Vec<&'static str> {
+ ALL_EXTENSIONS.get(self).unwrap()
}
// TODO: pub(crate)
diff --git a/src/file/mod.rs b/src/file/mod.rs
index 7ab77d8..d19f973 100644
--- a/src/file/mod.rs
+++ b/src/file/mod.rs
@@ -5,6 +5,7 @@ use source::Source;
use error::*;
use value::Value;
use std::collections::HashMap;
+use std::path::Path;
use self::source::FileSource;
pub use self::format::FileFormat;
@@ -44,6 +45,17 @@ impl File<source::file::FileSourceFile> {
source: source::file::FileSourceFile::new(name),
}
}
+
+ /// Given the basename of a file, will attempt to locate a file by setting its
+ /// extension to a registered format.
+ pub fn with_name(name: &str) -> Self {
+ File {
+ format: None,
+ required: true,
+ namespace: None,
+ source: source::file::FileSourceFile::new(name),
+ }
+ }
}
impl<T: FileSource> File<T> {
@@ -61,10 +73,10 @@ impl<T: FileSource> File<T> {
impl<T: FileSource> Source for File<T> {
fn collect(&self) -> Result<HashMap<String, Value>> {
// Coerce the file contents to a string
- let (uri, contents) = match self.source.resolve(self.format).map_err(|err| {
+ let (uri, contents, format) = match self.source.resolve(self.format).map_err(|err| {
ConfigError::Foreign(err)
}) {
- Ok((uri, contents)) => (uri, contents),
+ Ok((uri, contents, format)) => (uri, contents, format),
Err(error) => {
if !self.required {
@@ -76,7 +88,7 @@ impl<T: FileSource> Source for File<T> {
};
// Parse the string using the given format
- let result = self.format.unwrap().parse(uri.as_ref(), &contents, self.namespace.as_ref()).map_err(|cause| {
+ let result = format.parse(uri.as_ref(), &contents, self.namespace.as_ref()).map_err(|cause| {
ConfigError::FileParse {
uri: uri,
cause: cause
diff --git a/src/file/source/file.rs b/src/file/source/file.rs
index 124b7dd..79f46bf 100644
--- a/src/file/source/file.rs
+++ b/src/file/source/file.rs
@@ -3,9 +3,11 @@ use std::result;
use std::error::Error;
use std::path::{PathBuf, Path};
+use ::file::format::ALL_EXTENSIONS;
use std::io::{self, Read};
use std::fs;
use std::env;
+use std::iter::Iterator;
use source::Source;
use super::{FileFormat, FileSource};
@@ -28,10 +30,9 @@ impl FileSourceFile {
}
}
- fn find_file(&self, format_hint: Option<FileFormat>) -> Result<PathBuf, Box<Error>> {
+ fn find_file(&self, format_hint: Option<FileFormat>) -> Result<(PathBuf, FileFormat), Box<Error>> {
// Build expected configuration file
let mut basename = PathBuf::new();
- let extensions = format_hint.unwrap().extensions();
if let Some(ref path) = self.path {
basename.push(path.clone());
@@ -39,36 +40,59 @@ impl FileSourceFile {
basename.push(self.name.clone());
- // Find configuration file (algorithm similar to .git detection by git)
- let mut dir = env::current_dir()?;
- let mut filename = dir.as_path().join(basename.clone());
+ // First check for an _exact_ match
+ let mut filename = env::current_dir()?.as_path().join(basename.clone());
+ if filename.is_file() {
+ return match format_hint {
+ Some(format) => Ok((filename, format)),
+ None => {
+ for (format, extensions) in ALL_EXTENSIONS.iter() {
+ if extensions.contains(&filename.extension().unwrap_or_default().to_string_lossy().as_ref()) {
+ return Ok((filename, *format));
+ }
+ }
- loop {
- for ext in &extensions {
- filename.set_extension(ext);
+ Err(Box::new(io::Error::new(io::ErrorKind::NotFound,
+ format!("configuration file \"{}\" is not of a registered file format",
+ filename.to_string_lossy()))))
+ }
+ };
+ }
+
+ match format_hint {
+ Some(format) => {
+ for ext in format.extensions() {
+ filename.set_extension(ext);
- if filename.is_file() {
- // File exists and is a file
- return Ok(filename);
+ if filename.is_file() {
+ return Ok((filename, format));
+ }
}
}
- // Not found.. travse up via the dir
- if !dir.pop() {
- // Failed to find the configuration file
- return Err(Box::new(io::Error::new(io::ErrorKind::NotFound,
- format!("configuration file \"{}\" not found",
- basename.to_string_lossy()))
- ));
+ None => {
+ for (format, extensions) in ALL_EXTENSIONS.iter() {
+ for ext in format.extensions() {
+ filename.set_extension(ext);
+
+ if filename.is_file() {
+ return Ok((filename, *format));
+ }
+ }
+ }
}
}
+
+ Err(Box::new(io::Error::new(io::ErrorKind::NotFound,
+ format!("configuration file \"{}\" not found",
+ basename.to_string_lossy()))))
}
}
impl FileSource for FileSourceFile {
- fn resolve(&self, format_hint: Option<FileFormat>) -> Result<(Option<String>, String), Box<Error>> {
+ fn resolve(&self, format_hint: Option<FileFormat>) -> Result<(Option<String>, String, FileFormat), Box<Error>> {
// Find file
- let filename = self.find_file(format_hint)?;
+ let (filename, format) = self.find_file(format_hint)?;
// Attempt to use a relative path for the URI
let base = env::current_dir()?;
@@ -82,7 +106,7 @@ impl FileSource for FileSourceFile {
let mut text = String::new();
file.read_to_string(&mut text)?;
- Ok((Some(uri.to_string_lossy().into_owned()), text))
+ Ok((Some(uri.to_string_lossy().into_owned()), text, format))
}
}
diff --git a/src/file/source/mod.rs b/src/file/source/mod.rs
index 4aeafa5..89f1a3c 100644
--- a/src/file/source/mod.rs
+++ b/src/file/source/mod.rs
@@ -8,5 +8,5 @@ use super::FileFormat;
/// Describes where the file is sourced
pub trait FileSource {
- fn resolve(&self, format_hint: Option<FileFormat>) -> Result<(Option<String>, String), Box<Error>>;
+ fn resolve(&self, format_hint: Option<FileFormat>) -> Result<(Option<String>, String, FileFormat), Box<Error>>;
}
diff --git a/src/file/source/string.rs b/src/file/source/string.rs
index e1d9f64..922e46c 100644
--- a/src/file/source/string.rs
+++ b/src/file/source/string.rs
@@ -15,7 +15,7 @@ impl<'a> From<&'a str> for FileSourceString {
}
impl FileSource for FileSourceString {
- fn resolve(&self, _: Option<FileFormat>) -> Result<(Option<String>, String), Box<Error>> {
- Ok((None, self.0.clone()))
+ fn resolve(&self, format_hint: Option<FileFormat>) -> Result<(Option<String>, String, FileFormat), Box<Error>> {
+ Ok((None, self.0.clone(), format_hint.expect("from_str requires a set file format")))
}
}
diff --git a/src/lib.rs b/src/lib.rs
index fe02a93..c1fb07b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -27,6 +27,9 @@ extern crate serde;
extern crate nom;
+#[macro_use]
+extern crate lazy_static;
+
#[cfg(feature = "toml")]
extern crate toml;
diff --git a/tests/file.rs b/tests/file.rs
index 4dba71f..04a7361 100644
--- a/tests/file.rs
+++ b/tests/file.rs
@@ -29,3 +29,23 @@ fn test_file_auto() {
assert_eq!(c.get("debug").ok(), Some(false));
assert_eq!(c.get("production").ok(), Some(true));
}
+
+#[test]
+fn test_file_auto_not_found() {
+ let mut c = Config::default();
+ let res = c.merge(File::with_name("tests/NoSettings"));
+
+ assert!(res.is_err());
+ assert_eq!(res.unwrap_err().to_string(),
+ "configuration file \"tests/NoSettings\" not found"
+ .to_string());
+}
+
+#[test]
+fn test_file_ext() {
+ let mut c = Config::default();
+ c.merge(File::with_name("tests/Settings.json")).unwrap();
+
+ assert_eq!(c.get("debug").ok(), Some(true));
+ assert_eq!(c.get("production").ok(), Some(false));
+}