Merge pull request #49 from MikailBag/untagged

Add special utitilies for serde untagged repr
This commit is contained in:
Josh Stone 2020-08-10 13:17:19 -07:00 committed by GitHub
commit c5d0146996
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 152 additions and 3 deletions

View File

@ -3,7 +3,6 @@ sudo: false
# run builds for all the trains (and more)
rust:
- 1.12.0
- stable
- beta
- nightly
@ -24,6 +23,11 @@ env:
- FEATURES="serde"
matrix:
exclude:
include:
- rust: 1.12.0
env: FEATURES="serde"
env: FEATURES=""
before_script:
- |
cargo generate-lockfile &&
cargo update -p serde_json --precise 1.0.0 &&
cargo update -p serde --precise 1.0.0

View File

@ -22,6 +22,9 @@ serde = { version = "1.0", optional = true, features = ["derive"] }
default = ["use_std"]
use_std = []
[dev-dependencies]
serde_json = "1.0.0"
[package.metadata.release]
no-dev-version = true
tag-name = "{{version}}"

View File

@ -21,6 +21,12 @@ extern crate core as std;
#[macro_use]
extern crate serde;
#[cfg(feature = "serde")]
pub mod serde_untagged;
#[cfg(feature = "serde")]
pub mod serde_untagged_optional;
use std::convert::{AsRef, AsMut};
use std::fmt;
use std::iter;

65
src/serde_untagged.rs Normal file
View File

@ -0,0 +1,65 @@
//! Untagged serialization/deserialization support for Either<L, R>.
//!
//! `Either` uses default, externally-tagged representation.
//! However, sometimes it is useful to support several alternative types.
//! For example, we may have a field which is generally Map<String, i32>
//! but in typical cases Vec<String> would suffice, too.
//! ```rust
//! #[macro_use]
//! extern crate serde;
//! // or `use serde::{Serialize, Deserialize};` in newer rust versions.
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! use std::collections::HashMap;
//!
//! #[derive(Serialize, Deserialize, Debug)]
//! #[serde(transparent)]
//! struct IntOrString {
//! #[serde(with="either::serde_untagged")]
//! inner: either::Either<Vec<String>, HashMap<String, i32>>
//! };
//! // serialization
//! let data = IntOrString {
//! inner: either::Either::Left(vec!["Hello".to_string()])
//! };
//! // notice: no tags are emitted.
//! assert_eq!(serde_json::to_string(&data)?, r#"["Hello"]"#);
//! // deserialization
//! let data: IntOrString = serde_json::from_str(
//! r#"{"a": 0, "b": 14}"#
//! )?;
//! println!("found {:?}", data);
//! # Ok(())
//! }
//! ```
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[derive(Serialize, Deserialize)]
#[serde(untagged)]
enum Either<L, R> {
Left(L),
Right(R),
}
pub fn serialize<L, R, S>(this: &super::Either<L, R>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
L: Serialize,
R: Serialize,
{
let untagged = this.as_ref().either(Either::Left, Either::Right);
untagged.serialize(serializer)
}
pub fn deserialize<'de, L, R, D>(deserializer: D) -> Result<super::Either<L, R>, D::Error>
where
D: Deserializer<'de>,
L: Deserialize<'de>,
R: Deserialize<'de>,
{
let untagged: Either<L, R> = try!(Either::deserialize(deserializer));
match untagged {
Either::Left(left) => Ok(super::Either::Left(left)),
Either::Right(right) => Ok(super::Either::Right(right)),
}
}

View File

@ -0,0 +1,71 @@
//! Untagged serialization/deserialization support for Option<Either<L, R>>.
//!
//! `Either` uses default, externally-tagged representation.
//! However, sometimes it is useful to support several alternative types.
//! For example, we may have a field which is generally Map<String, i32>
//! but in typical cases Vec<String> would suffice, too.
//! ```rust
//! #[macro_use]
//! extern crate serde;
//! // or `use serde::{Serialize, Deserialize};` in newer rust versions.
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! use std::collections::HashMap;
//!
//! #[derive(Serialize, Deserialize, Debug)]
//! #[serde(transparent)]
//! struct IntOrString {
//! #[serde(with="either::serde_untagged_optional")]
//! inner: Option<either::Either<Vec<String>, HashMap<String, i32>>>
//! };
//! // serialization
//! let data = IntOrString {
//! inner: Some(either::Either::Left(vec!["Hello".to_string()]))
//! };
//! // notice: no tags are emitted.
//! assert_eq!(serde_json::to_string(&data)?, r#"["Hello"]"#);
//! // deserialization
//! let data: IntOrString = serde_json::from_str(
//! r#"{"a": 0, "b": 14}"#
//! )?;
//! println!("found {:?}", data);
//! # Ok(())
//! }
//! ```
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[derive(Serialize, Deserialize)]
#[serde(untagged)]
enum Either<L, R> {
Left(L),
Right(R),
}
pub fn serialize<L, R, S>(
this: &Option<super::Either<L, R>>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
L: Serialize,
R: Serialize,
{
let untagged = this
.as_ref()
.map(|either| either.as_ref().either(Either::Left, Either::Right));
untagged.serialize(serializer)
}
pub fn deserialize<'de, L, R, D>(deserializer: D) -> Result<Option<super::Either<L, R>>, D::Error>
where
D: Deserializer<'de>,
L: Deserialize<'de>,
R: Deserialize<'de>,
{
let untagged = try!(Option::<Either<L, R>>::deserialize(deserializer));
match untagged {
None => Ok(None),
Some(Either::Left(left)) => Ok(Some(super::Either::Left(left))),
Some(Either::Right(right)) => Ok(Some(super::Either::Right(right))),
}
}