1mod format;
2pub(crate) mod source;
3
4use std::fmt::Debug;
5use std::path::{Path, PathBuf};
6
7use self::source::FileSource;
8use crate::error::{ConfigError, Result};
9use crate::map::Map;
10use crate::source::Source;
11use crate::value::Value;
12use crate::Format;
13
14pub use self::format::FileFormat;
15pub use self::source::file::FileSourceFile;
16pub use self::source::string::FileSourceString;
17
18pub trait FileStoredFormat: Format {
22 fn file_extensions(&self) -> &'static [&'static str];
24}
25
26#[derive(Clone, Debug)]
30#[must_use]
31pub struct File<T, F> {
32 source: T,
33
34 format: Option<F>,
36
37 required: bool,
39}
40
41impl<F> File<FileSourceString, F>
42where
43 F: FileStoredFormat + 'static,
44{
45 pub fn from_str(s: &str, format: F) -> Self {
46 Self {
47 format: Some(format),
48 required: true,
49 source: s.into(),
50 }
51 }
52}
53
54impl<F> File<FileSourceFile, F>
55where
56 F: FileStoredFormat + 'static,
57{
58 pub fn new(name: &str, format: F) -> Self {
59 Self {
60 format: Some(format),
61 required: true,
62 source: FileSourceFile::new(name.into()),
63 }
64 }
65}
66
67impl File<FileSourceFile, FileFormat> {
68 pub fn with_name(base_name: &str) -> Self {
71 Self {
72 format: None,
73 required: true,
74 source: FileSourceFile::new(base_name.into()),
75 }
76 }
77}
78
79impl<T, F> File<T, F>
80where
81 F: FileStoredFormat + 'static,
82 T: FileSource<F>,
83{
84 pub fn format(mut self, format: F) -> Self {
85 self.format = Some(format);
86 self
87 }
88
89 pub fn required(mut self, required: bool) -> Self {
91 self.required = required;
92 self
93 }
94}
95
96impl<'a> From<&'a Path> for File<FileSourceFile, FileFormat> {
97 fn from(path: &'a Path) -> Self {
98 Self {
99 format: None,
100 required: true,
101 source: FileSourceFile::new(path.to_path_buf()),
102 }
103 }
104}
105
106impl From<PathBuf> for File<FileSourceFile, FileFormat> {
107 fn from(path: PathBuf) -> Self {
108 Self {
109 format: None,
110 required: true,
111 source: FileSourceFile::new(path),
112 }
113 }
114}
115
116impl<T, F> Source for File<T, F>
117where
118 F: FileStoredFormat + Debug + Clone + Send + Sync + 'static,
119 T: Sync + Send + FileSource<F> + 'static,
120{
121 fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
122 Box::new((*self).clone())
123 }
124
125 fn collect(&self) -> Result<Map<String, Value>> {
126 let (uri, contents, format) = match self
128 .source
129 .resolve(self.format.clone())
130 .map_err(ConfigError::Foreign)
131 {
132 Ok(result) => (result.uri, result.content, result.format),
133
134 Err(error) => {
135 if !self.required {
136 return Ok(Map::new());
137 }
138
139 return Err(error);
140 }
141 };
142
143 format
145 .parse(uri.as_ref(), &contents)
146 .map_err(|cause| ConfigError::FileParse { uri, cause })
147 }
148}