Fun with traits for better ergonomics of the JSONValue Enum
This commit is contained in:
parent
a4cb3f9b0e
commit
97bfd16b32
239
src/lib.rs
239
src/lib.rs
@ -7,6 +7,7 @@ use std::iter::FromIterator;
|
||||
use std::{char, u16};
|
||||
|
||||
pub type JSONResult = Result<JSONValue, ParseError>;
|
||||
pub type JSONArray = Vec<JSONValue>;
|
||||
pub type JSONMap = HashMap<String, JSONValue>;
|
||||
|
||||
/// The type of JSON value
|
||||
@ -34,6 +35,139 @@ pub enum JSONValue {
|
||||
Null,
|
||||
}
|
||||
|
||||
impl JSONValue {
|
||||
/// Convert the wrapped JSONValue to its simpler rust value
|
||||
///
|
||||
/// Example:
|
||||
/// ```
|
||||
/// use naive_json_parser::JSONValue;
|
||||
///
|
||||
/// let str = "Four score and seven years ago...";
|
||||
/// let wrapped = JSONValue::from(str);
|
||||
///
|
||||
/// // s is now the `String` that was in the `JSONValue` enum
|
||||
/// let s: String = wrapped.unwrap();
|
||||
///
|
||||
/// # assert_eq!(str, &s);
|
||||
/// ```
|
||||
pub fn unwrap<T: From<JSONValue>>(self) -> T {
|
||||
T::from(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<JSONValue> for JSONMap {
|
||||
/// Extracts the `HashMap` in the `JSONValue` enum, if it exists.
|
||||
/// Otherwise, panics.
|
||||
fn from(val: JSONValue) -> JSONMap {
|
||||
match val {
|
||||
JSONValue::Object(o) => o,
|
||||
_ => panic!("Invalid type conversion")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<JSONValue> for JSONArray{
|
||||
/// Extracts the `Vec` in the `JSONValue` enum, if it exists.
|
||||
/// Otherwise, panics.
|
||||
fn from(val: JSONValue) -> JSONArray {
|
||||
match val {
|
||||
JSONValue::Array(a) => a,
|
||||
_ => panic!("Invalid type conversion")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<JSONValue> for f64 {
|
||||
/// Extracts the `f64` in the `JSONValue` enum, if it exists.
|
||||
/// Otherwise, panics.
|
||||
fn from(val: JSONValue) -> f64 {
|
||||
match val {
|
||||
JSONValue::Number(n) => n,
|
||||
_ => panic!("Invalid type conversion")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<JSONValue> for String {
|
||||
/// Extracts the `String` in the `JSONValue` enum, if it exists.
|
||||
/// Otherwise, panics.
|
||||
fn from(val: JSONValue) -> String {
|
||||
match val {
|
||||
JSONValue::String(s) => s,
|
||||
_ => panic!("Invalid type conversion")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<JSONValue> for bool {
|
||||
/// Extracts the `bool` value from the `JSONValue` enum, if it exists.
|
||||
/// Otherwise, panics.
|
||||
fn from(val: JSONValue) -> bool {
|
||||
match val {
|
||||
JSONValue::True => true,
|
||||
JSONValue::False => false,
|
||||
_ => panic!("Invalid type conversion")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<JSONValue> for () {
|
||||
/// This will just swallow the enum value and return a unit tuple
|
||||
fn from(_: JSONValue) -> () { () }
|
||||
}
|
||||
|
||||
impl From<JSONMap> for JSONValue {
|
||||
/// Wraps the `HashMap` in the `JSONValue` enum
|
||||
fn from(val: JSONMap) -> JSONValue {
|
||||
Self::Object(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<JSONArray> for JSONValue {
|
||||
/// Wraps the `Vec` in the `JSONValue` enum
|
||||
fn from(val: JSONArray) -> JSONValue {
|
||||
Self::Array(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for JSONValue {
|
||||
/// Sets the `JSONValue` enum to the `True` or `False` value
|
||||
fn from(val: bool) -> Self {
|
||||
match val {
|
||||
true => Self::True,
|
||||
false => Self::False,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f64> for JSONValue {
|
||||
/// Wraps the `f64` in the `JSONValue` enum
|
||||
fn from(n: f64) -> Self {
|
||||
Self::Number(n)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<()> for JSONValue {
|
||||
/// Sets the `JSONValue` enum to the `Null` value
|
||||
fn from(_s: ()) -> Self {
|
||||
Self::Null
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for JSONValue {
|
||||
/// Wraps the `String` in the `JSONValue` enum
|
||||
fn from(s: String) -> Self {
|
||||
Self::String(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for JSONValue {
|
||||
/// Creates a `String` and wraps it in the `JSONValue` enum
|
||||
fn from(s: &str) -> Self {
|
||||
Self::String(String::from(s))
|
||||
}
|
||||
}
|
||||
|
||||
/// The type of error returned by the parser
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum ParseError {
|
||||
@ -111,9 +245,7 @@ impl JSON {
|
||||
);
|
||||
|
||||
// Every parser failed, so the syntax is probably incorrect
|
||||
Err(ParseError::UnexpectedEndOfInput(String::from(
|
||||
"Doesn't seem to be valid JSON",
|
||||
)))
|
||||
Err(ParseError::UnexpectedEndOfInput(format!("Doesn't seem to be valid JSON")))
|
||||
}
|
||||
|
||||
/// See if there's a `JSONValue::Object` next in the JSON
|
||||
@ -315,7 +447,7 @@ impl JSON {
|
||||
match str.parse::<f64>() {
|
||||
Ok(number) => {
|
||||
self.increment(str.len());
|
||||
return Ok(Some(JSONValue::Number(number)));
|
||||
return Ok(Some(JSONValue::from(number)));
|
||||
}
|
||||
Err(e) => Err(ParseError::ExpectedDigit(format!("'{}', {:#?}", str, e))),
|
||||
}
|
||||
@ -391,20 +523,39 @@ mod tests {
|
||||
use super::JSONValue::{Array, False, Null, Number, Object, True};
|
||||
use super::*;
|
||||
|
||||
impl JSONValue {
|
||||
fn unwrap_object(self) -> JSONMap {
|
||||
match self {
|
||||
JSONValue::Object(o) => o,
|
||||
_ => panic!("Tried to unwrap a non-object"),
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn value_conversion() {
|
||||
let map: JSONMap = HashMap::new();
|
||||
let num = 9.380831539;
|
||||
let str = "applesauce";
|
||||
let arr: JSONArray = vec![JSONValue::from(map.clone()), JSONValue::from(num), JSONValue::from(str)];
|
||||
|
||||
fn unwrap_array(self) -> Vec<JSONValue> {
|
||||
match self {
|
||||
JSONValue::Array(a) => a,
|
||||
_ => panic!("Tried to unwrap a non-array"),
|
||||
}
|
||||
}
|
||||
assert_eq!(map.clone(), JSONMap::from(JSONValue::from(map.clone())));
|
||||
assert_eq!(num, f64::from(JSONValue::from(num)));
|
||||
assert_eq!(String::from(str), String::from(JSONValue::from(str)));
|
||||
assert_eq!(arr.clone(), JSONArray::from(JSONValue::from(arr.clone())));
|
||||
assert_eq!(true, bool::from(JSONValue::from(true)));
|
||||
assert_eq!(false, bool::from(JSONValue::from(false)));
|
||||
assert_eq!((), <()>::from(JSONValue::from(())));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wrap_and_unwrap() {
|
||||
let map: JSONMap = HashMap::new();
|
||||
let num = 9.380831539;
|
||||
let str = "applesauce";
|
||||
let arr: JSONArray = vec![JSONValue::from(map.clone()), JSONValue::from(num), JSONValue::from(str)];
|
||||
|
||||
let s: String = JSONValue::from(str).unwrap();
|
||||
let a: JSONArray = JSONValue::from(arr.clone()).unwrap();
|
||||
|
||||
assert_eq!(map.clone(), JSONValue::from(map.clone()).unwrap());
|
||||
assert_eq!(num, JSONValue::from(num).unwrap());
|
||||
assert_eq!(str, &s);
|
||||
assert_eq!(arr.clone(), a);
|
||||
assert_eq!(true, JSONValue::from(true).unwrap());
|
||||
assert_eq!(false, JSONValue::from(false).unwrap());
|
||||
assert_eq!((), JSONValue::from(()).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -426,10 +577,10 @@ mod tests {
|
||||
#[test]
|
||||
fn parse_string() {
|
||||
let res = JSON::new(r#""\t""#).parse_string();
|
||||
assert_eq!(res, Ok(Some(JSONValue::String(String::from("\t")))));
|
||||
assert_eq!(res, Ok(Some(JSONValue::from("\t"))));
|
||||
|
||||
let res = JSON::new(r#""\u203d""#).parse_string();
|
||||
assert_eq!(res, Ok(Some(JSONValue::String(String::from("‽")))));
|
||||
assert_eq!(res, Ok(Some(JSONValue::from("‽"))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -468,7 +619,7 @@ mod tests {
|
||||
let result = JSON::new(r#"{"foo": "bar"}"#).parse_object();
|
||||
|
||||
let mut hash_map: JSONMap = HashMap::new();
|
||||
hash_map.insert(String::from("foo"), JSONValue::String(String::from("bar")));
|
||||
hash_map.insert(String::from("foo"), JSONValue::from("bar"));
|
||||
|
||||
assert_eq!(result, Ok(Some(JSONValue::Object(hash_map))));
|
||||
}
|
||||
@ -491,7 +642,7 @@ mod tests {
|
||||
let res = JSON::parse(r#""/^$/""#);
|
||||
assert_eq!(
|
||||
res,
|
||||
Ok(JSONValue::String(String::from("/^$/"))),
|
||||
Ok(JSONValue::from("/^$/")),
|
||||
"Failed to parse string"
|
||||
);
|
||||
|
||||
@ -542,16 +693,16 @@ mod tests {
|
||||
let result =
|
||||
JSON::parse(r#"["\"", "\\", "/", "\b", "\f", "\n", "\r", "\t", "\u0001", "\uface"]"#);
|
||||
let expected = Ok(Array(vec![
|
||||
JSONValue::String(String::from("\"")),
|
||||
JSONValue::String(String::from("\\")),
|
||||
JSONValue::String(String::from("/")),
|
||||
JSONValue::String(String::from("\u{8}")),
|
||||
JSONValue::String(String::from("\x0C")),
|
||||
JSONValue::String(String::from("\n")),
|
||||
JSONValue::String(String::from("\r")),
|
||||
JSONValue::String(String::from("\t")),
|
||||
JSONValue::String(String::from("\u{1}")),
|
||||
JSONValue::String(String::from("\u{face}")),
|
||||
JSONValue::from("\""),
|
||||
JSONValue::from("\\"),
|
||||
JSONValue::from("/"),
|
||||
JSONValue::from("\u{8}"),
|
||||
JSONValue::from("\x0C"),
|
||||
JSONValue::from("\n"),
|
||||
JSONValue::from("\r"),
|
||||
JSONValue::from("\t"),
|
||||
JSONValue::from("\u{1}"),
|
||||
JSONValue::from("\u{face}"),
|
||||
]));
|
||||
|
||||
assert_eq!(result, expected);
|
||||
@ -591,27 +742,29 @@ mod tests {
|
||||
String::from("c"),
|
||||
Array(vec![Number(1f64), Number(2f64), Number(3f64)]),
|
||||
);
|
||||
map.insert(String::from("d"), JSONValue::String(String::from("foo")));
|
||||
map.insert(String::from("d"), JSONValue::from("foo"));
|
||||
map.insert(String::from("e"), Object(emap));
|
||||
|
||||
map.insert(
|
||||
String::from("i"),
|
||||
Array(vec![
|
||||
JSONValue::String(String::from("\"")),
|
||||
JSONValue::String(String::from("\\")),
|
||||
JSONValue::String(String::from("/")),
|
||||
JSONValue::String(String::from("\u{8}")),
|
||||
JSONValue::String(String::from("\x0C")),
|
||||
JSONValue::String(String::from("\n")),
|
||||
JSONValue::String(String::from("\r")),
|
||||
JSONValue::String(String::from("\t")),
|
||||
JSONValue::String(String::from("\u{1}")),
|
||||
JSONValue::String(String::from("\u{face}")),
|
||||
JSONValue::from("\""),
|
||||
JSONValue::from("\\"),
|
||||
JSONValue::from("/"),
|
||||
JSONValue::from("\u{8}"),
|
||||
JSONValue::from("\x0C"),
|
||||
JSONValue::from("\n"),
|
||||
JSONValue::from("\r"),
|
||||
JSONValue::from("\t"),
|
||||
JSONValue::from("\u{1}"),
|
||||
JSONValue::from("\u{face}"),
|
||||
]),
|
||||
);
|
||||
|
||||
assert!(result.is_ok(), format!("{:#?}", result));
|
||||
let result_map = result.unwrap().unwrap_array()[0].clone().unwrap_object();
|
||||
|
||||
let outer_array: Vec<JSONValue> = result.unwrap().unwrap();
|
||||
let result_map: JSONMap = outer_array[0].clone().unwrap();
|
||||
|
||||
for (k, v) in &map {
|
||||
assert_eq!(
|
||||
|
Loading…
Reference in New Issue
Block a user