storm_config/file/source/
file.rs1use std::error::Error;
2use std::path::PathBuf;
3use std::{env, fs, io};
4
5use read_url::*;
6
7use crate::FileFormat;
8use crate::file::format::ALL_EXTENSIONS;
9use crate::file::source::FileSourceResult;
10use crate::file::{FileSource, FileStoredFormat, Format};
11
12#[derive(Clone, Debug)]
14pub struct FileSourceFile {
15 name: PathBuf,
17}
18
19impl FileSourceFile {
20 pub fn new(name: PathBuf) -> Self {
21 Self { name }
22 }
23
24 fn find_file<F>(
25 &self,
26 format_hint: Option<F>,
27 ) -> Result<(PathBuf, Box<dyn Format>), Box<dyn Error + Send + Sync>>
28 where
29 F: FileStoredFormat + Format + 'static,
30 {
31 let filename = if self.name.is_absolute() {
32 self.name.clone()
33 } else {
34 env::current_dir()?.as_path().join(&self.name)
35 };
36
37 if filename.is_file() {
39 return if let Some(format) = format_hint {
40 Ok((filename, Box::new(format)))
41 } else {
42 for (format, extensions) in ALL_EXTENSIONS.iter() {
43 if extensions
44 .contains(&filename.extension().unwrap_or_default().to_string_lossy().as_ref())
45 {
46 return Ok((filename, Box::new(*format)));
47 }
48 }
49
50 Err(Box::new(io::Error::new(
51 io::ErrorKind::NotFound,
52 format!(
53 "configuration file \"{}\" is not of a registered file format",
54 filename.to_string_lossy()
55 ),
56 )))
57 };
58 }
59
60 let mut filename = add_dummy_extension(filename);
63
64 match format_hint {
65 Some(format) => {
66 for ext in format.file_extensions() {
67 filename.set_extension(ext);
68
69 if filename.is_file() {
70 return Ok((filename, Box::new(format)));
71 }
72 }
73 }
74
75 None => {
76 for format in ALL_EXTENSIONS.keys() {
77 for ext in format.extensions() {
78 filename.set_extension(ext);
79
80 if filename.is_file() {
81 return Ok((filename, Box::new(*format)));
82 }
83 }
84 }
85 }
86 }
87
88 Err(Box::new(io::Error::new(
89 io::ErrorKind::NotFound,
90 format!("configuration file \"{}\" not found", self.name.to_string_lossy()),
91 )))
92 }
93}
94
95impl<F> FileSource<F> for FileSourceFile
96where
97 F: Format + FileStoredFormat + 'static,
98{
99 fn resolve(
100 &self,
101 format_hint: Option<F>,
102 ) -> Result<FileSourceResult, Box<dyn Error + Send + Sync>> {
103 if format_hint.is_none() {
104 return Ok(FileSourceResult {
105 uri: None,
106 content: "{}".into(),
107 format: Box::new(FileFormat::default()),
108 });
109 }
110
111 match self.find_file(format_hint) {
113 Ok((filename, format)) => {
114 return Ok(FileSourceResult {
115 uri: Some(
116 env::current_dir()
117 .ok()
118 .and_then(|base| pathdiff::diff_paths(&filename, base))
119 .unwrap_or_else(|| filename.clone())
120 .to_string_lossy()
121 .into_owned(),
122 ),
123 content: fs::read_to_string(filename)?,
124 format,
125 });
126 }
127 Err(e) => {
128 let context = UrlContext::new();
129
130 let url = context.url(self.name.to_str().unwrap_or_default())?;
131 let mut reader = url.open()?;
132
133 let mut content = String::default();
134 reader.read_to_string(&mut content)?;
135
136 return Ok(FileSourceResult {
137 uri: Some(url.to_string()),
138 content,
139 format: Box::new(find_file_extension(self.name.clone())),
140 });
141 }
142 };
143 }
144}
145
146fn add_dummy_extension(mut filename: PathBuf) -> PathBuf {
147 match filename.extension() {
148 Some(extension) => {
149 let mut ext = extension.to_os_string();
150 ext.push(".");
151 ext.push("dummy");
152 filename.set_extension(ext);
153 }
154 None => {
155 filename.set_extension("dummy");
156 }
157 }
158
159 filename
160}
161
162fn find_file_extension(filename: PathBuf) -> FileFormat {
163 if let Some(extension) = filename.extension() {
164 let ext = extension.to_string_lossy();
165 for format in ALL_EXTENSIONS.keys() {
166 if format.extensions().contains(&ext.as_ref()) {
167 return format.clone();
168 }
169 }
170 }
171
172 FileFormat::default()
173}