Vendor dependencies

This commit is contained in:
2026-08-01 16:11:49 +03:00
parent 7f139a0241
commit 6b5e7f0f8b
29706 changed files with 9575646 additions and 0 deletions
@@ -0,0 +1 @@
{"$comment":"This file only protects against accidental modifications. It is not a security mechanism and does not protect against malicious changes.","files":{".cargo_vcs_info.json":"91d9294d0a9528acab562be5f828f7862d5d125eb59860f2c9a7976bdfd04e32",".rustfmt.toml":"d06ae739fe7e6201404c0fd2b72114d207964c78343f0863b608b157d34300a0","CHANGELOG.md":"a9c18aec0fae666f37825aae255b220f67b149a3ce19066eea9a35e2890809cb","Cargo.lock":"fbf60738c625aee9bb34cae5761911028b1f8c951e4c7cc4d2993f9a0b9fb162","Cargo.toml":"a405fbef476cddbcc5b8ec1d6dc0d080e89a351e9211501744942eb2af81778a","Cargo.toml.orig":"9362ee3a269d2edf9b5c7f072ead2cdf85c22cfb76cf408f652bd4ebd10e35df","LICENSE":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"3df901135428a092e2c360986b1f4e5b07f620deee45a5a7f33dd574e37b4655","benches/upstream_comparison/benches.rs":"a6d117fbf2c61054985ae06c16a0787895a55ec7b68c2882d075f3163c68bb5f","benches/upstream_comparison/main.rs":"d1962bd48640ed279ee8fd8ac9740be45e34f47114da0432ff267a87771492fc","src/de.rs":"06254dfd877d2e8f6428da54d9d23355f0dd807a849802f244670920f2088be2","src/de/part.rs":"3757ae236b47c977c05b8785c2674ca9c438660abdb92ab86f2befbb84a2474f","src/de/tests.rs":"62df683d6cb93f9b4db072167239d5a16716fcba3885e6c1ba86c844530d2825","src/de/val_or_vec.rs":"8887492134383380888ed92563d8c796c6f2e296c75d142e4d48deb50c1431f2","src/lib.rs":"d9eab263ab001a6b4d65004a9d5587699afeeb0dbcdfb1d3c89440d9a881a8f5","src/ser.rs":"185cc5413ec7dc7f182d26e80720441268626b7f7b933a9cffb45e7037a2249b","src/ser/error.rs":"8da85969608db15c34e7337b2f96c82e44e3c3b23f6dae6262d343137d0dc128","src/ser/key.rs":"617f99f7053a9e2a87d7b301f9084434e83a502ce73177e8ef1fbfa8f3beb89d","src/ser/pair.rs":"a6bb2f177249e01617956b9b52057220e5bb5c0adf51098f7847c521d06843c4","src/ser/part.rs":"a079431c4b6a2618af373b7212eec6690d50e7c690531687337d412de0bcda48","src/ser/tests.rs":"2b008c7d39c2e9565f17d89268ed42ce021f0d44e3450e5690514fcb241bb123","src/ser/value.rs":"2b893174275885f699750965a89a5b69632ce1c5d08a94b3486f52247cbb4578"},"package":"b2f2d7ff8a2140333718bb329f5c40fc5f0865b84c426183ce14c97d2ab8154f"}
@@ -0,0 +1,6 @@
{
"git": {
"sha1": "cbd49ca9e5c0b261d489d8d656f9d824db06eeee"
},
"path_in_vcs": ""
}
+6
View File
@@ -0,0 +1,6 @@
format_code_in_doc_comments = true
imports_granularity = "Crate"
use_small_heuristics = "Max"
newline_style = "Unix"
reorder_imports = true
use_try_shorthand = true
+70
View File
@@ -0,0 +1,70 @@
# 0.2.8
Switch `serde` dependency to `serde_core`.
# 0.2.7
Add `Deserializer::from_bytes`.
# 0.2.6
Fix deserialization of optional sequences of a single non-string element.
# 0.2.5
Add `push_to_string` for serializing a struct to the end of an existing `String`
buffer (instead of allocating a fresh one for the serialized output).
# 0.2.4
Fix deserialization of optional sequences of a single element.
# 0.2.3
Improve README and crate documentation (now the exact same, instead of just a
single-line description).
# 0.2.2
This release only upgrades one of the crates' dev-dependencies.
# 0.2.1
This release only upgrades one of the crates' private dependencies.
# 0.2.0
Support deserialization of sequences with duplicate keys.
This used to fail, but passes now:
```rust
let result = vec![("foo".to_owned(), 1), ("bar".to_owned(), 2), ("foo".to_owned(), 3)];
assert_eq!(super::from_str("foo=1&bar=2&foo=3"), Ok(result));
```
This should mainly affect deserialization to a type that's explicitly a sequence, like arrays or `Vec`,
but some other things were changed too so if you are getting unexpected deserialization errors, please open an issue.
This release has a minimum Rust version of 1.56.
# 0.1.1
Support deserialization of `Option`al values to better support forms with optional inputs of non-string types:
```rust
#[derive(Deserialize, PartialEq)]
struct MyForm {
field: Option<u16>,
}
// What browsers send when a value is given
assert_eq!(serde_html_form::from_str("field=5").unwrap(), MyForm { field: Some(5) });
// What browsers send when no value is given
assert_eq!(serde_html_form::from_str("field=").unwrap(), MyForm { field: None });
// This also works
assert_eq!(serde_html_form::from_str("").unwrap(), MyForm { field: None });
```
# 0.1.0
Initial release.
+408
View File
@@ -0,0 +1,408 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "anstyle"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
[[package]]
name = "assert_matches2"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15832d94c458da98cac0ffa6eca52cc19c2a3c6c951058500a5ae8f01f0fdf56"
[[package]]
name = "bitflags"
version = "2.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "4.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c"
dependencies = [
"clap_builder",
]
[[package]]
name = "clap_builder"
version = "4.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7"
dependencies = [
"anstyle",
"clap_lex",
"terminal_size",
]
[[package]]
name = "clap_lex"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
[[package]]
name = "condtype"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf0a07a401f374238ab8e2f11a104d2851bf9ce711ec69804834de8af45c7af"
[[package]]
name = "divan"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5398159ee27f2b123d89b856bad61725442f37df5fb98c30cd570c318d594aee"
dependencies = [
"cfg-if",
"clap",
"condtype",
"divan-macros",
"libc",
"regex-lite",
]
[[package]]
name = "divan-macros"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5092f66eb3563a01e85552731ae82c04c934ff4efd7ad1a0deae7b948f4b3ec4"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "errno"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
dependencies = [
"libc",
"windows-sys 0.52.0",
]
[[package]]
name = "form_urlencoded"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
dependencies = [
"percent-encoding",
]
[[package]]
name = "hashbrown"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]]
name = "indexmap"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "itoa"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
[[package]]
name = "libc"
version = "0.2.152"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
[[package]]
name = "linux-raw-sys"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
[[package]]
name = "percent-encoding"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "proc-macro2"
version = "1.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex-lite"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30b661b2f27137bdbc16f00eda72866a92bb28af1753ffbd56744fb6e2e9cd8e"
[[package]]
name = "rustix"
version = "0.38.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.52.0",
]
[[package]]
name = "ryu"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
[[package]]
name = "serde"
version = "1.0.221"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "341877e04a22458705eb4e131a1508483c877dca2792b3781d4e5d8a6019ec43"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.221"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c459bc0a14c840cb403fc14b148620de1e0778c96ecd6e0c8c3cacb6d8d00fe"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.221"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6185cf75117e20e62b1ff867b9518577271e58abe0037c40bb4794969355ab0"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_html_form"
version = "0.2.8"
dependencies = [
"assert_matches2",
"divan",
"form_urlencoded",
"indexmap",
"itoa",
"ryu",
"serde",
"serde_core",
"serde_urlencoded",
]
[[package]]
name = "serde_urlencoded"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
dependencies = [
"form_urlencoded",
"itoa",
"ryu",
"serde",
]
[[package]]
name = "syn"
version = "2.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "terminal_size"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7"
dependencies = [
"rustix",
"windows-sys 0.48.0",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets 0.48.5",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.0",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm 0.48.5",
"windows_aarch64_msvc 0.48.5",
"windows_i686_gnu 0.48.5",
"windows_i686_msvc 0.48.5",
"windows_x86_64_gnu 0.48.5",
"windows_x86_64_gnullvm 0.48.5",
"windows_x86_64_msvc 0.48.5",
]
[[package]]
name = "windows-targets"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
dependencies = [
"windows_aarch64_gnullvm 0.52.0",
"windows_aarch64_msvc 0.52.0",
"windows_i686_gnu 0.52.0",
"windows_i686_msvc 0.52.0",
"windows_x86_64_gnu 0.52.0",
"windows_x86_64_gnullvm 0.52.0",
"windows_x86_64_msvc 0.52.0",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
+83
View File
@@ -0,0 +1,83 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
rust-version = "1.56"
name = "serde_html_form"
version = "0.2.8"
build = false
exclude = ["/.github"]
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "(De-)serialization support for the `application/x-www-form-urlencoded` format"
readme = "README.md"
keywords = [
"serde",
"serialization",
"urlencoded",
]
categories = [
"encoding",
"web-programming",
]
license = "MIT"
repository = "https://github.com/jplatte/serde_html_form"
[features]
default = ["ryu"]
[lib]
name = "serde_html_form"
path = "src/lib.rs"
bench = false
[[bench]]
name = "upstream_comparison"
path = "benches/upstream_comparison/main.rs"
harness = false
[dependencies.form_urlencoded]
version = "1.0.1"
[dependencies.indexmap]
version = "2.0.0"
[dependencies.itoa]
version = "1.0.1"
[dependencies.ryu]
version = "1.0.9"
optional = true
[dependencies.serde_core]
version = "1.0.221"
[dev-dependencies.assert_matches2]
version = "0.1.0"
[dev-dependencies.divan]
version = "0.1.11"
[dev-dependencies.serde]
version = "1.0.221"
features = ["derive"]
[dev-dependencies.serde_urlencoded]
version = "0.7.1"
[lints.rust.unexpected_cfgs]
level = "warn"
priority = 0
check-cfg = ["cfg(rust_analyzer)"]
+46
View File
@@ -0,0 +1,46 @@
[package]
name = "serde_html_form"
version = "0.2.8"
license = "MIT"
repository = "https://github.com/jplatte/serde_html_form"
description = "(De-)serialization support for the `application/x-www-form-urlencoded` format"
categories = ["encoding", "web-programming"]
keywords = ["serde", "serialization", "urlencoded"]
exclude = ["/.github"]
edition = "2021"
rust-version = "1.56"
[lib]
bench = false
[features]
default = ["ryu"]
[dependencies]
# Percent encoding and mapping of query string to pair of key-values
form_urlencoded = "1.0.1"
# Used for internal buffering during deserialization
indexmap = "2.0.0"
# Fast integer serialization
itoa = "1.0.1"
# Fast and better-looking float serialization
ryu = { version = "1.0.9", optional = true }
# Contains the Serializer and Deserializer traits
serde_core = "1.0.221"
[dev-dependencies]
# For the assert_matches! macro
assert_matches2 = "0.1.0"
# Some tests use structs that derive Serialize / Deserialize
serde = { version = "1.0.221", features = ["derive"] }
# For benchmarks
divan = "0.1.11"
serde_urlencoded = "0.7.1"
[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(rust_analyzer)'] }
[[bench]]
name = "upstream_comparison"
harness = false
+23
View File
@@ -0,0 +1,23 @@
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.
+119
View File
@@ -0,0 +1,119 @@
# `serde_html_form`
(De-)serialization support for the `application/x-www-form-urlencoded` format.
This crate is a Rust library for serialising to and deserialising from
the [`application/x-www-form-urlencoded`][urlencoded] format. It is built
upon [Serde], a high performance generic serialization framework and [rust-url],
a URL parser for Rust.
It is a fork of [`serde_urlencoded`], with additional support for maps or
structs with fields of sequence type (e.g. `Vec<String>`). It also supports
`Option` in values, treating `foo=` as `foo: None`.
[rust-url]: https://github.com/servo/rust-url
[Serde]: https://github.com/serde-rs/serde
[urlencoded]: https://url.spec.whatwg.org/#application/x-www-form-urlencoded
[`serde_urlencoded`]: https://github.com/nox/serde_urlencoded
## Examples
Sequences like `value=x&value=y`:
```rust
use serde::Deserialize;
#[derive(Debug, PartialEq, Deserialize)]
struct Form {
// By default, at least one occurrence of this field must be present (this
// is mandated by how serde works).
//
// Since this is usually not desired, use `serde(default)` to instantiate
// this struct's field with a `Default` value if input doesn't contain that
// field.
#[serde(default)]
value: Vec<String>,
}
assert_eq!(
serde_html_form::from_str("value=&value=abc"),
Ok(Form { value: vec!["".to_owned(), "abc".to_owned()] })
);
assert_eq!(
serde_html_form::from_str(""),
Ok(Form { value: vec![] })
);
```
Sequences like `value[]=x&value[]=y`:
```rust
use serde::Deserialize;
#[derive(Debug, PartialEq, Deserialize)]
struct Form {
// If you want to support `value[]=x&value[]=y`, you can use
// `serde(rename)`. You could even use `serde(alias)` instead to allow both,
// but note that mixing both in one input string would also be allowed then.
#[serde(default, rename = "value[]")]
value: Vec<String>,
}
assert_eq!(
serde_html_form::from_str("value[]=x&value[]=y"),
Ok(Form { value: vec!["x".to_owned(), "y".to_owned()] })
);
assert_eq!(
serde_html_form::from_str("value[]=hello"),
Ok(Form { value: vec!["hello".to_owned()] })
);
```
Optional values:
```rust
use serde::Deserialize;
#[derive(Debug, PartialEq, Deserialize)]
struct Form {
// Finally, this crate also supports deserializing empty values as `None`
// if your values are `Option`s.
// Note that serde's `Deserialize` derive implicitly allows omission of
// `Option`-typed fields (except when combined with some other attributes).
single: Option<u32>,
// Not using `serde(default)` here to require at least one occurrence.
at_least_one: Vec<Option<u32>>,
}
assert_eq!(
serde_html_form::from_str("at_least_one=5"),
Ok(Form {
// Implicit `serde(default)` in action.
single: None,
// `serde_html_form`'s support for optional values being used.
at_least_one: vec![Some(5)],
})
);
assert_eq!(
serde_html_form::from_str("at_least_one=&single=1&at_least_one=5"),
Ok(Form {
single: Some(1),
at_least_one: vec![
// Empty strings get deserialized as `None`.
None,
// It's no problem that the `at_least_one` field repetitions are
// not consecutive (single comes in between).
Some(5),
]
})
);
assert!(
serde_html_form::from_str::<Form>("").is_err(),
"at_least_one is not part of the input"
);
```
## License
This crate is licensed under the MIT license ([LICENSE](LICENSE) or
<https://opensource.org/license/mit/>).
@@ -0,0 +1,56 @@
use std::collections::BTreeMap;
// For rust-analyzer, this file is included as a regular module. Make IDE
// functionality work by pretending that the symbols being tested always come
// from `serde_html_form`.
#[cfg(rust_analyzer)]
use serde_html_form::from_str;
use crate::{SimpleEnum, StructForm, StructForm2};
#[divan::bench]
fn deserialize_list_enum() {
from_str::<Vec<(u64, SimpleEnum)>>("10=VariantB&5=VariantC&2=VariantA&1=VariantA").unwrap();
}
#[divan::bench]
fn deserialize_list_duplicate_keys() {
from_str::<Vec<(&str, &str)>>("a=test&b=test&a=test&b=test").unwrap();
}
#[divan::bench]
fn deserialize_map() {
from_str::<BTreeMap<&str, i32>>("a=0&bb=1&ccc=123").unwrap();
}
#[divan::bench]
fn deserialize_map_many_entries() {
from_str::<BTreeMap<i32, i32>>(
"0=0&1=1&2=2&3=3&4=4&5=5&6=6&7=7&8=8&\
200=0&201=1&202=2&203=3&204=4&205=5&206=6&207=7&208=8&\
50=0&51=1&52=2&53=3&54=4&55=5&56=6&57=7&58=8&\
1230=0&1231=1&1232=2&1233=3&1234=4&1235=5&1236=6&1237=7&1238=8&\
80=0&81=1&82=2&83=3&84=4&85=5&86=6&87=7&88=8",
)
.unwrap();
}
#[divan::bench]
fn deserialize_struct_simple() {
from_str::<StructForm>("foo=value").unwrap();
}
#[divan::bench]
fn deserialize_struct_long_parameters() {
from_str::<StructForm2>("float_needs_more_complex_parsing_and_has_very_long_field_name=10")
.unwrap();
}
#[divan::bench]
fn deserialize_struct_long_parameters_2() {
from_str::<StructForm2>(
"float_needs_more_complex_parsing_and_has_very_long_field_name=1.0000000000123&\
optional_field=12300000000000000",
)
.unwrap();
}
@@ -0,0 +1,46 @@
use serde::Deserialize;
fn main() {
divan::main();
}
#[derive(Deserialize)]
#[allow(dead_code)]
struct StructForm {
foo: String,
}
#[derive(Deserialize)]
#[allow(dead_code)]
struct StructForm2 {
optional_field: Option<u64>,
float_needs_more_complex_parsing_and_has_very_long_field_name: f64,
}
#[derive(Deserialize)]
enum SimpleEnum {
VariantA,
VariantB,
VariantC,
}
// For rust-analyzer, treat `benches.rs` as a regular module.
// The module itself also contains some `cfg(rust_analyzer)` code to include
// the symbols otherwise introduced through the inline modules below.
#[cfg(rust_analyzer)]
mod benches;
// For actual execution, treat `benches.rs` as two modules, one where `from_str`
// from `serde_html_form` is benchmarked, one where `from_str` from
// `serde_urlencoded` is benchmarked.
#[cfg(not(rust_analyzer))]
mod serde_html_form {
use serde_html_form::from_str;
include!("benches.rs");
}
#[cfg(not(rust_analyzer))]
mod serde_urlencoded {
use serde_html_form::from_str;
include!("benches.rs");
}
+216
View File
@@ -0,0 +1,216 @@
//! Deserialization support for the `application/x-www-form-urlencoded` format.
use std::io::Read;
use form_urlencoded::{parse, Parse as UrlEncodedParse};
use indexmap::map::{self, IndexMap};
use serde_core::{
de::{self, value::MapDeserializer},
forward_to_deserialize_any,
};
#[doc(inline)]
pub use serde_core::de::value::Error;
mod part;
mod val_or_vec;
use self::{part::Part, 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()),
/// ("meat".to_owned(), "ham".to_owned()),
/// ("fat".to_owned(), "butter".to_owned()),
/// ];
///
/// assert_eq!(
/// serde_html_form::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::from_bytes(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()),
/// ("meat".to_owned(), "ham".to_owned()),
/// ("fat".to_owned(), "butter".to_owned()),
/// ];
///
/// assert_eq!(
/// serde_html_form::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: UrlEncodedParse<'de>,
}
impl<'de> Deserializer<'de> {
/// Returns a new `Deserializer`.
pub fn new(parse: UrlEncodedParse<'de>) -> Self {
Deserializer { inner: parse }
}
/// Returns a new `Deserializer` from a `&[u8]`.
pub fn from_bytes(input: &'de [u8]) -> Self {
Self::new(parse(input))
}
}
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_seq(visitor)
}
fn deserialize_map<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
visitor.visit_map(MapDeserializer::new(group_entries(self.inner).into_iter()))
}
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
visitor.visit_seq(MapDeserializer::new(PartIterator(self.inner)))
}
fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
let deserializer = MapDeserializer::new(PartIterator(self.inner));
deserializer.end()?;
visitor.visit_unit()
}
fn deserialize_newtype_struct<V>(self, _name: &str, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
visitor.visit_newtype_struct(self)
}
fn deserialize_struct<V>(
self,
_name: &'static str,
_fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
self.deserialize_map(visitor)
}
forward_to_deserialize_any! {
bool
u8
u16
u32
u64
i8
i16
i32
i64
f32
f64
char
str
string
option
bytes
byte_buf
unit_struct
tuple_struct
identifier
tuple
enum
ignored_any
}
}
struct PartIterator<'de>(UrlEncodedParse<'de>);
impl<'de> Iterator for PartIterator<'de> {
type Item = (Part<'de>, Part<'de>);
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|(k, v)| (Part(k), Part(v)))
}
}
fn group_entries(parse: UrlEncodedParse<'_>) -> IndexMap<Part<'_>, ValOrVec<Part<'_>>> {
use map::Entry::*;
let mut res = IndexMap::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
}
#[cfg(test)]
mod tests;
+187
View File
@@ -0,0 +1,187 @@
use std::borrow::Cow;
use serde_core::{
de::{self, Error as _, IntoDeserializer},
forward_to_deserialize_any,
};
use super::Error;
#[derive(PartialEq, PartialOrd, Eq, Ord, Hash)]
pub(super) struct Part<'de>(pub 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>,
{
if self.0.is_empty() {
visitor.visit_none()
} else {
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(self)
}
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)
}
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
visitor.visit_seq(PartSeqAccess(Some(self)))
}
forward_to_deserialize_any! {
char
str
string
unit
bytes
byte_buf
unit_struct
tuple_struct
struct
identifier
tuple
ignored_any
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,
}
}
impl<'de> de::EnumAccess<'de> for Part<'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 PartSeqAccess<'de>(Option<Part<'de>>);
impl<'de> de::SeqAccess<'de> for PartSeqAccess<'de> {
type Error = Error;
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
where
T: de::DeserializeSeed<'de>,
{
match self.0.take() {
Some(value) => seed.deserialize(value).map(Some),
None => Ok(None),
}
}
fn size_hint(&self) -> Option<usize> {
Some(self.0.is_some() as usize)
}
}
pub(crate) 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"))
}
}
+212
View File
@@ -0,0 +1,212 @@
use std::collections::BTreeMap;
use serde::Deserialize;
#[derive(Deserialize, Debug, PartialEq)]
struct NewType<T>(T);
#[test]
fn deserialize_newtype_i32() {
let result = vec![("field".to_owned(), NewType(11))];
assert_eq!(super::from_str("field=11"), Ok(result));
}
#[test]
fn deserialize_bytes() {
let result = vec![("first".to_owned(), 23), ("last".to_owned(), 42)];
assert_eq!(super::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!(super::from_str("first=23&last=42"), Ok(result));
}
#[test]
fn deserialize_borrowed_str() {
let result = vec![("first", 23), ("last", 42)];
assert_eq!(super::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!(super::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!(super::from_str("first=23&last=42"), Ok(result));
}
#[test]
fn deserialize_empty_string() {
let result = vec![("first".to_owned(), "")];
assert_eq!(super::from_str("first="), Ok(result));
}
#[test]
fn deserialize_map() {
let result = BTreeMap::from_iter([("first".to_owned(), 23), ("second".to_owned(), 42)]);
assert_eq!(super::from_str("first=23&second=42"), Ok(result));
}
#[test]
fn deserialize_map_vec() {
let result =
BTreeMap::from_iter([("first".to_owned(), vec![23, 1]), ("second".to_owned(), vec![42])]);
assert_eq!(super::from_str("first=23&second=42&first=1"), Ok(result));
}
#[test]
fn deserialize_tuple_list() {
let result = vec![("foo".to_owned(), 1), ("bar".to_owned(), 2), ("foo".to_owned(), 3)];
assert_eq!(super::from_str("foo=1&bar=2&foo=3"), Ok(result));
}
#[test]
fn deserialize_vec_strings() {
#[derive(Deserialize, PartialEq, Debug)]
struct Form {
value: Vec<String>,
}
assert_eq!(
super::from_str("value=&value=abc"),
Ok(Form { value: vec!["".to_owned(), "abc".to_owned()] })
);
}
#[test]
fn deserialize_option_vec() {
#[derive(Deserialize, PartialEq, Debug)]
struct Form {
value: Option<Vec<String>>,
}
assert_eq!(super::from_str(""), Ok(Form { value: None }));
assert_eq!(super::from_str("value=abc"), Ok(Form { value: Some(vec!["abc".to_owned()]) }));
assert_eq!(
super::from_str("value=abc&value=def"),
Ok(Form { value: Some(vec!["abc".to_owned(), "def".to_owned()]) })
);
}
#[test]
fn deserialize_option_vec_int() {
#[derive(Deserialize, PartialEq, Debug)]
struct Form {
value: Option<Vec<i32>>,
}
assert_eq!(super::from_str(""), Ok(Form { value: None }));
assert_eq!(super::from_str("value=0"), Ok(Form { value: Some(vec![0]) }));
assert_eq!(super::from_str("value=3&value=-1"), Ok(Form { value: Some(vec![3, -1]) }));
}
#[test]
fn deserialize_option_no_value() {
#[derive(Deserialize, PartialEq, Debug)]
struct Form {
value: Option<f64>,
}
assert_eq!(super::from_str("value="), Ok(Form { value: None }));
}
#[test]
fn deserialize_vec_options_no_value() {
#[derive(Deserialize, PartialEq, Debug)]
struct Form {
value: Vec<Option<f64>>,
}
assert_eq!(super::from_str("value=&value=&value="), Ok(Form { value: vec![None, None, None] }));
}
#[test]
fn deserialize_vec_options_some_values() {
#[derive(Deserialize, PartialEq, Debug)]
struct Form {
value: Vec<Option<f64>>,
}
assert_eq!(
super::from_str("value=&value=4&value="),
Ok(Form { value: vec![None, Some(4.0), None] })
);
}
#[test]
fn deserialize_option_vec_no_value() {
#[derive(Deserialize, PartialEq, Debug)]
struct Form {
value: Option<Vec<f64>>,
}
assert_eq!(
super::from_str::<Form>("value=&value=&value=").unwrap_err().to_string(),
"cannot parse float from empty string"
);
}
#[test]
fn deserialize_option_vec_with_values() {
#[derive(Deserialize, PartialEq, Debug)]
struct Form {
value: Option<Vec<f64>>,
}
assert_eq!(
super::from_str("value=3&value=4&value=5"),
Ok(Form { value: Some(vec![3.0, 4.0, 5.0]) })
);
}
#[test]
fn deserialize_no_value_err() {
#[derive(Deserialize, PartialEq, Debug)]
struct Form {
value: f64,
}
assert_eq!(
super::from_str::<Form>("value=").unwrap_err().to_string(),
"cannot parse float from empty string"
);
}
#[test]
fn deserialize_unit() {
assert_eq!(super::from_str(""), Ok(()));
assert_eq!(super::from_str("&"), Ok(()));
assert_eq!(super::from_str("&&"), Ok(()));
assert!(super::from_str::<()>("first=23").is_err());
}
#[derive(Deserialize, Debug, PartialEq, Eq)]
enum X {
A,
B,
C,
}
#[test]
fn deserialize_unit_enum() {
let result =
vec![("one".to_owned(), X::A), ("two".to_owned(), X::B), ("three".to_owned(), X::C)];
assert_eq!(super::from_str("one=A&two=B&three=C"), Ok(result));
}
#[test]
fn deserialize_unit_type() {
assert_eq!(super::from_str(""), Ok(()));
}
@@ -0,0 +1,263 @@
use std::{hint::unreachable_unchecked, iter, mem, vec};
use serde_core::de::{
self,
value::{Error, SeqDeserializer},
Deserializer, IntoDeserializer,
};
#[derive(Debug)]
pub(crate) enum ValOrVec<T> {
Val(T),
Vec(Vec<T>),
}
impl<T> ValOrVec<T> {
pub fn push(&mut self, new_val: T) {
match self {
Self::Val(_) => {
// Change self to a Vec variant and take ownership of the previous value
let old_self = mem::replace(self, ValOrVec::Vec(Vec::with_capacity(2)));
let old_val = match old_self {
Self::Val(v) => v,
// Safety: We would not be in the outer branch otherwise
_ => unsafe { unreachable_unchecked() },
};
let vec = match self {
ValOrVec::Vec(v) => v,
// Safety: We set self to Vec with the mem::replace above
_ => unsafe { unreachable_unchecked() },
};
vec.push(old_val);
vec.push(new_val);
}
Self::Vec(vec) => vec.push(new_val),
}
}
fn deserialize_val<U, E, F>(self, f: F) -> Result<U, E>
where
F: FnOnce(T) -> Result<U, E>,
E: de::Error,
{
match self {
ValOrVec::Val(val) => f(val),
ValOrVec::Vec(_) => Err(de::Error::custom("unsupported value")),
}
}
}
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>
{
self.deserialize_val(move |val| val.$method(visitor))
}
)*
}
}
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 {
Self::Val(val) => val.deserialize_any(visitor),
Self::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>,
{
self.deserialize_val(move |val| val.deserialize_enum(name, variants, visitor))
}
fn deserialize_tuple<V>(self, len: usize, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
self.deserialize_val(move |val| val.deserialize_tuple(len, visitor))
}
fn deserialize_struct<V>(
self,
name: &'static str,
fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
self.deserialize_val(move |val| val.deserialize_struct(name, fields, visitor))
}
fn deserialize_unit_struct<V>(
self,
name: &'static str,
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
self.deserialize_val(move |val| val.deserialize_unit_struct(name, visitor))
}
fn deserialize_tuple_struct<V>(
self,
name: &'static str,
len: usize,
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
self.deserialize_val(move |val| val.deserialize_tuple_struct(name, len, visitor))
}
fn deserialize_newtype_struct<V>(
self,
name: &'static str,
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
self.deserialize_val(move |val| val.deserialize_newtype_struct(name, visitor))
}
fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
visitor.visit_unit()
}
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
match self {
ValOrVec::Val(val) => val.deserialize_option(visitor),
ValOrVec::Vec(_) => visitor.visit_some(self),
}
}
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_identifier,
deserialize_map,
}
}
#[cfg(test)]
mod tests {
use std::borrow::Cow;
use assert_matches2::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));
assert_eq!(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));
assert_eq!(v, vec!["a".to_owned(), "b".to_owned(), "c".to_owned()]);
}
}
+24
View File
@@ -0,0 +1,24 @@
#![doc = include_str!("../README.md")]
#![warn(
rust_2018_idioms,
unused_qualifications,
clippy::branches_sharing_code,
clippy::cloned_instead_of_copied,
clippy::empty_line_after_outer_attr,
clippy::inefficient_to_string,
clippy::mut_mut,
clippy::nonstandard_macro_braces,
clippy::semicolon_if_nothing_returned,
clippy::str_to_string,
clippy::unreadable_literal,
clippy::unseparated_literal_suffix,
clippy::wildcard_imports
)]
pub mod de;
pub mod ser;
#[doc(inline)]
pub use crate::de::{from_bytes, from_reader, from_str, Deserializer};
#[doc(inline)]
pub use crate::ser::{push_to_string, to_string, Serializer};
+458
View File
@@ -0,0 +1,458 @@
//! Serialization support for the `application/x-www-form-urlencoded` format.
mod error;
mod key;
mod pair;
mod part;
mod value;
use std::{borrow::Cow, str};
use form_urlencoded::{Serializer as UrlEncodedSerializer, Target as UrlEncodedTarget};
use serde_core::ser;
pub use self::error::Error;
/// Serializes a value into a `application/x-www-form-urlencoded` `String` buffer.
///
/// ```
/// let meal = &[("bread", "baguette"), ("cheese", "comté"), ("meat", "ham"), ("fat", "butter")];
///
/// assert_eq!(
/// serde_html_form::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 target = String::new();
push_to_string(&mut target, input)?;
Ok(target)
}
/// Serializes a value into the provided `application/x-www-form-urlencoded` `String` buffer.
///
/// ```
/// let meal = &[("bread", "baguette"), ("cheese", "comté"), ("meat", "ham"), ("fat", "butter")];
///
/// let mut target = "/cook?".to_owned();
///
/// serde_html_form::ser::push_to_string(&mut target, meal).unwrap();
///
/// assert_eq!(target, "/cook?bread=baguette&cheese=comt%C3%A9&meat=ham&fat=butter");
/// ```
pub fn push_to_string<T: ser::Serialize>(target: &mut String, input: T) -> Result<(), Error> {
let start_position = target.len();
let mut urlencoder = UrlEncodedSerializer::for_suffix(target, start_position);
input.serialize(Serializer::new(&mut urlencoder))?;
urlencoder.finish();
Ok(())
}
/// 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: 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 }
}
}
/// Sequence serializer.
pub struct SeqSerializer<'input, 'output, Target: UrlEncodedTarget> {
urlencoder: &'output mut UrlEncodedSerializer<'input, Target>,
}
/// Tuple serializer.
///
/// Mostly used for arrays.
pub struct TupleSerializer<'input, 'output, Target: UrlEncodedTarget> {
urlencoder: &'output mut UrlEncodedSerializer<'input, Target>,
}
/// Tuple struct serializer.
///
/// Never instantiated, tuple structs are not supported.
pub struct TupleStructSerializer<'input, 'output, T: 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: UrlEncodedTarget> {
inner: ser::Impossible<&'output mut UrlEncodedSerializer<'input, T>, Error>,
}
/// Map serializer.
pub struct MapSerializer<'input, 'output, Target: UrlEncodedTarget> {
urlencoder: &'output mut UrlEncodedSerializer<'input, Target>,
key: Option<Cow<'static, str>>,
}
/// Struct serializer.
pub struct StructSerializer<'input, 'output, Target: UrlEncodedTarget> {
urlencoder: &'output mut UrlEncodedSerializer<'input, Target>,
}
/// Struct variant serializer.
///
/// Never instantiated, struct variants are not supported.
pub struct StructVariantSerializer<'input, 'output, T: 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()
}
}
#[cfg(test)]
mod tests;
+84
View File
@@ -0,0 +1,84 @@
use std::{
borrow::Cow,
error, fmt,
str::{self, Utf8Error},
};
use serde_core::ser;
/// Errors returned during serializing to `application/x-www-form-urlencoded`.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Error(ErrorKind);
#[derive(Clone, Debug, PartialEq, Eq)]
enum ErrorKind {
Custom(Cow<'static, str>),
Utf8(Utf8Error),
}
impl Error {
pub(super) fn done() -> Self {
Error(ErrorKind::Custom("this pair has already been serialized".into()))
}
pub(super) fn not_done() -> Self {
Error(ErrorKind::Custom("this pair has not yet been serialized".into()))
}
pub(super) fn unsupported_key() -> Self {
Error(ErrorKind::Custom("unsupported key".into()))
}
pub(super) fn unsupported_value() -> Self {
Error(ErrorKind::Custom("unsupported value".into()))
}
pub(super) fn unsupported_pair() -> Self {
Error(ErrorKind::Custom("unsupported pair".into()))
}
pub(super) fn top_level() -> Self {
Error(ErrorKind::Custom("top-level serializer supports only maps and structs".into()))
}
pub(super) fn no_key() -> Self {
Error(ErrorKind::Custom("tried to serialize a value before serializing key".into()))
}
pub(super) fn utf8(error: Utf8Error) -> Self {
Error(ErrorKind::Utf8(error))
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.0 {
ErrorKind::Custom(msg) => msg.fmt(f),
ErrorKind::Utf8(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.0 {
ErrorKind::Custom(_) => None,
ErrorKind::Utf8(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.0 {
ErrorKind::Custom(_) => None,
ErrorKind::Utf8(err) => Some(err),
}
}
}
impl ser::Error for Error {
fn custom<T: fmt::Display>(msg: T) -> Self {
Self(ErrorKind::Custom(format!("{}", msg).into()))
}
}
+79
View File
@@ -0,0 +1,79 @@
use std::{borrow::Cow, ops::Deref};
use serde_core::ser::{self, Serialize};
use super::{part::Sink, Error};
pub enum Key<'key> {
Static(&'static str),
Dynamic(Cow<'key, str>),
}
impl Deref for 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 + 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::unsupported_key()
}
}
+233
View File
@@ -0,0 +1,233 @@
use std::{borrow::Cow, mem};
use form_urlencoded::{Serializer as UrlEncodedSerializer, Target as UrlEncodedTarget};
use serde_core::ser;
use crate::ser::{key::KeySink, part::PartSerializer, value::ValueSink, Error};
pub struct PairSerializer<'input, '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<'target, Target> ser::Serializer for PairSerializer<'_, '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<'target, Target> ser::SerializeTuple for PairSerializer<'_, '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,
}
+229
View File
@@ -0,0 +1,229 @@
use std::str;
use serde_core::ser::{self, Serializer as _};
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_u128(self, v: u128) -> Result<S::Ok, Error> {
self.serialize_integer(v)
}
fn serialize_i128(self, v: i128) -> Result<S::Ok, Error> {
self.serialize_integer(v)
}
fn serialize_f32(self, v: f32) -> Result<S::Ok, Error> {
#[cfg(feature = "ryu")]
return self.serialize_floating(v);
#[cfg(not(feature = "ryu"))]
return self.serialize_str(&v.to_string());
}
fn serialize_f64(self, v: f64) -> Result<S::Ok, Error> {
#[cfg(feature = "ryu")]
return self.serialize_floating(v);
#[cfg(not(feature = "ryu"))]
return self.serialize_str(&v.to_string());
}
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 = itoa::Buffer::new();
self.serialize_str(buf.format(value))
}
#[cfg(feature = "ryu")]
fn serialize_floating<F>(self, value: F) -> Result<S::Ok, Error>
where
F: ryu::Float,
{
let mut buf = ryu::Buffer::new();
self.serialize_str(buf.format(value))
}
}
+198
View File
@@ -0,0 +1,198 @@
use serde::Serialize;
#[derive(Serialize)]
struct NewType<T>(T);
#[test]
fn serialize_newtype_i32() {
let params = &[("field", Some(NewType(11)))];
assert_eq!(super::to_string(params), Ok("field=11".to_owned()));
}
#[test]
fn serialize_newtype_u128() {
let params = &[("field", Some(NewType(u128::MAX)))];
assert_eq!(super::to_string(params), Ok(format!("field={}", u128::MAX)));
}
#[test]
fn serialize_newtype_i128() {
let params = &[("field", Some(NewType(i128::MIN)))];
assert_eq!(super::to_string(params), Ok(format!("field={}", i128::MIN)));
}
#[test]
fn serialize_option_map_int() {
let params = &[("first", Some(23)), ("middle", None), ("last", Some(42))];
assert_eq!(super::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!(super::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!(super::to_string(params), Ok("one=true&two=false".to_owned()));
}
#[test]
fn serialize_map_bool() {
let params = &[("one", true), ("two", false)];
assert_eq!(super::to_string(params), Ok("one=true&two=false".to_owned()));
}
#[test]
fn serialize_map_duplicate_keys() {
let params = &[("foo", "a"), ("foo", "b")];
assert_eq!(super::to_string(params), Ok("foo=a&foo=b".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!(super::to_string(params), Ok("one=A&two=B&three=C".to_owned()));
}
#[derive(Serialize)]
struct Unit;
#[test]
fn serialize_unit_struct() {
assert_eq!(super::to_string(Unit), Ok("".to_owned()));
}
#[test]
fn serialize_unit_type() {
assert_eq!(super::to_string(()), Ok("".to_owned()));
}
#[derive(Serialize)]
struct Wrapper<T> {
item: T,
}
#[derive(Serialize)]
struct NewStruct {
list: Vec<String>,
}
#[derive(Serialize)]
struct Struct {
list: Vec<Option<String>>,
}
#[derive(Serialize)]
struct ListStruct {
list: Vec<NewType<usize>>,
}
#[test]
fn serialize_newstruct() {
let s = NewStruct { list: vec!["hello".into(), "world".into()] };
assert_eq!("list=hello&list=world".to_owned(), super::to_string(s).unwrap());
}
#[test]
fn serialize_vec_bool() {
let params = Wrapper { item: vec![true, false, false] };
assert_eq!(super::to_string(params).unwrap(), "item=true&item=false&item=false".to_owned());
}
#[test]
fn serialize_vec_num() {
let params = Wrapper { item: vec![0, 1, 2] };
assert_eq!(super::to_string(params).unwrap(), "item=0&item=1&item=2".to_owned());
}
#[test]
fn serialize_vec_str() {
let params = Wrapper { item: vec!["hello", "world", "hello"] };
assert_eq!(super::to_string(params).unwrap(), "item=hello&item=world&item=hello".to_owned());
}
#[test]
fn serialize_struct_opt() {
let s = Struct { list: vec![Some("hello".into()), Some("world".into())] };
assert_eq!("list=hello&list=world".to_owned(), super::to_string(s).unwrap());
}
#[test]
fn serialize_struct_newtype() {
let s = ListStruct { list: vec![NewType(0), NewType(1)] };
assert_eq!("list=0&list=1".to_owned(), super::to_string(s).unwrap());
}
#[test]
fn serialize_struct_unit_enum() {
let params = Wrapper { item: vec![X::A, X::B, X::C] };
assert_eq!(super::to_string(params), Ok("item=A&item=B&item=C".to_owned()));
}
#[test]
fn serialize_list_of_str() {
let params = &[("list", vec!["hello", "world"])];
assert_eq!(super::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!(super::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![0_u8]])];
assert!(super::to_string(params).unwrap_err().to_string().contains("unsupported"));
}
#[test]
fn serialize_list_of_option() {
let params = &[("list", vec![Some(10), Some(100)])];
assert_eq!(super::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!(super::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!(super::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("a", "hello");
s.insert("b", "world");
let encoded = super::to_string(s).unwrap();
assert_eq!("a=hello&b=world", encoded);
}
+94
View File
@@ -0,0 +1,94 @@
use std::str;
use form_urlencoded::{Serializer as UrlEncodedSerializer, Target as UrlEncodedTarget};
use serde_core::ser::{Serialize, SerializeSeq};
use super::{
part::{PartSerializer, Sink},
Error,
};
pub struct ValueSink<'input, 'key, 'target, Target>
where
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<'target, Target> Sink for ValueSink<'_, '_, '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 + 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::unsupported_value()
}
}
impl<'target, Target> SerializeSeq for ValueSink<'_, '_, 'target, Target>
where
Target: 'target + UrlEncodedTarget,
{
type Ok = ();
type Error = Error;
fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: Serialize + ?Sized,
{
value.serialize(PartSerializer::new(ValueSink {
urlencoder: self.urlencoder,
key: self.key,
nested: true,
}))
}
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
}