Add 'ruma-serde/' from commit '851ffea6d20bef1c66f1c8e0ceb4d4a00c9804fc'

git-subtree-dir: ruma-serde
git-subtree-mainline: ec8f70f42a5a3e44e082de21b153d5ff2bece72d
git-subtree-split: 851ffea6d20bef1c66f1c8e0ceb4d4a00c9804fc
This commit is contained in:
Jonas Platte 2020-06-05 01:27:53 +02:00
commit f8a25dabca
29 changed files with 3165 additions and 0 deletions

View File

@ -0,0 +1,27 @@
image: archlinux
packages:
- rustup
sources:
- https://github.com/ruma/ruma-serde
tasks:
- rustup: |
# We specify --profile minimal because we'd otherwise download docs
rustup toolchain install beta --profile minimal -c rustfmt -c clippy
rustup default beta
- test: |
cd ruma-serde
# We don't want the build to stop on individual failure of independent
# tools, so capture tool exit codes and set the task exit code manually
set +e
cargo fmt -- --check
fmt_exit=$?
cargo clippy --all-targets --all-features -- -D warnings
clippy_exit=$?
cargo test --all-features --verbose
test_exit=$?
exit $(( $fmt_exit || $clippy_exit || $test_exit ))

View File

@ -0,0 +1,14 @@
image: archlinux
packages:
- rustup
sources:
- https://github.com/ruma/ruma-serde
tasks:
- rustup: |
# We specify --profile minimal because we'd otherwise download docs
rustup toolchain install 1.36.0 --profile minimal
rustup default 1.36.0
- test: |
cd ruma-serde
cargo build --verbose

View File

@ -0,0 +1,32 @@
image: archlinux
packages:
- rustup
sources:
- https://github.com/ruma/ruma-serde
tasks:
- rustup: |
rustup toolchain install nightly --profile minimal
rustup default nightly
# Try installing rustfmt & clippy for nightly, but don't fail the build
# if they are not available
rustup component add rustfmt || true
rustup component add clippy || true
- test: |
cd ruma-serde
# We don't want the build to stop on individual failure of independent
# tools, so capture tool exit codes and set the task exit code manually
set +e
if ( rustup component list | grep -q rustfmt ); then
cargo fmt -- --check
fi
fmt_exit=$?
if ( rustup component list | grep -q clippy ); then
cargo clippy --all-targets --all-features -- -D warnings
fi
clippy_exit=$?
exit $(( $fmt_exit || $clippy_exit ))

View File

@ -0,0 +1,29 @@
image: archlinux
packages:
- rustup
sources:
- https://github.com/ruma/ruma-serde
tasks:
- rustup: |
# We specify --profile minimal because we'd otherwise download docs
rustup toolchain install stable --profile minimal -c rustfmt -c clippy
rustup default stable
- test: |
cd ruma-serde
# We don't want the build to stop on individual failure of independent
# tools, so capture tool exit codes and set the task exit code manually
set +e
cargo fmt -- --check
fmt_exit=$?
cargo clippy --all-targets --all-features -- -D warnings
clippy_exit=$?
cargo test --verbose
test_exit=$?
exit $(( $fmt_exit || $clippy_exit || $test_exit ))
# TODO: Add audit task once cargo-audit binary releases are available.
# See https://github.com/RustSec/cargo-audit/issues/66

2
ruma-serde/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/target
Cargo.lock

24
ruma-serde/Cargo.toml Normal file
View File

@ -0,0 +1,24 @@
[package]
name = "ruma-serde"
description = "De-/serialization helpers for other ruma crates"
documentation = "https://docs.rs/ruma-serde"
license = "MIT"
authors = [
"Jonas Platte <jplatte@posteo.de>",
"Isaiah Inuwa <isaiah.inuwa@gmail.com>",
"Anthony Ramine <n.oxyde@gmail.com>",
]
version = "0.2.2"
repository = "https://github.com/ruma/ruma-serde"
edition = "2018"
[dependencies]
dtoa = "0.4.5"
js_int = { version = "0.1.5", features = ["serde"] }
itoa = "0.4.5"
serde = { version = "1.0.110", features = ["derive"] }
serde_json = "1.0.53"
url = "2.1.1"
[dev-dependencies]
matches = "0.1.8"

20
ruma-serde/LICENSE Normal file
View File

@ -0,0 +1,20 @@
Copyright (c) 2016 - 2019 Anthony Ramine
Copyright (c) 2020 Jonas Platte
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

12
ruma-serde/README.md Normal file
View File

@ -0,0 +1,12 @@
# ruma-serde
This crate contains (de)serialization helpers for other ruma crates.
Part of that is a fork of serde_urlencoded, with support for sequences in `Deserialize` /
`Serialize` structs (e.g. `Vec<Something>`) that are (de)serialized as `field=val1&field=val2`
(instead of the more common `field[]=val1&field[]=val2` format supported by other crates like
`serde_qs`).
## Minimum Rust version
ruma-serde requires Rust 1.36.0 or later.

4
ruma-serde/rustfmt.toml Normal file
View File

@ -0,0 +1,4 @@
max_width = 80
newline_style = "Unix"
reorder_imports = true
use_try_shorthand = true

View File

@ -0,0 +1,4 @@
//! De-/serialization functions for `std::time::Duration` objects
pub mod opt_ms;
pub mod secs;

View File

@ -0,0 +1,111 @@
//! De-/serialization functions for `Option<std::time::Duration>` objects represented as milliseconds.
//! Delegates to `js_int::UInt` to ensure integer size is within bounds.
use std::{convert::TryFrom, time::Duration};
use js_int::UInt;
use serde::{
de::{Deserialize, Deserializer},
ser::{Error, Serialize, Serializer},
};
/// Serialize an Option<Duration>.
///
/// Will fail if integer is greater than the maximum integer that can be
/// unambiguously represented by an f64.
pub fn serialize<S>(
opt_duration: &Option<Duration>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match opt_duration {
Some(duration) => match UInt::try_from(duration.as_millis()) {
Ok(uint) => uint.serialize(serializer),
Err(err) => Err(S::Error::custom(err)),
},
None => serializer.serialize_none(),
}
}
/// Deserializes an Option<Duration>.
///
/// Will fail if integer is greater than the maximum integer that can be
/// unambiguously represented by an f64.
pub fn deserialize<'de, D>(
deserializer: D,
) -> Result<Option<Duration>, D::Error>
where
D: Deserializer<'de>,
{
Ok(Option::<UInt>::deserialize(deserializer)?
.map(|millis| Duration::from_millis(millis.into())))
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use serde::{Deserialize, Serialize};
use serde_json::json;
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
struct DurationTest {
#[serde(
with = "super",
default,
skip_serializing_if = "Option::is_none"
)]
timeout: Option<Duration>,
}
#[test]
fn test_deserialize_some() {
let json = json!({ "timeout": 3000 });
assert_eq!(
serde_json::from_value::<DurationTest>(json).unwrap(),
DurationTest {
timeout: Some(Duration::from_millis(3000))
},
);
}
#[test]
fn test_deserialize_none_by_absence() {
let json = json!({});
assert_eq!(
serde_json::from_value::<DurationTest>(json).unwrap(),
DurationTest { timeout: None },
);
}
#[test]
fn test_deserialize_none_by_null() {
let json = json!({ "timeout": null });
assert_eq!(
serde_json::from_value::<DurationTest>(json).unwrap(),
DurationTest { timeout: None },
);
}
#[test]
fn test_serialize_some() {
let request = DurationTest {
timeout: Some(Duration::new(2, 0)),
};
assert_eq!(
serde_json::to_value(&request).unwrap(),
json!({ "timeout": 2000 })
);
}
#[test]
fn test_serialize_none() {
let request = DurationTest { timeout: None };
assert_eq!(serde_json::to_value(&request).unwrap(), json!({}));
}
}

View File

@ -0,0 +1,75 @@
//! De-/serialization functions for `Option<std::time::Duration>` objects represented as milliseconds.
//! Delegates to `js_int::UInt` to ensure integer size is within bounds.
use std::{convert::TryFrom, time::Duration};
use js_int::UInt;
use serde::{
de::{Deserialize, Deserializer},
ser::{Error, Serialize, Serializer},
};
/// Serializes a Duration to an integer representing seconds.
///
/// Will fail if integer is greater than the maximum integer that can be
/// unambiguously represented by an f64.
pub fn serialize<S>(
duration: &Duration,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match UInt::try_from(duration.as_secs()) {
Ok(uint) => uint.serialize(serializer),
Err(err) => Err(S::Error::custom(err)),
}
}
/// Deserializes an integer representing seconds into a Duration.
///
/// Will fail if integer is greater than the maximum integer that can be
/// unambiguously represented by an f64.
pub fn deserialize<'de, D>(deserializer: D) -> Result<Duration, D::Error>
where
D: Deserializer<'de>,
{
UInt::deserialize(deserializer).map(|secs| Duration::from_secs(secs.into()))
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use serde::{Deserialize, Serialize};
use serde_json::json;
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
struct DurationTest {
#[serde(with = "super")]
timeout: Duration,
}
#[test]
fn test_deserialize() {
let json = json!({ "timeout": 3 });
assert_eq!(
serde_json::from_value::<DurationTest>(json).unwrap(),
DurationTest {
timeout: Duration::from_secs(3)
},
);
}
#[test]
fn test_serialize() {
let test = DurationTest {
timeout: Duration::from_millis(7000),
};
assert_eq!(
serde_json::to_value(test).unwrap(),
json!({ "timeout": 7 }),
);
}
}

89
ruma-serde/src/empty.rs Normal file
View File

