1 // If no features are used, there is an "unused mut" warning in `ALL_EXTENSIONS`
2 // BUG: ? For some reason this doesn't do anything if I try and function scope this
3 #![allow(unused_mut)]
4 
5 use lazy_static::lazy_static;
6 use std::collections::HashMap;
7 use std::error::Error;
8 
9 use crate::map::Map;
10 use crate::{file::FileStoredFormat, value::Value, Format};
11 
12 #[cfg(feature = "toml")]
13 mod toml;
14 
15 #[cfg(feature = "json")]
16 mod json;
17 
18 #[cfg(feature = "yaml")]
19 mod yaml;
20 
21 #[cfg(feature = "ini")]
22 mod ini;
23 
24 #[cfg(feature = "ron")]
25 mod ron;
26 
27 #[cfg(feature = "json5")]
28 mod json5;
29 
30 /// File formats provided by the library.
31 ///
32 /// Although it is possible to define custom formats using [`Format`] trait it is recommended to use FileFormat if possible.
33 #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
34 pub enum FileFormat {
35     /// TOML (parsed with toml)
36     #[cfg(feature = "toml")]
37     Toml,
38 
39     /// JSON (parsed with serde_json)
40     #[cfg(feature = "json")]
41     Json,
42 
43     /// YAML (parsed with yaml_rust)
44     #[cfg(feature = "yaml")]
45     Yaml,
46 
47     /// INI (parsed with rust_ini)
48     #[cfg(feature = "ini")]
49     Ini,
50 
51     /// RON (parsed with ron)
52     #[cfg(feature = "ron")]
53     Ron,
54 
55     /// JSON5 (parsed with json5)
56     #[cfg(feature = "json5")]
57     Json5,
58 }
59 
60 lazy_static! {
61     #[doc(hidden)]
62     // #[allow(unused_mut)] ?
63     pub static ref ALL_EXTENSIONS: HashMap<FileFormat, Vec<&'static str>> = {
64         let mut formats: HashMap<FileFormat, Vec<_>> = HashMap::new();
65 
66         #[cfg(feature = "toml")]
67         formats.insert(FileFormat::Toml, vec!["toml"]);
68 
69         #[cfg(feature = "json")]
70         formats.insert(FileFormat::Json, vec!["json"]);
71 
72         #[cfg(feature = "yaml")]
73         formats.insert(FileFormat::Yaml, vec!["yaml", "yml"]);
74 
75         #[cfg(feature = "ini")]
76         formats.insert(FileFormat::Ini, vec!["ini"]);
77 
78         #[cfg(feature = "ron")]
79         formats.insert(FileFormat::Ron, vec!["ron"]);
80 
81         #[cfg(feature = "json5")]
82         formats.insert(FileFormat::Json5, vec!["json5"]);
83 
84         formats
85     };
86 }
87 
88 impl FileFormat {
extensions(&self) -> &'static [&'static str]89     pub(crate) fn extensions(&self) -> &'static [&'static str] {
90         // It should not be possible for this to fail
91         // A FileFormat would need to be declared without being added to the
92         // ALL_EXTENSIONS map.
93         ALL_EXTENSIONS.get(self).unwrap()
94     }
95 
parse( &self, uri: Option<&String>, text: &str, ) -> Result<Map<String, Value>, Box<dyn Error + Send + Sync>>96     pub(crate) fn parse(
97         &self,
98         uri: Option<&String>,
99         text: &str,
100     ) -> Result<Map<String, Value>, Box<dyn Error + Send + Sync>> {
101         match self {
102             #[cfg(feature = "toml")]
103             FileFormat::Toml => toml::parse(uri, text),
104 
105             #[cfg(feature = "json")]
106             FileFormat::Json => json::parse(uri, text),
107 
108             #[cfg(feature = "yaml")]
109             FileFormat::Yaml => yaml::parse(uri, text),
110 
111             #[cfg(feature = "ini")]
112             FileFormat::Ini => ini::parse(uri, text),
113 
114             #[cfg(feature = "ron")]
115             FileFormat::Ron => ron::parse(uri, text),
116 
117             #[cfg(feature = "json5")]
118             FileFormat::Json5 => json5::parse(uri, text),
119 
120             #[cfg(all(
121                 not(feature = "toml"),
122                 not(feature = "json"),
123                 not(feature = "yaml"),
124                 not(feature = "ini"),
125                 not(feature = "ron"),
126                 not(feature = "json5"),
127             ))]
128             _ => unreachable!("No features are enabled, this library won't work without features"),
129         }
130     }
131 }
132 
133 impl Format for FileFormat {
parse( &self, uri: Option<&String>, text: &str, ) -> Result<Map<String, Value>, Box<dyn Error + Send + Sync>>134     fn parse(
135         &self,
136         uri: Option<&String>,
137         text: &str,
138     ) -> Result<Map<String, Value>, Box<dyn Error + Send + Sync>> {
139         self.parse(uri, text)
140     }
141 }
142 
143 impl FileStoredFormat for FileFormat {
file_extensions(&self) -> &'static [&'static str]144     fn file_extensions(&self) -> &'static [&'static str] {
145         self.extensions()
146     }
147 }
148