From cefaa86ded31a1ec465513e88a1bc9b74118a184 Mon Sep 17 00:00:00 2001 From: Timothy Warren Date: Wed, 12 Feb 2020 13:33:20 -0500 Subject: [PATCH] Use TryFrom trait instead of From trait for type conversions from JSONValue --- src/lib.rs | 99 ++++++++++++++++++++++++++------------------ tests/happy_paths.rs | 17 ++++---- 2 files changed, 69 insertions(+), 47 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1d283e8..41d5f03 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,7 +7,8 @@ //! //! Basic usage: //! ```rust -//! use naive_json_parser::{JSON, JSONArray}; +//! use std::convert::TryFrom; +//! use naive_json_parser::{JSON, JSONArray, JSONValue}; //! //! // Convert the JSON string to a `JSONValue` //! let result = JSON::parse("[0, 1, 2]"); @@ -17,13 +18,23 @@ //! let result = result.unwrap(); //! //! // If you want the value inside of the top `JSONValue`, you -//! // may use the `into` or `unwrap` methods -//! let array: JSONArray = result.clone().into(); // or -//! let array: JSONArray = result.clone().unwrap(); // or -//! let array = JSONArray::from(result.clone()); +//! // may use the `unwrap` method +//! let array: JSONArray = result.clone().unwrap(); +//! +//! // You may also try the type conversion directly, so you can handle a potential error +//! let array = match JSONArray::try_from(result.clone()) { +//! Ok(a) => a, +//! Err(_) => todo!(), +//! }; +//! +//! // If you want to create a `JSONValue` from one of its wrapped types, you +//! // may use the `from` or `into` methods +//! let json_array = JSONValue::from(array.clone()); // or +//! let json_array: JSONValue = array.clone().into(); //! ``` #![forbid(unsafe_code)] use std::collections::HashMap; +use std::convert::TryFrom; use std::iter::FromIterator; use std::{char, u16}; @@ -79,63 +90,71 @@ impl JSONValue { /// /// # assert_eq!(str, &s); /// ``` - pub fn unwrap>(self) -> T { - T::from(self) + pub fn unwrap>(self) -> T { + match T::try_from(self) { + Ok(val) => val, + Err(_) => panic!("Tried to unwrap an empty value") + } } } -impl From for JSONMap { +impl TryFrom for JSONMap { + type Error = &'static str; + /// Extracts the `HashMap` in the `JSONValue` enum, if it exists. - /// Otherwise, panics. - fn from(val: JSONValue) -> JSONMap { - match val { - JSONValue::Object(o) => o, - _ => unreachable!(), + fn try_from(v: JSONValue) -> Result { + match v { + JSONValue::Object(o) => Ok(o), + _ => Err("Invalid type conversion") } } } -impl From for JSONArray { +impl TryFrom for JSONArray { + type Error = &'static str; + /// Extracts the `Vec` in the `JSONValue` enum, if it exists. - /// Otherwise, panics. - fn from(val: JSONValue) -> JSONArray { - match val { - JSONValue::Array(a) => a, - _ => unreachable!(), + fn try_from(v: JSONValue) -> Result { + match v { + JSONValue::Array(a) => Ok(a), + _ => Err("Invalid type conversion") } } } -impl From for f64 { +impl TryFrom for f64 { + type Error = &'static str; + /// Extracts the `f64` in the `JSONValue` enum, if it exists. - /// Otherwise, panics. - fn from(val: JSONValue) -> f64 { - match val { - JSONValue::Number(n) => n, - _ => unreachable!(), + fn try_from(v: JSONValue) -> Result { + match v { + JSONValue::Number(n) => Ok(n), + _ => Err("Invalid type conversion") } } } -impl From for String { +impl TryFrom for String { + type Error = &'static str; + /// Extracts the `String` in the `JSONValue` enum, if it exists. - /// Otherwise, panics. - fn from(val: JSONValue) -> String { - match val { - JSONValue::String(s) => s, - _ => unreachable!(), + fn try_from(v: JSONValue) -> Result { + match v { + JSONValue::String(s) => Ok(s), + _ => Err("Invalid type conversion") } } } -impl From 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, - _ => unreachable!(), +impl TryFrom for bool { + type Error = &'static str; + + /// Extracts the `bool` in the `JSONValue` enum, if it exists. + fn try_from(v: JSONValue) -> Result { + match v { + JSONValue::True => Ok(true), + JSONValue::False => Ok(false), + _ => Err("Invalid type conversion") } } } @@ -320,7 +339,7 @@ impl JSON { self.skip_whitespace(); self.eat(':')?; - let key = maybe_key.unwrap().into(); + let key = maybe_key.unwrap().unwrap(); let value = self.parse_value()?; result.insert(key, value); diff --git a/tests/happy_paths.rs b/tests/happy_paths.rs index d72df22..8e6df64 100644 --- a/tests/happy_paths.rs +++ b/tests/happy_paths.rs @@ -1,10 +1,11 @@ use naive_json_parser::JSONValue::{Array, False, Null, Number, Object, True}; use naive_json_parser::*; use std::collections::HashMap; +use std::convert::TryFrom; use std::f64::consts::PI; #[test] -fn value_conversion() { +fn value_conversion() -> Result<(), &'static str> { let map: JSONMap = HashMap::new(); let num = 9.380831539; let str = "applesauce"; @@ -14,13 +15,15 @@ fn value_conversion() { JSONValue::from(str), ]; - 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!(map.clone(), JSONMap::try_from(JSONValue::from(map.clone()))?); + assert_eq!(num, f64::try_from(JSONValue::from(num))?); + assert_eq!(String::from(str), String::try_from(JSONValue::from(str))?); + assert_eq!(arr.clone(), JSONArray::try_from(JSONValue::from(arr.clone()))?); + assert_eq!(true, bool::try_from(JSONValue::from(true))?); + assert_eq!(false, bool::try_from(JSONValue::from(false))?); assert_eq!((), <()>::from(JSONValue::from(()))); + + Ok(()) } #[test]