@ -0,0 +1,89 @@
use std::fmt::{self, Formatter};
use serde::{
de::{Deserialize, Deserializer, MapAccess, Visitor},
ser::{Serialize, SerializeMap, Serializer},
};
/// A meaningless value that serializes to an empty JSON object.
///
/// This type is used in a few places where the Matrix specification requires an empty JSON object,
/// but it's wasteful to represent it as a `BTreeMap` in Rust code.
#[derive(Clone, Debug, PartialEq)]
pub struct Empty;
impl Serialize for Empty {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_map(Some(0))?.end()
}
}
impl<'de> Deserialize<'de> for Empty {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct EmptyMapVisitor;
impl<'de> Visitor<'de> for EmptyMapVisitor {
type Value = Empty;
fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "an object/map")
}
fn visit_map<A>(self, _map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
Ok(Empty)
}
}
deserializer.deserialize_map(EmptyMapVisitor)
}
}
/// Serde serialization and deserialization functions that map a `Vec<T>` to a
/// `BTreeMap<T, Empty>`.
///
/// The Matrix spec sometimes specifies lists as hash maps so the list entries
/// can be expanded with attributes without breaking compatibility. As that
/// would be a breaking change for ruma's event types anyway, we convert them to
/// `Vec`s for simplicity, using this module.
///
/// To be used as `#[serde(with = "vec_as_map_of_empty")]`.
pub mod vec_as_map_of_empty {
use std::collections::BTreeMap;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use super::Empty;
#[allow(clippy::ptr_arg)]
pub fn serialize<S, T>(
vec: &Vec<T>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
T: Serialize + Eq + Ord,
{
vec.iter()
.map(|v| (v, Empty))
.collect::<BTreeMap<_, _>>()
.serialize(serializer)
}
pub fn deserialize<'de, D, T>(deserializer: D) -> Result<Vec<T>, D::Error>
where
D: Deserializer<'de>,
T: Deserialize<'de> + Eq + Ord,
{
BTreeMap::<T, Empty>::deserialize(deserializer)
.map(|hashmap| hashmap.into_iter().map(|(k, _)| k).collect())
}
}

View File

@ -0,0 +1,24 @@
//! De-/serialization functions to and from json strings, allows the type to be used as a query string.
use serde::{
de::{Deserialize, DeserializeOwned, Deserializer, Error as _},
ser::{Error as _, Serialize, Serializer},
};
pub fn serialize<T, S>(filter: T, serializer: S) -> Result<S::Ok, S::Error>
where
T: Serialize,
S: Serializer,
{
let json = serde_json::to_string(&filter).map_err(S::Error::custom)?;
serializer.serialize_str(&json)
}
pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
where
T: DeserializeOwned,
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
serde_json::from_str(&s).map_err(D::Error::custom)
}

59
ruma-serde/src/lib.rs Normal file
View File

@ -0,0 +1,59 @@
//! De-/serialization helpers for other ruma crates
use serde::de::{Deserialize, IntoDeserializer};
pub mod duration;
pub mod empty;
pub mod json_string;
pub mod test;
pub mod time;
pub mod urlencoded;
pub use empty::vec_as_map_of_empty;
/// Check whether a value is equal to its default value.
pub fn is_default<T: Default + PartialEq>(val: &T) -> bool {
val == &T::default()
}
/// Simply returns `true`.
///
/// Useful for `#[serde(default = ...)]`.
pub fn default_true() -> bool {
true
}
/// Simplfy dereferences the given bool.
///
/// Useful for `#[serde(skip_serializing_if = ...)]`
#[allow(clippy::trivially_copy_pass_by_ref)]
pub fn is_true(b: &bool) -> bool {
*b
}
/// Serde deserialization decorator to map empty Strings to None,
/// and forward non-empty Strings to the Deserialize implementation for T.
/// Useful for the typical
/// "A room with an X event with an absent, null, or empty Y field
/// should be treated the same as a room with no such event."
/// formulation in the spec.
///
/// To be used like this:
/// `#[serde(deserialize_with = "empty_string_as_none"]`
/// Relevant serde issue: https://github.com/serde-rs/serde/issues/1425
pub fn empty_string_as_none<'de, D, T>(de: D) -> Result<Option<T>, D::Error>
where
D: serde::Deserializer<'de>,
T: serde::Deserialize<'de>,
{
let opt = Option::<String>::deserialize(de)?;
// TODO: Switch to and remove this attribute `opt.as_deref()` once MSRV is >= 1.40
#[allow(clippy::option_as_ref_deref, clippy::unknown_clippy_lints)]
let opt = opt.as_ref().map(String::as_str);
match opt {
None | Some("") => Ok(None),
// If T = String, like in m.room.name, the second deserialize is actually superfluous.
// TODO: optimize that somehow?
Some(s) => T::deserialize(s.into_deserializer()).map(Some),
}
}

13
ruma-serde/src/test.rs Normal file
View File

@ -0,0 +1,13 @@
//! Helpers for tests
use std::fmt::Debug;
use serde::{de::DeserializeOwned, Serialize};
pub fn serde_json_eq<T>(de: T, se: serde_json::Value)
where
T: Clone + Debug + PartialEq + Serialize + DeserializeOwned,
{
assert_eq!(se, serde_json::to_value(de.clone()).unwrap());
assert_eq!(de, serde_json::from_value(se).unwrap());
}

4
ruma-serde/src/time.rs Normal file
View File

@ -0,0 +1,4 @@
//! De-/serialization functions for `std::time::SystemTime` objects
pub mod ms_since_unix_epoch;
pub mod opt_ms_since_unix_epoch;

View File

@ -0,0 +1,78 @@
//! De-/serialization functions for `std::time::SystemTime` objects represented as milliseconds
//! since the UNIX epoch. Delegates to `js_int::UInt` to ensure integer size is within bounds.
use std::{
convert::TryFrom,
time::{Duration, SystemTime, UNIX_EPOCH},
};
use js_int::UInt;
use serde::{
de::{Deserialize, Deserializer},
ser::{Error, Serialize, Serializer},
};
/// Serialize a SystemTime.
///
/// Will fail if integer is greater than the maximum integer that can be unambiguously represented
/// by an f64.
pub fn serialize<S>(time: &SystemTime, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
// If this unwrap fails, the system this is executed is completely broken.
let time_since_epoch = time.duration_since(UNIX_EPOCH).unwrap();
match UInt::try_from(time_since_epoch.as_millis()) {
Ok(uint) => uint.serialize(serializer),
Err(err) => Err(S::Error::custom(err)),
}
}
/// Deserializes a SystemTime.
///
/// Will fail if integer is greater than the maximum integer that can be unambiguously represented
/// by an f64.
pub fn deserialize<'de, D>(deserializer: D) -> Result<SystemTime, D::Error>
where
D: Deserializer<'de>,
{
let millis = UInt::deserialize(deserializer)?;
Ok(UNIX_EPOCH + Duration::from_millis(millis.into()))
}
#[cfg(test)]
mod tests {
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use serde::{Deserialize, Serialize};
use serde_json::json;
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
struct SystemTimeTest {
#[serde(with = "super")]
timestamp: SystemTime,
}
#[test]
fn test_deserialize() {
let json = json!({ "timestamp": 3000 });
assert_eq!(
serde_json::from_value::<SystemTimeTest>(json).unwrap(),
SystemTimeTest {
timestamp: UNIX_EPOCH + Duration::from_millis(3000),
},
);
}
#[test]
fn test_serialize() {
let request = SystemTimeTest {
timestamp: UNIX_EPOCH + Duration::new(2, 0),
};
assert_eq!(
serde_json::to_value(&request).unwrap(),
json!({ "timestamp": 2000 })
);
}
}

View File

@ -0,0 +1,109 @@
//! De-/serialization functions for `Option<std::time::SystemTime>` objects represented as
//! milliseconds since the UNIX epoch. Delegates to `js_int::UInt` to ensure integer size is within
//! bounds.
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use js_int::UInt;
use serde::{
de::{Deserialize, Deserializer},
ser::{Serialize, Serializer},
};
/// Serialize an `Option<SystemTime>`.
///
/// Will fail if integer is greater than the maximum integer that can be unambiguously represented
/// by an f64.
pub fn serialize<S>(
opt_time: &Option<SystemTime>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match opt_time {
Some(time) => super::ms_since_unix_epoch::serialize(time, serializer),
None => Option::<UInt>::serialize(&None, serializer),
}
}
/// Deserializes an `Option<SystemTime>`.
///
/// Will fail if integer is greater than the maximum integer that can be unambiguously represented
/// by an f64.
pub fn deserialize<'de, D>(
deserializer: D,
) -> Result<Option<SystemTime>, D::Error>
where
D: Deserializer<'de>,
{
Ok(Option::<UInt>::deserialize(deserializer)?
.map(|millis| UNIX_EPOCH + Duration::from_millis(millis.into())))
}
#[cfg(test)]
mod tests {
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use serde::{Deserialize, Serialize};
use serde_json::json;
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
struct SystemTimeTest {
#[serde(
with = "super",
default,
skip_serializing_if = "Option::is_none"
)]
timestamp: Option<SystemTime>,
}
#[test]
fn test_deserialize_some() {
let json = json!({ "timestamp": 3000 });
assert_eq!(
serde_json::from_value::<SystemTimeTest>(json).unwrap(),
SystemTimeTest {
timestamp: Some(UNIX_EPOCH + Duration::from_millis(3000))
},
);
}
#[test]
fn test_deserialize_none_by_absence() {
let json = json!({});
assert_eq!(
serde_json::from_value::<SystemTimeTest>(json).unwrap(),
SystemTimeTest { timestamp: None },
);
}
#[test]
fn test_deserialize_none_by_null() {
let json = json!({ "timestamp": null });
assert_eq!(
serde_json::from_value::<SystemTimeTest>(json).unwrap(),
SystemTimeTest { timestamp: None },
);
}
#[test]
fn test_serialize_some() {
let request = SystemTimeTest {
timestamp: Some(UNIX_EPOCH + Duration::new(2, 0)),
};
assert_eq!(
serde_json::to_value(&request).unwrap(),
json!({ "timestamp": 2000 })
);
}
#[test]
fn test_serialize_none() {
let request = SystemTimeTest { timestamp: None };
assert_eq!(serde_json::to_value(&request).unwrap(), json!({}));
}
}

