-
Notifications
You must be signed in to change notification settings - Fork 579
Add as_borrow() generic method for Value #153
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -48,7 +48,7 @@ use num_traits::NumCast; | |
use serde::de; | ||
use serde::ser; | ||
|
||
use error::{Error, ErrorCode}; | ||
use error::{Error, ErrorCode, TypeError}; | ||
|
||
/// Represents a key/value type. | ||
#[cfg(not(feature = "preserve_order"))] | ||
|
@@ -215,6 +215,92 @@ impl Value { | |
} | ||
} | ||
|
||
/// If the `Value` is of type `T`, returns borrow of internal value. | ||
/// Returns Err otherwise. | ||
/// | ||
/// Useful for generics with larger/allocated types to speed things up. | ||
/// | ||
/// # Example | ||
/// | ||
/// ```rust | ||
/// extern crate serde_json; | ||
/// | ||
/// use serde_json::{to_value,Value}; | ||
/// use serde_json::value::ValueType; | ||
/// use std::collections::BTreeMap; | ||
/// | ||
/// fn main() { | ||
/// let null = to_value(()); | ||
/// let boolean = to_value(true); | ||
/// let int64 = to_value(-42i64); | ||
/// let uint64 = to_value(18446744073709551337u64); | ||
/// let float = to_value(3.1415926535f64); | ||
/// let string = to_value("Hello world!"); | ||
/// let array = to_value(&[1, 2, 3, 4, 5]); | ||
/// let mut map = BTreeMap::new(); | ||
/// map.insert("foo", to_value("bar")); | ||
/// let map = to_value(&map); | ||
/// | ||
/// let _: &() = null.as_borrow().unwrap(); | ||
/// let _: &bool = boolean.as_borrow().unwrap(); | ||
/// let _: &i64 = int64.as_borrow().unwrap(); | ||
/// let _: &u64 = uint64.as_borrow().unwrap(); | ||
/// let _: &f64 = float.as_borrow().unwrap(); | ||
/// let _: &str = string.as_borrow().unwrap(); | ||
/// let _: &String = string.as_borrow().unwrap(); | ||
/// let _: &[Value] = array.as_borrow().unwrap(); | ||
/// let _: &Vec<Value> = array.as_borrow().unwrap(); | ||
/// let _: &BTreeMap<String, Value> = map.as_borrow().unwrap(); | ||
/// } | ||
pub fn as_borrow<'a, T: TryBorrow<'a>>(&'a self) -> Result<T, TypeError> { | ||
T::try_borrow(self) | ||
} | ||
|
||
/// If the `Value` is of type `T`, returns mutable borrow of internal value. | ||
/// Returns Err otherwise. | ||
/// | ||
/// Useful for generics with larger/allocated types to speed things up. | ||
/// | ||
/// # Example | ||
/// | ||
/// ```rust | ||
/// extern crate serde_json; | ||
/// | ||
/// use serde_json::{to_value,Value}; | ||
/// use serde_json::value::ValueType; | ||
/// use std::collections::BTreeMap; | ||
/// | ||
/// fn main() { | ||
/// let mut null = to_value(()); | ||
/// let mut boolean = to_value(true); | ||
/// let mut int64 = to_value(-42i64); | ||
/// let mut uint64 = to_value(18446744073709551337u64); | ||
/// let mut float = to_value(3.1415926535f64); | ||
/// let mut string = to_value("Hello world!"); | ||
/// let mut array = to_value(&[1, 2, 3, 4, 5]); | ||
/// let mut map = BTreeMap::new(); | ||
/// map.insert("foo", to_value("bar")); | ||
/// let mut map = to_value(&map); | ||
/// | ||
/// let _: &mut () = null.as_borrow_mut().unwrap(); | ||
/// let _: &mut bool = boolean.as_borrow_mut().unwrap(); | ||
/// let _: &mut i64 = int64.as_borrow_mut().unwrap(); | ||
/// let _: &mut u64 = uint64.as_borrow_mut().unwrap(); | ||
/// let _: &mut f64 = float.as_borrow_mut().unwrap(); | ||
/// { | ||
/// let _: &mut str = string.as_borrow_mut().unwrap(); | ||
/// } | ||
/// let _: &mut String = string.as_borrow_mut().unwrap(); | ||
/// { | ||
/// let _: &mut [Value] = array.as_borrow_mut().unwrap(); | ||
/// } | ||
/// let _: &mut Vec<Value> = array.as_borrow_mut().unwrap(); | ||
/// let _: &mut BTreeMap<String, Value> = map.as_borrow_mut().unwrap(); | ||
/// } | ||
pub fn as_borrow_mut<'a, T: TryBorrowMut<'a>>(&'a mut self) -> Result<T, TypeError> { | ||
T::try_borrow_mut(self) | ||
} | ||
|
||
/// Returns true if the `Value` is an Object. Returns false otherwise. | ||
pub fn is_object(&self) -> bool { | ||
self.as_object().is_some() | ||
|
@@ -473,6 +559,99 @@ impl de::Deserialize for Value { | |
} | ||
} | ||
|
||
/// Represents type of a JSON value | ||
#[derive(Clone, Copy, Eq, PartialEq, Debug)] | ||
pub enum ValueType { | ||
/// Represents a JSON null value type | ||
Null, | ||
|
||
/// Represents a JSON Boolean type | ||
Bool, | ||
|
||
/// Represents a JSON signed integer type | ||
I64, | ||
|
||
/// Represents a JSON unsigned integer type | ||
U64, | ||
|
||
/// Represents a JSON floating point number type | ||
F64, | ||
|
||
/// Represents a JSON string type | ||
String, | ||
|
||
/// Represents a JSON array type | ||
Array, | ||
|
||
/// Represents a JSON object type | ||
Object, | ||
} | ||
|
||
impl ValueType { | ||
|
||
/// Returns type of a value | ||
/// | ||
/// # Example | ||
/// | ||
/// ```rust | ||
/// extern crate serde_json; | ||
/// | ||
/// use serde_json::to_value; | ||
/// use serde_json::value::ValueType; | ||
/// use std::collections::BTreeMap; | ||
/// | ||
/// fn main() { | ||
/// let null = to_value(()); | ||
/// let boolean = to_value(true); | ||
/// let int64 = to_value(-42i64); | ||
/// let uint64 = to_value(18446744073709551337u64); | ||
/// let float = to_value(3.1415926535f64); | ||
/// let string = to_value("Hello world!"); | ||
/// let array = to_value(&[1, 2, 3, 4, 5]); | ||
/// let mut map = BTreeMap::new(); | ||
/// map.insert("foo", to_value("bar")); | ||
/// let map = to_value(&map); | ||
/// | ||
/// assert_eq!(ValueType::of(&null), ValueType::Null); | ||
/// assert_eq!(ValueType::of(&boolean), ValueType::Bool); | ||
/// assert_eq!(ValueType::of(&int64), ValueType::I64); | ||
/// assert_eq!(ValueType::of(&uint64), ValueType::U64); | ||
/// assert_eq!(ValueType::of(&float), ValueType::F64); | ||
/// assert_eq!(ValueType::of(&string), ValueType::String); | ||
/// assert_eq!(ValueType::of(&array), ValueType::Array); | ||
/// assert_eq!(ValueType::of(&map), ValueType::Object); | ||
/// } | ||
pub fn of(val: &Value) -> Self { | ||
match *val { | ||
Value::Null => ValueType::Null, | ||
Value::Bool(_) => ValueType::Bool, | ||
Value::I64(_) => ValueType::I64, | ||
Value::U64(_) => ValueType::U64, | ||
Value::F64(_) => ValueType::F64, | ||
Value::String(_) => ValueType::String, | ||
Value::Array(_) => ValueType::Array, | ||
Value::Object(_) => ValueType::Object, | ||
} | ||
} | ||
} | ||
|
||
impl fmt::Display for ValueType { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { | ||
use self::ValueType::*; | ||
|
||
write!(f, "{}", match *self { | ||
Null => "Null", | ||
Bool => "Bool", | ||
I64 => "I64", | ||
U64 => "U64", | ||
F64 => "F64", | ||
String => "String", | ||
Array => "Array", | ||
Object => "Object", | ||
}) | ||
} | ||
} | ||
|
||
struct WriterFormatter<'a, 'b: 'a> { | ||
inner: &'a mut fmt::Formatter<'b>, | ||
} | ||
|
@@ -1333,3 +1512,139 @@ impl<T: ?Sized> ToJson for T | |
to_value(&self) | ||
} | ||
} | ||
|
||
/// Just like `std::borrow::Borrow<Value>` but borrowing may fail. | ||
pub trait TryBorrow<'a>: 'a + ::std::marker::Sized { | ||
/// Returns reference to internal data if type matches. | ||
fn try_borrow(value: &'a Value) -> Result<Self, TypeError>; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this trait could include the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The reason is to mimic However, I'm open to put them together, if it's possible and advantageous. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right, I forgot about that |
||
} | ||
|
||
impl<'a> TryBorrow<'a> for &'a () { | ||
fn try_borrow(value: &'a Value) -> Result<Self, TypeError> { | ||
static EMPTY: () = (); | ||
if value.is_null() { Ok(&EMPTY) } else { Err(TypeError { expected: ValueType::Null, provided: ValueType::of(value) }) } | ||
} | ||
} | ||
|
||
impl<'a> TryBorrow<'a> for &'a bool { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. all these impls should be abstracted away into a macro There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was thinking about it too lately. I'm not sure how to define the macro to be readable enough. I was thinking about something like this:
What do you think? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds good to me |
||
fn try_borrow(value: &'a Value) -> Result<Self, TypeError> { | ||
if let &Value::Bool(ref val) = value { Ok(val) } else { Err(TypeError { expected: ValueType::Bool, provided: ValueType::of(value) }) } | ||
} | ||
} | ||
|
||
impl<'a> TryBorrow<'a> for &'a u64 { | ||
fn try_borrow(value: &'a Value) -> Result<Self, TypeError> { | ||
if let &Value::U64(ref val) = value { Ok(val) } else { Err(TypeError { expected: ValueType::U64, provided: ValueType::of(value) }) } | ||
} | ||
} | ||
|
||
impl<'a> TryBorrow<'a> for &'a i64 { | ||
fn try_borrow(value: &'a Value) -> Result<Self, TypeError> { | ||
if let &Value::I64(ref val) = value { Ok(val) } else { Err(TypeError { expected: ValueType::I64, provided: ValueType::of(value) }) } | ||
} | ||
} | ||
|
||
impl<'a> TryBorrow<'a> for &'a f64 { | ||
fn try_borrow(value: &'a Value) -> Result<Self, TypeError> { | ||
if let &Value::F64(ref val) = value { Ok(val) } else { Err(TypeError { expected: ValueType::F64, provided: ValueType::of(value) }) } | ||
} | ||
} | ||
|
||
impl<'a> TryBorrow<'a> for &'a str { | ||
fn try_borrow(value: &'a Value) -> Result<Self, TypeError> { | ||
if let &Value::String(ref val) = value { Ok(val) } else { Err(TypeError { expected: ValueType::String, provided: ValueType::of(value) }) } | ||
} | ||
} | ||
|
||
impl<'a> TryBorrow<'a> for &'a String { | ||
fn try_borrow(value: &'a Value) -> Result<Self, TypeError> { | ||
if let &Value::String(ref val) = value { Ok(val) } else { Err(TypeError { expected: ValueType::String, provided: ValueType::of(value) }) } | ||
} | ||
} | ||
|
||
impl<'a> TryBorrow<'a> for &'a [Value] { | ||
fn try_borrow(value: &'a Value) -> Result<Self, TypeError> { | ||
if let &Value::Array(ref val) = value { Ok(val) } else { Err(TypeError { expected: ValueType::Array, provided: ValueType::of(value) }) } | ||
} | ||
} | ||
|
||
impl<'a> TryBorrow<'a> for &'a Vec<Value> { | ||
fn try_borrow(value: &'a Value) -> Result<Self, TypeError> { | ||
if let &Value::Array(ref val) = value { Ok(val) } else { Err(TypeError { expected: ValueType::Array, provided: ValueType::of(value) }) } | ||
} | ||
} | ||
|
||
impl<'a> TryBorrow<'a> for &'a Map<String, Value> { | ||
fn try_borrow(value: &'a Value) -> Result<Self, TypeError> { | ||
if let &Value::Object(ref val) = value { Ok(val) } else { Err(TypeError { expected: ValueType::Object, provided: ValueType::of(value) }) } | ||
} | ||
} | ||
|
||
/// Just like `std::borrow::BorrowMut` but borrowing may fail. | ||
pub trait TryBorrowMut<'a>: 'a + ::std::marker::Sized { | ||
/// Returns mutable reference to internal data if type matches. | ||
fn try_borrow_mut(value: &'a mut Value) -> Result<Self, TypeError>; | ||
} | ||
|
||
impl<'a> TryBorrowMut<'a> for &'a mut () { | ||
fn try_borrow_mut(value: &'a mut Value) -> Result<Self, TypeError> { | ||
static mut EMPTY: () = (); | ||
// Use of static &mut () is always safe, because it's value actually can't be changed | ||
if value.is_null() { unsafe { Ok(&mut EMPTY) } } else { Err(TypeError { expected: ValueType::Null, provided: ValueType::of(value) }) } | ||
} | ||
} | ||
|
||
impl<'a> TryBorrowMut<'a> for &'a mut bool { | ||
fn try_borrow_mut(value: &'a mut Value) -> Result<Self, TypeError> { | ||
if let &mut Value::Bool(ref mut val) = value { Ok(val) } else { Err(TypeError { expected: ValueType::Bool, provided: ValueType::of(value) }) } | ||
} | ||
} | ||
|
||
impl<'a> TryBorrowMut<'a> for &'a mut u64 { | ||
fn try_borrow_mut(value: &'a mut Value) -> Result<Self, TypeError> { | ||
if let &mut Value::U64(ref mut val) = value { Ok(val) } else { Err(TypeError { expected: ValueType::U64, provided: ValueType::of(value) }) } | ||
} | ||
} | ||
|
||
impl<'a> TryBorrowMut<'a> for &'a mut i64 { | ||
fn try_borrow_mut(value: &'a mut Value) -> Result<Self, TypeError> { | ||
if let &mut Value::I64(ref mut val) = value { Ok(val) } else { Err(TypeError { expected: ValueType::I64, provided: ValueType::of(value) }) } | ||
} | ||
} | ||
|
||
impl<'a> TryBorrowMut<'a> for &'a mut f64 { | ||
fn try_borrow_mut(value: &'a mut Value) -> Result<Self, TypeError> { | ||
if let &mut Value::F64(ref mut val) = value { Ok(val) } else { Err(TypeError { expected: ValueType::F64, provided: ValueType::of(value) }) } | ||
} | ||
} | ||
|
||
impl<'a> TryBorrowMut<'a> for &'a mut str { | ||
fn try_borrow_mut(value: &'a mut Value) -> Result<Self, TypeError> { | ||
if let &mut Value::String(ref mut val) = value { Ok(val) } else { Err(TypeError { expected: ValueType::String, provided: ValueType::of(value) }) } | ||
} | ||
} | ||
|
||
impl<'a> TryBorrowMut<'a> for &'a mut String { | ||
fn try_borrow_mut(value: &'a mut Value) -> Result<Self, TypeError> { | ||
if let &mut Value::String(ref mut val) = value { Ok(val) } else { Err(TypeError { expected: ValueType::String, provided: ValueType::of(value) }) } | ||
} | ||
} | ||
|
||
impl<'a> TryBorrowMut<'a> for &'a mut [Value] { | ||
fn try_borrow_mut(value: &'a mut Value) -> Result<Self, TypeError> { | ||
if let &mut Value::Array(ref mut val) = value { Ok(val) } else { Err(TypeError { expected: ValueType::Array, provided: ValueType::of(value) }) } | ||
} | ||
} | ||
|
||
impl<'a> TryBorrowMut<'a> for &'a mut Vec<Value> { | ||
fn try_borrow_mut(value: &'a mut Value) -> Result<Self, TypeError> { | ||
if let &mut Value::Array(ref mut val) = value { Ok(val) } else { Err(TypeError { expected: ValueType::Array, provided: ValueType::of(value) }) } | ||
} | ||
} | ||
|
||
impl<'a> TryBorrowMut<'a> for &'a mut Map<String, Value> { | ||
fn try_borrow_mut(value: &'a mut Value) -> Result<Self, TypeError> { | ||
if let &mut Value::Object(ref mut val) = value { Ok(val) } else { Err(TypeError { expected: ValueType::Object, provided: ValueType::of(value) }) } | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This type sounds a lot like
std::mem::discriminant_value
, so we could just wait until that is stable (3 Months probably)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. The library is still unstable, so we can wait. I hope time is not concern to anyone else.
I think I'm going to leave this as is for now until discriminant is stabilized. However, I'm not sure whether discriminant will allow to do exactly what I mean. I will check that out.