storm_config/file/source/
file.rs1use std::env;
2use std::error::Error;
3use std::fs;
4use std::io;
5use std::path::PathBuf;
6
7use crate::file::{
8 format::ALL_EXTENSIONS, source::FileSourceResult, FileSource, FileStoredFormat, Format,
9};
10
11#[derive(Clone, Debug)]
13pub struct FileSourceFile {
14 name: PathBuf,
16}
17
18impl FileSourceFile {
19 pub fn new(name: PathBuf) -> Self {
20 Self { name }
21 }
22
23 fn find_file<F>(
24 &self,
25 format_hint: Option<F>,
26 ) -> Result<(PathBuf, Box<dyn Format>), Box<dyn Error + Send + Sync>>
27 where
28 F: FileStoredFormat + Format + 'static,
29 {
30 let filename = if self.name.is_absolute() {
31 self.name.clone()
32 } else {
33 env::current_dir()?.as_path().join(&self.name)
34 };
35
36 if filename.is_file() {
38 return if let Some(format) = format_hint {
39 Ok((filename, Box::new(format)))
40 } else {
41 for (format, extensions) in ALL_EXTENSIONS.iter() {
42 if extensions
43 .contains(&filename.extension().unwrap_or_default().to_string_lossy().as_ref())
44 {
45 return Ok((filename, Box::new(*format)));
46 }
47 }
48
49 Err(Box::new(io::Error::new(
50 io::ErrorKind::NotFound,
51 format!(
52 "configuration file \"{}\" is not of a registered file format",
53 filename.to_string_lossy()
54 ),
55 )))
56 };
57 }
58 let mut filename = add_dummy_extension(filename);
61
62 match format_hint {
63 Some(format) => {
64 for ext in format.file_extensions() {
65 filename.set_extension(ext);
66
67 if filename.is_file() {
68 return Ok((filename, Box::new(format)));
69 }
70 }
71 }
72
73 None => {
74 for format in ALL_EXTENSIONS.keys() {
75 for ext in format.extensions() {
76 filename.set_extension(ext);
77
78 if filename.is_file() {
79 return Ok((filename, Box::new(*format)));
80 }
81 }
82 }
83 }
84 }
85
86 Err(Box::new(io::Error::new(
87 io::ErrorKind::NotFound,
88 format!("configuration file \"{}\" not found", self.name.to_string_lossy()),
89 )))
90 }
91}
92
93impl<F> FileSource<F> for FileSourceFile
94where
95 F: Format + FileStoredFormat + 'static,
96{
97 fn resolve(
98 &self,
99 format_hint: Option<F>,
100 ) -> Result<FileSourceResult, Box<dyn Error + Send + Sync>> {
101 let (filename, format) = self.find_file(format_hint)?;
103
104 let uri = env::current_dir()
106 .ok()
107 .and_then(|base| pathdiff::diff_paths(&filename, base))
108 .unwrap_or_else(|| filename.clone());
109
110 let text = fs::read_to_string(filename)?;
112
113 Ok(FileSourceResult { uri: Some(uri.to_string_lossy().into_owned()), content: text, format })
114 }
115}
116
117fn add_dummy_extension(mut filename: PathBuf) -> PathBuf {
118 match filename.extension() {
119 Some(extension) => {
120 let mut ext = extension.to_os_string();
121 ext.push(".");
122 ext.push("dummy");
123 filename.set_extension(ext);
124 }
125 None => {
126 filename.set_extension("dummy");
127 }
128 }
129 filename
130}