diff options
author | Ryan Leckey <ryan@launchbadge.com> | 2017-06-14 00:28:43 -0700 |
---|---|---|
committer | Ryan Leckey <ryan@launchbadge.com> | 2017-06-14 00:28:43 -0700 |
commit | 1716c2fe7571b03215a5ae09c91f508d03335c2a (patch) | |
tree | f2b3144044f907c8eb1c594460d6cd74eed92b1f /src/file | |
parent | 9a2e756075dce24e764c8c89e2d71703a69fd80a (diff) | |
parent | 736738a2a798d58b1a73b0da146924a7e92ceba3 (diff) |
Merge branch 'feature/with_name' of https://github.com/JordiPolo/config-rs into JordiPolo-feature/with_name
Diffstat (limited to 'src/file')
-rw-r--r-- | src/file/format/mod.rs | 33 | ||||
-rw-r--r-- | src/file/mod.rs | 18 | ||||
-rw-r--r-- | src/file/source/file.rs | 66 | ||||
-rw-r--r-- | src/file/source/mod.rs | 2 | ||||
-rw-r--r-- | src/file/source/string.rs | 4 |
5 files changed, 84 insertions, 39 deletions
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"))) } } |