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.contains(
43 &filename
44 .extension()
45 .unwrap_or_default()
46 .to_string_lossy()
47 .as_ref(),
48 ) {
49 return Ok((filename, Box::new(*format)));
50 }
51 }
52
53 Err(Box::new(io::Error::new(
54 io::ErrorKind::NotFound,
55 format!(
56 "configuration file \"{}\" is not of a registered file format",
57 filename.to_string_lossy()
58 ),
59 )))
60 };
61 }
62 let mut filename = add_dummy_extension(filename);
65
66 match format_hint {
67 Some(format) => {
68 for ext in format.file_extensions() {
69 filename.set_extension(ext);
70
71 if filename.is_file() {
72 return Ok((filename, Box::new(format)));
73 }
74 }
75 }
76
77 None => {
78 for format in all_extensions().keys() {
79 for ext in format.extensions() {
80 filename.set_extension(ext);
81
82 if filename.is_file() {
83 return Ok((filename, Box::new(*format)));
84 }
85 }
86 }
87 }
88 }
89
90 Err(Box::new(io::Error::new(
91 io::ErrorKind::NotFound,
92 format!(
93 "configuration file \"{}\" not found",
94 self.name.to_string_lossy()
95 ),
96 )))
97 }
98}
99
100impl<F> FileSource<F> for FileSourceFile
101where
102 F: Format + FileStoredFormat + 'static,
103{
104 fn resolve(
105 &self,
106 format_hint: Option<F>,
107 ) -> Result<FileSourceResult, Box<dyn Error + Send + Sync>> {
108 let (filename, format) = self.find_file(format_hint)?;
110
111 let uri = env::current_dir()
113 .ok()
114 .and_then(|base| pathdiff::diff_paths(&filename, base))
115 .unwrap_or_else(|| filename.clone());
116
117 let text = fs::read_to_string(filename)?;
119
120 Ok(FileSourceResult {
121 uri: Some(uri.to_string_lossy().into_owned()),
122 content: text,
123 format,
124 })
125 }
126}
127
128fn add_dummy_extension(mut filename: PathBuf) -> PathBuf {
129 match filename.extension() {
130 Some(extension) => {
131 let mut ext = extension.to_os_string();
132 ext.push(".");
133 ext.push("dummy");
134 filename.set_extension(ext);
135 }
136 None => {
137 filename.set_extension("dummy");
138 }
139 }
140 filename
141}