summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorup9cloud <8325632+up9cloud@users.noreply.github.com>2020-07-06 13:07:05 -0700
committerMatthias Beyer <mail@beyermatthias.de>2021-05-15 14:35:58 +0200
commit70c503af8b3cb3d73bbbea673bb49460df318e5e (patch)
tree3550f1015af6186e5f6c1cade83345ee48609e22 /src
parent266f504d9f23e192c03ef486f58a678847249b60 (diff)
Support format json5
Diffstat (limited to 'src')
-rw-r--r--src/error.rs12
-rw-r--r--src/file/format/json5.rs78
-rw-r--r--src/file/format/mod.rs14
-rw-r--r--src/lib.rs8
4 files changed, 109 insertions, 3 deletions
diff --git a/src/error.rs b/src/error.rs
index 28142b5..9778649 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -94,6 +94,18 @@ impl ConfigError {
}
}
+ // Have a proper error fire if the root of a file is ever not a Table
+ // TODO: for now only json5 checked, need to finish others
+ #[doc(hidden)]
+ pub fn invalid_root(origin: Option<&String>, unexpected: Unexpected) -> Box<Self> {
+ Box::new(ConfigError::Type {
+ origin: origin.map(|s| s.to_owned()),
+ unexpected,
+ expected: "a map",
+ key: None,
+ })
+ }
+
// FIXME: pub(crate)
#[doc(hidden)]
pub fn extend_with_key(self, key: &str) -> Self {
diff --git a/src/file/format/json5.rs b/src/file/format/json5.rs
new file mode 100644
index 0000000..320e574
--- /dev/null
+++ b/src/file/format/json5.rs
@@ -0,0 +1,78 @@
+use serde_derive::Deserialize;
+
+use std::collections::HashMap;
+use std::error::Error;
+
+use crate::error::{ConfigError, Unexpected};
+use crate::value::{Value, ValueKind};
+
+#[derive(Deserialize, Debug)]
+#[serde(untagged)]
+pub enum Val {
+ Null,
+ Boolean(bool),
+ Integer(i64),
+ Float(f64),
+ String(String),
+ Array(Vec<Val>),
+ Object(HashMap<String, Val>),
+}
+
+pub fn parse(
+ uri: Option<&String>,
+ text: &str,
+) -> Result<HashMap<String, Value>, Box<dyn Error + Send + Sync>> {
+ let root = json5_rs::from_str::<Val>(&text)?;
+ if let Some(err) = match root {
+ Val::String(ref value) => Some(Unexpected::Str(value.clone())),
+ Val::Integer(value) => Some(Unexpected::Integer(value)),
+ Val::Float(value) => Some(Unexpected::Float(value)),
+ Val::Boolean(value) => Some(Unexpected::Bool(value)),
+ Val::Object(_) => None,
+ Val::Array(_) => Some(Unexpected::Seq),
+ Val::Null => Some(Unexpected::Unit),
+ } {
+ return Err(ConfigError::invalid_root(uri, err));
+ }
+
+ let value = from_json5_value(uri, root);
+ match value.kind {
+ ValueKind::Table(map) => Ok(map),
+
+ _ => Ok(HashMap::new()),
+ }
+}
+
+fn from_json5_value(uri: Option<&String>, value: Val) -> Value {
+ match value {
+ Val::String(v) => Value::new(uri, ValueKind::String(v)),
+
+ Val::Integer(v) => Value::new(uri, ValueKind::Integer(v)),
+
+ Val::Float(v) => Value::new(uri, ValueKind::Float(v)),
+
+ Val::Boolean(v) => Value::new(uri, ValueKind::Boolean(v)),
+
+ Val::Object(table) => {
+ let mut m = HashMap::new();
+
+ for (key, value) in table {
+ m.insert(key, from_json5_value(uri, value));
+ }
+
+ Value::new(uri, ValueKind::Table(m))
+ }
+
+ Val::Array(array) => {
+ let mut l = Vec::new();
+
+ for value in array {
+ l.push(from_json5_value(uri, value));
+ }
+
+ Value::new(uri, ValueKind::Array(l))
+ }
+
+ Val::Null => Value::new(uri, ValueKind::Nil),
+ }
+}
diff --git a/src/file/format/mod.rs b/src/file/format/mod.rs
index bbd62a2..53bacf6 100644
--- a/src/file/format/mod.rs
+++ b/src/file/format/mod.rs
@@ -25,6 +25,9 @@ mod ini;
#[cfg(feature = "ron")]
mod ron;
+#[cfg(feature = "json5")]
+mod json5;
+
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum FileFormat {
/// TOML (parsed with toml)
@@ -42,6 +45,7 @@ pub enum FileFormat {
/// HJSON (parsed with serde_hjson)
#[cfg(feature = "hjson")]
Hjson,
+
/// INI (parsed with rust_ini)
#[cfg(feature = "ini")]
Ini,
@@ -49,6 +53,10 @@ pub enum FileFormat {
/// RON (parsed with ron)
#[cfg(feature = "ron")]
Ron,
+
+ /// JSON5 (parsed with json5)
+ #[cfg(feature = "json5")]
+ Json5,
}
lazy_static! {
@@ -75,6 +83,9 @@ lazy_static! {
#[cfg(feature = "ron")]
formats.insert(FileFormat::Ron, vec!["ron"]);
+ #[cfg(feature = "json5")]
+ formats.insert(FileFormat::Json5, vec!["json5"]);
+
formats
};
}
@@ -115,6 +126,9 @@ impl FileFormat {
#[cfg(feature = "ron")]
FileFormat::Ron => ron::parse(uri, text),
+
+ #[cfg(feature = "json5")]
+ FileFormat::Json5 => json5::parse(uri, text),
}
}
}
diff --git a/src/lib.rs b/src/lib.rs
index ae02c36..986f36a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -6,7 +6,7 @@
//! - Environment variables
//! - Another Config instance
//! - Remote configuration: etcd, Consul
-//! - Files: TOML, JSON, YAML, HJSON, INI, RON
+//! - Files: TOML, JSON, YAML, HJSON, INI, RON, JSON5
//! - Manual, programmatic override (via a `.set` method on the Config instance)
//!
//! Additionally, Config supports:
@@ -24,8 +24,7 @@
#[macro_use]
extern crate serde;
-#[cfg(test)]
-#[macro_use]
+#[cfg(any(test, feature = "json5"))]
extern crate serde_derive;
extern crate nom;
@@ -51,6 +50,9 @@ extern crate ini;
#[cfg(feature = "ron")]
extern crate ron;
+#[cfg(feature = "json5")]
+extern crate json5_rs;
+
mod builder;
mod config;
mod de;