Skip to content

Commit 95e24be

Browse files
authored
Merge pull request #47 from csnover/circus-of-values
Improve API consistency and flexibility
2 parents d077b71 + 9707f00 commit 95e24be

File tree

9 files changed

+307
-93
lines changed

9 files changed

+307
-93
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ categories = ["parsing", "parser-implementations", "encoding"]
44
authors = ["Pascal Seitz <pascal.seitz@gmail.com>"]
55
description = "Provides JSON deserialization into a borrowed DOM"
66
version = "0.9.0"
7-
edition = "2021"
7+
edition = "2024"
88
license = "MIT"
99
keywords = ["JSON", "serde", "deserialization", "ref", "borrowed"]
1010
exclude = ["benches/**/*.json"]

benches/bench.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -190,11 +190,11 @@ fn access_json_borrowed(el: &OwnedValue, access: &[&[&str]]) -> usize {
190190
let mut total_size = 0;
191191
for access in access {
192192
// walk the access keys until the end. return 0 if value does not exist
193-
let mut val = el.get_value();
193+
let mut val = Some(el.get_value());
194194
for key in *access {
195-
val = val.get(*key);
195+
val = val.and_then(|v| v.get(key));
196196
}
197-
if let Some(v) = val.as_str() {
197+
if let Some(val) = val && let Some(v) = val.as_str() {
198198
total_size += v.len();
199199
}
200200
}

src/cowstr.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,32 @@ impl<'a> From<&'a str> for CowStr<'a> {
3131
}
3232
}
3333

34+
impl From<String> for CowStr<'_> {
35+
fn from(s: String) -> Self {
36+
Self(Cow::Owned(s))
37+
}
38+
}
39+
40+
impl<'a> From<Cow<'a, str>> for CowStr<'a> {
41+
fn from(s: Cow<'a, str>) -> Self {
42+
Self(s)
43+
}
44+
}
45+
3446
impl<'a> From<CowStr<'a>> for Cow<'a, str> {
3547
fn from(s: CowStr<'a>) -> Self {
3648
s.0
3749
}
3850
}
51+
52+
#[cfg(test)]
53+
mod tests {
54+
use super::*;
55+
56+
#[test]
57+
fn from() {
58+
assert_eq!(CowStr::from("text"), "text");
59+
assert_eq!(CowStr::from(Cow::Borrowed("text")), "text");
60+
assert_eq!(CowStr::from(String::from("text")), "text");
61+
}
62+
}

src/de.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -175,14 +175,14 @@ mod tests {
175175
"#;
176176

177177
let val: Value = serde_json::from_str(json_obj).unwrap();
178-
assert_eq!(val.get("bool"), &Value::Bool(true));
178+
assert_eq!(val.get("bool"), Some(&Value::Bool(true)));
179179
assert_eq!(
180180
val.get("string_key"),
181-
&Value::Str(Cow::Borrowed("string_val"))
181+
Some(&Value::Str(Cow::Borrowed("string_val")))
182182
);
183-
assert_eq!(val.get("float"), &Value::Number(1.23.into()));
184-
assert_eq!(val.get("i64"), &Value::Number((-123i64).into()));
185-
assert_eq!(val.get("u64"), &Value::Number(123u64.into()));
183+
assert_eq!(val.get("float"), Some(&Value::Number(1.23.into())));
184+
assert_eq!(val.get("i64"), Some(&Value::Number((-123i64).into())));
185+
assert_eq!(val.get("u64"), Some(&Value::Number(123u64.into())));
186186
}
187187

188188
#[test]
@@ -196,10 +196,10 @@ mod tests {
196196
"#;
197197

198198
let val: Value = serde_json::from_str(json_obj).unwrap();
199-
assert_eq!(val.get("bool"), &Value::Bool(true));
199+
assert_eq!(val.get("bool"), Some(&Value::Bool(true)));
200200
assert_eq!(
201201
val.get("string_key"),
202-
&Value::Str(Cow::Borrowed("string\"_val"))
202+
Some(&Value::Str(Cow::Borrowed("string\"_val")))
203203
);
204204
}
205205
}

src/index.rs

