okcodes_config/file/format/
mod.rs

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
5use lazy_static::lazy_static;
6use std::collections::HashMap;
7use std::error::Error;
8
9use crate::map::Map;
10use crate::{file::FileStoredFormat, value::Value, Format};
11
12#[cfg(feature = "toml")]
13mod toml;
14
15#[cfg(feature = "json")]
16mod json;
17
18#[cfg(feature = "yaml")]
19mod yaml;
20
21#[cfg(feature = "ini")]
22mod ini;
23
24#[cfg(feature = "ron")]
25mod ron;
26
27#[cfg(feature = "json5")]
28mod 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)]
34pub 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_rust2)
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
60lazy_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
88impl FileFormat {
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
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
133impl Format for FileFormat {
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
143impl FileStoredFormat for FileFormat {
144    fn file_extensions(&self) -> &'static [&'static str] {
145        self.extensions()
146    }
147}