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