From f7f18cf977f8f67d6bff563dc4d3ce27e7aa9306 Mon Sep 17 00:00:00 2001 From: Patrick Hayes Date: Wed, 2 Nov 2022 12:18:37 -0700 Subject: [PATCH 1/2] TryInto impls for Value --- src/value/mod.rs | 1 + src/value/try_into.rs | 161 ++++++++++++++++++++++++++++++++++++++++++ tests/test.rs | 87 +++++++++++++++++++++++ 3 files changed, 249 insertions(+) create mode 100644 src/value/try_into.rs diff --git a/src/value/mod.rs b/src/value/mod.rs index c467df6cc..0a589195a 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -880,6 +880,7 @@ mod from; mod index; mod partial_eq; mod ser; +mod try_into; /// Convert a `T` into `serde_json::Value` which is an enum that can represent /// any valid JSON data. diff --git a/src/value/try_into.rs b/src/value/try_into.rs new file mode 100644 index 000000000..e1e83179d --- /dev/null +++ b/src/value/try_into.rs @@ -0,0 +1,161 @@ +use core::convert::TryInto; +use crate::map::Map; +use crate::error::Error; +use super::Value; + +impl TryInto for Value { + type Error = Error; + + fn try_into(self) -> Result { + match self { + Value::String(s) => Ok(s), + _ => Err(::custom("value is not a string")), + } + } +} + +impl<'a> TryInto<&'a String> for &'a Value { + type Error = Error; + + fn try_into(self) -> Result<&'a String, Error> { + match self { + Value::String(s) => Ok(s), + _ => Err(::custom("value is not a string")), + } + } +} + +impl<'a> TryInto<&'a str> for &'a Value { + type Error = Error; + + fn try_into(self) -> Result<&'a str, Error> { + self.as_str().ok_or_else(|| ::custom("value is not a string")) + } +} + +impl TryInto for Value { + type Error = Error; + + fn try_into(self) -> Result { + self.as_f64().ok_or_else(|| ::custom("value is not an f64")) + } +} + + +impl TryInto for &Value { + type Error = Error; + + fn try_into(self) -> Result { + self.as_f64().ok_or_else(|| ::custom("value is not an f64")) + } +} + +impl TryInto for Value { + type Error = Error; + + fn try_into(self) -> Result { + self.as_i64().ok_or_else(|| ::custom("value is not an i64")) + } +} + +impl TryInto for &Value { + type Error = Error; + + fn try_into(self) -> Result { + self.as_i64().ok_or_else(|| ::custom("value is not an i64")) + } +} + +impl TryInto for Value { + type Error = Error; + + fn try_into(self) -> Result { + self.as_u64().ok_or_else(|| ::custom("value is not an u64")) + } +} + +impl TryInto for &Value { + type Error = Error; + + fn try_into(self) -> Result { + self.as_u64().ok_or_else(|| ::custom("value is not an u64")) + } +} + +impl TryInto for Value { + type Error = Error; + + fn try_into(self) -> Result { + self.as_bool().ok_or_else(|| ::custom("value is not a bool")) + } +} + +impl TryInto for &Value { + type Error = Error; + + fn try_into(self) -> Result { + self.as_bool().ok_or_else(|| ::custom("value is not a bool")) + } +} + +impl<'a> TryInto<&'a Vec> for &'a Value { + type Error = Error; + + fn try_into(self) -> Result<&'a Vec, Error> { + self.as_array().ok_or_else(|| ::custom("value is not an array")) + } +} + +impl<'a> TryInto<&'a [Value]> for &'a Value { + type Error = Error; + + fn try_into(self) -> Result<&'a [Value], Error> { + self.as_array().map(|v| v.as_slice()).ok_or_else(|| ::custom("value is not an array")) + } +} + +impl TryInto> for Value { + type Error = Error; + + fn try_into(self) -> Result, Error> { + match self { + Value::Array(a) => Ok(a), + _ => Err(::custom("value is not an array")), + } + } +} + +impl TryInto> for Value { + type Error = Error; + + fn try_into(self) -> Result, Error> { + match self { + Value::Object(o) => Ok(o), + _ => Err(::custom("value is not an object")), + } + } +} + +impl<'a> TryInto<&'a Map> for &'a Value { + type Error = Error; + + fn try_into(self) -> Result<&'a Map, Error> { + self.as_object().ok_or_else(|| ::custom("value is not an object")) + } +} + +impl TryInto<()> for Value { + type Error = Error; + + fn try_into(self) -> Result<(), Error> { + self.as_null().ok_or_else(|| ::custom("value is not a null")) + } +} + +impl TryInto<()> for &Value { + type Error = Error; + + fn try_into(self) -> Result<(), Error> { + self.as_null().ok_or_else(|| ::custom("value is not a null")) + } +} diff --git a/tests/test.rs b/tests/test.rs index 0c0f00e8b..842fac7b1 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -2060,6 +2060,93 @@ fn test_partialeq_bool() { assert_ne!(v, 0); } +#[test] +fn test_tryinto_string() { + use std::convert::TryInto; + let v = to_value("42").unwrap(); + let s: &String = (&v).try_into().unwrap(); + assert_eq!(s, "42"); + let s: &str = (&v).try_into().unwrap(); + assert_eq!(s, "42"); + let s: String = v.clone().try_into().unwrap(); + assert_eq!(s, "42"); +} + +#[test] +fn test_tryinto_number() { + use std::convert::TryInto; + let v = to_value(42).unwrap(); + let n: u64 = (&v).try_into().unwrap(); + assert_eq!(n, 42); + let n: i64 = (&v).try_into().unwrap(); + assert_eq!(n, 42); + let n: f64 = (&v).try_into().unwrap(); + assert_eq!(n, 42.0); + let n: u64 = v.clone().try_into().unwrap(); + assert_eq!(n, 42); + let n: i64 = v.clone().try_into().unwrap(); + assert_eq!(n, 42); + let n: f64 = v.clone().try_into().unwrap(); + assert_eq!(n, 42.0); +} + +#[test] +fn test_tryinto_bool() { + use std::convert::TryInto; + let v = to_value(true).unwrap(); + let b: bool = (&v).try_into().unwrap(); + assert_eq!(b, true); + let b: bool = v.try_into().unwrap(); + assert_eq!(b, true); + + let v = to_value(false).unwrap(); + let b: bool = (&v).try_into().unwrap(); + assert_eq!(b, false); + let b: bool = v.try_into().unwrap(); + assert_eq!(b, false); +} + +#[test] +fn test_tryinto_null() { + use std::convert::TryInto; + let v = to_value(()).unwrap(); + let n: () = v.try_into().unwrap(); + assert_eq!(n, ()); +} + +#[test] +fn test_tryinto_array() { + use std::convert::TryInto; + + let v = json!([1, 2, 3]); + let a: &Vec = (&v).try_into().unwrap(); + assert_eq!(a, &vec![1, 2, 3]); + let a: &[Value] = (&v).try_into().unwrap(); + assert_eq!(a, vec![1, 2, 3]); + let a: Vec = v.try_into().unwrap(); + assert_eq!(a, vec![1, 2, 3]); + + let v = json!([1, 2.0, "foo", { "bar": "baz"}, null, false]); + let _a: &Vec = (&v).try_into().unwrap(); + let _a: &[Value] = (&v).try_into().unwrap(); + let _a: Vec = v.try_into().unwrap(); +} + +#[test] +fn test_tryinto_object() { + use std::convert::TryInto; + use serde_json::map::Map; + + let mut map = Map::new(); + map.insert("foo".to_string(), json!("bar")); + + let v = json!({"foo": "bar"}); + let m: &Map = (&v).try_into().unwrap(); + assert_eq!(m, &map); + let m: Map = v.try_into().unwrap(); + assert_eq!(m, map); +} + struct FailReader(io::ErrorKind); impl io::Read for FailReader { From 55ff89bcc56d2ef45b63f32d0565fe4ee54d510a Mon Sep 17 00:00:00 2001 From: Patrick Hayes Date: Wed, 2 Nov 2022 13:03:17 -0700 Subject: [PATCH 2/2] Make sure use of alloc is explicit in TryInto impls for Value --- src/value/try_into.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/value/try_into.rs b/src/value/try_into.rs index e1e83179d..5114364a6 100644 --- a/src/value/try_into.rs +++ b/src/value/try_into.rs @@ -1,4 +1,6 @@ use core::convert::TryInto; +use alloc::string::String; +use alloc::vec::Vec; use crate::map::Map; use crate::error::Error; use super::Value;