Lines changed: 85 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ use super::Value;
1313
/// # Examples
1414
///
1515
/// ```
16+
/// # use std::borrow::Cow;
1617
/// # use serde_json_borrow::Value;
17-
/// #
1818
/// let json_obj = r#"
1919
/// {
2020
/// "x": {
@@ -24,36 +24,106 @@ use super::Value;
2424
/// "#;
2525
///
2626
/// let data: Value = serde_json::from_str(json_obj).unwrap();
27+
/// let y = data.get("x").unwrap().get("y").unwrap();
28+
/// assert_eq!(y.get(0), Some(&Value::Str(Cow::Borrowed("z"))));
29+
/// assert_eq!(y.get(1), Some(&Value::Str(Cow::Borrowed("zz"))));
30+
/// assert_eq!(y.get(2), None);
2731
///
28-
/// assert_eq!(data.get("x").get("y").get(0), &Value::Str(std::borrow::Cow::Borrowed("z")));
29-
/// assert_eq!(data.get("x").get("y").get(1), &Value::Str(std::borrow::Cow::Borrowed("zz")));
30-
/// assert_eq!(data.get("x").get("y").get(2), &Value::Null);
31-
///
32-
/// assert_eq!(data.get("a"), &Value::Null);
33-
/// assert_eq!(data.get("a").get("b"), &Value::Null);
32+
/// assert_eq!(data.get("a"), None);
3433
/// ```
35-
pub trait Index<'v> {
34+
pub trait Index: private::Sealed {
3635
/// Return None if the key is not already in the array or object.
3736
#[doc(hidden)]
38-
fn index_into(self, v: &'v Value<'v>) -> Option<&'v Value<'v>>;
37+
fn index_into<'a, 'ctx: 'a>(&self, v: &'a Value<'ctx>) -> Option<&'a Value<'ctx>>;
3938
}
4039

41-
impl<'v> Index<'v> for usize {
40+
impl Index for usize {
4241
#[inline]
43-
fn index_into(self, v: &'v Value<'v>) -> Option<&'v Value<'v>> {
42+
fn index_into<'a, 'ctx: 'a>(&self, v: &'a Value<'ctx>) -> Option<&'a Value<'ctx>> {
4443
match v {
45-
Value::Array(vec) => vec.get(self),
44+
Value::Array(vec) => vec.get(*self),
4645
_ => None,
4746
}
4847
}
4948
}
5049

51-
impl<'v, 'a: 'v> Index<'v> for &'a str {
50+
impl Index for str {
5251
#[inline]
53-
fn index_into(self, v: &'v Value<'v>) -> Option<&'v Value<'v>> {
52+
fn index_into<'a, 'ctx: 'a>(&self, v: &'a Value<'ctx>) -> Option<&'a Value<'ctx>> {
5453
match v {
55-
Value::Object(map) => map.iter().find(|(k, _v)| k == &self).map(|(_k, v)| v),
54+
Value::Object(map) => map.iter().find(|(k, _v)| *k == self).map(|(_k, v)| v),
5655
_ => None,
5756
}
5857
}
5958
}
59+
60+
impl Index for String {
61+
#[inline]
62+
fn index_into<'a, 'ctx: 'a>(&self, v: &'a Value<'ctx>) -> Option<&'a Value<'ctx>> {
63+
self.as_str().index_into(v)
64+
}
65+
}
66+
67+
#[cfg(feature = "cowkeys")]
68+
impl Index for std::borrow::Cow<'_, str> {
69+
#[inline]
70+
fn index_into<'a, 'ctx: 'a>(&self, v: &'a Value<'ctx>) -> Option<&'a Value<'ctx>> {
71+
(**self).index_into(v)
72+
}
73+
}
74+
75+
impl<T> Index for &T where T: Index + ?Sized {
76+
fn index_into<'a, 'ctx: 'a>(&self, v: &'a Value<'ctx>) -> Option<&'a Value<'ctx>> {
77+
(**self).index_into(v)
78+
}
79+
}
80+
81+
mod private {
82+
pub trait Sealed {}
83+
impl Sealed for usize {}
84+
impl Sealed for str {}
85+
impl Sealed for String {}
86+
#[cfg(feature = "cowkeys")]
87+
impl Sealed for std::borrow::Cow<'_, str> {}
88+
impl<T> Sealed for &T where T: ?Sized + Sealed {}
89+
}
90+
91+
#[cfg(test)]
92+
mod tests {
93+
use super::*;
94+
95+
#[cfg(feature = "cowkeys")]
96+
#[test]
97+
fn index_cow() {
98+
use std::borrow::Cow;
99+
100+
let key = Cow::Borrowed("key");
101+
let value = Value::Object(
102+
vec![(&*key, Value::Str("value".into()))].into()
103+
);
104+
assert_eq!(value.get(&key).and_then(Value::as_str), Some("value"));
105+
assert_eq!(value.get(Cow::Owned("key".into())).and_then(Value::as_str), Some("value"));
106+
}
107+
108+
#[test]
109+
fn index_lifetime() {
110+
fn get_str<'a>(v: &'a Value<'_>, k: &str) -> Option<&'a str> {
111+
v.get(k).and_then(Value::as_str)
112+
}
113+
let key = String::from("key");
114+
let value = Value::Object(
115+
vec![(key.as_str(), Value::Str("value".into()))].into()
116+
);
117+
assert_eq!(get_str(&value, &key), Some("value"));
118+
}
119+
120+
#[test]
121+
fn index_string() {
122+
let key = String::from("key");
123+
let value = Value::Object(
124+
vec![(key.as_str(), Value::Str("value".into()))].into()
125+
);
126+
assert_eq!(value.get(&key).and_then(Value::as_str), Some("value"));
127+
assert_eq!(value.get(key.clone()).and_then(Value::as_str), Some("value"));
128+
}
129+
}