View File

@ -0,0 +1,9 @@
//! `x-www-form-urlencoded` meets Serde
pub mod de;
pub mod ser;
#[doc(inline)]
pub use de::{from_bytes, from_reader, from_str, Deserializer};
#[doc(inline)]
pub use ser::{to_string, Serializer};

View File

@ -0,0 +1,365 @@
//! Deserialization support for the `application/x-www-form-urlencoded` format.
use std::{
borrow::Cow,
collections::btree_map::{self, BTreeMap},
io::Read,
};
use serde::{
de::{self, value::MapDeserializer, Error as de_Error, IntoDeserializer},
forward_to_deserialize_any,
};
use url::form_urlencoded::{parse, Parse as UrlEncodedParse};
#[doc(inline)]
pub use serde::de::value::Error;
mod val_or_vec;
use val_or_vec::ValOrVec;
/// Deserializes a `application/x-www-form-urlencoded` value from a `&[u8]`.
///
/// ```
/// let meal = vec![
/// ("bread".to_owned(), "baguette".to_owned()),
/// ("cheese".to_owned(), "comté".to_owned()),
/// ("fat".to_owned(), "butter".to_owned()),
/// ("meat".to_owned(), "ham".to_owned()),
/// ];
///
/// assert_eq!(
/// ruma_serde::urlencoded::from_bytes::<Vec<(String, String)>>(
/// b"bread=baguette&cheese=comt%C3%A9&meat=ham&fat=butter"),
/// Ok(meal));
/// ```
pub fn from_bytes<'de, T>(input: &'de [u8]) -> Result<T, Error>
where
T: de::Deserialize<'de>,
{
T::deserialize(Deserializer::new(parse(input)))
}
/// Deserializes a `application/x-www-form-urlencoded` value from a `&str`.
///
/// ```
/// let meal = vec![
/// ("bread".to_owned(), "baguette".to_owned()),
/// ("cheese".to_owned(), "comté".to_owned()),
/// ("fat".to_owned(), "butter".to_owned()),
/// ("meat".to_owned(), "ham".to_owned()),
/// ];
///
/// assert_eq!(
/// ruma_serde::urlencoded::from_str::<Vec<(String, String)>>(
/// "bread=baguette&cheese=comt%C3%A9&meat=ham&fat=butter"),
/// Ok(meal));
/// ```
pub fn from_str<'de, T>(input: &'de str) -> Result<T, Error>
where
T: de::Deserialize<'de>,
{
from_bytes(input.as_bytes())
}
/// Convenience function that reads all bytes from `reader` and deserializes
/// them with `from_bytes`.
pub fn from_reader<T, R>(mut reader: R) -> Result<T, Error>
where
T: de::DeserializeOwned,
R: Read,
{
let mut buf = vec![];
reader.read_to_end(&mut buf).map_err(|e| {
de::Error::custom(format_args!("could not read input: {}", e))
})?;
from_bytes(&buf)
}
/// A deserializer for the `application/x-www-form-urlencoded` format.
///
/// * Supported top-level outputs are structs, maps and sequences of pairs,
/// with or without a given length.
///
/// * Main `deserialize` methods defers to `deserialize_map`.
///
/// * Everything else but `deserialize_seq` and `deserialize_seq_fixed_size`
/// defers to `deserialize`.
pub struct Deserializer<'de> {
inner: MapDeserializer<'de, EntryIterator<'de>, Error>,
}
impl<'de> Deserializer<'de> {
/// Returns a new `Deserializer`.
pub fn new(parse: UrlEncodedParse<'de>) -> Self {
Deserializer {
inner: MapDeserializer::new(group_entries(parse).into_iter()),
}
}
}
impl<'de> de::Deserializer<'de> for Deserializer<'de> {
type Error = Error;
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
self.deserialize_map(visitor)
}
fn deserialize_map<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
visitor.visit_map(self.inner)
}
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
visitor.visit_seq(self.inner)
}
fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
self.inner.end()?;
visitor.visit_unit()
}
forward_to_deserialize_any! {
bool
u8
u16
u32
u64
i8
i16
i32
i64
f32
f64
char
str
string
option
bytes
byte_buf
unit_struct
newtype_struct
tuple_struct
struct
identifier
tuple
enum
ignored_any
}
}
fn group_entries<'de>(
parse: UrlEncodedParse<'de>,
) -> BTreeMap<Part<'de>, ValOrVec<Part<'de>>> {
use btree_map::Entry::*;
let mut res = BTreeMap::new();
for (key, value) in parse {
match res.entry(Part(key)) {
Vacant(v) => {
v.insert(ValOrVec::Val(Part(value)));
}
Occupied(mut o) => {
o.get_mut().push(Part(value));
}
}
}
res
}
/*
input: a=b&c=d&a=c
vvvvv
next(): a => Wrapper([b, c])
next(): c => Wrapper([d])
struct Foo {
a: Vec<String>,
c: Vec<String>,
}
struct Bar {
a: Vec<String>,
c: String,
}
struct Baz {
a: String,
}
*/
type EntryIterator<'de> = btree_map::IntoIter<Part<'de>, ValOrVec<Part<'de>>>;
#[derive(PartialEq, PartialOrd, Eq, Ord)]
struct Part<'de>(Cow<'de, str>);
impl<'de> IntoDeserializer<'de> for Part<'de> {
type Deserializer = Self;
fn into_deserializer(self) -> Self::Deserializer {
self
}
}
macro_rules! forward_parsed_value {
($($ty:ident => $method:ident,)*) => {
$(
fn $method<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where V: de::Visitor<'de>
{
match self.0.parse::<$ty>() {
Ok(val) => val.into_deserializer().$method(visitor),
Err(e) => Err(de::Error::custom(e))
}
}
)*
}
}
impl<'de> de::Deserializer<'de> for Part<'de> {
type Error = Error;
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
match self.0 {
Cow::Borrowed(value) => visitor.visit_borrowed_str(value),
Cow::Owned(value) => visitor.visit_string(value),
}
}
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
visitor.visit_some(self)
}
fn deserialize_enum<V>(
self,
_name: &'static str,
_variants: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
visitor.visit_enum(ValueEnumAccess(self.0))
}
fn deserialize_newtype_struct<V>(
self,
_name: &'static str,
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
visitor.visit_newtype_struct(self)
}
forward_to_deserialize_any! {
char
str
string
unit
bytes
byte_buf
unit_struct
tuple_struct
struct
identifier
tuple
ignored_any
seq
map
}
forward_parsed_value! {
bool => deserialize_bool,
u8 => deserialize_u8,
u16 => deserialize_u16,
u32 => deserialize_u32,
u64 => deserialize_u64,
i8 => deserialize_i8,
i16 => deserialize_i16,
i32 => deserialize_i32,
i64 => deserialize_i64,
f32 => deserialize_f32,
f64 => deserialize_f64,
}
}
struct ValueEnumAccess<'de>(Cow<'de, str>);
impl<'de> de::EnumAccess<'de> for ValueEnumAccess<'de> {
type Error = Error;
type Variant = UnitOnlyVariantAccess;
fn variant_seed<V>(
self,
seed: V,
) -> Result<(V::Value, Self::Variant), Self::Error>
where
V: de::DeserializeSeed<'de>,
{
let variant = seed.deserialize(self.0.into_deserializer())?;
Ok((variant, UnitOnlyVariantAccess))
}
}
struct UnitOnlyVariantAccess;
impl<'de> de::VariantAccess<'de> for UnitOnlyVariantAccess {
type Error = Error;
fn unit_variant(self) -> Result<(), Self::Error> {
Ok(())
}
fn newtype_variant_seed<T>(self, _seed: T) -> Result<T::Value, Self::Error>
where
T: de::DeserializeSeed<'de>,
{
Err(Error::custom("expected unit variant"))
}
fn tuple_variant<V>(
self,
_len: usize,
_visitor: V,
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
Err(Error::custom("expected unit variant"))
}
fn struct_variant<V>(
self,
_fields: &'static [&'static str],
_visitor: V,
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
Err(Error::custom("expected unit variant"))
}
}

View File

