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