Skip to content

Commit

Permalink
Fix optional features.
Browse files Browse the repository at this point in the history
  • Loading branch information
timothee-haudebourg committed Jan 24, 2024
1 parent 457c7e4 commit 6bb61fd
Show file tree
Hide file tree
Showing 8 changed files with 173 additions and 514 deletions.
8 changes: 4 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "json-syntax"
version = "0.10.0"
version = "0.11.0"
edition = "2021"
authors = ["Timothée Haudebourg <author@haudebourg.net>"]
description = "Strict JSON parsing and mapping library"
Expand All @@ -12,13 +12,13 @@ license = "MIT/Apache-2.0"
readme = "README.md"

[features]
# JSON Canonicalization Scheme (JCS) implementation.
## JSON Canonicalization Scheme (JCS) implementation.
canonicalize = [ "ryu-js", "json-number/canonical" ]

# Serialization/Deserialization support using `serde`.
## Serialization/Deserialization support using `serde`.
serde = [ "dep:serde", "json-number/serde" ]

# Compatibility layer with the `serde_json` crate.
## Compatibility layer with the `serde_json` crate.
serde_json = [ "dep:serde_json", "json-number/serde_json" ]

[dependencies]
Expand Down
22 changes: 1 addition & 21 deletions examples/serde_json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,7 @@ fn main() {
});

// We convert the `serde_json` value into a `json_syntax` value.
let b = json_syntax::Value::from_serde_json(a, |fragment| {
// `json_syntax` keeps metadata information attached to every value
// fragment, which `serde_json` does not. This is why this closure is
// necessary. It is called for every `serde_json` fragment to let you
// choose the metadata you want to associate to the fragment.
// This is intended to store code mapping information, but you can store
// any information. Here we store a text description of the fragment.
match fragment {
json_syntax::SerdeJsonFragment::Key(key) => {
format!("I'm an object key `{key}`")
}
json_syntax::SerdeJsonFragment::Value(value) => match value {
serde_json::Value::Null => "I'm the `null` value".to_string(),
serde_json::Value::Bool(b) => format!("I'm the boolean `{b:?}`"),
serde_json::Value::Number(n) => format!("I'm the number {n}"),
serde_json::Value::String(s) => format!("I'm the string {s:?}"),
serde_json::Value::Array(a) => format!("I'm an array of {} elements", a.len()),
serde_json::Value::Object(o) => format!("I'm an object of {} entries", o.len()),
},
}
});
let b = json_syntax::Value::from_serde_json(a);

// We convert it back into a `serde_json` value.
let _ = json_syntax::Value::into_serde_json(b);
Expand Down
3 changes: 0 additions & 3 deletions src/convert.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,2 @@
#[cfg(feature = "serde_json")]
mod serde_json;

#[cfg(feature = "serde_json")]
pub use self::serde_json::*;
130 changes: 31 additions & 99 deletions src/convert/serde_json.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,8 @@
use locspan::Meta;

use crate::{object::Entry, Value};

