config_maint/file/
mod.rs

1mod format;
2pub mod source;
3
4use error::*;
5use source::Source;
6use std::collections::HashMap;
7use std::path::{Path, PathBuf};
8use value::Value;
9
10pub use self::format::FileFormat;
11use self::source::FileSource;
12
13pub use self::source::file::FileSourceFile;
14pub use self::source::string::FileSourceString;
15
16#[derive(Clone, Debug)]
17pub struct File<T>
18where
19    T: FileSource,
20{
21    source: T,
22
23    /// Format of file (which dictates what driver to use).
24    format: Option<FileFormat>,
25
26    /// A required File will error if it cannot be found
27    required: bool,
28}
29
30impl File<source::string::FileSourceString> {
31    pub fn from_str(s: &str, format: FileFormat) -> Self {
32        File {
33            format: Some(format),
34            required: true,
35            source: s.into(),
36        }
37    }
38}
39
40impl File<source::file::FileSourceFile> {
41    pub fn new(name: &str, format: FileFormat) -> Self {
42        File {
43            format: Some(format),
44            required: true,
45            source: source::file::FileSourceFile::new(name.into()),
46        }
47    }
48
49    /// Given the basename of a file, will attempt to locate a file by setting its
50    /// extension to a registered format.
51    pub fn with_name(name: &str) -> Self {
52        File {
53            format: None,
54            required: true,
55            source: source::file::FileSourceFile::new(name.into()),
56        }
57    }
58}
59
60impl<'a> From<&'a Path> for File<source::file::FileSourceFile> {
61    fn from(path: &'a Path) -> Self {
62        File {
63            format: None,
64            required: true,
65            source: source::file::FileSourceFile::new(path.to_path_buf()),
66        }
67    }
68}
69
70impl From<PathBuf> for File<source::file::FileSourceFile> {
71    fn from(path: PathBuf) -> Self {
72        File {
73            format: None,
74            required: true,
75            source: source::file::FileSourceFile::new(path),
76        }
77    }
78}
79
80impl<T: FileSource> File<T> {
81    pub fn format(mut self, format: FileFormat) -> Self {
82        self.format = Some(format);
83        self
84    }
85
86    pub fn required(mut self, required: bool) -> Self {
87        self.required = required;
88        self
89    }
90}
91
92impl<T: FileSource> Source for File<T>
93where
94    T: 'static,
95    T: Sync + Send,
96{
97    fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
98        Box::new((*self).clone())
99    }
100
101    fn collect(&self) -> Result<HashMap<String, Value>> {
102        // Coerce the file contents to a string
103        let (uri, contents, format) = match self
104            .source
105            .resolve(self.format)
106            .map_err(|err| ConfigError::Foreign(err))
107        {
108            Ok((uri, contents, format)) => (uri, contents, format),
109
110            Err(error) => {
111                if !self.required {
112                    return Ok(HashMap::new());
113                }
114
115                return Err(error);
116            }
117        };
118
119        // Parse the string using the given format
120        format
121            .parse(uri.as_ref(), &contents)
122            .map_err(|cause| ConfigError::FileParse { uri, cause })
123    }
124}