@ -0,0 +1,264 @@
use std::{iter, ptr, vec};
use serde::de::{
self,
value::{Error, SeqDeserializer},
Deserializer, IntoDeserializer,
};
#[derive(Debug)]
pub enum ValOrVec<T> {
Val(T),
Vec(Vec<T>),
}
impl<T> ValOrVec<T> {
pub fn push(&mut self, new_val: T) {
match self {
// To transform a Self::Val into a Self::Vec, we take the existing
// value out via ptr::read and add it to a vector, together with the
// new value. Since setting self to `ValOrVec::Vec` normally would
// cause T's Drop implementation to run if it has one (which would
// free resources that will now be owned by the first vec element),
// we instead use ptr::write to set self to Self::Vec.
ValOrVec::Val(val) => {
let mut vec = Vec::with_capacity(2);
// Safety: since the vec is pre-allocated, push can't panic, so
// there is no opportunity for outside code to observe an
// invalid state of self.
unsafe {
let existing_val = ptr::read(val);
vec.push(existing_val);
vec.push(new_val);
ptr::write(self, ValOrVec::Vec(vec))
}
}
ValOrVec::Vec(vec) => vec.push(new_val),
}
}
}
impl<T> IntoIterator for ValOrVec<T> {
type Item = T;
type IntoIter = IntoIter<T>;
fn into_iter(self) -> Self::IntoIter {
IntoIter::new(self)
}
}
pub enum IntoIter<T> {
Val(iter::Once<T>),
Vec(vec::IntoIter<T>),
}
impl<T> IntoIter<T> {
fn new(vv: ValOrVec<T>) -> Self {
match vv {
ValOrVec::Val(val) => IntoIter::Val(iter::once(val)),
ValOrVec::Vec(vec) => IntoIter::Vec(vec.into_iter()),
}
}
}
impl<T> Iterator for IntoIter<T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
match self {
IntoIter::Val(iter) => iter.next(),
IntoIter::Vec(iter) => iter.next(),
}
}
}
impl<'de, T> IntoDeserializer<'de> for ValOrVec<T>
where
T: IntoDeserializer<'de> + Deserializer<'de, Error = Error>,
{
type Deserializer = Self;
fn into_deserializer(self) -> Self::Deserializer {
self
}
}
macro_rules! forward_to_part {
($($method:ident,)*) => {
$(
fn $method<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where V: de::Visitor<'de>
{
match self {
ValOrVec::Val(val) => val.$method(visitor),
ValOrVec::Vec(_) => Err(de::Error::custom("unsupported value")),
}
}
)*
}
}
impl<'de, T> Deserializer<'de> for ValOrVec<T>
where
T: IntoDeserializer<'de> + Deserializer<'de, Error = Error>,
{
type Error = Error;
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
match self {
ValOrVec::Val(val) => val.deserialize_any(visitor),
ValOrVec::Vec(_) => self.deserialize_seq(visitor),
}
}
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
visitor.visit_seq(SeqDeserializer::new(self.into_iter()))
}
fn deserialize_enum<V>(
self,
name: &'static str,
variants: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
match self {
ValOrVec::Val(val) => val.deserialize_enum(name, variants, visitor),
ValOrVec::Vec(_) => Err(de::Error::custom("unsupported value")),
}
}
fn deserialize_tuple<V>(
self,
len: usize,
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
match self {
ValOrVec::Val(val) => val.deserialize_tuple(len, visitor),
ValOrVec::Vec(_) => Err(de::Error::custom("unsupported value")),
}
}
fn deserialize_struct<V>(
self,
name: &'static str,
fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
match self {
ValOrVec::Val(val) => val.deserialize_struct(name, fields, visitor),
ValOrVec::Vec(_) => Err(de::Error::custom("unsupported value")),
}
}
fn deserialize_unit_struct<V>(
self,
name: &'static str,
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
match self {
ValOrVec::Val(val) => val.deserialize_unit_struct(name, visitor),
ValOrVec::Vec(_) => Err(de::Error::custom("unsupported value")),
}
}
fn deserialize_tuple_struct<V>(
self,
name: &'static str,
len: usize,
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
match self {
ValOrVec::Val(val) => {
val.deserialize_tuple_struct(name, len, visitor)
}
ValOrVec::Vec(_) => Err(de::Error::custom("unsupported value")),
}
}
fn deserialize_newtype_struct<V>(
self,
name: &'static str,
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
match self {
ValOrVec::Val(val) => val.deserialize_newtype_struct(name, visitor),
ValOrVec::Vec(_) => Err(de::Error::custom("unsupported value")),
}
}
forward_to_part! {
deserialize_bool,
deserialize_char,
deserialize_str,
deserialize_string,
deserialize_bytes,
deserialize_byte_buf,
deserialize_unit,
deserialize_u8,
deserialize_u16,
deserialize_u32,
deserialize_u64,
deserialize_i8,
deserialize_i16,
deserialize_i32,
deserialize_i64,
deserialize_f32,
deserialize_f64,
deserialize_option,
deserialize_identifier,
deserialize_ignored_any,
deserialize_map,
}
}
#[cfg(test)]
mod tests {
use std::borrow::Cow;
use matches::assert_matches;
use super::ValOrVec;
#[test]
fn cow_borrowed() {
let mut x = ValOrVec::Val(Cow::Borrowed("a"));
x.push(Cow::Borrowed("b"));
x.push(Cow::Borrowed("c"));
assert_matches!(x, ValOrVec::Vec(v) if v == vec!["a", "b", "c"]);
}
#[test]
fn cow_owned() {
let mut x = ValOrVec::Val(Cow::from("a".to_owned()));
x.push(Cow::from("b".to_owned()));
x.push(Cow::from("c".to_owned()));
assert_matches!(
x,
ValOrVec::Vec(v) if v == vec!["a".to_owned(), "b".to_owned(), "c".to_owned()]
);
}
}

View File