src/lib.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,12 @@
3232
//! let data: &str = r#"{"bool": true, "key": "123"}"#;
3333
//! // Note that serde_json_borrow::Value<'ctx> is tied to the lifetime of data.
3434
//! let value: serde_json_borrow::Value = serde_json::from_str(&data)?;
35-
//! assert_eq!(value.get("bool"), &serde_json_borrow::Value::Bool(true));
36-
//! assert_eq!(value.get("key"), &serde_json_borrow::Value::Str("123".into()));
35+
//! assert_eq!(value.get("bool"), Some(&serde_json_borrow::Value::Bool(true)));
36+
//! assert_eq!(value.get("key"), Some(&serde_json_borrow::Value::Str("123".into())));
3737
//! // Using OwnedValue will take ownership of the String.
3838
//! let value: serde_json_borrow::OwnedValue = serde_json_borrow::OwnedValue::from_str(&data)?;
39-
//! assert_eq!(value.get("bool"), &serde_json_borrow::Value::Bool(true));
40-
//! assert_eq!(value.get("key"), &serde_json_borrow::Value::Str("123".into()));
39+
//! assert_eq!(value.get("bool"), Some(&serde_json_borrow::Value::Bool(true)));
40+
//! assert_eq!(value.get("key"), Some(&serde_json_borrow::Value::Str("123".into())));
4141
//! Ok(())
4242
//! }
4343
//! ```

src/object_vec.rs

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,23 +25,34 @@ pub type KeyStrType<'a> = &'a str;
2525
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
2626
pub struct ObjectAsVec<'ctx>(pub(crate) Vec<(KeyStrType<'ctx>, Value<'ctx>)>);
2727

