Skip to content

Commit 6ca707b

Browse files
committed
refactor: Use a common from_value method
DRY: The `json`, `json5` and `toml` parsers all leverage `serde` and can share a common enum to deserialize data into, instead of individual methods performing roughly the same transformations. - While `ron` improves their support for serde untagged enums with v0.9, it is still not compatible with this approach (_Their README details why_). - The `yaml` support doesn't leverage `serde` thus is not compatible. The new common method is based on the `json5` technique. - It has been adjusted to reflect the `ValueKind` enum, which could not directly be used due to the `Table` and `Array` types using `Value` as their value storage type instead of self-referencing the enum. - Very similar to a `impl From`, but supports the complimentary `uri` parameter for each `Value` derived. Signed-off-by: Brennan Kinney <[email protected]>
1 parent 55c464e commit 6ca707b

File tree

4 files changed

+58
-119
lines changed

4 files changed

+58
-119
lines changed

src/file/format/json.rs

Lines changed: 3 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,53 +2,13 @@ use std::error::Error;
22

33
use crate::format;
44
use crate::map::Map;
5-
use crate::value::{Value, ValueKind};
5+
use crate::value::Value;
66

77
pub fn parse(
88
uri: Option<&String>,
99
text: &str,
1010
) -> Result<Map<String, Value>, Box<dyn Error + Send + Sync>> {
11-
// Parse a JSON object value from the text
12-
let value = from_json_value(uri, &serde_json::from_str(text)?);
11+
// Parse a JSON input from the provided text
12+
let value = format::from_parsed_value(uri, serde_json::from_str(text)?);
1313
format::extract_root_table(uri, value)
1414
}
15-
16-
fn from_json_value(uri: Option<&String>, value: &serde_json::Value) -> Value {
17-
match *value {
18-
serde_json::Value::String(ref value) => Value::new(uri, ValueKind::String(value.clone())),
19-
20-
serde_json::Value::Number(ref value) => {
21-
if let Some(value) = value.as_i64() {
22-
Value::new(uri, ValueKind::I64(value))
23-
} else if let Some(value) = value.as_f64() {
24-
Value::new(uri, ValueKind::Float(value))
25-
} else {
26-
unreachable!();
27-
}
28-
}
29-
30-
serde_json::Value::Bool(value) => Value::new(uri, ValueKind::Boolean(value)),
31-
32-
serde_json::Value::Object(ref table) => {
33-
let mut m = Map::new();
34-
35-
for (key, value) in table {
36-
m.insert(key.clone(), from_json_value(uri, value));
37-
}
38-
39-
Value::new(uri, ValueKind::Table(m))
40-
}
41-
42-
serde_json::Value::Array(ref array) => {
43-
let mut l = Vec::new();
44-
45-
for value in array {
46-
l.push(from_json_value(uri, value));
47-
}
48-
49-
Value::new(uri, ValueKind::Array(l))
50-
}
51-
52-
serde_json::Value::Null => Value::new(uri, ValueKind::Nil),
53-
}
54-
}

src/file/format/json5.rs

Lines changed: 3 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,53 +2,13 @@ use std::error::Error;
22

33
use crate::format;
44
use crate::map::Map;
5-
use crate::value::{Value, ValueKind};
6-
7-
#[derive(serde::Deserialize, Debug)]
8-
#[serde(untagged)]
9-
pub enum Val {
10-
Null,
11-
Boolean(bool),
12-
Integer(i64),
13-
Float(f64),
14-
String(String),
15-
Array(Vec<Self>),
16-
Object(Map<String, Self>),
17-
}
5+
use crate::value::Value;
186

197
pub fn parse(
208
uri: Option<&String>,
219
text: &str,
2210
) -> Result<Map<String, Value>, Box<dyn Error + Send + Sync>> {
23-
let value = from_json5_value(uri, json5_rs::from_str::<Val>(text)?);
11+
// Parse a JSON5 input from the provided text
12+
let value = format::from_parsed_value(uri, json5_rs::from_str(text)?);
2413
format::extract_root_table(uri, value)
2514
}
26-
27-
fn from_json5_value(uri: Option<&String>, value: Val) -> Value {
28-
let vk = match value {
29-
Val::Null => ValueKind::Nil,
30-
Val::String(v) => ValueKind::String(v),
31-
Val::Integer(v) => ValueKind::I64(v),
32-
Val::Float(v) => ValueKind::Float(v),
33-
Val::Boolean(v) => ValueKind::Boolean(v),
34-
Val::Object(table) => {
35-
let m = table
36-
.into_iter()
37-
.map(|(k, v)| (k, from_json5_value(uri, v)))
38-
.collect();
39-
40-
ValueKind::Table(m)
41-
}
42-
43-
Val::Array(array) => {
44-
let l = array
45-
.into_iter()
46-
.map(|v| from_json5_value(uri, v))
47-
.collect();
48-
49-
ValueKind::Array(l)
50-
}
51-
};
52-
53-
Value::new(uri, vk)
54-
}