@ -0,0 +1,558 @@
//! Serialization support for the `application/x-www-form-urlencoded` format.
mod key;
mod pair;
mod part;
mod value;
use std::{borrow::Cow, error, fmt, str};
use serde::ser;
use url::form_urlencoded::{
Serializer as UrlEncodedSerializer, Target as UrlEncodedTarget,
};
/// Serializes a value into a `application/x-www-form-urlencoded` `String` buffer.
///
/// ```
/// let meal = &[
/// ("bread", "baguette"),
/// ("cheese", "comté"),
/// ("meat", "ham"),
/// ("fat", "butter"),
/// ];
///
/// assert_eq!(
/// ruma_serde::urlencoded::to_string(meal),
/// Ok("bread=baguette&cheese=comt%C3%A9&meat=ham&fat=butter".to_owned()));
/// ```
pub fn to_string<T: ser::Serialize>(input: T) -> Result<String, Error> {
let mut urlencoder = UrlEncodedSerializer::new("".to_owned());
input.serialize(Serializer::new(&mut urlencoder))?;
Ok(urlencoder.finish())
}
/// A serializer for the `application/x-www-form-urlencoded` format.
///
/// * Supported top-level inputs are structs, maps and sequences of pairs,
/// with or without a given length.
///
/// * Supported keys and values are integers, bytes (if convertible to strings),
/// unit structs and unit variants.
///
/// * Newtype structs defer to their inner values.
pub struct Serializer<'input, 'output, Target: 'output + UrlEncodedTarget> {
urlencoder: &'output mut UrlEncodedSerializer<'input, Target>,
}
impl<'input, 'output, Target: 'output + UrlEncodedTarget>
Serializer<'input, 'output, Target>
{
/// Returns a new `Serializer`.
pub fn new(
urlencoder: &'output mut UrlEncodedSerializer<'input, Target>,
) -> Self {
Serializer { urlencoder }
}
}
/// Errors returned during serializing to `application/x-www-form-urlencoded`.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Error {
Custom(Cow<'static, str>),
Utf8(str::Utf8Error),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::Custom(ref msg) => msg.fmt(f),
Error::Utf8(ref err) => write!(f, "invalid UTF-8: {}", err),
}
}
}
impl error::Error for Error {
/// The lower-level cause of this error, in the case of a `Utf8` error.
fn cause(&self) -> Option<&dyn error::Error> {
match *self {
Error::Custom(_) => None,
Error::Utf8(ref err) => Some(err),
}
}
/// The lower-level source of this error, in the case of a `Utf8` error.
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match *self {
Error::Custom(_) => None,
Error::Utf8(ref err) => Some(err),
}
}
}
impl ser::Error for Error {
fn custom<T: fmt::Display>(msg: T) -> Self {
Error::Custom(format!("{}", msg).into())
}
}
/// Sequence serializer.
pub struct SeqSerializer<'input, 'output, Target: 'output + UrlEncodedTarget> {
urlencoder: &'output mut UrlEncodedSerializer<'input, Target>,
}
/// Tuple serializer.
///
/// Mostly used for arrays.
pub struct TupleSerializer<'input, 'output, Target: 'output + UrlEncodedTarget>
{
urlencoder: &'output mut UrlEncodedSerializer<'input, Target>,
}
/// Tuple struct serializer.
///
/// Never instantiated, tuple structs are not supported.
pub struct TupleStructSerializer<'input, 'output, T: 'output + UrlEncodedTarget>
{
inner: ser::Impossible<&'output mut UrlEncodedSerializer<'input, T>, Error>,
}
/// Tuple variant serializer.
///
/// Never instantiated, tuple variants are not supported.
pub struct TupleVariantSerializer<
'input,
'output,
T: 'output + UrlEncodedTarget,
> {
inner: ser::Impossible<&'output mut UrlEncodedSerializer<'input, T>, Error>,
}
/// Map serializer.
pub struct MapSerializer<'input, 'output, Target: 'output + UrlEncodedTarget> {
urlencoder: &'output mut UrlEncodedSerializer<'input, Target>,
key: Option<Cow<'static, str>>,
}
/// Struct serializer.
pub struct StructSerializer<'input, 'output, Target: 'output + UrlEncodedTarget>
{
urlencoder: &'output mut UrlEncodedSerializer<'input, Target>,
}
/// Struct variant serializer.
///
/// Never instantiated, struct variants are not supported.
pub struct StructVariantSerializer<
'input,
'output,
T: 'output + UrlEncodedTarget,
> {
inner: ser::Impossible<&'output mut UrlEncodedSerializer<'input, T>, Error>,
}
impl<'input, 'output, Target> ser::Serializer
for Serializer<'input, 'output, Target>
where
Target: 'output + UrlEncodedTarget,
{
type Ok = &'output mut UrlEncodedSerializer<'input, Target>;
type Error = Error;
type SerializeSeq = SeqSerializer<'input, 'output, Target>;
type SerializeTuple = TupleSerializer<'input, 'output, Target>;
type SerializeTupleStruct = TupleStructSerializer<'input, 'output, Target>;
type SerializeTupleVariant =
TupleVariantSerializer<'input, 'output, Target>;
type SerializeMap = MapSerializer<'input, 'output, Target>;
type SerializeStruct = StructSerializer<'input, 'output, Target>;
type SerializeStructVariant =
StructVariantSerializer<'input, 'output, Target>;
/// Returns an error.
fn serialize_bool(self, _v: bool) -> Result<Self::Ok, Error> {
Err(Error::top_level())
}
/// Returns an error.
fn serialize_i8(self, _v: i8) -> Result<Self::Ok, Error> {
Err(Error::top_level())
}
/// Returns an error.
fn serialize_i16(self, _v: i16) -> Result<Self::Ok, Error> {
Err(Error::top_level())
}
/// Returns an error.
fn serialize_i32(self, _v: i32) -> Result<Self::Ok, Error> {
Err(Error::top_level())
}
/// Returns an error.
fn serialize_i64(self, _v: i64) -> Result<Self::Ok, Error> {
Err(Error::top_level())
}
/// Returns an error.
fn serialize_u8(self, _v: u8) -> Result<Self::Ok, Error> {
Err(Error::top_level())
}
/// Returns an error.
fn serialize_u16(self, _v: u16) -> Result<Self::Ok, Error> {
Err(Error::top_level())
}
/// Returns an error.
fn serialize_u32(self, _v: u32) -> Result<Self::Ok, Error> {
Err(Error::top_level())
}
/// Returns an error.
fn serialize_u64(self, _v: u64) -> Result<Self::Ok, Error> {
Err(Error::top_level())
}
/// Returns an error.
fn serialize_f32(self, _v: f32) -> Result<Self::Ok, Error> {
Err(Error::top_level())
}
/// Returns an error.
fn serialize_f64(self, _v: f64) -> Result<Self::Ok, Error> {
Err(Error::top_level())
}
/// Returns an error.
fn serialize_char(self, _v: char) -> Result<Self::Ok, Error> {
Err(Error::top_level())
}
/// Returns an error.
fn serialize_str(self, _value: &str) -> Result<Self::Ok, Error> {
Err(Error::top_level())
}
/// Returns an error.
fn serialize_bytes(self, _value: &[u8]) -> Result<Self::Ok, Error> {
Err(Error::top_level())
}
/// Returns `Ok`.
fn serialize_unit(self) -> Result<Self::Ok, Error> {
Ok(self.urlencoder)
}
/// Returns `Ok`.
fn serialize_unit_struct(
self,
_name: &'static str,
) -> Result<Self::Ok, Error> {
Ok(self.urlencoder)
}
/// Returns an error.
fn serialize_unit_variant(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
) -> Result<Self::Ok, Error> {
Err(Error::top_level())
}
/// Serializes the inner value, ignoring the newtype name.
fn serialize_newtype_struct<T: ?Sized + ser::Serialize>(
self,
_name: &'static str,
value: &T,
) -> Result<Self::Ok, Error> {
value.serialize(self)
}
/// Returns an error.
fn serialize_newtype_variant<T: ?Sized + ser::Serialize>(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_value: &T,
) -> Result<Self::Ok, Error> {
Err(Error::top_level())
}
/// Returns `Ok`.
fn serialize_none(self) -> Result<Self::Ok, Error> {
Ok(self.urlencoder)
}
/// Serializes the given value.
fn serialize_some<T: ?Sized + ser::Serialize>(
self,
value: &T,
) -> Result<Self::Ok, Error> {
value.serialize(self)
}
/// Serialize a sequence, given length (if any) is ignored.
fn serialize_seq(
self,
_len: Option<usize>,
) -> Result<Self::SerializeSeq, Error> {
Ok(SeqSerializer {
urlencoder: self.urlencoder,
})
}
/// Returns an error.
fn serialize_tuple(
self,
_len: usize,
) -> Result<Self::SerializeTuple, Error> {
Ok(TupleSerializer {
urlencoder: self.urlencoder,
})
}
/// Returns an error.
fn serialize_tuple_struct(
self,
_name: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleStruct, Error> {
Err(Error::top_level())
}
/// Returns an error.
fn serialize_tuple_variant(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleVariant, Error> {
Err(Error::top_level())
}
/// Serializes a map, given length is ignored.
fn serialize_map(
self,
_len: Option<usize>,
) -> Result<Self::SerializeMap, Error> {
Ok(MapSerializer {
urlencoder: self.urlencoder,
key: None,
})
}
/// Serializes a struct, given length is ignored.
fn serialize_struct(
self,
_name: &'static str,
_len: usize,
) -> Result<Self::SerializeStruct, Error> {
Ok(StructSerializer {
urlencoder: self.urlencoder,
})
}
/// Returns an error.
fn serialize_struct_variant(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_len: usize,
) -> Result<Self::SerializeStructVariant, Error> {
Err(Error::top_level())
}
}
impl<'input, 'output, Target> ser::SerializeSeq
for SeqSerializer<'input, 'output, Target>
where
Target: 'output + UrlEncodedTarget,
{
type Ok = &'output mut UrlEncodedSerializer<'input, Target>;
type Error = Error;
fn serialize_element<T: ?Sized + ser::Serialize>(
&mut self,
value: &T,
) -> Result<(), Error> {
value.serialize(pair::PairSerializer::new(self.urlencoder))
}
fn end(self) -> Result<Self::Ok, Error> {
Ok(self.urlencoder)
}
}
impl<'input, 'output, Target> ser::SerializeTuple
for TupleSerializer<'input, 'output, Target>
where
Target: 'output + UrlEncodedTarget,
{
type Ok = &'output mut UrlEncodedSerializer<'input, Target>;
type Error = Error;
fn serialize_element<T: ?Sized + ser::Serialize>(
&mut self,
value: &T,
) -> Result<(), Error> {
value.serialize(pair::PairSerializer::new(self.urlencoder))
}
fn end(self) -> Result<Self::Ok, Error> {
Ok(self.urlencoder)
}
}
impl<'input, 'output, Target> ser::SerializeTupleStruct
for TupleStructSerializer<'input, 'output, Target>
where
Target: 'output + UrlEncodedTarget,
{
type Ok = &'output mut UrlEncodedSerializer<'input, Target>;
type Error = Error;
fn serialize_field<T: ?Sized + ser::Serialize>(
&mut self,
value: &T,
) -> Result<(), Error> {
self.inner.serialize_field(value)
}
fn end(self) -> Result<Self::Ok, Error> {
self.inner.end()
}
}
impl<'input, 'output, Target> ser::SerializeTupleVariant
for TupleVariantSerializer<'input, 'output, Target>
where
Target: 'output + UrlEncodedTarget,
{
type Ok = &'output mut UrlEncodedSerializer<'input, Target>;
type Error = Error;
fn serialize_field<T: ?Sized + ser::Serialize>(
&mut self,
value: &T,
) -> Result<(), Error> {
self.inner.serialize_field(value)
}
fn end(self) -> Result<Self::Ok, Error> {
self.inner.end()
}
}
impl<'input, 'output, Target> ser::SerializeMap
for MapSerializer<'input, 'output, Target>
where
Target: 'output + UrlEncodedTarget,
{
type Ok = &'output mut UrlEncodedSerializer<'input, Target>;
type Error = Error;
fn serialize_entry<
K: ?Sized + ser::Serialize,
V: ?Sized + ser::Serialize,
>(
&mut self,
key: &K,
value: &V,
) -> Result<(), Error> {
let key_sink = key::KeySink::new(|key| {
let value_sink = value::ValueSink::new(self.urlencoder, &key);
value.serialize(part::PartSerializer::new(value_sink))?;
self.key = None;
Ok(())
});
let entry_serializer = part::PartSerializer::new(key_sink);
key.serialize(entry_serializer)
}
fn serialize_key<T: ?Sized + ser::Serialize>(
&mut self,
key: &T,
) -> Result<(), Error> {
let key_sink = key::KeySink::new(|key| Ok(key.into()));
let key_serializer = part::PartSerializer::new(key_sink);
self.key = Some(key.serialize(key_serializer)?);
Ok(())
}
fn serialize_value<T: ?Sized + ser::Serialize>(
&mut self,
value: &T,
) -> Result<(), Error> {
{
let key = self.key.as_ref().ok_or_else(Error::no_key)?;
let value_sink = value::ValueSink::new(self.urlencoder, &key);
value.serialize(part::PartSerializer::new(value_sink))?;
}
self.key = None;
Ok(())
}
fn end(self) -> Result<Self::Ok, Error> {
Ok(self.urlencoder)
}
}
impl<'input, 'output, Target> ser::SerializeStruct
for StructSerializer<'input, 'output, Target>
where
Target: 'output + UrlEncodedTarget,
{
type Ok = &'output mut UrlEncodedSerializer<'input, Target>;
type Error = Error;
fn serialize_field<T: ?Sized + ser::Serialize>(
&mut self,
key: &'static str,
value: &T,
) -> Result<(), Error> {
let value_sink = value::ValueSink::new(self.urlencoder, key);
value.serialize(part::PartSerializer::new(value_sink))
}
fn end(self) -> Result<Self::Ok, Error> {
Ok(self.urlencoder)
}
}
impl<'input, 'output, Target> ser::SerializeStructVariant
for StructVariantSerializer<'input, 'output, Target>
where
Target: 'output + UrlEncodedTarget,
{
type Ok = &'output mut UrlEncodedSerializer<'input, Target>;
type Error = Error;
fn serialize_field<T: ?Sized + ser::Serialize>(
&mut self,
key: &'static str,
value: &T,
) -> Result<(), Error> {
self.inner.serialize_field(key, value)
}
fn end(self) -> Result<Self::Ok, Error> {
self.inner.end()
}
}
impl Error {
fn top_level() -> Self {
let msg = "top-level serializer supports only maps and structs";
Error::Custom(msg.into())
}
fn no_key() -> Self {
let msg = "tried to serialize a value before serializing key";
Error::Custom(msg.into())
}
}

View File

@ -0,0 +1,82 @@
use std::{borrow::Cow, ops::Deref};
use serde::ser;
use super::{part::Sink, Error};
pub enum Key<'key> {
Static(&'static str),
Dynamic(Cow<'key, str>),
}
impl<'key> Deref for Key<'key> {
type Target = str;
fn deref(&self) -> &str {
match *self {
Key::Static(key) => key,
Key::Dynamic(ref key) => key,
}
}
}
impl<'key> From<Key<'key>> for Cow<'static, str> {
fn from(key: Key<'key>) -> Self {
match key {
Key::Static(key) => key.into(),
Key::Dynamic(key) => key.into_owned().into(),
}
}
}
pub struct KeySink<End> {
end: End,
}
impl<End, Ok> KeySink<End>
where
End: for<'key> FnOnce(Key<'key>) -> Result<Ok, Error>,
{
pub fn new(end: End) -> Self {
KeySink { end }
}
}
impl<End, Ok> Sink for KeySink<End>
where
End: for<'key> FnOnce(Key<'key>) -> Result<Ok, Error>,
{
type Ok = Ok;
type SerializeSeq = ser::Impossible<Self::Ok, Error>;
fn serialize_static_str(self, value: &'static str) -> Result<Ok, Error> {
(self.end)(Key::Static(value))
}
fn serialize_str(self, value: &str) -> Result<Ok, Error> {
(self.end)(Key::Dynamic(value.into()))
}
fn serialize_string(self, value: String) -> Result<Ok, Error> {
(self.end)(Key::Dynamic(value.into()))
}
fn serialize_none(self) -> Result<Ok, Error> {
Err(self.unsupported())
}
fn serialize_some<T: ?Sized + ser::Serialize>(
self,
_value: &T,
) -> Result<Ok, Error> {
Err(self.unsupported())
}
fn serialize_seq(self) -> Result<Self::SerializeSeq, Error> {
Err(self.unsupported())
}
fn unsupported(self) -> Error {
Error::Custom("unsupported key".into())
}
}