28-
#[cfg(feature = "cowkeys")]
29-
impl<'ctx> From<Vec<(&'ctx str, Value<'ctx>)>> for ObjectAsVec<'ctx> {
30-
fn from(vec: Vec<(&'ctx str, Value<'ctx>)>) -> Self {
28+
impl<'ctx, K, V> From<Vec<(K, V)>> for ObjectAsVec<'ctx>
29+
where
30+
K: Into<KeyStrType<'ctx>>,
31+
V: Into<Value<'ctx>>,
32+
{
33+
fn from(vec: Vec<(K, V)>) -> Self {
3134
Self::from_iter(vec)
3235
}
3336
}
3437

35-
#[cfg(not(feature = "cowkeys"))]
36-
impl<'ctx> From<Vec<(&'ctx str, Value<'ctx>)>> for ObjectAsVec<'ctx> {
37-
fn from(vec: Vec<(&'ctx str, Value<'ctx>)>) -> Self {
38-
Self(vec)
38+
impl<'ctx, K, V, const N: usize> From<[(K, V); N]> for ObjectAsVec<'ctx>
39+
where
40+
K: Into<KeyStrType<'ctx>>,
41+
V: Into<Value<'ctx>>
42+
{
43+
#[inline]
44+
fn from(value: [(K, V); N]) -> Self {
45+
Self::from_iter(value)
3946
}
4047
}
4148

42-
impl<'ctx> FromIterator<(&'ctx str, Value<'ctx>)> for ObjectAsVec<'ctx> {
43-
fn from_iter<T: IntoIterator<Item = (&'ctx str, Value<'ctx>)>>(iter: T) -> Self {
44-
Self(iter.into_iter().map(|(k, v)| (k.into(), v)).collect())
49+
impl<'ctx, K, V> FromIterator<(K, V)> for ObjectAsVec<'ctx>
50+
where
51+
K: Into<KeyStrType<'ctx>>,
52+
V: Into<Value<'ctx>>
53+
{
54+
fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
55+
Self(iter.into_iter().map(|(k, v)| (k.into(), v.into())).collect())
4556
}
4657
}
4758

@@ -212,14 +223,20 @@ impl<'ctx> ObjectAsVec<'ctx> {
212223
/// This operation is linear in the size of the Vec because it potentially requires iterating
213224
/// through all elements to find a matching key.
214225
#[inline]
215-
pub fn insert(&mut self, key: &'ctx str, value: Value<'ctx>) -> Option<Value<'ctx>> {
226+
pub fn insert<K, V>(&mut self, key: K, value: V) -> Option<Value<'ctx>>
227+
where
228+
K: Into<KeyStrType<'ctx>>,
229+
V: Into<Value<'ctx>>,
230+
{
231+
let key = key.into();
232+
let value = value.into();
216233
for (k, v) in &mut self.0 {
217234
if *k == key {
218235
return Some(std::mem::replace(v, value));
219236
}
220237
}
221238
// If the key is not found, push the new key-value pair to the end of the Vec
222-
self.0.push((key.into(), value));
239+
self.0.push((key, value));
223240
None
224241
}
225242

@@ -306,12 +323,26 @@ mod tests {
306323
assert_eq!(obj.len(), 0);
307324
}
308325

326+
#[test]
327+
fn test_initialization_from_array() {
328+
let obj = ObjectAsVec::from([
329+
("a", 0u64),
330+
("b", 1u64),
331+
("c", 2u64),
332+
]);
333+
334+
assert_eq!(obj.len(), 3);
335+
assert_eq!(obj.get("a"), Some(&Value::Number(0u64.into())));
336+
assert_eq!(obj.get("b"), Some(&Value::Number(1u64.into())));
337+
assert_eq!(obj.get("c"), Some(&Value::Number(2u64.into())));
338+
}
339+
309340
#[test]
310341
fn test_initialization_from_vec() {
311342
let obj = ObjectAsVec::from(vec![
312-
("a", Value::Number(0u64.into())),
313-
("b", Value::Number(1u64.into())),
314-
("c", Value::Number(2u64.into())),
343+
("a", 0u64),
344+
("b", 1u64),
345+
("c", 2u64),
315346
]);
316347

317348
assert_eq!(obj.len(), 3);
@@ -441,7 +472,7 @@ mod tests {
441472
fn test_insert_multiple_types() {
442473
let mut obj = ObjectAsVec::default();
443474
obj.insert("boolean", Value::Bool(true));
444-
obj.insert("number", Value::Number(3.14.into()));
475+
obj.insert("number", Value::Number(1.23.into()));
445476
obj.insert("string", Value::Str(Cow::Borrowed("Hello")));
446477
obj.insert("null", Value::Null);
447478

@@ -454,7 +485,7 @@ mod tests {
454485
);
455486
assert_eq!(obj.len(), 5);
456487
assert_eq!(obj.get("boolean"), Some(&Value::Bool(true)));
457-
assert_eq!(obj.get("number"), Some(&Value::Number(3.14.into())));
488+
assert_eq!(obj.get("number"), Some(&Value::Number(1.23.into())));
458489
assert_eq!(obj.get("string"), Some(&Value::Str(Cow::Borrowed("Hello"))));
459490
assert_eq!(obj.get("null"), Some(&Value::Null));
460491
assert_eq!(
@@ -516,7 +547,7 @@ mod tests {
516547
.unwrap();
517548

518549
// ensure that the found object matches the searched for object
519-
let (key, value) = obj.get_key_value_at(idx).unwrap();
550+
let (key, _) = obj.get_key_value_at(idx).unwrap();
520551
assert_eq!(key, "city");
521552
}
522553
}

0 commit comments

Comments
 (0)