From bfc44c331a77d8c341c076e72df5ed0b56fbd422 Mon Sep 17 00:00:00 2001 From: Ryan Leckey Date: Thu, 1 Jun 2017 23:22:04 -0700 Subject: Move things around and get some tests in place --- src/file/source/file.rs | 129 ++++++++++++++++++++++++++++++++++++++++++++++ src/file/source/mod.rs | 12 +++++ src/file/source/string.rs | 21 ++++++++ 3 files changed, 162 insertions(+) create mode 100644 src/file/source/file.rs create mode 100644 src/file/source/mod.rs create mode 100644 src/file/source/string.rs (limited to 'src/file/source') diff --git a/src/file/source/file.rs b/src/file/source/file.rs new file mode 100644 index 0000000..124b7dd --- /dev/null +++ b/src/file/source/file.rs @@ -0,0 +1,129 @@ +use std::str::FromStr; +use std::result; +use std::error::Error; + +use std::path::{PathBuf, Path}; +use std::io::{self, Read}; +use std::fs; +use std::env; + +use source::Source; +use super::{FileFormat, FileSource}; + +/// Describes a file sourced from a file +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, +} + +impl FileSourceFile { + pub fn new(name: &str) -> FileSourceFile { + FileSourceFile { + name: name.into(), + path: None, + } + } + + fn find_file(&self, format_hint: Option) -> Result> { + // 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()); + } + + 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()); + + loop { + 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(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) -> Result<(Option, String), Box> { + // Find file + let filename = self.find_file(format_hint)?; + + // Attempt to use a relative path for the URI + let base = env::current_dir()?; + let uri = match path_relative_from(&filename, &base) { + Some(value) => value, + None => filename.clone(), + }; + + // Read contents from file + let mut file = fs::File::open(filename.clone())?; + let mut text = String::new(); + file.read_to_string(&mut text)?; + + Ok((Some(uri.to_string_lossy().into_owned()), text)) + } +} + +// TODO: This should probably be a crate +// https://github.com/rust-lang/rust/blob/master/src/librustc_trans/back/rpath.rs#L128 +fn path_relative_from(path: &Path, base: &Path) -> Option { + use std::path::Component; + + if path.is_absolute() != base.is_absolute() { + if path.is_absolute() { + Some(PathBuf::from(path)) + } else { + None + } + } else { + let mut ita = path.components(); + let mut itb = base.components(); + let mut comps: Vec = vec![]; + loop { + match (ita.next(), itb.next()) { + (None, None) => break, + (Some(a), None) => { + comps.push(a); + comps.extend(ita.by_ref()); + break; + } + (None, _) => comps.push(Component::ParentDir), + (Some(a), Some(b)) if comps.is_empty() && a == b => (), + (Some(a), Some(b)) if b == Component::CurDir => comps.push(a), + (Some(_), Some(b)) if b == Component::ParentDir => return None, + (Some(a), Some(_)) => { + comps.push(Component::ParentDir); + for _ in itb { + comps.push(Component::ParentDir); + } + comps.push(a); + comps.extend(ita.by_ref()); + break; + } + } + } + Some(comps.iter().map(|c| c.as_os_str()).collect()) + } +} diff --git a/src/file/source/mod.rs b/src/file/source/mod.rs new file mode 100644 index 0000000..4aeafa5 --- /dev/null +++ b/src/file/source/mod.rs @@ -0,0 +1,12 @@ +pub mod file; +pub mod string; + +use std::error::Error; + +use source::Source; +use super::FileFormat; + +/// Describes where the file is sourced +pub trait FileSource { + fn resolve(&self, format_hint: Option) -> Result<(Option, String), Box>; +} diff --git a/src/file/source/string.rs b/src/file/source/string.rs new file mode 100644 index 0000000..e1d9f64 --- /dev/null +++ b/src/file/source/string.rs @@ -0,0 +1,21 @@ +use std::str::FromStr; +use std::result; +use std::error::Error; + +use source::Source; +use super::{FileSource, FileFormat}; + +/// Describes a file sourced from a string +pub struct FileSourceString(String); + +impl<'a> From<&'a str> for FileSourceString { + fn from(s: &'a str) -> Self { + FileSourceString(s.into()) + } +} + +impl FileSource for FileSourceString { + fn resolve(&self, _: Option) -> Result<(Option, String), Box> { + Ok((None, self.0.clone())) + } +} -- cgit v1.2.3