View File

@ -0,0 +1,270 @@
use std::{borrow::Cow, mem};
use serde::ser;
use url::form_urlencoded::{
Serializer as UrlEncodedSerializer, Target as UrlEncodedTarget,
};
use super::{key::KeySink, part::PartSerializer, value::ValueSink, Error};
pub struct PairSerializer<'input, 'target, Target: 'target + UrlEncodedTarget> {
urlencoder: &'target mut UrlEncodedSerializer<'input, Target>,
state: PairState,
}
impl<'input, 'target, Target> PairSerializer<'input, 'target, Target>
where
Target: 'target + UrlEncodedTarget,
{
pub fn new(
urlencoder: &'target mut UrlEncodedSerializer<'input, Target>,
) -> Self {
PairSerializer {
urlencoder,
state: PairState::WaitingForKey,
}
}
}
impl<'input, 'target, Target> ser::Serializer
for PairSerializer<'input, 'target, Target>
where
Target: 'target + UrlEncodedTarget,
{
type Ok = ();
type Error = Error;
type SerializeSeq = ser::Impossible<(), Error>;
type SerializeTuple = Self;
type SerializeTupleStruct = ser::Impossible<(), Error>;
type SerializeTupleVariant = ser::Impossible<(), Error>;
type SerializeMap = ser::Impossible<(), Error>;
type SerializeStruct = ser::Impossible<(), Error>;
type SerializeStructVariant = ser::Impossible<(), Error>;
fn serialize_bool(self, _v: bool) -> Result<(), Error> {
Err(Error::unsupported_pair())
}
fn serialize_i8(self, _v: i8) -> Result<(), Error> {
Err(Error::unsupported_pair())
}
fn serialize_i16(self, _v: i16) -> Result<(), Error> {
Err(Error::unsupported_pair())
}
fn serialize_i32(self, _v: i32) -> Result<(), Error> {
Err(Error::unsupported_pair())
}
fn serialize_i64(self, _v: i64) -> Result<(), Error> {
Err(Error::unsupported_pair())
}
fn serialize_u8(self, _v: u8) -> Result<(), Error> {
Err(Error::unsupported_pair())
}
fn serialize_u16(self, _v: u16) -> Result<(), Error> {
Err(Error::unsupported_pair())
}
fn serialize_u32(self, _v: u32) -> Result<(), Error> {
Err(Error::unsupported_pair())
}
fn serialize_u64(self, _v: u64) -> Result<(), Error> {
Err(Error::unsupported_pair())
}
fn serialize_f32(self, _v: f32) -> Result<(), Error> {
Err(Error::unsupported_pair())
}
fn serialize_f64(self, _v: f64) -> Result<(), Error> {
Err(Error::unsupported_pair())
}
fn serialize_char(self, _v: char) -> Result<(), Error> {
Err(Error::unsupported_pair())
}
fn serialize_str(self, _value: &str) -> Result<(), Error> {
Err(Error::unsupported_pair())
}
fn serialize_bytes(self, _value: &[u8]) -> Result<(), Error> {
Err(Error::unsupported_pair())
}
fn serialize_unit(self) -> Result<(), Error> {
Err(Error::unsupported_pair())
}
fn serialize_unit_struct(self, _name: &'static str) -> Result<(), Error> {
Err(Error::unsupported_pair())
}
fn serialize_unit_variant(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
) -> Result<(), Error> {
Err(Error::unsupported_pair())
}
fn serialize_newtype_struct<T: ?Sized + ser::Serialize>(
self,
_name: &'static str,
value: &T,
) -> Result<(), Error> {
value.serialize(self)
}
fn serialize_newtype_variant<T: ?Sized + ser::Serialize>(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_value: &T,
) -> Result<(), Error> {
Err(Error::unsupported_pair())
}
fn serialize_none(self) -> Result<(), Error> {
Ok(())
}
fn serialize_some<T: ?Sized + ser::Serialize>(
self,
value: &T,
) -> Result<(), Error> {
value.serialize(self)
}
fn serialize_seq(
self,
_len: Option<usize>,
) -> Result<Self::SerializeSeq, Error> {
Err(Error::unsupported_pair())
}
fn serialize_tuple(self, len: usize) -> Result<Self, Error> {
if len == 2 {
Ok(self)
} else {
Err(Error::unsupported_pair())
}
}
fn serialize_tuple_struct(
self,
_name: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleStruct, Error> {
Err(Error::unsupported_pair())
}
fn serialize_tuple_variant(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleVariant, Error> {
Err(Error::unsupported_pair())
}
fn serialize_map(
self,
_len: Option<usize>,
) -> Result<Self::SerializeMap, Error> {
Err(Error::unsupported_pair())
}
fn serialize_struct(
self,
_name: &'static str,
_len: usize,
) -> Result<Self::SerializeStruct, Error> {
Err(Error::unsupported_pair())
}
fn serialize_struct_variant(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_len: usize,
) -> Result<Self::SerializeStructVariant, Error> {
Err(Error::unsupported_pair())
}
}
impl<'input, 'target, Target> ser::SerializeTuple
for PairSerializer<'input, 'target, Target>
where
Target: 'target + UrlEncodedTarget,
{
type Ok = ();
type Error = Error;
fn serialize_element<T: ?Sized + ser::Serialize>(
&mut self,
value: &T,
) -> Result<(), Error> {
match mem::replace(&mut self.state, PairState::Done) {
PairState::WaitingForKey => {
let key_sink = KeySink::new(|key| Ok(key.into()));
let key_serializer = PartSerializer::new(key_sink);
self.state = PairState::WaitingForValue {
key: value.serialize(key_serializer)?,
};
Ok(())
}
PairState::WaitingForValue { key } => {
let result = {
let value_sink = ValueSink::new(self.urlencoder, &key);
let value_serializer = PartSerializer::new(value_sink);
value.serialize(value_serializer)
};
if result.is_ok() {
self.state = PairState::Done;
} else {
self.state = PairState::WaitingForValue { key };
}
result
}
PairState::Done => Err(Error::done()),
}
}
fn end(self) -> Result<(), Error> {
if let PairState::Done = self.state {
Ok(())
} else {
Err(Error::not_done())
}
}
}
enum PairState {
WaitingForKey,
WaitingForValue { key: Cow<'static, str> },
Done,
}
impl Error {
fn done() -> Self {
Error::Custom("this pair has already been serialized".into())
}
fn not_done() -> Self {
Error::Custom("this pair has not yet been serialized".into())
}
fn unsupported_pair() -> Self {
Error::Custom("unsupported pair".into())
}
}

View File

@ -0,0 +1,235 @@
use std::str;
use serde::ser;
use super::Error;
pub struct PartSerializer<S> {
sink: S,
}
impl<S: Sink> PartSerializer<S> {
pub fn new(sink: S) -> Self {
PartSerializer { sink }
}
}
pub trait Sink: Sized {
type Ok;
type SerializeSeq: ser::SerializeSeq<Ok = Self::Ok, Error = Error>;
fn serialize_static_str(
self,
value: &'static str,
) -> Result<Self::Ok, Error>;
fn serialize_str(self, value: &str) -> Result<Self::Ok, Error>;
fn serialize_string(self, value: String) -> Result<Self::Ok, Error>;
fn serialize_none(self) -> Result<Self::Ok, Error>;
fn serialize_some<T: ?Sized + ser::Serialize>(
self,
value: &T,
) -> Result<Self::Ok, Error>;
fn serialize_seq(self) -> Result<Self::SerializeSeq, Error>;
fn unsupported(self) -> Error;
}
impl<S: Sink> ser::Serializer for PartSerializer<S> {
type Ok = S::Ok;
type Error = Error;
type SerializeSeq = S::SerializeSeq;
type SerializeTuple = ser::Impossible<S::Ok, Error>;
type SerializeTupleStruct = ser::Impossible<S::Ok, Error>;
type SerializeTupleVariant = ser::Impossible<S::Ok, Error>;
type SerializeMap = ser::Impossible<S::Ok, Error>;
type SerializeStruct = ser::Impossible<S::Ok, Error>;
type SerializeStructVariant = ser::Impossible<S::Ok, Error>;
fn serialize_bool(self, v: bool) -> Result<S::Ok, Error> {
self.sink
.serialize_static_str(if v { "true" } else { "false" })
}
fn serialize_i8(self, v: i8) -> Result<S::Ok, Error> {
self.serialize_integer(v)
}
fn serialize_i16(self, v: i16) -> Result<S::Ok, Error> {
self.serialize_integer(v)
}
fn serialize_i32(self, v: i32) -> Result<S::Ok, Error> {
self.serialize_integer(v)
}
fn serialize_i64(self, v: i64) -> Result<S::Ok, Error> {
self.serialize_integer(v)
}
fn serialize_u8(self, v: u8) -> Result<S::Ok, Error> {
self.serialize_integer(v)
}
fn serialize_u16(self, v: u16) -> Result<S::Ok, Error> {
self.serialize_integer(v)
}
fn serialize_u32(self, v: u32) -> Result<S::Ok, Error> {
self.serialize_integer(v)
}
fn serialize_u64(self, v: u64) -> Result<S::Ok, Error> {
self.serialize_integer(v)
}
fn serialize_f32(self, v: f32) -> Result<S::Ok, Error> {
self.serialize_floating(v)
}
fn serialize_f64(self, v: f64) -> Result<S::Ok, Error> {
self.serialize_floating(v)
}
fn serialize_char(self, v: char) -> Result<S::Ok, Error> {
self.sink.serialize_string(v.to_string())
}
fn serialize_str(self, value: &str) -> Result<S::Ok, Error> {
self.sink.serialize_str(value)
}
fn serialize_bytes(self, value: &[u8]) -> Result<S::Ok, Error> {
match str::from_utf8(value) {
Ok(value) => self.sink.serialize_str(value),
Err(err) => Err(Error::Utf8(err)),
}
}
fn serialize_unit(self) -> Result<S::Ok, Error> {
Err(self.sink.unsupported())
}
fn serialize_unit_struct(self, name: &'static str) -> Result<S::Ok, Error> {
self.sink.serialize_static_str(name)
}
fn serialize_unit_variant(
self,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
) -> Result<S::Ok, Error> {
self.sink.serialize_static_str(variant)
}
fn serialize_newtype_struct<T: ?Sized + ser::Serialize>(
self,
_name: &'static str,
value: &T,
) -> Result<S::Ok, Error> {
value.serialize(self)
}
fn serialize_newtype_variant<T: ?Sized + ser::Serialize>(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_value: &T,
) -> Result<S::Ok, Error> {
Err(self.sink.unsupported())
}
fn serialize_none(self) -> Result<S::Ok, Error> {
self.sink.serialize_none()
}
fn serialize_some<T: ?Sized + ser::Serialize>(
self,
value: &T,
) -> Result<S::Ok, Error> {
self.sink.serialize_some(value)
}
fn serialize_seq(
self,
_len: Option<usize>,
) -> Result<Self::SerializeSeq, Error> {
self.sink.serialize_seq()
}
fn serialize_tuple(
self,
_len: usize,
) -> Result<Self::SerializeTuple, Error> {
Err(self.sink.unsupported())
}
fn serialize_tuple_struct(
self,
_name: &'static str,
_len: usize,
) -> Result<Self::SerializeTuple, Error> {
Err(self.sink.unsupported())
}
fn serialize_tuple_variant(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleVariant, Error> {
Err(self.sink.unsupported())
}
fn serialize_map(
self,
_len: Option<usize>,
) -> Result<Self::SerializeMap, Error> {
Err(self.sink.unsupported())
}
fn serialize_struct(
self,
_name: &'static str,
_len: usize,
) -> Result<Self::SerializeStruct, Error> {
Err(self.sink.unsupported())
}
fn serialize_struct_variant(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_len: usize,
) -> Result<Self::SerializeStructVariant, Error> {
Err(self.sink.unsupported())
}
}
impl<S: Sink> PartSerializer<S> {
fn serialize_integer<I>(self, value: I) -> Result<S::Ok, Error>
where
I: itoa::Integer,
{
let mut buf = [b'\0'; 20];
let len = itoa::write(&mut buf[..], value).unwrap();
let part = unsafe { str::from_utf8_unchecked(&buf[0..len]) };
ser::Serializer::serialize_str(self, part)
}
fn serialize_floating<F>(self, value: F) -> Result<S::Ok, Error>
where
F: dtoa::Floating,
{
let mut buf = [b'\0'; 24];
let len = dtoa::write(&mut buf[..], value).unwrap();
let part = unsafe { str::from_utf8_unchecked(&buf[0..len]) };
ser::Serializer::serialize_str(self, part)
}
}

View File

@ -0,0 +1,108 @@
use std::str;
use serde::ser;
use url::form_urlencoded::{
Serializer as UrlEncodedSerializer, Target as UrlEncodedTarget,
};
use super::{
part::{PartSerializer, Sink},
Error,
};
pub struct ValueSink<'input, 'key, 'target, Target>
where
Target: 'target + UrlEncodedTarget,
{
urlencoder: &'target mut UrlEncodedSerializer<'input, Target>,
key: &'key str,
nested: bool,
}
impl<'input, 'key, 'target, Target> ValueSink<'input, 'key, 'target, Target>
where
Target: 'target + UrlEncodedTarget,
{
pub fn new(
urlencoder: &'target mut UrlEncodedSerializer<'input, Target>,
key: &'key str,
) -> Self {
ValueSink {
urlencoder,
key,
nested: false,
}
}
}
impl<'input, 'key, 'target, Target> Sink
for ValueSink<'input, 'key, 'target, Target>
where
Target: 'target + UrlEncodedTarget,
{
type Ok = ();
type SerializeSeq = Self;
fn serialize_str(self, value: &str) -> Result<(), Error> {
self.urlencoder.append_pair(self.key, value);
Ok(())
}
fn serialize_static_str(self, value: &'static str) -> Result<(), Error> {
self.serialize_str(value)
}
fn serialize_string(self, value: String) -> Result<(), Error> {
self.serialize_str(&value)
}
fn serialize_none(self) -> Result<Self::Ok, Error> {
Ok(())
}
fn serialize_some<T: ?Sized + ser::Serialize>(
self,
value: &T,
) -> Result<Self::Ok, Error> {
value.serialize(PartSerializer::new(self))
}
fn serialize_seq(self) -> Result<Self, Error> {
if self.nested {
Err(self.unsupported())
} else {
Ok(self)
}
}
fn unsupported(self) -> Error {
Error::Custom("unsupported value".into())
}
}
impl<'input, 'key, 'target, Target> ser::SerializeSeq
for ValueSink<'input, 'key, 'target, Target>
where
Target: 'target + UrlEncodedTarget,
{
type Ok = ();
type Error = Error;
fn serialize_element<T: ?Sized>(
&mut self,
value: &T,
) -> Result<(), Self::Error>
where
T: ser::Serialize,
{
value.serialize(PartSerializer::new(ValueSink {
urlencoder: self.urlencoder,
key: self.key,
nested: true,
}))
}
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
}

View File

@ -0,0 +1,332 @@
use matches::assert_matches;
use ruma_serde::urlencoded;
use serde::Deserialize;
use url::form_urlencoded::Serializer as Encoder;
#[derive(Deserialize, Debug, PartialEq)]
struct NewType<T>(T);
#[test]
fn deserialize_newtype_i32() {
let result = vec![("field".to_owned(), NewType(11))];
assert_eq!(urlencoded::from_str("field=11"), Ok(result));
}
#[test]
fn deserialize_bytes() {
let result = vec![("first".to_owned(), 23), ("last".to_owned(), 42)];
assert_eq!(urlencoded::from_bytes(b"first=23&last=42"), Ok(result));
}
#[test]
fn deserialize_str() {
let result = vec![("first".to_owned(), 23), ("last".to_owned(), 42)];
assert_eq!(urlencoded::from_str("first=23&last=42"), Ok(result));
}
#[test]
fn deserialize_borrowed_str() {
let result = vec![("first", 23), ("last", 42)];
assert_eq!(urlencoded::from_str("first=23&last=42"), Ok(result));
}
#[test]
fn deserialize_reader() {
let result = vec![("first".to_owned(), 23), ("last".to_owned(), 42)];
assert_eq!(
urlencoded::from_reader(b"first=23&last=42" as &[_]),
Ok(result)
);
}
#[test]
fn deserialize_option() {
let result = vec![
("first".to_owned(), Some(23)),
("last".to_owned(), Some(42)),
];
assert_eq!(urlencoded::from_str("first=23&last=42"), Ok(result));
}
#[test]
fn deserialize_unit() {
assert_eq!(urlencoded::from_str(""), Ok(()));
assert_eq!(urlencoded::from_str("&"), Ok(()));
assert_eq!(urlencoded::from_str("&&"), Ok(()));
assert!(urlencoded::from_str::<()>("first=23").is_err());
}
#[derive(Deserialize, Debug, PartialEq, Eq)]
enum X {
A,
B,
C,
}
#[test]
fn deserialize_unit_enum() {
let result: Vec<(String, X)> =
urlencoded::from_str("one=A&two=B&three=C").unwrap();
assert_eq!(result.len(), 3);
assert!(result.contains(&("one".to_owned(), X::A)));
assert!(result.contains(&("two".to_owned(), X::B)));
assert!(result.contains(&("three".to_owned(), X::C)));
}
#[test]
fn deserialize_unit_type() {
assert_eq!(urlencoded::from_str(""), Ok(()));
}
#[derive(Clone, Copy, Debug, PartialEq, Deserialize)]
struct Params<'a> {
a: usize,
b: &'a str,
c: Option<u8>,
}
#[test]
fn deserialize_struct() {
let de = Params {
a: 10,
b: "Hello",
c: None,
};
assert_eq!(urlencoded::from_str("a=10&b=Hello"), Ok(de));
assert_eq!(urlencoded::from_str("b=Hello&a=10"), Ok(de));
}
#[test]
fn deserialize_list_of_str() {
// TODO: It would make sense to support this.
assert_matches!(
urlencoded::from_str::<Vec<(&str, &str)>>("a=a&a=b"),
Err(error) if error.to_string().contains("unsupported")
);
assert_eq!(
urlencoded::from_str("a=a&a=b"),
Ok(vec![("a", vec!["a", "b"])])
)
}
#[test]
fn deserialize_multiple_lists() {
#[derive(Debug, PartialEq, Deserialize)]
struct Lists {
xs: Vec<bool>,
ys: Vec<u32>,
}
assert_eq!(
urlencoded::from_str("xs=true&xs=false&ys=3&ys=2&ys=1"),
Ok(Lists {
xs: vec![true, false],
ys: vec![3, 2, 1],
})
);
assert_eq!(
urlencoded::from_str("ys=3&xs=true&ys=2&xs=false&ys=1"),
Ok(Lists {
xs: vec![true, false],
ys: vec![3, 2, 1],
})
);
}
#[test]
fn deserialize_with_serde_attributes() {
use std::time::{Duration, SystemTime, UNIX_EPOCH};
#[derive(Debug, PartialEq, Deserialize)]
struct FieldsWithAttributes {
#[serde(default)]
xs: Vec<bool>,
#[serde(default)]
def: Option<u8>,
#[serde(
default,
deserialize_with = "ruma_serde::time::opt_ms_since_unix_epoch::deserialize"
)]
time: Option<SystemTime>,
#[serde(default)]
flag: bool,
}
assert_eq!(
urlencoded::from_str("xs=true&xs=false&def=3&time=1&flag=true"),
Ok(FieldsWithAttributes {
xs: vec![true, false],
def: Some(3),
time: Some(UNIX_EPOCH + Duration::from_millis(1)),
flag: true,
})
);
assert_eq!(
urlencoded::from_str(""),
Ok(FieldsWithAttributes {
xs: vec![],
def: None,
time: None,
flag: false,
})
);
}
#[test]
fn deserialize_nested_list() {
assert!(urlencoded::from_str::<Vec<(&str, Vec<Vec<bool>>)>>("a=b").is_err());
}
#[test]
fn deserialize_list_of_option() {
assert_eq!(
urlencoded::from_str("list=10&list=100"),
Ok(vec![("list", vec![Some(10), Some(100)])])
);
}
#[test]
fn deserialize_list_of_newtype() {
assert_eq!(
urlencoded::from_str("list=test"),
Ok(vec![("list", vec![NewType("test")])])
);
}
#[test]
fn deserialize_list_of_enum() {
assert_eq!(
urlencoded::from_str("item=A&item=B&item=C"),
Ok(vec![("item", vec![X::A, X::B, X::C])])
);
}
#[derive(Debug, Deserialize, PartialEq)]
struct Wrapper<T> {
item: T,
}
#[derive(Debug, PartialEq, Deserialize)]
struct NewStruct<'a> {
#[serde(borrow)]
list: Vec<&'a str>,
}
#[derive(Debug, PartialEq, Deserialize)]
struct Struct<'a> {
#[serde(borrow)]
list: Vec<Option<&'a str>>,
}
#[derive(Debug, PartialEq, Deserialize)]
struct NumList {
list: Vec<u8>,
}
#[derive(Debug, PartialEq, Deserialize)]
struct ListStruct {
list: Vec<NewType<usize>>,
}
#[test]
fn deserialize_newstruct() {
let de = NewStruct {
list: vec!["hello", "world"],
};
assert_eq!(urlencoded::from_str("list=hello&list=world"), Ok(de));
}
#[test]
fn deserialize_numlist() {
let de = NumList {
list: vec![1, 2, 3, 4],
};
assert_eq!(urlencoded::from_str("list=1&list=2&list=3&list=4"), Ok(de));
}
#[derive(Debug, Deserialize, PartialEq)]
struct Nested<T> {
item: T,
}
#[derive(Debug, Deserialize, PartialEq)]
struct Inner<'a> {
c: &'a str,
a: usize,
b: &'a str,
}
#[derive(Debug, Deserialize, PartialEq)]
struct InnerList<T> {
list: Vec<T>,
}
#[test]
#[ignore]
fn deserialize_nested_struct() {
let mut encoder = Encoder::new(String::new());
let nested = Nested {
item: Inner {
c: "hello",
a: 10,
b: "bye",
},
};
assert_eq!(
urlencoded::from_str(
&encoder
.append_pair("item", r#"{"c":"hello","a":10,"b":"bye"}"#)
.finish(),
),
Ok(nested)
);
}
#[test]
#[ignore]
fn deserialize_nested_struct_with_list() {
let mut encoder = Encoder::new(String::new());
let nested = Nested {
item: InnerList {
list: vec![1, 2, 3],
},
};
assert_eq!(
urlencoded::from_str(
&encoder.append_pair("item", r#"{"list":[1,2,3]}"#).finish(),
),
Ok(nested)
);
}
#[test]
#[ignore]
fn deserialize_nested_list_option() {
let mut encoder = Encoder::new(String::new());
let nested = Nested {
item: InnerList {
list: vec![Some(1), Some(2), None],
},
};
assert_eq!(
urlencoded::from_str(
&encoder
.append_pair("item", r#"{"list":[1,2,null]}"#)
.finish(),
),
Ok(nested)
);
}

View File

@ -0,0 +1,212 @@
use matches::assert_matches;
use ruma_serde::urlencoded::{self, ser::Error};
use serde::Serialize;
use url::form_urlencoded::Serializer as Encoder;
#[derive(Serialize)]
struct NewType<T>(T);
#[test]
fn serialize_newtype_i32() {
let params = &[("field", Some(NewType(11)))];
assert_eq!(urlencoded::to_string(params), Ok("field=11".to_owned()));
}
#[test]
fn serialize_option_map_int() {
let params = &[("first", Some(23)), ("middle", None), ("last", Some(42))];
assert_eq!(
urlencoded::to_string(params),
Ok("first=23&last=42".to_owned())
);
}
#[test]
fn serialize_option_map_string() {
let params = &[
("first", Some("hello")),
("middle", None),
("last", Some("world")),
];
assert_eq!(
urlencoded::to_string(params),
Ok("first=hello&last=world".to_owned())
);
}
#[test]
fn serialize_option_map_bool() {
let params = &[("one", Some(true)), ("two", Some(false))];
assert_eq!(
urlencoded::to_string(params),
Ok("one=true&two=false".to_owned())
);
}
#[test]
fn serialize_map_bool() {
let params = &[("one", true), ("two", false)];
assert_eq!(
urlencoded::to_string(params),
Ok("one=true&two=false".to_owned())
);
}
#[derive(Serialize)]
enum X {
A,
B,
C,
}
#[test]
fn serialize_unit_enum() {
let params = &[("one", X::A), ("two", X::B), ("three", X::C)];
assert_eq!(
urlencoded::to_string(params),
Ok("one=A&two=B&three=C".to_owned())
);
}
#[derive(Serialize)]
struct Unit;
#[test]
fn serialize_unit_struct() {
assert_eq!(urlencoded::to_string(Unit), Ok("".to_owned()));
}
#[test]
fn serialize_unit_type() {
assert_eq!(urlencoded::to_string(()), Ok("".to_owned()));
}
#[test]
fn serialize_list_of_str() {
let params = &[("list", vec!["hello", "world"])];
assert_eq!(
urlencoded::to_string(params),
Ok("list=hello&list=world".to_owned())
);
}
#[test]
fn serialize_multiple_lists() {
#[derive(Serialize)]
struct Lists {
xs: Vec<bool>,
ys: Vec<u32>,
}
let params = Lists {
xs: vec![true, false],
ys: vec![3, 2, 1],
};
assert_eq!(
urlencoded::to_string(params),
Ok("xs=true&xs=false&ys=3&ys=2&ys=1".to_owned())
);
}
#[test]
fn serialize_nested_list() {
let params = &[("list", vec![vec![0u8]])];
assert_matches!(
urlencoded::to_string(params),
Err(Error::Custom(s)) if s.contains("unsupported")
)
}
#[test]
fn serialize_list_of_option() {
let params = &[("list", vec![Some(10), Some(100)])];
assert_eq!(
urlencoded::to_string(params),
Ok("list=10&list=100".to_owned())
);
}
#[test]
fn serialize_list_of_newtype() {
let params = &[("list", vec![NewType("test".to_owned())])];
assert_eq!(urlencoded::to_string(params), Ok("list=test".to_owned()));
}
#[test]
fn serialize_list_of_enum() {
let params = &[("item", vec![X::A, X::B, X::C])];
assert_eq!(
urlencoded::to_string(params),
Ok("item=A&item=B&item=C".to_owned())
);
}
#[test]
fn serialize_map() {
let mut s = std::collections::BTreeMap::new();
s.insert("hello", "world");
s.insert("seri", "alize");
s.insert("matrix", "ruma");
let encoded = urlencoded::to_string(s).unwrap();
assert_eq!("hello=world&matrix=ruma&seri=alize", encoded);
}
#[derive(Serialize)]
struct Nested<T> {
item: T,
}
#[derive(Serialize)]
struct Inner {
c: String,
a: usize,
b: String,
}
#[derive(Debug, Serialize, PartialEq)]
struct InnerList<T> {
list: Vec<T>,
}
#[test]
#[ignore]
fn serialize_nested_struct() {
let mut encoder = Encoder::new(String::new());
let s = Nested {
item: Inner {
c: "hello".into(),
a: 10,
b: "bye".into(),
},
};
assert_eq!(
encoder
.append_pair("item", r#"{"c":"hello","a":10,"b":"bye"}"#)
.finish(),
urlencoded::to_string(s).unwrap()
);
}
#[test]
#[ignore]
fn serialize_nested_struct_with_list() {
let mut encoder = Encoder::new(String::new());
let s = Nested {
item: InnerList {
list: vec![1, 2, 3],
},
};
assert_eq!(
encoder.append_pair("item", r#"{"list":[1,2,3]}"#).finish(),
urlencoded::to_string(s).unwrap()
);
}