Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ include = [
[badges]
maintenance = { status = "passively-maintained" }

[features]
ordered-map = ["dep:indexmap"]

[dependencies]
indexmap = { version = "2.4", optional = true }

[dev-dependencies]
walkdir = "2"
Expand Down
5 changes: 2 additions & 3 deletions bench/benches/generate.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use criterion::{criterion_group, criterion_main, Criterion};
use std::collections::HashMap;
use tinyjson::JsonValue;
use tinyjson::{JsonMap, JsonValue};

fn generate(c: &mut Criterion) {
c.bench_function("generate::string", |b| {
Expand Down Expand Up @@ -36,7 +35,7 @@ fn generate(c: &mut Criterion) {
});
});
c.bench_function("generate::object", |b| {
let mut kv = HashMap::new();
let mut kv = JsonMap::new();
kv.insert("num".into(), 123.45.into());
kv.insert("bool".into(), true.into());
kv.insert("str".into(), "this is test".to_string().into());
Expand Down
7 changes: 3 additions & 4 deletions src/generator.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use crate::JsonValue;
use std::collections::HashMap;
use crate::{JsonMap, JsonValue};
use std::fmt;
use std::io::{self, Write};

Expand Down Expand Up @@ -162,7 +161,7 @@ impl<'indent, W: Write> JsonGenerator<'indent, W> {
self.out.write_all(b"]")
}

fn encode_object(&mut self, m: &HashMap<String, JsonValue>) -> io::Result<()> {
fn encode_object(&mut self, m: &JsonMap) -> io::Result<()> {
self.out.write_all(b"{")?;
let mut first = true;
for (k, v) in m {
Expand Down Expand Up @@ -219,7 +218,7 @@ impl<'indent, W: Write> JsonGenerator<'indent, W> {

fn format_object(
&mut self,
m: &HashMap<String, JsonValue>,
m: &JsonMap,
indent: &str,
level: usize,
) -> io::Result<()> {
Expand Down
77 changes: 39 additions & 38 deletions src/json_value.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
use crate::generator::{format, stringify, JsonGenerateResult, JsonGenerator};
use crate::query::{JsonQuery, JsonQueryMut};
use std::collections::HashMap;
use std::convert::TryFrom;
use std::fmt;
use std::io;
use std::ops::{Index, IndexMut};

#[cfg(feature = "ordered-map")]
use indexmap::IndexMap;
#[cfg(not(feature = "ordered-map"))]
use std::collections::HashMap;

const NULL: () = ();

#[cfg(feature = "ordered-map")]
pub type JsonMap = IndexMap<String, JsonValue>;
#[cfg(not(feature = "ordered-map"))]
pub type JsonMap = HashMap<String, JsonValue>;


/// Enum to represent one JSON value. Each variant represents corresponding JSON types.
/// ```
/// use tinyjson::JsonValue;
Expand Down Expand Up @@ -42,7 +52,7 @@ pub enum JsonValue {
/// Array type value.
Array(Vec<JsonValue>),
/// Object type value.
Object(HashMap<String, JsonValue>),
Object(JsonMap),
}

/// Trait to access to inner value of `JsonValue` as reference.
Expand Down Expand Up @@ -71,7 +81,7 @@ impl_inner_ref!(bool, Boolean(b) => b);
impl_inner_ref!(String, String(s) => s);
impl_inner_ref!((), Null => &NULL);
impl_inner_ref!(Vec<JsonValue>, Array(a) => a);
impl_inner_ref!(HashMap<String, JsonValue>, Object(h) => h);
impl_inner_ref!(JsonMap, Object(h) => h);

/// Trait to access to inner value of `JsonValue` as mutable reference.
///
Expand All @@ -98,7 +108,7 @@ impl_inner_ref_mut!(f64, Number(n) => n);
impl_inner_ref_mut!(bool, Boolean(b) => b);
impl_inner_ref_mut!(String, String(s) => s);
impl_inner_ref_mut!(Vec<JsonValue>, Array(a) => a);
impl_inner_ref_mut!(HashMap<String, JsonValue>, Object(h) => h);
impl_inner_ref_mut!(JsonMap, Object(h) => h);

// Note: matches! is available from Rust 1.42
macro_rules! is_xxx {
Expand Down Expand Up @@ -241,16 +251,15 @@ impl JsonValue {
/// allows to write `if` guard if you use Rust 1.42.0 or later.
///
/// ```
/// use tinyjson::JsonValue;
/// use std::collections::HashMap;
/// use tinyjson::{JsonMap, JsonValue};
///
/// let v = JsonValue::from(HashMap::new());
/// let v = JsonValue::from(JsonMap::new());
/// assert!(v.is_object());
/// let v = JsonValue::from(vec![]);
/// assert!(!v.is_object());
///
/// // matches! macro may be better choice
/// let mut m = HashMap::new();
/// let mut m = JsonMap::new();
/// m.insert("hello".to_string(), "world".to_string().into());
/// let v = JsonValue::from(m);
/// assert!(matches!(&v, JsonValue::Object(o) if o.contains_key("hello")));
Expand Down Expand Up @@ -388,10 +397,9 @@ impl JsonValue {
/// Access the element value of the key of object.
///
/// ```
/// use tinyjson::JsonValue;
/// use std::collections::HashMap;
/// use tinyjson::{JsonMap, JsonValue};
///
/// let mut m = HashMap::new();
/// let mut m = JsonMap::new();
/// m.insert("foo".to_string(), 1.0.into());
/// let v = JsonValue::from(m);
/// let i = &v["foo"];
Expand All @@ -409,9 +417,8 @@ impl JsonValue {
/// or when the key does not exist in the object.
///
/// ```should_panic
/// # use tinyjson::JsonValue;
/// # use std::collections::HashMap;
/// let v = JsonValue::from(HashMap::new());
/// # use tinyjson::{JsonMap, JsonValue};
/// let v = JsonValue::from(JsonMap::new());
/// let _ = &v["foo"]; // Panic
/// ```
///
Expand Down Expand Up @@ -465,9 +472,8 @@ impl<'a> Index<&'a str> for JsonValue {
/// Like standard containers such as `Vec` or `HashMap`, it will panic when the given `JsonValue` value is not an array
///
/// ```should_panic
/// # use tinyjson::JsonValue;
/// use std::collections::HashMap;
/// let v = JsonValue::from(HashMap::new());
/// # use tinyjson::{JsonMap, JsonValue};
/// let v = JsonValue::from(JsonMap::new());
/// let _ = &v[0]; // Panic
/// ```
///
Expand Down Expand Up @@ -496,10 +502,9 @@ impl Index<usize> for JsonValue {
/// Access the element value of the key of mutable object.
///
/// ```
/// use tinyjson::JsonValue;
/// use std::collections::HashMap;
/// use tinyjson::{JsonMap, JsonValue};
///
/// let mut m = HashMap::new();
/// let mut m = JsonMap::new();
/// m.insert("foo".to_string(), 1.0.into());
/// let mut v = JsonValue::from(m);
/// v["foo"] = JsonValue::Number(3.14);
Expand All @@ -517,9 +522,8 @@ impl Index<usize> for JsonValue {
/// or when the key does not exist in the object.
///
/// ```should_panic
/// # use tinyjson::JsonValue;
/// # use std::collections::HashMap;
/// let mut v = JsonValue::from(HashMap::new());
/// # use tinyjson::{JsonMap, JsonValue};
/// let mut v = JsonValue::from(JsonMap::new());
/// let _ = &mut v["foo"]; // Panic
/// ```
///
Expand Down Expand Up @@ -572,9 +576,8 @@ impl<'a> IndexMut<&'a str> for JsonValue {
/// Like standard containers such as `Vec` or `HashMap`, it will panic when the given `JsonValue` value is not an array
///
/// ```should_panic
/// # use tinyjson::JsonValue;
/// use std::collections::HashMap;
/// let mut v = JsonValue::from(HashMap::new());
/// # use tinyjson::{JsonMap, JsonValue};
/// let mut v = JsonValue::from(JsonMap::new());
/// let _ = &mut v[0]; // Panic
/// ```
///
Expand Down Expand Up @@ -666,17 +669,16 @@ impl_from!(
a: Vec<JsonValue> => Array(a)
);
impl_from!(
/// Convert `HashMap` value into `JsonValue`.
/// Convert `JsonMap` value into `JsonValue`.
///
/// ```
/// use tinyjson::JsonValue;
/// use std::collections::HashMap;
/// let mut m = HashMap::new();
/// use tinyjson::{JsonMap, JsonValue};
/// let mut m = JsonMap::new();
/// m.insert("foo".to_string(), 1.0.into());
/// let v = JsonValue::from(m);
/// assert!(v.is_object());
/// ```
o: HashMap<String, JsonValue> => Object(o)
o: JsonMap => Object(o)
);

/// Error caused when trying to convert `JsonValue` into some wrong type value.
Expand Down Expand Up @@ -855,24 +857,23 @@ impl_try_from!(
Vec<JsonValue>,
);
impl_try_from!(
/// Try to convert the `JsonValue` value into `HashMap<String, JsonValue>`. `UnexpectedValue` error happens when
/// Try to convert the `JsonValue` value into `JsonMap`. `UnexpectedValue` error happens when
/// trying to convert an incorrect type value.
///
/// ```
/// use tinyjson::JsonValue;
/// use tinyjson::{JsonMap, JsonValue};
/// use std::convert::TryFrom;
/// use std::collections::HashMap;
///
/// let mut m = HashMap::new();
/// let mut m = JsonMap::new();
/// m.insert("foo".to_string(), 42.0.into());
/// let v = JsonValue::from(m);
/// let r = <HashMap<_, _>>::try_from(v);
/// let r = <JsonMap>::try_from(v);
/// assert!(r.is_ok());
///
/// let v = JsonValue::from(1.0);
/// let r = <HashMap<_, _>>::try_from(v);
/// let r = <JsonMap>::try_from(v);
/// assert!(r.is_err());
/// ```
JsonValue::Object(o) => o,
HashMap<String, JsonValue>,
JsonMap,
);
31 changes: 15 additions & 16 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@
//! Example:
//!
//! ```
//! use tinyjson::JsonValue;
//! use std::collections::HashMap;
//! use tinyjson::{JsonMap, JsonValue};
//! use std::convert::TryInto;
//!
//! let s = r#"
Expand All @@ -39,8 +38,8 @@
//! let parsed: JsonValue = s.parse().unwrap();
//!
//! // Access to inner value represented with standard containers
//! let object: &HashMap<_, _> = parsed.get().unwrap();
//! println!("Parsed HashMap: {:?}", object);
//! let object: &JsonMap = parsed.get().unwrap();
//! println!("Parsed map: {:?}", object);
//!
//! // Generate JSON string
//! println!("{}", parsed.stringify().unwrap());
Expand All @@ -52,11 +51,11 @@
//! println!("Second element of \"arr\": {:?}", elem);
//!
//! // Convert to inner value represented with standard containers
//! let object: HashMap<_, _> = parsed.try_into().unwrap();
//! println!("Converted into HashMap: {:?}", object);
//! let object: JsonMap = parsed.try_into().unwrap();
//! println!("Converted into map: {:?}", object);
//!
//! // Create JSON values from standard containers
//! let mut m = HashMap::new();
//! let mut m = JsonMap::new();
//! m.insert("foo".to_string(), true.into());
//! let mut v = JsonValue::from(m);
//!
Expand All @@ -68,14 +67,14 @@
//!
//! Any JSON value is represented with [`JsonValue`] enum. Each JSON types are mapped to Rust types as follows:
//!
//! | JSON | Rust |
//! |---------|------------------------------|
//! | Number | `f64` |
//! | Boolean | `bool` |
//! | String | `String` |
//! | Null | `()` |
//! | Array | `Vec<JsonValue>` |
//! | Object | `HashMap<String, JsonValue>` |
//! | JSON | Rust |
//! |---------|---------------------------------------------------------------|
//! | Number | `f64` |
//! | Boolean | `bool` |
//! | String | `String` |
//! | Null | `()` |
//! | Array | `Vec<JsonValue>` |
//! | Object | `HashMap<String, JsonValue>` or `IndexMap<String, JsonValue>` |
//!
//! Flexible query APIs are available to access nested elements easily without panic. See [`JsonQuery`] and
//! [`JsonQueryMut`] for more details.
Expand All @@ -92,6 +91,6 @@ mod parser;
mod query;

pub use generator::*;
pub use json_value::{InnerAsRef, InnerAsRefMut, JsonValue, UnexpectedValue};
pub use json_value::{InnerAsRef, InnerAsRefMut, JsonMap, JsonValue, UnexpectedValue};
pub use parser::*;
pub use query::{ChildIndex, JsonQuery, JsonQueryMut};
7 changes: 3 additions & 4 deletions src/parser.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
use std::char;
use std::collections::HashMap;
use std::fmt;
use std::iter::Peekable;
use std::str::FromStr;

use crate::JsonValue;
use crate::{JsonValue, JsonMap};

/// Parse error.
///
Expand Down Expand Up @@ -172,10 +171,10 @@ impl<I: Iterator<Item = char>> JsonParser<I> {

if self.peek()? == '}' {
self.consume().unwrap();
return Ok(JsonValue::Object(HashMap::new()));
return Ok(JsonValue::Object(JsonMap::new()));
}

let mut m = HashMap::new();
let mut m = JsonMap::new();
loop {
let key = match self.parse_any()? {
JsonValue::String(s) => s,
Expand Down
13 changes: 6 additions & 7 deletions tests/generator.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use std::collections::HashMap;
use std::f64;
use tinyjson::JsonValue;
use tinyjson::{JsonMap, JsonValue};

#[test]
fn test_number() {
Expand Down Expand Up @@ -61,7 +60,7 @@ fn test_array() {

#[test]
fn test_object() {
let mut m = HashMap::new();
let mut m = JsonMap::new();
m.insert("foo".to_string(), JsonValue::Number(1.0));
m.insert("bar".to_string(), JsonValue::Boolean(false));
m.insert("piyo".to_string(), JsonValue::Null);
Expand All @@ -72,7 +71,7 @@ fn test_object() {
assert!(s.contains(r#""bar":false"#));
assert!(s.contains(r#""piyo":null"#));
assert!(s.ends_with('}'));
let v = JsonValue::Object(HashMap::new());
let v = JsonValue::Object(JsonMap::new());
let s = v.stringify().unwrap();
assert_eq!(&s, "{}");
}
Expand Down Expand Up @@ -109,7 +108,7 @@ fn test_format_array() {
JsonValue::Array(vec![
JsonValue::Array(vec![
{
let mut m = HashMap::new();
let mut m = JsonMap::new();
m.insert("foo".to_string(), JsonValue::String("bar".to_string()));
JsonValue::Object(m)
},
Expand Down Expand Up @@ -146,7 +145,7 @@ fn test_format_array() {

#[test]
fn test_format_object() {
let mut m = HashMap::new();
let mut m = JsonMap::new();
m.insert("foo".to_string(), JsonValue::Number(1.0));
m.insert("bar".to_string(), JsonValue::Boolean(false));
m.insert("piyo".to_string(), JsonValue::Null);
Expand All @@ -157,7 +156,7 @@ fn test_format_object() {
assert!(s.contains(r#" "bar": false"#));
assert!(s.contains(r#" "piyo": null"#));
assert!(s.ends_with('}'));
let v = JsonValue::Object(HashMap::new());
let v = JsonValue::Object(JsonMap::new());
let s = v.format().unwrap();
assert_eq!(&s, "{}");
}
Loading