src/file/format/toml.rs

Lines changed: 2 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8,38 +8,7 @@ pub fn parse(
88
uri: Option<&String>,
99
text: &str,
1010
) -> Result<Map<String, Value>, Box<dyn Error + Send + Sync>> {
11-
// Parse a TOML value from the provided text
12-
let value = from_toml_value(uri, &toml::from_str(text)?);
11+
// Parse a TOML input from the provided text
12+
let value = format::from_parsed_value(uri, toml::from_str(text)?);
1313
format::extract_root_table(uri, value)
1414
}
15-
16-
fn from_toml_value(uri: Option<&String>, value: &toml::Value) -> Value {
17-
match *value {
18-
toml::Value::String(ref value) => Value::new(uri, value.to_string()),
19-
toml::Value::Float(value) => Value::new(uri, value),
20-
toml::Value::Integer(value) => Value::new(uri, value),
21-
toml::Value::Boolean(value) => Value::new(uri, value),
22-
23-
toml::Value::Table(ref table) => {
24-
let mut m = Map::new();
25-
26-
for (key, value) in table {
27-
m.insert(key.clone(), from_toml_value(uri, value));
28-
}
29-
30-
Value::new(uri, m)
31-
}
32-
33-
toml::Value::Array(ref array) => {
34-
let mut l = Vec::new();
35-
36-
for value in array {
37-
l.push(from_toml_value(uri, value));
38-
}
39-
40-
Value::new(uri, l)
41-
}
42-
43-
toml::Value::Datetime(ref datetime) => Value::new(uri, datetime.to_string()),
44-
}
45-
}

src/format.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,53 @@ pub fn extract_root_table(
4444
.map_err(|err| ConfigError::invalid_root(uri, err))
4545
.map_err(|err| Box::new(err) as Box<dyn Error + Send + Sync>)
4646
}
47+
48+
// Equivalent to ValueKind, except Table + Array store the same enum
49+
// Useful for serde to serialize values into, then convert to Value
50+
#[derive(serde::Deserialize, Debug)]
51+
#[serde(untagged)]
52+
pub enum ParsedValue {
53+
Nil,
54+
Boolean(bool),
55+
I64(i64),
56+
I128(i128),
57+
U64(u64),
58+
U128(u128),
59+
Float(f64),
60+
String(String),
61+
Table(Map<String, Self>),
62+
Array(Vec<Self>),
63+
}
64+
65+
// Value wrap ValueKind values, with optional uri (origin)
66+
pub fn from_parsed_value(uri: Option<&String>, value: ParsedValue) -> Value {
67+
let vk = match value {
68+
ParsedValue::Nil => ValueKind::Nil,
69+
ParsedValue::String(v) => ValueKind::String(v),
70+
ParsedValue::I64(v) => ValueKind::I64(v),
71+
ParsedValue::I128(v) => ValueKind::I128(v),
72+
ParsedValue::U64(v) => ValueKind::U64(v),
73+
ParsedValue::U128(v) => ValueKind::U128(v),
74+
ParsedValue::Float(v) => ValueKind::Float(v),
75+
ParsedValue::Boolean(v) => ValueKind::Boolean(v),
76+
ParsedValue::Table(table) => {
77+
let m = table
78+
.into_iter()
79+
.map(|(k, v)| (k, from_parsed_value(uri, v)))
80+
.collect();
81+
82+
ValueKind::Table(m)
83+
}
84+
85+
ParsedValue::Array(array) => {
86+
let l = array
87+
.into_iter()
88+
.map(|v| from_parsed_value(uri, v))
89+
.collect();
90+
91+
ValueKind::Array(l)
92+
}
93+
};
94+
95+
Value::new(uri, vk)
96+
}

0 commit comments

Comments
 (0)