/// A fragment of a [`serde_json::Value`].
///
/// Used by the [`Value::from_serde_json`] to annotate the output value.
pub enum SerdeJsonFragment<'a> {
Key(&'a str),
Value(&'a serde_json::Value),
}

impl<M> Value<M> {
impl Value {
/// Converts a [`serde_json::Value`] into a `Value`.
///
/// The function `f` is used to annotate the output each sub value
/// (passed as parameter).
///
/// # Example
///
/// ```
Expand All @@ -26,61 +13,29 @@ impl<M> Value<M> {
/// });
///
/// // We convert the `serde_json` value into a `json_syntax` value.
/// let b = json_syntax::Value::from_serde_json(a, |fragment| {
/// // `json_syntax` keeps metadata information attached to every value
/// // fragment, which `serde_json` does not. This is why this closure is
/// // necessary. It is called for every `serde_json` fragment to let you
/// // choose the metadata you want to associate to the fragment.
/// // This is intended to store code mapping information, but you can store
/// // any information. Here we store a text description of the fragment.
/// match fragment {
/// json_syntax::SerdeJsonFragment::Key(key) => {
/// format!("I'm an object key `{key}`")
/// }
/// json_syntax::SerdeJsonFragment::Value(value) => match value {
/// serde_json::Value::Null => "I'm the `null` value".to_string(),
/// serde_json::Value::Bool(b) => format!("I'm the boolean `{b:?}`"),
/// serde_json::Value::Number(n) => format!("I'm the number {n}"),
/// serde_json::Value::String(s) => format!("I'm the string {s:?}"),
/// serde_json::Value::Array(a) => format!("I'm an array of {} elements", a.len()),
/// serde_json::Value::Object(o) => format!("I'm an object of {} entries", o.len()),
/// },
/// }
/// });
/// let b = json_syntax::Value::from_serde_json(a);
///
/// // We convert it back into a `serde_json` value.
/// let _ = json_syntax::Value::into_serde_json(b);
/// ```
pub fn from_serde_json(
value: serde_json::Value,
f: impl Clone + Fn(SerdeJsonFragment) -> M,
) -> Meta<Self, M> {
let meta = f(SerdeJsonFragment::Value(&value));

let v = match value {
pub fn from_serde_json(value: serde_json::Value) -> Self {
match value {
serde_json::Value::Null => Self::Null,
serde_json::Value::Bool(b) => Self::Boolean(b),
serde_json::Value::Number(n) => Self::Number(n.into()),
serde_json::Value::String(s) => Self::String(s.into()),
serde_json::Value::Array(a) => Self::Array(
a.into_iter()
.map(|i| Self::from_serde_json(i, f.clone()))
.collect(),
),
serde_json::Value::Array(a) => {
Self::Array(a.into_iter().map(Self::from_serde_json).collect())
}
serde_json::Value::Object(o) => Self::Object(
o.into_iter()
.map(|(k, v)| {
let k_meta = f(SerdeJsonFragment::Key(&k));
Entry::new(Meta(k.into(), k_meta), Self::from_serde_json(v, f.clone()))
})
.map(|(k, v)| Entry::new(k.into(), Self::from_serde_json(v)))
.collect(),
),
};

Meta(v, meta)
}
}

/// Converts a `Value` into a [`serde_json::Value`], stripping the metadata.
/// Converts a `Value` into a [`serde_json::Value`].
///
/// # Example
///
Expand All @@ -92,61 +47,38 @@ impl<M> Value<M> {
/// });
///
/// // We convert the `serde_json` value into a `json_syntax` value.
/// let b = json_syntax::Value::from_serde_json(a, |fragment| {
/// // `json_syntax` keeps metadata information attached to every value
/// // fragment, which `serde_json` does not. This is why this closure is
/// // necessary. It is called for every `serde_json` fragment to let you
/// // choose the metadata you want to associate to the fragment.
/// // This is intended to store code mapping information, but you can store
/// // any information. Here we store a text description of the fragment.
/// match fragment {
/// json_syntax::SerdeJsonFragment::Key(key) => {
/// format!("I'm an object key `{key}`")
/// }
/// json_syntax::SerdeJsonFragment::Value(value) => match value {
/// serde_json::Value::Null => "I'm the `null` value".to_string(),
/// serde_json::Value::Bool(b) => format!("I'm the boolean `{b:?}`"),
/// serde_json::Value::Number(n) => format!("I'm the number {n}"),
/// serde_json::Value::String(s) => format!("I'm the string {s:?}"),
/// serde_json::Value::Array(a) => format!("I'm an array of {} elements", a.len()),
/// serde_json::Value::Object(o) => format!("I'm an object of {} entries", o.len()),
/// },
/// }
/// });
/// let b = json_syntax::Value::from_serde_json(a);
///
/// // We convert it back into a `serde_json` value.
/// let _ = json_syntax::Value::into_serde_json(b);
/// ```
pub fn into_serde_json(Meta(this, _): Meta<Self, M>) -> serde_json::Value {
this.into()
pub fn into_serde_json(self) -> serde_json::Value {
match self {
Self::Null => serde_json::Value::Null,
Self::Boolean(b) => serde_json::Value::Bool(b),
Self::Number(n) => serde_json::Value::Number(n.into()),
Self::String(s) => serde_json::Value::String(s.into_string()),
Self::Array(a) => {
serde_json::Value::Array(a.into_iter().map(Value::into_serde_json).collect())
}
Self::Object(o) => serde_json::Value::Object(
o.into_iter()
.map(|Entry { key, value }| (key.into_string(), Value::into_serde_json(value)))
.collect(),
),
}
}
}

impl<M: Default> From<serde_json::Value> for Value<M> {
impl From<serde_json::Value> for Value {
#[inline(always)]
fn from(value: serde_json::Value) -> Self {
Value::from_serde_json(value, |_| M::default()).into_value()
Self::from_serde_json(value)
}
}

impl<M> From<Value<M>> for serde_json::Value {
fn from(value: Value<M>) -> Self {
match value {
Value::Null => Self::Null,
Value::Boolean(b) => Self::Bool(b),
Value::Number(n) => Self::Number(n.into()),
Value::String(s) => Self::String(s.into_string()),
Value::Array(a) => Self::Array(a.into_iter().map(Value::into_serde_json).collect()),
Value::Object(o) => Self::Object(
o.into_iter()
.map(
|Entry {
key: Meta(key, _),
value,
}| { (key.into_string(), Value::into_serde_json(value)) },
)
.collect(),
),
}
impl From<Value> for serde_json::Value {
fn from(value: Value) -> Self {
value.into_serde_json()
}
}
7 changes: 2 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,6 @@ mod serde;
#[cfg(feature = "serde")]
pub use self::serde::*;

#[cfg(feature = "serde_json")]
pub use convert::*;
pub use unordered::*;

/// String stack capacity.
Expand Down Expand Up @@ -713,7 +711,7 @@ mod tests {
#[test]
fn canonicalize_01() {
use super::*;
let mut value: Meta<Value<()>, ()> = json!({
let mut value: Value = json!({
"b": 0.00000000001,
"c": {
"foo": true,
Expand All @@ -734,13 +732,12 @@ mod tests {
#[test]
fn canonicalize_02() {
use super::*;
let mut value = Value::parse_str(
let (mut value, _) = Value::parse_str(
"{
\"numbers\": [333333333.33333329, 1E30, 4.50, 2e-3, 0.000000000000000000000000001],
\"string\": \"\\u20ac$\\u000F\\u000aA'\\u0042\\u0022\\u005c\\\\\\\"\\/\",
\"literals\": [null, true, false]
}",
|span| span,
)
.unwrap();

Expand Down
60 changes: 7 additions & 53 deletions src/serde.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use crate::{MetaValue, Value};
use locspan::Meta;
use crate::Value;
use serde::{de::DeserializeOwned, Serialize};

mod de;
Expand All @@ -8,7 +7,7 @@ mod ser;
pub use de::*;
pub use ser::*;

/// Serializes the given `value` into a JSON [`Value`], with `()` as metadata.
/// Serializes the given `value` into a JSON [`Value`].
///
/// # Example
///
Expand All @@ -30,7 +29,7 @@ pub use ser::*;
/// let expected: Value = json!({
/// "fingerprint": "0xF9BA143B95FF6D82",
/// "location": "Menlo Park, CA",
/// }).into_value();
/// });
///
/// let v = json_syntax::to_value(u).unwrap();
/// assert_eq!(v, expected);
Expand All @@ -39,53 +38,10 @@ pub fn to_value<T>(value: T) -> Result<Value, SerializeError>
where
T: Serialize,
{
Ok(value.serialize(Serializer::new(|| ()))?.into_value())
}

/// Serializes the given `value` into a JSON [`Value<M>`] using the input
/// `metadata` function to annotate the output value.
pub fn to_value_with<T, M>(
value: T,
metadata: impl Clone + Fn() -> M,
) -> Result<Meta<Value<M>, M>, SerializeError>
where
T: Serialize,
{
value.serialize(Serializer::new(metadata))
}

/// Deserializes the JSON `value` into an instance of type `T`.
///
/// # Example
///
/// ```
/// use serde::Deserialize;
/// use json_syntax::{json, Value};
///
/// #[derive(Deserialize, Debug)]
/// struct User {
/// fingerprint: String,
/// location: String,
/// }
///
/// let j: Value = json!({
/// "fingerprint": "0xF9BA143B95FF6D82",
/// "location": "Menlo Park, CA"
/// }).into_value();
///
/// let u: User = json_syntax::from_value(j).unwrap();
/// println!("{:#?}", u);
/// ```
pub fn from_value<T, M>(value: Value<M>) -> Result<T, DeserializeError>
where
T: DeserializeOwned,
{
T::deserialize(value).map_err(DeserializeErrorFragment::strip)
value.serialize(Serializer)
}

/// Deserializes the JSON `value` into an instance of type `T`.
/// Contrarily to [`from_value`], on error this function will return the
/// metadata associated to the error location.
///
/// # Example
///
Expand All @@ -102,16 +58,14 @@ where
/// let j: Value = json!({
/// "fingerprint": "0xF9BA143B95FF6D82",
/// "location": "Menlo Park, CA"
/// }).into_value();
/// });
///
/// let u: User = json_syntax::from_value(j).unwrap();
/// println!("{:#?}", u);
/// ```
pub fn from_meta_value<T, M>(
Meta(value, meta): MetaValue<M>,
) -> Result<T, Meta<DeserializeError, M>>
pub fn from_value<T>(value: Value) -> Result<T, DeserializeError>
where
T: DeserializeOwned,
{
T::deserialize(value).map_err(|e| e.with_metadata(meta))
T::deserialize(value)
}
Loading

0 comments on commit 6bb61fd

Please sign in to comment.