зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1836229 - Update serde_with to 3.0.0. r=emilio,supply-chain-reviewers
We upgrade enumset alongside so that we keep only one copy of darling. Differential Revision: https://phabricator.services.mozilla.com/D179651
This commit is contained in:
Родитель
c17dace30a
Коммит
d0b7892964
|
@ -1140,16 +1140,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.13.99"
|
||||
dependencies = [
|
||||
"darling 0.14.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.14.3"
|
||||
version = "0.20.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0808e1bd8671fb44a113a14e13497557533369847788fa2ae912b6ebfce9fa8"
|
||||
checksum = "0558d22a7b463ed0241e993f76f09f30b126687447751a8638587b864e4b3944"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
|
@ -1157,27 +1150,27 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.14.3"
|
||||
version = "0.20.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "001d80444f28e193f30c2f293455da62dcf9a6b29918a4253152ae2b1de592cb"
|
||||
checksum = "ab8bfa2e259f8ee1ce5e97824a3c55ec4404a0d772ca7fa96bf19f0752a046eb"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn 1.0.107",
|
||||
"syn 2.0.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.14.3"
|
||||
version = "0.20.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b36230598a2d5de7ec1c6f51f72d8a99a9208daff41de2084d06e3fd3ea56685"
|
||||
checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn 1.0.107",
|
||||
"syn 2.0.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1244,11 +1237,11 @@ dependencies = [
|
|||
name = "derive_common"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"darling 0.14.3",
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.107",
|
||||
"synstructure",
|
||||
"syn 2.0.18",
|
||||
"synstructure 0.13.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1442,23 +1435,23 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "enumset"
|
||||
version = "1.0.12"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19be8061a06ab6f3a6cf21106c873578bf01bd42ad15e0311a9c76161cb1c753"
|
||||
checksum = "e875f1719c16de097dee81ed675e2d9bb63096823ed3f0ca827b7dea3028bbbb"
|
||||
dependencies = [
|
||||
"enumset_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enumset_derive"
|
||||
version = "0.6.1"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03e7b551eba279bf0fa88b83a46330168c1560a52a94f5126f892f0b364ab3e0"
|
||||
checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af"
|
||||
dependencies = [
|
||||
"darling 0.14.3",
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.107",
|
||||
"syn 2.0.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3069,7 +3062,7 @@ checksum = "632647502a8bfa82458c07134791fffa7a719f00427d1afd79c3cb6d4960a982"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn 1.0.107",
|
||||
"synstructure",
|
||||
"synstructure 0.12.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3925,7 +3918,7 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.107",
|
||||
"synstructure",
|
||||
"synstructure 0.12.6",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
|
@ -4233,7 +4226,7 @@ dependencies = [
|
|||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"serde_with",
|
||||
"serde_with 1.999.999",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -4685,9 +4678,16 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_with"
|
||||
version = "1.14.0"
|
||||
version = "1.999.999"
|
||||
dependencies = [
|
||||
"serde_with 3.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_with"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff"
|
||||
checksum = "9f02d8aa6e3c385bf084924f660ce2a3a6bd333ba55b35e8590b321f35d88513"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_with_macros",
|
||||
|
@ -4695,14 +4695,14 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_with_macros"
|
||||
version = "1.5.2"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082"
|
||||
checksum = "edc7d5d3932fb12ce722ee5e64dd38c504efba37567f0c402f6ca728c3b8b070"
|
||||
dependencies = [
|
||||
"darling 0.13.99",
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.107",
|
||||
"syn 2.0.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -4966,12 +4966,12 @@ dependencies = [
|
|||
name = "style_derive"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"darling 0.14.3",
|
||||
"darling",
|
||||
"derive_common",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.107",
|
||||
"synstructure",
|
||||
"syn 2.0.18",
|
||||
"synstructure 0.13.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -5079,6 +5079,18 @@ dependencies = [
|
|||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "285ba80e733fac80aa4270fbcdf83772a79b80aa35c97075320abfee4a915b06"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.18",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tabs"
|
||||
version = "0.1.0"
|
||||
|
@ -5244,12 +5256,12 @@ dependencies = [
|
|||
name = "to_shmem_derive"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"darling 0.14.3",
|
||||
"darling",
|
||||
"derive_common",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.107",
|
||||
"synstructure",
|
||||
"syn 2.0.18",
|
||||
"synstructure 0.13.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -102,8 +102,8 @@ bitflags = { path = "build/rust/bitflags" }
|
|||
# Patch cfg-if 0.1 to 1.0
|
||||
cfg-if = { path = "build/rust/cfg-if" }
|
||||
|
||||
# Patch darling 0.13 to 0.14
|
||||
darling = { path = "build/rust/darling" }
|
||||
# Patch serde_with 1.0 to 3.0
|
||||
serde_with = { path = "build/rust/serde_with" }
|
||||
|
||||
# Patch redox_users to an empty crate
|
||||
redox_users = { path = "build/rust/redox_users" }
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
[package]
|
||||
name = "darling"
|
||||
version = "0.13.99"
|
||||
edition = "2018"
|
||||
license = "MPL-2.0"
|
||||
|
||||
[lib]
|
||||
path = "lib.rs"
|
||||
|
||||
[dependencies.darling]
|
||||
version = "0.14"
|
|
@ -0,0 +1,13 @@
|
|||
[package]
|
||||
name = "serde_with"
|
||||
version = "1.999.999"
|
||||
edition = "2018"
|
||||
license = "MPL-2.0"
|
||||
|
||||
[lib]
|
||||
path = "lib.rs"
|
||||
|
||||
[dependencies.serde_with]
|
||||
version = "3"
|
||||
default-features = false
|
||||
features = ["macros"]
|
|
@ -2,4 +2,4 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
pub use darling::*;
|
||||
pub use serde_with::*;
|
|
@ -109,6 +109,7 @@ TOLERATED_DUPES = {
|
|||
"time": 2,
|
||||
# Transition is underway from syn 1.x to 2.x. (bug 1835053)
|
||||
"syn": 2,
|
||||
"synstructure": 2,
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -9,8 +9,8 @@ publish = false
|
|||
path = "lib.rs"
|
||||
|
||||
[dependencies]
|
||||
darling = { version = "0.14", default-features = false }
|
||||
darling = { version = "0.20", default-features = false }
|
||||
proc-macro2 = "1"
|
||||
quote = "1"
|
||||
syn = { version = "1", default-features = false, features = ["clone-impls", "parsing"] }
|
||||
synstructure = "0.12"
|
||||
syn = { version = "2", default-features = false, features = ["clone-impls", "parsing"] }
|
||||
synstructure = "0.13"
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
use darling::{FromDeriveInput, FromField, FromVariant};
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::TokenStreamExt;
|
||||
use syn::{self, AngleBracketedGenericArguments, Binding, DeriveInput, Field};
|
||||
use syn::{self, AngleBracketedGenericArguments, AssocType, DeriveInput, Field};
|
||||
use syn::{GenericArgument, GenericParam, Ident, Path};
|
||||
use syn::{PathArguments, PathSegment, QSelf, Type, TypeArray, TypeGroup};
|
||||
use syn::{TypeParam, TypeParen, TypePath, TypeSlice, TypeTuple};
|
||||
|
@ -252,8 +252,8 @@ where
|
|||
&GenericArgument::Type(ref data) => GenericArgument::Type(
|
||||
map_type_params(data, params, self_type, f),
|
||||
),
|
||||
&GenericArgument::Binding(ref data) => {
|
||||
GenericArgument::Binding(Binding {
|
||||
&GenericArgument::AssocType(ref data) => {
|
||||
GenericArgument::AssocType(AssocType {
|
||||
ty: map_type_params(&data.ty, params, self_type, f),
|
||||
..data.clone()
|
||||
})
|
||||
|
|
|
@ -10,9 +10,9 @@ path = "lib.rs"
|
|||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
darling = { version = "0.14", default-features = false }
|
||||
darling = { version = "0.20", default-features = false }
|
||||
derive_common = { path = "../derive_common" }
|
||||
proc-macro2 = "1"
|
||||
quote = "1"
|
||||
syn = { version = "1", default-features = false, features = ["clone-impls", "derive", "parsing"] }
|
||||
synstructure = "0.12"
|
||||
syn = { version = "2", default-features = false, features = ["clone-impls", "derive", "parsing"] }
|
||||
synstructure = "0.13"
|
||||
|
|
|
@ -10,9 +10,9 @@ path = "lib.rs"
|
|||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
darling = { version = "0.14", default-features = false }
|
||||
darling = { version = "0.20", default-features = false }
|
||||
derive_common = { path = "../derive_common" }
|
||||
proc-macro2 = "1"
|
||||
quote = "1"
|
||||
syn = { version = "1", default-features = false, features = ["derive", "parsing"] }
|
||||
synstructure = "0.12"
|
||||
syn = { version = "2", default-features = false, features = ["derive", "parsing"] }
|
||||
synstructure = "0.13"
|
||||
|
|
|
@ -958,6 +958,11 @@ who = "Mike Hommey <mh+mozilla@glandium.org>"
|
|||
criteria = "safe-to-deploy"
|
||||
delta = "0.14.2 -> 0.14.3"
|
||||
|
||||
[[audits.darling]]
|
||||
who = "Mike Hommey <mh+mozilla@glandium.org>"
|
||||
criteria = "safe-to-deploy"
|
||||
delta = "0.14.3 -> 0.20.1"
|
||||
|
||||
[[audits.darling_core]]
|
||||
who = "Mike Hommey <mh+mozilla@glandium.org>"
|
||||
criteria = "safe-to-deploy"
|
||||
|
@ -968,6 +973,11 @@ who = "Mike Hommey <mh+mozilla@glandium.org>"
|
|||
criteria = "safe-to-deploy"
|
||||
delta = "0.14.2 -> 0.14.3"
|
||||
|
||||
[[audits.darling_core]]
|
||||
who = "Mike Hommey <mh+mozilla@glandium.org>"
|
||||
criteria = "safe-to-deploy"
|
||||
delta = "0.14.3 -> 0.20.1"
|
||||
|
||||
[[audits.darling_macro]]
|
||||
who = "Mike Hommey <mh+mozilla@glandium.org>"
|
||||
criteria = "safe-to-deploy"
|
||||
|
@ -978,6 +988,11 @@ who = "Mike Hommey <mh+mozilla@glandium.org>"
|
|||
criteria = "safe-to-deploy"
|
||||
delta = "0.14.2 -> 0.14.3"
|
||||
|
||||
[[audits.darling_macro]]
|
||||
who = "Mike Hommey <mh+mozilla@glandium.org>"
|
||||
criteria = "safe-to-deploy"
|
||||
delta = "0.14.3 -> 0.20.1"
|
||||
|
||||
[[audits.data-encoding]]
|
||||
who = "Mike Hommey <mh+mozilla@glandium.org>"
|
||||
criteria = "safe-to-deploy"
|
||||
|
@ -1103,11 +1118,21 @@ who = "Mike Hommey <mh+mozilla@glandium.org>"
|
|||
criteria = "safe-to-deploy"
|
||||
delta = "1.0.11 -> 1.0.12"
|
||||
|
||||
[[audits.enumset]]
|
||||
who = "Mike Hommey <mh+mozilla@glandium.org>"
|
||||
criteria = "safe-to-deploy"
|
||||
delta = "1.0.12 -> 1.1.2"
|
||||
|
||||
[[audits.enumset_derive]]
|
||||
who = "Mike Hommey <mh+mozilla@glandium.org>"
|
||||
criteria = "safe-to-deploy"
|
||||
delta = "0.6.0 -> 0.6.1"
|
||||
|
||||
[[audits.enumset_derive]]
|
||||
who = "Mike Hommey <mh+mozilla@glandium.org>"
|
||||
criteria = "safe-to-deploy"
|
||||
delta = "0.6.1 -> 0.8.1"
|
||||
|
||||
[[audits.env_logger]]
|
||||
who = "Mike Hommey <mh+mozilla@glandium.org>"
|
||||
criteria = "safe-to-deploy"
|
||||
|
@ -2542,6 +2567,16 @@ who = "Mike Hommey <mh+mozilla@glandium.org>"
|
|||
criteria = "safe-to-run"
|
||||
delta = "0.1.9 -> 0.1.10"
|
||||
|
||||
[[audits.serde_with]]
|
||||
who = "Mike Hommey <mh+mozilla@glandium.org>"
|
||||
criteria = "safe-to-deploy"
|
||||
delta = "1.14.0 -> 3.0.0"
|
||||
|
||||
[[audits.serde_with_macros]]
|
||||
who = "Mike Hommey <mh+mozilla@glandium.org>"
|
||||
criteria = "safe-to-deploy"
|
||||
delta = "1.5.2 -> 3.0.0"
|
||||
|
||||
[[audits.serde_yaml]]
|
||||
who = "Mike Hommey <mh+mozilla@glandium.org>"
|
||||
criteria = "safe-to-run"
|
||||
|
@ -2635,6 +2670,11 @@ maintainer. The one use of `unsafe` is unnecessary, but documented and
|
|||
harmless. It will be removed in the next version.
|
||||
"""
|
||||
|
||||
[[audits.synstructure]]
|
||||
who = "Mike Hommey <mh+mozilla@glandium.org>"
|
||||
criteria = "safe-to-deploy"
|
||||
delta = "0.12.6 -> 0.13.0"
|
||||
|
||||
[[audits.termcolor]]
|
||||
who = "Mike Hommey <mh+mozilla@glandium.org>"
|
||||
criteria = "safe-to-deploy"
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"CHANGELOG.md":"36a132403864fecc7e435509e8339d9f2c0962a451c53357c1d9b08f6d8a36bc","Cargo.lock":"e77a5ba77d6ef12db6c6cdafde228004d938601f46b0bd28a0df7ca836a90142","Cargo.toml":"6ed8f0ac89f171b24a69d4da2c4e8cd711028f0463bd47e9e07026c92f92d16e","LICENSE":"8ea93490d74a5a1b1af3ff71d786271b3f1e5f0bea79ac16e02ec533cef040d6","README.md":"2147b655029789a691d4b9428c836ee55716bea250fba63b8b1c3b4b280d761c","clippy.toml":"0728f8cee5cdb954e4d5c64c9a84c83ff356a913d531412baf4b5852ba6712ef","compiletests.sh":"a255ae505d79b4a40bdc80b215a5384eadee228593d042fbed60e99cc278e220","examples/automatic_bounds.rs":"2950c8d33bb40f095decba1990d7d0bcd48dffc6e9b7cefce2fcb3818ecf1d18","examples/consume_fields.rs":"ce436936441f1f6734f47074e6625ebf64f15180b9c126a58e24aaa33613b59c","examples/fallible_read.rs":"1e5f2b69e436d899209dc8a4deec0dbe3e998f10a6632a79c0876c46f68267b4","examples/shorthand_or_long_field.rs":"ec2e2b155fd0803c80f98d0ba94e8419dd7307012ccfb916b8f752925a295d55","examples/supports_struct.rs":"08c5cc46400a0e5cf811c0d254a37b42de94b34cd04ac801069105bc792051f6","src/lib.rs":"7fb89b93193dbbab4f32327576fa49d8ba677fc32b09a36f84d3c942b2d7ea06","src/macros_public.rs":"7d2ce0c5026227ef7854db11d7a885ad891255438b2e49bbdfda56fa2f92feec","tests/accrue_errors.rs":"4f0f5be65c5cd639b107a6a14e9fb51573b27cfa9210a2182fa5f450bc1d56db","tests/compile-fail/default_expr_wrong_type.rs":"e09889ac3a243bd6096a0ef7076822b146f1481b70aae703e170ea6527071448","tests/compile-fail/default_expr_wrong_type.stderr":"dce0c05f344be36bf66407b459eb1f6abb560b6f5500d4b771d3fd5babc78325","tests/compile-fail/not_impl_from_meta.rs":"20ef167d88ea06967c26a05234d3b1787f0695d45b4ab1150bf9dc77625f9c74","tests/compile-fail/not_impl_from_meta.stderr":"3e52d37d2a5a6f98e4af70389327f9248ea283aac9bdb0660058ec92e23999b8","tests/compile-fail/skip_field_not_impl_default.rs":"5cb7e2e68d7dc42e6508eb0c170869179f127cf641b44f08f6403760f30c469b","tests/compile-fail/skip_field_not_impl_default.stderr":"664f0c2a5c3c85901b65e923b8907870bead9b2781a4c75497eb6df621a0f701","tests/compiletests.rs":"dc2b570d0e0ab4895d7cd039fa9572d507221f2ff2e546a3dacfd9c64fe1c936","tests/computed_bound.rs":"aed9c00f2b8373e9426337137207e5b9341d236adef682bd2c9427db9ce1d1ff","tests/custom_bound.rs":"9b823063b7fc6c6b3b23905405ce7f316e8a937d5c67c416b89642e537bf9110","tests/defaults.rs":"6894fded24b2f9d173b11f54bbe3b640946c6eb6f5ff5492369e8cd84fc217ef","tests/enums_newtype.rs":"ed63735b88fdfd9037580d878be895a311c13d7d8083ee3077f5ab61e754eb7c","tests/enums_struct.rs":"36ca3d4996644d566b45c78d8c343c4a74fcaa2eba96b4e012c8a1819eb6f4c6","tests/enums_unit.rs":"7f94f793e6adc265c6cdc4b657aa237da1ab0be03b03bce23d3e2180cd3e8d03","tests/error.rs":"f5f84991472e184e1167f0fe8d5f2cbad3844c4022987c9eff46b4db2bcc804a","tests/from_generics.rs":"f2c1f98f654c4e6ad3606d7b12ea7ea66782950c05da132e8d7f2f7e1bdf3af0","tests/from_meta.rs":"b9c73340c44a53a43d7234c6030d37d1ab7c63ea2ec2ae1f7f09e5f2d65baa6b","tests/from_type_param.rs":"94d2766d5ae11d69750497225d6aa3c2f34b09fbc8c3580d61f077d7bb41265b","tests/from_type_param_default.rs":"e00e2f0c779753f66b95e5c0106451f65cbd6fbc28e676381d276290da6254b6","tests/from_variant.rs":"48046b156f6c5d9b3e9c3d0b36b5eebaba1d417447e3babf81ed9c74bee3bfcb","tests/generics.rs":"0c2830acf511148d71ecd4a8b5aa17f80e377aa89f7fe0fc10f6db34671d034a","tests/happy_path.rs":"c7a540fc1755cef757aa5e6cd202a49a47a2040facb0c05c167ec62f8ebbc557","tests/hash_map.rs":"c30764bf1845ca81bc1d752dbe0de965ba70cfbb1571884a20a873ed7bf26360","tests/multiple.rs":"1362ec057f4796ffabf7161033b561b51f069b818af7bac85fe66935c62038dd","tests/newtype.rs":"b5ecf605652b194372cab6d6fef96a2dd4b63ac24649cb52ca944ef9647512ad","tests/skip.rs":"11b5f190d6eac837d4a44a7dedd1ba9e623b0c7a8bf2bdc92882e1f8a8d2aeac","tests/split_declaration.rs":"019863370414af227144aac13272fc39a1e256a9ed0bd3ca2dbf1114f1a9e1ba","tests/suggestions.rs":"e9f8ab55718a5853411a4606f1be1473b57fc7a2789f622d0ed807fcd8ac5606","tests/supports.rs":"fd27b20893a1b1078eff077d426fea7d715b8decd12ad1e0b940cfbfd4fbbfba","tests/unsupported_attributes.rs":"96333cd6602a6f18f47563d5faa923e423b2c02f2ae0d09a15e2d3514593c38d"},"package":"c0808e1bd8671fb44a113a14e13497557533369847788fa2ae912b6ebfce9fa8"}
|
||||
{"files":{"CHANGELOG.md":"68745fb7dd94b72ba5f87271f40ee89b6a472e9a2e88153967a4add77af9ef13","Cargo.lock":"0b5e050bf93a5f957c0374aacae02aa8cf2070dc4b38a999877b7237609efae0","Cargo.toml":"1e9933d5e20abccde499d6872e07bd8be8792b673c9c008a6b38528eb7873d28","LICENSE":"8ea93490d74a5a1b1af3ff71d786271b3f1e5f0bea79ac16e02ec533cef040d6","README.md":"19aaa2e1cd8ccc8d9c344711a0e5ee3a96657d3e0c522f7a82a18b7f4865c60a","clippy.toml":"c5ef3489cce9d5ed7c766e6486a18a0a3dc0f4c677fcf05166e1d47104f6cd3f","compiletests.sh":"a255ae505d79b4a40bdc80b215a5384eadee228593d042fbed60e99cc278e220","examples/automatic_bounds.rs":"2950c8d33bb40f095decba1990d7d0bcd48dffc6e9b7cefce2fcb3818ecf1d18","examples/consume_fields.rs":"ce436936441f1f6734f47074e6625ebf64f15180b9c126a58e24aaa33613b59c","examples/fallible_read.rs":"1d081d26d1aa585b2b5ed157fc9b6562274c9c932bda3be8d8d22e2d4272a213","examples/shorthand_or_long_field.rs":"ec2e2b155fd0803c80f98d0ba94e8419dd7307012ccfb916b8f752925a295d55","examples/supports_struct.rs":"08c5cc46400a0e5cf811c0d254a37b42de94b34cd04ac801069105bc792051f6","src/lib.rs":"cbb8bb4317216ca6824c8f176f3c58effcb234b26c6b3edf1725baf0d1fcfb84","src/macros_public.rs":"7d2ce0c5026227ef7854db11d7a885ad891255438b2e49bbdfda56fa2f92feec","tests/accrue_errors.rs":"4f0f5be65c5cd639b107a6a14e9fb51573b27cfa9210a2182fa5f450bc1d56db","tests/compile-fail/default_expr_wrong_type.rs":"e09889ac3a243bd6096a0ef7076822b146f1481b70aae703e170ea6527071448","tests/compile-fail/default_expr_wrong_type.stderr":"dce0c05f344be36bf66407b459eb1f6abb560b6f5500d4b771d3fd5babc78325","tests/compile-fail/not_impl_from_meta.rs":"20ef167d88ea06967c26a05234d3b1787f0695d45b4ab1150bf9dc77625f9c74","tests/compile-fail/not_impl_from_meta.stderr":"3e52d37d2a5a6f98e4af70389327f9248ea283aac9bdb0660058ec92e23999b8","tests/compile-fail/skip_field_not_impl_default.rs":"5cb7e2e68d7dc42e6508eb0c170869179f127cf641b44f08f6403760f30c469b","tests/compile-fail/skip_field_not_impl_default.stderr":"664f0c2a5c3c85901b65e923b8907870bead9b2781a4c75497eb6df621a0f701","tests/compiletests.rs":"dc2b570d0e0ab4895d7cd039fa9572d507221f2ff2e546a3dacfd9c64fe1c936","tests/computed_bound.rs":"aed9c00f2b8373e9426337137207e5b9341d236adef682bd2c9427db9ce1d1ff","tests/custom_bound.rs":"9b823063b7fc6c6b3b23905405ce7f316e8a937d5c67c416b89642e537bf9110","tests/defaults.rs":"6894fded24b2f9d173b11f54bbe3b640946c6eb6f5ff5492369e8cd84fc217ef","tests/enums_newtype.rs":"ed63735b88fdfd9037580d878be895a311c13d7d8083ee3077f5ab61e754eb7c","tests/enums_struct.rs":"36ca3d4996644d566b45c78d8c343c4a74fcaa2eba96b4e012c8a1819eb6f4c6","tests/enums_unit.rs":"7f94f793e6adc265c6cdc4b657aa237da1ab0be03b03bce23d3e2180cd3e8d03","tests/error.rs":"f5f84991472e184e1167f0fe8d5f2cbad3844c4022987c9eff46b4db2bcc804a","tests/from_generics.rs":"8be63cc3390e94f2740932dea280c8c44d929a1fcc2cfff68cec4d3bceadc4d9","tests/from_meta.rs":"b9c73340c44a53a43d7234c6030d37d1ab7c63ea2ec2ae1f7f09e5f2d65baa6b","tests/from_type_param.rs":"94d2766d5ae11d69750497225d6aa3c2f34b09fbc8c3580d61f077d7bb41265b","tests/from_type_param_default.rs":"e00e2f0c779753f66b95e5c0106451f65cbd6fbc28e676381d276290da6254b6","tests/from_variant.rs":"48046b156f6c5d9b3e9c3d0b36b5eebaba1d417447e3babf81ed9c74bee3bfcb","tests/generics.rs":"0c2830acf511148d71ecd4a8b5aa17f80e377aa89f7fe0fc10f6db34671d034a","tests/happy_path.rs":"c7a540fc1755cef757aa5e6cd202a49a47a2040facb0c05c167ec62f8ebbc557","tests/hash_map.rs":"2559783b10108a975466b6944ed89737be6197c64a35c66cfad43a272c085a01","tests/multiple.rs":"1362ec057f4796ffabf7161033b561b51f069b818af7bac85fe66935c62038dd","tests/newtype.rs":"b5ecf605652b194372cab6d6fef96a2dd4b63ac24649cb52ca944ef9647512ad","tests/skip.rs":"11b5f190d6eac837d4a44a7dedd1ba9e623b0c7a8bf2bdc92882e1f8a8d2aeac","tests/split_declaration.rs":"019863370414af227144aac13272fc39a1e256a9ed0bd3ca2dbf1114f1a9e1ba","tests/suggestions.rs":"e9f8ab55718a5853411a4606f1be1473b57fc7a2789f622d0ed807fcd8ac5606","tests/supports.rs":"fd27b20893a1b1078eff077d426fea7d715b8decd12ad1e0b940cfbfd4fbbfba","tests/unsupported_attributes.rs":"84ba14c2762c3ad4432c238000a03b70b3bf1a4565d604ba00d0346952c3f6bb"},"package":"0558d22a7b463ed0241e993f76f09f30b126687447751a8638587b864e4b3944"}
|
|
@ -1,5 +1,48 @@
|
|||
# Changelog
|
||||
|
||||
## v0.20.1 (May 2, 2023)
|
||||
|
||||
- Add `Clone` impl for `NestedMeta` [#230](https://github.com/TedDriggs/darling/pull/230)
|
||||
|
||||
## v0.20.0 (April 27, 2023)
|
||||
|
||||
- Bump syn to version 2, courtesy of @jonasbb [#227](https://github.com/TedDriggs/darling/issues/227)
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- Replace all occurrences of syn::NestedMeta with darling::ast::NestedMeta.
|
||||
|
||||
- Replacement for the deprecated AttributeArgs:
|
||||
|
||||
```rust
|
||||
// Before
|
||||
|
||||
parse_macro_input!(args as AttributeArgs);
|
||||
|
||||
// After
|
||||
|
||||
match NestedMeta::parse_meta_list(args) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
return TokenStream::from(Error::from(e).write_errors());
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
- In GenericParamExt, `LifetimeDef` is now `LifetimeParam`.
|
||||
- In GenericParamExt, `as_lifetime_def` is renamed to `as_lifetime_param`.
|
||||
- Flag and SpannedValue no longer implement `syn::spanned::Spanned`.
|
||||
- The MSRV (minimum supported Rust version) is now 1.56, because of syn.
|
||||
|
||||
### Deprecation Warnings
|
||||
|
||||
In previous versions of `darling`, arbitrary expressions were passed in attributes by wrapping them in quotation marks.
|
||||
v0.20.0 preserves this behavior for `syn::Expr`, but as a result a field expecting a `syn::Expr` cannot accept a string literal - it will incorrectly attempt to parse the contents. If this is an issue for you, please add a comment to [#229](https://github.com/TedDriggs/darling/issues/229).
|
||||
|
||||
## v0.14.4 (March 9, 2023)
|
||||
|
||||
- Add support for child diagnostics when `diagnostics` feature enabled [#224](https://github.com/TedDriggs/darling/issues/224)
|
||||
|
||||
## v0.14.3 (February 3, 2023)
|
||||
|
||||
- Re-export `syn` from `darling` to avoid requiring that consuming crates have a `syn` dependency.
|
||||
|
|
|
@ -4,40 +4,40 @@ version = 3
|
|||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.14.3"
|
||||
version = "0.20.1"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn",
|
||||
"syn 2.0.15",
|
||||
"trybuild",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.14.3"
|
||||
version = "0.20.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "001d80444f28e193f30c2f293455da62dcf9a6b29918a4253152ae2b1de592cb"
|
||||
checksum = "ab8bfa2e259f8ee1ce5e97824a3c55ec4404a0d772ca7fa96bf19f0752a046eb"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn",
|
||||
"syn 2.0.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.14.3"
|
||||
version = "0.20.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b36230598a2d5de7ec1c6f51f72d8a99a9208daff41de2084d06e3fd3ea56685"
|
||||
checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -72,18 +72,18 @@ checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.37"
|
||||
version = "1.0.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1"
|
||||
checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.18"
|
||||
version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
|
||||
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
@ -114,7 +114,7 @@ checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.91",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -145,6 +145,17 @@ dependencies = [
|
|||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.3"
|
||||
|
@ -178,6 +189,12 @@ dependencies = [
|
|||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.2"
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
[package]
|
||||
edition = "2018"
|
||||
name = "darling"
|
||||
version = "0.14.3"
|
||||
version = "0.20.1"
|
||||
authors = ["Ted Driggs <ted.driggs@outlook.com>"]
|
||||
exclude = [
|
||||
"/.travis.yml",
|
||||
|
@ -23,16 +23,16 @@ description = """
|
|||
A proc-macro library for reading attributes into structs when
|
||||
implementing custom derives.
|
||||
"""
|
||||
documentation = "https://docs.rs/darling/0.14.3"
|
||||
documentation = "https://docs.rs/darling/0.20.1"
|
||||
readme = "README.md"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/TedDriggs/darling"
|
||||
|
||||
[dependencies.darling_core]
|
||||
version = "=0.14.3"
|
||||
version = "=0.20.1"
|
||||
|
||||
[dependencies.darling_macro]
|
||||
version = "=0.14.3"
|
||||
version = "=0.20.1"
|
||||
|
||||
[dev-dependencies.proc-macro2]
|
||||
version = "1.0.37"
|
||||
|
@ -41,7 +41,7 @@ version = "1.0.37"
|
|||
version = "1.0.18"
|
||||
|
||||
[dev-dependencies.syn]
|
||||
version = "1.0.91"
|
||||
version = "2.0.15"
|
||||
|
||||
[features]
|
||||
default = ["suggestions"]
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
Darling
|
||||
=======
|
||||
# Darling
|
||||
|
||||
[![Build Status](https://github.com/TedDriggs/darling/workflows/CI/badge.svg)](https://github.com/TedDriggs/darling/actions)
|
||||
[![Latest Version](https://img.shields.io/crates/v/darling.svg)](https://crates.io/crates/darling)
|
||||
[![Rustc Version 1.31+](https://img.shields.io/badge/rustc-1.31+-lightgray.svg)](https://blog.rust-lang.org/2018/12/06/Rust-1.31-and-rust-2018.html)
|
||||
[![Rustc Version 1.56+](https://img.shields.io/badge/rustc-1.56+-lightgray.svg)]
|
||||
|
||||
`darling` is a crate for proc macro authors, which enables parsing attributes into structs. It is heavily inspired by `serde` both in its internals and in its API.
|
||||
|
||||
# Benefits
|
||||
* Easy and declarative parsing of macro input - make your proc-macros highly controllable with minimal time investment.
|
||||
* Great validation and errors, no work required. When users of your proc-macro make a mistake, `darling` makes sure they get error markers at the right place in their source, and provides "did you mean" suggestions for misspelled fields.
|
||||
|
||||
- Easy and declarative parsing of macro input - make your proc-macros highly controllable with minimal time investment.
|
||||
- Great validation and errors, no work required. When users of your proc-macro make a mistake, `darling` makes sure they get error markers at the right place in their source, and provides "did you mean" suggestions for misspelled fields.
|
||||
|
||||
# Usage
|
||||
|
||||
`darling` provides a set of traits which can be derived or manually implemented.
|
||||
|
||||
1. `FromMeta` is used to extract values from a meta-item in an attribute. Implementations are likely reusable for many libraries, much like `FromStr` or `serde::Deserialize`. Trait implementations are provided for primitives, some std types, and some `syn` types.
|
||||
|
@ -21,9 +22,10 @@ Darling
|
|||
5. `FromAttributes` is a lower-level version of the more-specific `FromDeriveInput`, `FromField`, and `FromVariant` traits. Structs deriving this trait get a meta-item extractor and error collection which works for any syntax element, including traits, trait items, and functions. This is useful for non-derive proc macros.
|
||||
|
||||
## Additional Modules
|
||||
* `darling::ast` provides generic types for representing the AST.
|
||||
* `darling::usage` provides traits and functions for determining where type parameters and lifetimes are used in a struct or enum.
|
||||
* `darling::util` provides helper types with special `FromMeta` implementations, such as `IdentList`.
|
||||
|
||||
- `darling::ast` provides generic types for representing the AST.
|
||||
- `darling::usage` provides traits and functions for determining where type parameters and lifetimes are used in a struct or enum.
|
||||
- `darling::util` provides helper types with special `FromMeta` implementations, such as `IdentList`.
|
||||
|
||||
# Example
|
||||
|
||||
|
@ -59,14 +61,17 @@ pub struct ConsumingType;
|
|||
```
|
||||
|
||||
# Attribute Macros
|
||||
|
||||
Non-derive attribute macros are supported.
|
||||
To parse arguments for attribute macros, derive `FromMeta` on the argument receiver type, then pass `&syn::AttributeArgs` to the `from_list` method.
|
||||
This will produce a normal `darling::Result<T>` that can be used the same as a result from parsing a `DeriveInput`.
|
||||
|
||||
## Macro Code
|
||||
|
||||
```rust,ignore
|
||||
use darling::FromMeta;
|
||||
use syn::{AttributeArgs, ItemFn};
|
||||
use darling::{Error, FromMeta};
|
||||
use darling::ast::NestedMeta;
|
||||
use syn::ItemFn;
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
#[derive(Debug, FromMeta)]
|
||||
|
@ -76,10 +81,13 @@ pub struct MacroArgs {
|
|||
path: String,
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
// #[proc_macro_attribute]
|
||||
fn your_attr(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
let attr_args = parse_macro_input!(args as AttributeArgs);
|
||||
let _input = parse_macro_input!(input as ItemFn);
|
||||
let attr_args = match NestedMeta::parse_meta_list(args) {
|
||||
Ok(v) => v,
|
||||
Err(e) => { return TokenStream::from(Error::from(e).write_errors()); }
|
||||
};
|
||||
let _input = syn::parse_macro_input!(input as ItemFn);
|
||||
|
||||
let _args = match MacroArgs::from_list(&attr_args) {
|
||||
Ok(v) => v,
|
||||
|
@ -92,6 +100,7 @@ fn your_attr(args: TokenStream, input: TokenStream) -> TokenStream {
|
|||
```
|
||||
|
||||
## Consuming Code
|
||||
|
||||
```rust,ignore
|
||||
use your_crate::your_attr;
|
||||
|
||||
|
@ -102,37 +111,39 @@ fn do_stuff() {
|
|||
```
|
||||
|
||||
# Features
|
||||
|
||||
Darling's features are built to work well for real-world projects.
|
||||
|
||||
* **Defaults**: Supports struct- and field-level defaults, using the same path syntax as `serde`.
|
||||
Additionally, `Option<T>` and `darling::util::Flag` fields are innately optional; you don't need to declare `#[darling(default)]` for those.
|
||||
* **Field Renaming**: Fields can have different names in usage vs. the backing code.
|
||||
* **Auto-populated fields**: Structs deriving `FromDeriveInput` and `FromField` can declare properties named `ident`, `vis`, `ty`, `attrs`, and `generics` to automatically get copies of the matching values from the input AST. `FromDeriveInput` additionally exposes `data` to get access to the body of the deriving type, and `FromVariant` exposes `fields`.
|
||||
* **Mapping function**: Use `#[darling(map="path")]` or `#[darling(and_then="path")]` to specify a function that runs on the result of parsing a meta-item field. This can change the return type, which enables you to parse to an intermediate form and convert that to the type you need in your struct.
|
||||
* **Skip fields**: Use `#[darling(skip)]` to mark a field that shouldn't be read from attribute meta-items.
|
||||
* **Multiple-occurrence fields**: Use `#[darling(multiple)]` on a `Vec` field to allow that field to appear multiple times in the meta-item. Each occurrence will be pushed into the `Vec`.
|
||||
* **Span access**: Use `darling::util::SpannedValue` in a struct to get access to that meta item's source code span. This can be used to emit warnings that point at a specific field from your proc macro. In addition, you can use `darling::Error::write_errors` to automatically get precise error location details in most cases.
|
||||
* **"Did you mean" suggestions**: Compile errors from derived darling trait impls include suggestions for misspelled fields.
|
||||
- **Defaults**: Supports struct- and field-level defaults, using the same path syntax as `serde`.
|
||||
Additionally, `Option<T>` and `darling::util::Flag` fields are innately optional; you don't need to declare `#[darling(default)]` for those.
|
||||
- **Field Renaming**: Fields can have different names in usage vs. the backing code.
|
||||
- **Auto-populated fields**: Structs deriving `FromDeriveInput` and `FromField` can declare properties named `ident`, `vis`, `ty`, `attrs`, and `generics` to automatically get copies of the matching values from the input AST. `FromDeriveInput` additionally exposes `data` to get access to the body of the deriving type, and `FromVariant` exposes `fields`.
|
||||
- **Mapping function**: Use `#[darling(map="path")]` or `#[darling(and_then="path")]` to specify a function that runs on the result of parsing a meta-item field. This can change the return type, which enables you to parse to an intermediate form and convert that to the type you need in your struct.
|
||||
- **Skip fields**: Use `#[darling(skip)]` to mark a field that shouldn't be read from attribute meta-items.
|
||||
- **Multiple-occurrence fields**: Use `#[darling(multiple)]` on a `Vec` field to allow that field to appear multiple times in the meta-item. Each occurrence will be pushed into the `Vec`.
|
||||
- **Span access**: Use `darling::util::SpannedValue` in a struct to get access to that meta item's source code span. This can be used to emit warnings that point at a specific field from your proc macro. In addition, you can use `darling::Error::write_errors` to automatically get precise error location details in most cases.
|
||||
- **"Did you mean" suggestions**: Compile errors from derived darling trait impls include suggestions for misspelled fields.
|
||||
|
||||
## Shape Validation
|
||||
|
||||
Some proc-macros only work on structs, while others need enums whose variants are either unit or newtype variants.
|
||||
Darling makes this sort of validation extremely simple.
|
||||
On the receiver that derives `FromDeriveInput`, add `#[darling(supports(...))]` and then list the shapes that your macro should accept.
|
||||
|
||||
|Name|Description|
|
||||
|---|---|
|
||||
|`any`|Accept anything|
|
||||
|`struct_any`|Accept any struct|
|
||||
|`struct_named`|Accept structs with named fields, e.g. `struct Example { field: String }`|
|
||||
|`struct_newtype`|Accept newtype structs, e.g. `struct Example(String)`|
|
||||
|`struct_tuple`|Accept tuple structs, e.g. `struct Example(String, String)`|
|
||||
|`struct_unit`|Accept unit structs, e.g. `struct Example;`|
|
||||
|`enum_any`|Accept any enum|
|
||||
|`enum_named`|Accept enum variants with named fields|
|
||||
|`enum_newtype`|Accept newtype enum variants|
|
||||
|`enum_tuple`|Accept tuple enum variants|
|
||||
|`enum_unit`|Accept unit enum variants|
|
||||
| Name | Description |
|
||||
| ---------------- | ------------------------------------------------------------------------- |
|
||||
| `any` | Accept anything |
|
||||
| `struct_any` | Accept any struct |
|
||||
| `struct_named` | Accept structs with named fields, e.g. `struct Example { field: String }` |
|
||||
| `struct_newtype` | Accept newtype structs, e.g. `struct Example(String)` |
|
||||
| `struct_tuple` | Accept tuple structs, e.g. `struct Example(String, String)` |
|
||||
| `struct_unit` | Accept unit structs, e.g. `struct Example;` |
|
||||
| `enum_any` | Accept any enum |
|
||||
| `enum_named` | Accept enum variants with named fields |
|
||||
| `enum_newtype` | Accept newtype enum variants |
|
||||
| `enum_tuple` | Accept tuple enum variants |
|
||||
| `enum_unit` | Accept unit enum variants |
|
||||
|
||||
Each one is additive, so listing `#[darling(supports(struct_any, enum_newtype))]` would accept all structs and any enum where every variant is a newtype variant.
|
||||
|
||||
This can also be used when deriving `FromVariant`, without the `enum_` prefix.
|
||||
This can also be used when deriving `FromVariant`, without the `enum_` prefix.
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
msrv = "1.31.0"
|
||||
msrv = "1.56.0"
|
||||
disallowed-names = [] # we want to be able to use placeholder names in tests
|
|
@ -51,7 +51,7 @@ impl MyInputReceiver {
|
|||
// we'll go ahead and make it positive.
|
||||
let amplitude = match amplitude {
|
||||
Ok(amp) => amp,
|
||||
Err(mi) => (i64::from_meta(&mi)?).abs() as u64,
|
||||
Err(mi) => (i64::from_meta(&mi)?).unsigned_abs(),
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
|
|
|
@ -102,6 +102,8 @@ pub mod export {
|
|||
pub use darling_core::syn;
|
||||
pub use std::string::ToString;
|
||||
pub use std::vec::Vec;
|
||||
|
||||
pub use crate::ast::NestedMeta;
|
||||
}
|
||||
|
||||
#[macro_use]
|
||||
|
|
|
@ -48,8 +48,8 @@ fn expand_some() {
|
|||
.expect("Input is well-formed");
|
||||
assert!(rec.generics.where_clause.is_none());
|
||||
|
||||
// Make sure we've preserved the lifetime def, though we don't do anything with it.
|
||||
assert!(rec.generics.params[0].as_lifetime_def().is_some());
|
||||
// Make sure we've preserved the lifetime param, though we don't do anything with it.
|
||||
assert!(rec.generics.params[0].as_lifetime_param().is_some());
|
||||
|
||||
let mut ty_param_iter = rec.generics.type_params();
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ fn parse_map() {
|
|||
#[foo(first(name = "Hello", option), the::second(name = "Second"))]
|
||||
};
|
||||
|
||||
let meta = attr.parse_meta().unwrap();
|
||||
let meta = attr.meta;
|
||||
let map: HashMap<Path, MapValue> = FromMeta::from_meta(&meta).unwrap();
|
||||
|
||||
let comparison: HashMap<Path, MapValue> = vec![
|
||||
|
|
|
@ -20,8 +20,8 @@ fn non_meta_attribute_gets_own_error() {
|
|||
};
|
||||
|
||||
let errors: darling::Error = Bar::from_derive_input(&di).unwrap_err().flatten();
|
||||
// The number of errors here is 1 for the bad attribute + 2 for the missing fields
|
||||
assert_eq!(3, errors.len());
|
||||
// The number of errors here is 1 for the bad value of the `st` attribute
|
||||
assert_eq!(1, errors.len());
|
||||
// Make sure one of the errors propagates the syn error
|
||||
assert!(errors
|
||||
.into_iter()
|
||||
|
@ -40,8 +40,8 @@ fn non_meta_attribute_does_not_block_others() {
|
|||
};
|
||||
|
||||
let errors: darling::Error = Bar::from_derive_input(&di).unwrap_err().flatten();
|
||||
// The number of errors here is 1 for the bad attribute + 1 for the missing "st" field
|
||||
assert_eq!(2, errors.len());
|
||||
// The number of errors here is 1 for the bad value of the `st` attribute
|
||||
assert_eq!(1, errors.len());
|
||||
// Make sure one of the errors propagates the syn error
|
||||
assert!(errors
|
||||
.into_iter()
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -12,7 +12,7 @@
|
|||
[package]
|
||||
edition = "2018"
|
||||
name = "darling_core"
|
||||
version = "0.14.3"
|
||||
version = "0.20.1"
|
||||
authors = ["Ted Driggs <ted.driggs@outlook.com>"]
|
||||
description = """
|
||||
Helper crate for proc-macro library for reading attributes into structs when
|
||||
|
@ -38,7 +38,7 @@ version = "0.10.0"
|
|||
optional = true
|
||||
|
||||
[dependencies.syn]
|
||||
version = "1.0.91"
|
||||
version = "2.0.15"
|
||||
features = [
|
||||
"full",
|
||||
"extra-traits",
|
||||
|
|
|
@ -2,7 +2,10 @@ use std::{slice, vec};
|
|||
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::{quote, quote_spanned, ToTokens};
|
||||
use syn::ext::IdentExt;
|
||||
use syn::parse::Parser;
|
||||
use syn::spanned::Spanned;
|
||||
use syn::Token;
|
||||
|
||||
use crate::usage::{
|
||||
self, IdentRefSet, IdentSet, LifetimeRefSet, LifetimeSet, UsesLifetimes, UsesTypeParams,
|
||||
|
@ -410,6 +413,43 @@ impl<'a> From<&'a syn::Fields> for Style {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum NestedMeta {
|
||||
Meta(syn::Meta),
|
||||
Lit(syn::Lit),
|
||||
}
|
||||
|
||||
impl NestedMeta {
|
||||
pub fn parse_meta_list(tokens: TokenStream) -> syn::Result<Vec<Self>> {
|
||||
syn::punctuated::Punctuated::<NestedMeta, Token![,]>::parse_terminated
|
||||
.parse2(tokens)
|
||||
.map(|punctuated| punctuated.into_iter().collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for NestedMeta {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
if input.peek(syn::Lit) && !(input.peek(syn::LitBool) && input.peek2(Token![=])) {
|
||||
input.parse().map(NestedMeta::Lit)
|
||||
} else if input.peek(syn::Ident::peek_any)
|
||||
|| input.peek(Token![::]) && input.peek3(syn::Ident::peek_any)
|
||||
{
|
||||
input.parse().map(NestedMeta::Meta)
|
||||
} else {
|
||||
Err(input.error("expected identifier or literal"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for NestedMeta {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
match self {
|
||||
NestedMeta::Meta(meta) => meta.to_tokens(tokens),
|
||||
NestedMeta::Lit(lit) => lit.to_tokens(tokens),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
@ -14,7 +14,7 @@ use crate::{FromGenericParam, FromGenerics, FromTypeParam, Result};
|
|||
pub trait GenericParamExt {
|
||||
/// The type this GenericParam uses to represent type params and their bounds
|
||||
type TypeParam;
|
||||
type LifetimeDef;
|
||||
type LifetimeParam;
|
||||
type ConstParam;
|
||||
|
||||
/// If this GenericParam is a type param, get the underlying value.
|
||||
|
@ -23,7 +23,7 @@ pub trait GenericParamExt {
|
|||
}
|
||||
|
||||
/// If this GenericParam is a lifetime, get the underlying value.
|
||||
fn as_lifetime_def(&self) -> Option<&Self::LifetimeDef> {
|
||||
fn as_lifetime_param(&self) -> Option<&Self::LifetimeParam> {
|
||||
None
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,7 @@ pub trait GenericParamExt {
|
|||
|
||||
impl GenericParamExt for syn::GenericParam {
|
||||
type TypeParam = syn::TypeParam;
|
||||
type LifetimeDef = syn::LifetimeDef;
|
||||
type LifetimeParam = syn::LifetimeParam;
|
||||
type ConstParam = syn::ConstParam;
|
||||
|
||||
fn as_type_param(&self) -> Option<&Self::TypeParam> {
|
||||
|
@ -46,7 +46,7 @@ impl GenericParamExt for syn::GenericParam {
|
|||
}
|
||||
}
|
||||
|
||||
fn as_lifetime_def(&self) -> Option<&Self::LifetimeDef> {
|
||||
fn as_lifetime_param(&self) -> Option<&Self::LifetimeParam> {
|
||||
if let syn::GenericParam::Lifetime(ref val) = *self {
|
||||
Some(val)
|
||||
} else {
|
||||
|
@ -65,7 +65,7 @@ impl GenericParamExt for syn::GenericParam {
|
|||
|
||||
impl GenericParamExt for syn::TypeParam {
|
||||
type TypeParam = syn::TypeParam;
|
||||
type LifetimeDef = ();
|
||||
type LifetimeParam = ();
|
||||
type ConstParam = ();
|
||||
|
||||
fn as_type_param(&self) -> Option<&Self::TypeParam> {
|
||||
|
@ -75,7 +75,7 @@ impl GenericParamExt for syn::TypeParam {
|
|||
|
||||
/// A mirror of `syn::GenericParam` which is generic over all its contents.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum GenericParam<T = syn::TypeParam, L = syn::LifetimeDef, C = syn::ConstParam> {
|
||||
pub enum GenericParam<T = syn::TypeParam, L = syn::LifetimeParam, C = syn::ConstParam> {
|
||||
Type(T),
|
||||
Lifetime(L),
|
||||
Const(C),
|
||||
|
@ -103,7 +103,7 @@ impl<T: FromTypeParam> FromGenericParam for GenericParam<T> {
|
|||
|
||||
impl<T, L, C> GenericParamExt for GenericParam<T, L, C> {
|
||||
type TypeParam = T;
|
||||
type LifetimeDef = L;
|
||||
type LifetimeParam = L;
|
||||
type ConstParam = C;
|
||||
|
||||
fn as_type_param(&self) -> Option<&T> {
|
||||
|
@ -114,7 +114,7 @@ impl<T, L, C> GenericParamExt for GenericParam<T, L, C> {
|
|||
}
|
||||
}
|
||||
|
||||
fn as_lifetime_def(&self) -> Option<&L> {
|
||||
fn as_lifetime_param(&self) -> Option<&L> {
|
||||
if let GenericParam::Lifetime(ref val) = *self {
|
||||
Some(val)
|
||||
} else {
|
||||
|
|
|
@ -55,13 +55,18 @@ pub trait ExtractAttribute {
|
|||
#(#attr_names)|* => {
|
||||
match ::darling::util::parse_attribute_to_meta_list(__attr) {
|
||||
::darling::export::Ok(__data) => {
|
||||
if __data.nested.is_empty() {
|
||||
continue;
|
||||
match ::darling::export::NestedMeta::parse_meta_list(__data.tokens) {
|
||||
::darling::export::Ok(ref __items) => {
|
||||
if __items.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
#core_loop
|
||||
}
|
||||
::darling::export::Err(__err) => {
|
||||
__errors.push(__err.into());
|
||||
}
|
||||
}
|
||||
|
||||
let __items = &__data.nested;
|
||||
|
||||
#core_loop
|
||||
}
|
||||
// darling was asked to handle this attribute name, but the actual attribute
|
||||
// isn't one that darling can work with. This either indicates a typing error
|
||||
|
@ -92,7 +97,7 @@ pub trait ExtractAttribute {
|
|||
|
||||
for __attr in #attrs_accessor {
|
||||
// Filter attributes based on name
|
||||
match ::darling::export::ToString::to_string(&__attr.path.clone().into_token_stream()).as_str() {
|
||||
match ::darling::export::ToString::to_string(&__attr.path().clone().into_token_stream()).as_str() {
|
||||
#parse_handled
|
||||
#forward_unhandled
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ impl<'a> ToTokens for FromMetaImpl<'a> {
|
|||
let post_transform = base.post_transform_call();
|
||||
|
||||
quote!(
|
||||
fn from_list(__items: &[::darling::export::syn::NestedMeta]) -> ::darling::Result<Self> {
|
||||
fn from_list(__items: &[::darling::export::NestedMeta]) -> ::darling::Result<Self> {
|
||||
|
||||
#decls
|
||||
|
||||
|
@ -91,13 +91,13 @@ impl<'a> ToTokens for FromMetaImpl<'a> {
|
|||
};
|
||||
|
||||
quote!(
|
||||
fn from_list(__outer: &[::darling::export::syn::NestedMeta]) -> ::darling::Result<Self> {
|
||||
fn from_list(__outer: &[::darling::export::NestedMeta]) -> ::darling::Result<Self> {
|
||||
// An enum must have exactly one value inside the parentheses if it's not a unit
|
||||
// match arm
|
||||
match __outer.len() {
|
||||
0 => ::darling::export::Err(::darling::Error::too_few_items(1)),
|
||||
1 => {
|
||||
if let ::darling::export::syn::NestedMeta::Meta(ref __nested) = __outer[0] {
|
||||
if let ::darling::export::NestedMeta::Meta(ref __nested) = __outer[0] {
|
||||
match ::darling::util::path_to_string(__nested.path()).as_ref() {
|
||||
#(#struct_arms)*
|
||||
__other => ::darling::export::Err(::darling::Error::#unknown_variant_err.with_span(__nested))
|
||||
|
|
|
@ -121,7 +121,8 @@ impl<'a> ToTokens for DataMatchArm<'a> {
|
|||
tokens.append_all(quote!(
|
||||
#name_in_attr => {
|
||||
if let ::darling::export::syn::Meta::List(ref __data) = *__nested {
|
||||
let __items = &__data.nested;
|
||||
let __items = ::darling::export::NestedMeta::parse_meta_list(__data.tokens.clone())?;
|
||||
let __items = &__items;
|
||||
|
||||
#declare_errors
|
||||
|
||||
|
|
|
@ -60,14 +60,14 @@ impl<'a> FieldsGen<'a> {
|
|||
quote!(
|
||||
for __item in __items {
|
||||
match *__item {
|
||||
::darling::export::syn::NestedMeta::Meta(ref __inner) => {
|
||||
::darling::export::NestedMeta::Meta(ref __inner) => {
|
||||
let __name = ::darling::util::path_to_string(__inner.path());
|
||||
match __name.as_str() {
|
||||
#(#arms)*
|
||||
__other => { #handle_unknown }
|
||||
}
|
||||
}
|
||||
::darling::export::syn::NestedMeta::Lit(ref __inner) => {
|
||||
::darling::export::NestedMeta::Lit(ref __inner) => {
|
||||
__errors.push(::darling::Error::unsupported_format("literal")
|
||||
.with_span(__inner));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
use proc_macro2::Span;
|
||||
|
||||
/// Exhaustive mirror of [`proc_macro::Level`].
|
||||
#[derive(Debug, Clone)]
|
||||
pub(in crate::error) enum Level {
|
||||
Error,
|
||||
Warning,
|
||||
Note,
|
||||
Help,
|
||||
}
|
||||
|
||||
/// Supplemental message for an [`Error`](super::Error) when it's emitted as a `Diagnostic`.
|
||||
///
|
||||
/// # Example Output
|
||||
/// The `note` and `help` lines below come from child diagnostics.
|
||||
///
|
||||
/// ```text
|
||||
/// error: My custom error
|
||||
/// --> my_project/my_file.rs:3:5
|
||||
/// |
|
||||
/// 13 | FooBar { value: String },
|
||||
/// | ^^^^^^
|
||||
/// |
|
||||
/// = note: My note on the macro usage
|
||||
/// = help: Try doing this instead
|
||||
/// ```
|
||||
#[derive(Debug, Clone)]
|
||||
pub(in crate::error) struct ChildDiagnostic {
|
||||
level: Level,
|
||||
span: Option<Span>,
|
||||
message: String,
|
||||
}
|
||||
|
||||
impl ChildDiagnostic {
|
||||
pub(in crate::error) fn new(level: Level, span: Option<Span>, message: String) -> Self {
|
||||
Self {
|
||||
level,
|
||||
span,
|
||||
message,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ChildDiagnostic {
|
||||
/// Append this child diagnostic to a `Diagnostic`.
|
||||
///
|
||||
/// # Panics
|
||||
/// This method panics if `self` has a span and is being invoked outside of
|
||||
/// a proc-macro due to the behavior of [`Span::unwrap()`](Span).
|
||||
pub fn append_to(self, diagnostic: proc_macro::Diagnostic) -> proc_macro::Diagnostic {
|
||||
match self.level {
|
||||
Level::Error => {
|
||||
if let Some(span) = self.span {
|
||||
diagnostic.span_error(span.unwrap(), self.message)
|
||||
} else {
|
||||
diagnostic.error(self.message)
|
||||
}
|
||||
}
|
||||
Level::Warning => {
|
||||
if let Some(span) = self.span {
|
||||
diagnostic.span_warning(span.unwrap(), self.message)
|
||||
} else {
|
||||
diagnostic.warning(self.message)
|
||||
}
|
||||
}
|
||||
Level::Note => {
|
||||
if let Some(span) = self.span {
|
||||
diagnostic.span_note(span.unwrap(), self.message)
|
||||
} else {
|
||||
diagnostic.note(self.message)
|
||||
}
|
||||
}
|
||||
Level::Help => {
|
||||
if let Some(span) = self.span {
|
||||
diagnostic.span_help(span.unwrap(), self.message)
|
||||
} else {
|
||||
diagnostic.help(self.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,8 +13,10 @@ use std::iter::{self, Iterator};
|
|||
use std::string::ToString;
|
||||
use std::vec;
|
||||
use syn::spanned::Spanned;
|
||||
use syn::{Lit, LitStr, Path};
|
||||
use syn::{Expr, Lit, LitStr, Path};
|
||||
|
||||
#[cfg(feature = "diagnostics")]
|
||||
mod child;
|
||||
mod kind;
|
||||
|
||||
use crate::util::path_to_string;
|
||||
|
@ -62,6 +64,9 @@ pub struct Error {
|
|||
locations: Vec<String>,
|
||||
/// The span to highlight in the emitted diagnostic.
|
||||
span: Option<Span>,
|
||||
/// Additional diagnostic messages to show with the error.
|
||||
#[cfg(feature = "diagnostics")]
|
||||
children: Vec<child::ChildDiagnostic>,
|
||||
}
|
||||
|
||||
/// Error creation functions
|
||||
|
@ -71,6 +76,8 @@ impl Error {
|
|||
kind,
|
||||
locations: Vec::new(),
|
||||
span: None,
|
||||
#[cfg(feature = "diagnostics")]
|
||||
children: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,6 +149,53 @@ impl Error {
|
|||
Error::new(ErrorKind::UnexpectedType(ty.into()))
|
||||
}
|
||||
|
||||
pub fn unexpected_expr_type(expr: &Expr) -> Self {
|
||||
Error::unexpected_type(match *expr {
|
||||
Expr::Array(_) => "array",
|
||||
Expr::Assign(_) => "assign",
|
||||
Expr::Async(_) => "async",
|
||||
Expr::Await(_) => "await",
|
||||
Expr::Binary(_) => "binary",
|
||||
Expr::Block(_) => "block",
|
||||
Expr::Break(_) => "break",
|
||||
Expr::Call(_) => "call",
|
||||
Expr::Cast(_) => "cast",
|
||||
Expr::Closure(_) => "closure",
|
||||
Expr::Const(_) => "const",
|
||||
Expr::Continue(_) => "continue",
|
||||
Expr::Field(_) => "field",
|
||||
Expr::ForLoop(_) => "for_loop",
|
||||
Expr::Group(_) => "group",
|
||||
Expr::If(_) => "if",
|
||||
Expr::Index(_) => "index",
|
||||
Expr::Infer(_) => "infer",
|
||||
Expr::Let(_) => "let",
|
||||
Expr::Lit(_) => "lit",
|
||||
Expr::Loop(_) => "loop",
|
||||
Expr::Macro(_) => "macro",
|
||||
Expr::Match(_) => "match",
|
||||
Expr::MethodCall(_) => "method_call",
|
||||
Expr::Paren(_) => "paren",
|
||||
Expr::Path(_) => "path",
|
||||
Expr::Range(_) => "range",
|
||||
Expr::Reference(_) => "reference",
|
||||
Expr::Repeat(_) => "repeat",
|
||||
Expr::Return(_) => "return",
|
||||
Expr::Struct(_) => "struct",
|
||||
Expr::Try(_) => "try",
|
||||
Expr::TryBlock(_) => "try_block",
|
||||
Expr::Tuple(_) => "tuple",
|
||||
Expr::Unary(_) => "unary",
|
||||
Expr::Unsafe(_) => "unsafe",
|
||||
Expr::Verbatim(_) => "verbatim",
|
||||
Expr::While(_) => "while",
|
||||
Expr::Yield(_) => "yield",
|
||||
// non-exhaustive enum
|
||||
_ => "unknown",
|
||||
})
|
||||
.with_span(expr)
|
||||
}
|
||||
|
||||
/// Creates a new error for a field which has an unexpected literal type. This will automatically
|
||||
/// extract the literal type name from the passed-in `Lit` and set the span to encompass only the
|
||||
/// literal value.
|
||||
|
@ -181,6 +235,8 @@ impl Error {
|
|||
Lit::Float(_) => "float",
|
||||
Lit::Bool(_) => "bool",
|
||||
Lit::Verbatim(_) => "verbatim",
|
||||
// non-exhaustive enum
|
||||
_ => "unknown",
|
||||
})
|
||||
.with_span(lit)
|
||||
}
|
||||
|
@ -276,18 +332,36 @@ impl Error {
|
|||
}
|
||||
|
||||
/// Recursively converts a tree of errors to a flattened list.
|
||||
///
|
||||
/// # Child Diagnostics
|
||||
/// If the `diagnostics` feature is enabled, any child diagnostics on `self`
|
||||
/// will be cloned down to all the errors within `self`.
|
||||
pub fn flatten(self) -> Self {
|
||||
Error::multiple(self.into_vec())
|
||||
}
|
||||
|
||||
fn into_vec(self) -> Vec<Self> {
|
||||
if let ErrorKind::Multiple(errors) = self.kind {
|
||||
let mut flat = Vec::new();
|
||||
for error in errors {
|
||||
flat.extend(error.prepend_at(self.locations.clone()).into_vec());
|
||||
}
|
||||
let locations = self.locations;
|
||||
|
||||
flat
|
||||
#[cfg(feature = "diagnostics")]
|
||||
let children = self.children;
|
||||
|
||||
errors
|
||||
.into_iter()
|
||||
.flat_map(|error| {
|
||||
// This is mutated if the diagnostics feature is enabled
|
||||
#[allow(unused_mut)]
|
||||
let mut error = error.prepend_at(locations.clone());
|
||||
|
||||
// Any child diagnostics in `self` are cloned down to all the distinct
|
||||
// errors contained in `self`.
|
||||
#[cfg(feature = "diagnostics")]
|
||||
error.children.extend(children.iter().cloned());
|
||||
|
||||
error.into_vec()
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
vec![self]
|
||||
}
|
||||
|
@ -371,13 +445,17 @@ impl Error {
|
|||
//
|
||||
// If span information is available, don't include the error property path
|
||||
// since it's redundant and not consistent with native compiler diagnostics.
|
||||
match self.kind {
|
||||
let diagnostic = match self.kind {
|
||||
ErrorKind::UnknownField(euf) => euf.into_diagnostic(self.span),
|
||||
_ => match self.span {
|
||||
Some(span) => span.unwrap().error(self.kind.to_string()),
|
||||
None => Diagnostic::new(Level::Error, self.to_string()),
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
self.children
|
||||
.into_iter()
|
||||
.fold(diagnostic, |out, child| child.append_to(out))
|
||||
}
|
||||
|
||||
/// Transform this error and its children into a list of compiler diagnostics
|
||||
|
@ -419,6 +497,76 @@ impl Error {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "diagnostics")]
|
||||
macro_rules! add_child {
|
||||
($unspanned:ident, $spanned:ident, $level:ident) => {
|
||||
#[doc = concat!("Add a child ", stringify!($unspanned), " message to this error.")]
|
||||
#[doc = "# Example"]
|
||||
#[doc = "```rust"]
|
||||
#[doc = "# use darling_core::Error;"]
|
||||
#[doc = concat!(r#"Error::custom("Example")."#, stringify!($unspanned), r#"("message content");"#)]
|
||||
#[doc = "```"]
|
||||
pub fn $unspanned<T: fmt::Display>(mut self, message: T) -> Self {
|
||||
self.children.push(child::ChildDiagnostic::new(
|
||||
child::Level::$level,
|
||||
None,
|
||||
message.to_string(),
|
||||
));
|
||||
self
|
||||
}
|
||||
|
||||
#[doc = concat!("Add a child ", stringify!($unspanned), " message to this error with its own span.")]
|
||||
#[doc = "# Example"]
|
||||
#[doc = "```rust"]
|
||||
#[doc = "# use darling_core::Error;"]
|
||||
#[doc = "# let item_to_span = proc_macro2::Span::call_site();"]
|
||||
#[doc = concat!(r#"Error::custom("Example")."#, stringify!($spanned), r#"(&item_to_span, "message content");"#)]
|
||||
#[doc = "```"]
|
||||
pub fn $spanned<S: Spanned, T: fmt::Display>(mut self, span: &S, message: T) -> Self {
|
||||
self.children.push(child::ChildDiagnostic::new(
|
||||
child::Level::$level,
|
||||
Some(span.span()),
|
||||
message.to_string(),
|
||||
));
|
||||
self
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Add child diagnostics to the error.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ## Code
|
||||
///
|
||||
/// ```rust
|
||||
/// # use darling_core::Error;
|
||||
/// # let struct_ident = proc_macro2::Span::call_site();
|
||||
/// Error::custom("this is a demo")
|
||||
/// .with_span(&struct_ident)
|
||||
/// .note("we wrote this")
|
||||
/// .help("try doing this instead");
|
||||
/// ```
|
||||
/// ## Output
|
||||
///
|
||||
/// ```text
|
||||
/// error: this is a demo
|
||||
/// --> my_project/my_file.rs:3:5
|
||||
/// |
|
||||
/// 13 | FooBar { value: String },
|
||||
/// | ^^^^^^
|
||||
/// |
|
||||
/// = note: we wrote this
|
||||
/// = help: try doing this instead
|
||||
/// ```
|
||||
#[cfg(feature = "diagnostics")]
|
||||
impl Error {
|
||||
add_child!(error, span_error, Error);
|
||||
add_child!(warning, span_warning, Warning);
|
||||
add_child!(note, span_note, Note);
|
||||
add_child!(help, span_help, Help);
|
||||
}
|
||||
|
||||
impl StdError for Error {
|
||||
fn description(&self) -> &str {
|
||||
self.kind.description()
|
||||
|
|
|
@ -7,9 +7,11 @@ use std::rc::Rc;
|
|||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::Arc;
|
||||
|
||||
use syn::{Expr, Lit, Meta, NestedMeta};
|
||||
use syn::{Expr, Lit, Meta};
|
||||
|
||||
use crate::{util::path_to_string, Error, Result};
|
||||
use crate::ast::NestedMeta;
|
||||
use crate::util::path_to_string;
|
||||
use crate::{Error, Result};
|
||||
|
||||
/// Create an instance from an item in an attribute declaration.
|
||||
///
|
||||
|
@ -66,14 +68,10 @@ pub trait FromMeta: Sized {
|
|||
fn from_meta(item: &Meta) -> Result<Self> {
|
||||
(match *item {
|
||||
Meta::Path(_) => Self::from_word(),
|
||||
Meta::List(ref value) => Self::from_list(
|
||||
&value
|
||||
.nested
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect::<Vec<syn::NestedMeta>>()[..],
|
||||
),
|
||||
Meta::NameValue(ref value) => Self::from_value(&value.lit),
|
||||
Meta::List(ref value) => {
|
||||
Self::from_list(&NestedMeta::parse_meta_list(value.tokens.clone())?[..])
|
||||
}
|
||||
Meta::NameValue(ref value) => Self::from_expr(&value.value),
|
||||
})
|
||||
.map_err(|e| e.with_span(item))
|
||||
}
|
||||
|
@ -121,6 +119,15 @@ pub trait FromMeta: Sized {
|
|||
.map_err(|e| e.with_span(value))
|
||||
}
|
||||
|
||||
fn from_expr(expr: &Expr) -> Result<Self> {
|
||||
match *expr {
|
||||
Expr::Lit(ref lit) => Self::from_value(&lit.lit),
|
||||
Expr::Group(ref group) => Self::from_expr(&group.expr),
|
||||
_ => Err(Error::unexpected_expr_type(expr)),
|
||||
}
|
||||
.map_err(|e| e.with_span(expr))
|
||||
}
|
||||
|
||||
/// Create an instance from a char literal in a value position.
|
||||
#[allow(unused_variables)]
|
||||
fn from_char(value: char) -> Result<Self> {
|
||||
|
@ -267,6 +274,74 @@ impl<T: syn::parse::Parse, P: syn::parse::Parse> FromMeta for syn::punctuated::P
|
|||
}
|
||||
}
|
||||
|
||||
/// Support for arbitrary expressions as values in a meta item.
|
||||
///
|
||||
/// For backwards-compatibility to versions of `darling` based on `syn` 1,
|
||||
/// string literals will be "unwrapped" and their contents will be parsed
|
||||
/// as an expression.
|
||||
impl FromMeta for syn::Expr {
|
||||
fn from_expr(expr: &Expr) -> Result<Self> {
|
||||
if let syn::Expr::Lit(expr_lit) = expr {
|
||||
if let syn::Lit::Str(_) = &expr_lit.lit {
|
||||
return Self::from_value(&expr_lit.lit);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(expr.clone())
|
||||
}
|
||||
|
||||
fn from_string(value: &str) -> Result<Self> {
|
||||
syn::parse_str(value).map_err(|_| Error::unknown_value(value))
|
||||
}
|
||||
|
||||
fn from_value(value: &::syn::Lit) -> Result<Self> {
|
||||
if let ::syn::Lit::Str(ref v) = *value {
|
||||
v.parse::<syn::Expr>()
|
||||
.map_err(|_| Error::unknown_lit_str_value(v))
|
||||
} else {
|
||||
Err(Error::unexpected_lit_type(value))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Adapter for various expression types.
|
||||
///
|
||||
/// Prior to syn 2.0, darling supported arbitrary expressions as long as they
|
||||
/// were wrapped in quotation marks. This was helpful for people writing
|
||||
/// libraries that needed expressions, but it now creates an ambiguity when
|
||||
/// parsing a meta item.
|
||||
///
|
||||
/// To address this, the macro supports both formats; if it cannot parse the
|
||||
/// item as an expression of the right type and the passed-in expression is
|
||||
/// a string literal, it will fall back to parsing the string contents.
|
||||
macro_rules! from_syn_expr_type {
|
||||
($ty:path, $variant:ident) => {
|
||||
impl FromMeta for $ty {
|
||||
fn from_expr(expr: &syn::Expr) -> Result<Self> {
|
||||
if let syn::Expr::$variant(body) = expr {
|
||||
Ok(body.clone())
|
||||
} else if let syn::Expr::Lit(expr_lit) = expr {
|
||||
Self::from_value(&expr_lit.lit)
|
||||
} else {
|
||||
Err(Error::unexpected_expr_type(expr))
|
||||
}
|
||||
}
|
||||
|
||||
fn from_value(value: &::syn::Lit) -> Result<Self> {
|
||||
if let syn::Lit::Str(body) = &value {
|
||||
body.parse::<$ty>()
|
||||
.map_err(|_| Error::unknown_lit_str_value(body))
|
||||
} else {
|
||||
Err(Error::unexpected_lit_type(value))
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
from_syn_expr_type!(syn::ExprArray, Array);
|
||||
from_syn_expr_type!(syn::ExprPath, Path);
|
||||
|
||||
/// Adapter from `syn::parse::Parse` to `FromMeta`.
|
||||
///
|
||||
/// This cannot be a blanket impl, due to the `syn::Lit` family's need to handle non-string values.
|
||||
|
@ -291,9 +366,6 @@ macro_rules! from_syn_parse {
|
|||
}
|
||||
|
||||
from_syn_parse!(syn::Ident);
|
||||
from_syn_parse!(syn::Expr);
|
||||
from_syn_parse!(syn::ExprArray);
|
||||
from_syn_parse!(syn::ExprPath);
|
||||
from_syn_parse!(syn::Path);
|
||||
from_syn_parse!(syn::Type);
|
||||
from_syn_parse!(syn::TypeArray);
|
||||
|
@ -318,11 +390,9 @@ macro_rules! from_numeric_array {
|
|||
($ty:ident) => {
|
||||
/// Parsing an unsigned integer array, i.e. `example = "[1, 2, 3, 4]"`.
|
||||
impl FromMeta for Vec<$ty> {
|
||||
fn from_value(value: &Lit) -> Result<Self> {
|
||||
let expr_array = syn::ExprArray::from_value(value)?;
|
||||
// To meet rust <1.36 borrow checker rules on expr_array.elems
|
||||
let v =
|
||||
expr_array
|
||||
fn from_expr(expr: &syn::Expr) -> Result<Self> {
|
||||
if let syn::Expr::Array(expr_array) = expr {
|
||||
let v = expr_array
|
||||
.elems
|
||||
.iter()
|
||||
.map(|expr| match expr {
|
||||
|
@ -331,7 +401,17 @@ macro_rules! from_numeric_array {
|
|||
.with_span(expr)),
|
||||
})
|
||||
.collect::<Result<Vec<$ty>>>();
|
||||
v
|
||||
v
|
||||
} else if let syn::Expr::Lit(expr_lit) = expr {
|
||||
Self::from_value(&expr_lit.lit)
|
||||
} else {
|
||||
Err(Error::unexpected_expr_type(expr))
|
||||
}
|
||||
}
|
||||
|
||||
fn from_value(value: &Lit) -> Result<Self> {
|
||||
let expr_array = syn::ExprArray::from_value(value)?;
|
||||
Self::from_expr(&syn::Expr::Array(expr_array))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -519,7 +599,7 @@ impl KeyFromPath for syn::Ident {
|
|||
macro_rules! hash_map {
|
||||
($key:ty) => {
|
||||
impl<V: FromMeta, S: BuildHasher + Default> FromMeta for HashMap<$key, V, S> {
|
||||
fn from_list(nested: &[syn::NestedMeta]) -> Result<Self> {
|
||||
fn from_list(nested: &[NestedMeta]) -> Result<Self> {
|
||||
// Convert the nested meta items into a sequence of (path, value result) result tuples.
|
||||
// An outer Err means no (key, value) structured could be found, while an Err in the
|
||||
// second position of the tuple means that value was rejected by FromMeta.
|
||||
|
@ -530,14 +610,14 @@ macro_rules! hash_map {
|
|||
.iter()
|
||||
.map(|item| -> Result<(&syn::Path, Result<V>)> {
|
||||
match *item {
|
||||
syn::NestedMeta::Meta(ref inner) => {
|
||||
NestedMeta::Meta(ref inner) => {
|
||||
let path = inner.path();
|
||||
Ok((
|
||||
path,
|
||||
FromMeta::from_meta(inner).map_err(|e| e.at_path(&path)),
|
||||
))
|
||||
}
|
||||
syn::NestedMeta::Lit(_) => Err(Error::unsupported_format("literal")),
|
||||
NestedMeta::Lit(_) => Err(Error::unsupported_format("expression")),
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -613,9 +693,10 @@ mod tests {
|
|||
/// parse a string as a syn::Meta instance.
|
||||
fn pm(tokens: TokenStream) -> ::std::result::Result<syn::Meta, String> {
|
||||
let attribute: syn::Attribute = parse_quote!(#[#tokens]);
|
||||
attribute.parse_meta().map_err(|_| "Unable to parse".into())
|
||||
Ok(attribute.meta)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn fm<T: FromMeta>(tokens: TokenStream) -> T {
|
||||
FromMeta::from_meta(&pm(tokens).expect("Tests should pass well-formed input"))
|
||||
.expect("Tests should pass valid input")
|
||||
|
@ -829,10 +910,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_number_array() {
|
||||
assert_eq!(
|
||||
fm::<Vec<u8>>(quote!(ignore = "[16, 0xff]")),
|
||||
vec![0x10, 0xff]
|
||||
);
|
||||
assert_eq!(fm::<Vec<u8>>(quote!(ignore = [16, 0xff])), vec![0x10, 0xff]);
|
||||
assert_eq!(
|
||||
fm::<Vec<u16>>(quote!(ignore = "[32, 0xffff]")),
|
||||
vec![0x20, 0xffff]
|
||||
|
|
|
@ -166,7 +166,7 @@ impl<'a> From<&'a Core> for codegen::TraitImpl<'a> {
|
|||
.map_enum_variants(|variant| variant.as_codegen_variant(&v.ident)),
|
||||
default: v.as_codegen_default(),
|
||||
post_transform: v.post_transform.as_ref(),
|
||||
bound: v.bound.as_ref().map(|i| i.as_slice()),
|
||||
bound: v.bound.as_deref(),
|
||||
allow_unknown_fields: v.allow_unknown_fields.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use syn::NestedMeta;
|
||||
|
||||
use crate::ast::NestedMeta;
|
||||
use crate::util::PathList;
|
||||
use crate::{FromMeta, Result};
|
||||
|
||||
|
|
|
@ -52,13 +52,7 @@ impl ParseData for FdiOptions {
|
|||
}
|
||||
|
||||
fn parse_field(&mut self, field: &syn::Field) -> Result<()> {
|
||||
match field
|
||||
.ident
|
||||
.as_ref()
|
||||
.map(|v| v.to_string())
|
||||
.as_ref()
|
||||
.map(|v| v.as_str())
|
||||
{
|
||||
match field.ident.as_ref().map(|v| v.to_string()).as_deref() {
|
||||
Some("vis") => {
|
||||
self.vis = field.ident.clone();
|
||||
Ok(())
|
||||
|
|
|
@ -37,13 +37,7 @@ impl ParseData for FromFieldOptions {
|
|||
}
|
||||
|
||||
fn parse_field(&mut self, field: &syn::Field) -> Result<()> {
|
||||
match field
|
||||
.ident
|
||||
.as_ref()
|
||||
.map(|v| v.to_string())
|
||||
.as_ref()
|
||||
.map(|v| v.as_str())
|
||||
{
|
||||
match field.ident.as_ref().map(|v| v.to_string()).as_deref() {
|
||||
Some("vis") => {
|
||||
self.vis = field.ident.clone();
|
||||
Ok(())
|
||||
|
|
|
@ -37,13 +37,7 @@ impl ParseData for FromTypeParamOptions {
|
|||
}
|
||||
|
||||
fn parse_field(&mut self, field: &syn::Field) -> Result<()> {
|
||||
match field
|
||||
.ident
|
||||
.as_ref()
|
||||
.map(|v| v.to_string())
|
||||
.as_ref()
|
||||
.map(|v| v.as_str())
|
||||
{
|
||||
match field.ident.as_ref().map(|v| v.to_string()).as_deref() {
|
||||
Some("bounds") => {
|
||||
self.bounds = field.ident.clone();
|
||||
Ok(())
|
||||
|
|
|
@ -58,13 +58,7 @@ impl ParseAttribute for FromVariantOptions {
|
|||
|
||||
impl ParseData for FromVariantOptions {
|
||||
fn parse_field(&mut self, field: &Field) -> Result<()> {
|
||||
match field
|
||||
.ident
|
||||
.as_ref()
|
||||
.map(|v| v.to_string())
|
||||
.as_ref()
|
||||
.map(|v| v.as_str())
|
||||
{
|
||||
match field.ident.as_ref().map(|v| v.to_string()).as_deref() {
|
||||
Some("discriminant") => {
|
||||
self.discriminant = field.ident.clone();
|
||||
Ok(())
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use proc_macro2::Span;
|
||||
use syn::{parse_quote, spanned::Spanned};
|
||||
|
||||
use crate::ast::NestedMeta;
|
||||
use crate::{Error, FromMeta, Result};
|
||||
|
||||
mod core;
|
||||
|
@ -50,7 +51,7 @@ impl FromMeta for DefaultExpression {
|
|||
match item {
|
||||
syn::Meta::Path(_) => Ok(DefaultExpression::Trait { span: item.span() }),
|
||||
syn::Meta::List(nm) => Err(Error::unsupported_format("list").with_span(nm)),
|
||||
syn::Meta::NameValue(nv) => Self::from_value(&nv.lit),
|
||||
syn::Meta::NameValue(nv) => Self::from_expr(&nv.value),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,7 +67,7 @@ pub trait ParseAttribute: Sized {
|
|||
fn parse_attributes(mut self, attrs: &[syn::Attribute]) -> Result<Self> {
|
||||
let mut errors = Error::accumulator();
|
||||
for attr in attrs {
|
||||
if attr.path == parse_quote!(darling) {
|
||||
if attr.meta.path() == &parse_quote!(darling) {
|
||||
errors.handle(parse_attr(attr, &mut self));
|
||||
}
|
||||
}
|
||||
|
@ -80,10 +81,10 @@ pub trait ParseAttribute: Sized {
|
|||
|
||||
fn parse_attr<T: ParseAttribute>(attr: &syn::Attribute, target: &mut T) -> Result<()> {
|
||||
let mut errors = Error::accumulator();
|
||||
match attr.parse_meta().ok() {
|
||||
Some(syn::Meta::List(data)) => {
|
||||
for item in data.nested {
|
||||
if let syn::NestedMeta::Meta(ref mi) = item {
|
||||
match &attr.meta {
|
||||
syn::Meta::List(data) => {
|
||||
for item in NestedMeta::parse_meta_list(data.tokens.clone())? {
|
||||
if let NestedMeta::Meta(ref mi) = item {
|
||||
errors.handle(target.parse_nested(mi));
|
||||
} else {
|
||||
panic!("Wasn't able to parse: `{:?}`", item);
|
||||
|
@ -92,8 +93,7 @@ fn parse_attr<T: ParseAttribute>(attr: &syn::Attribute, target: &mut T) -> Resul
|
|||
|
||||
errors.finish()
|
||||
}
|
||||
Some(ref item) => panic!("Wasn't able to parse: `{:?}`", item),
|
||||
None => panic!("Unable to parse {:?}", attr),
|
||||
item => panic!("Wasn't able to parse: `{:?}`", item),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -66,13 +66,7 @@ impl ParseAttribute for OuterFrom {
|
|||
|
||||
impl ParseData for OuterFrom {
|
||||
fn parse_field(&mut self, field: &Field) -> Result<()> {
|
||||
match field
|
||||
.ident
|
||||
.as_ref()
|
||||
.map(|v| v.to_string())
|
||||
.as_ref()
|
||||
.map(|v| v.as_str())
|
||||
{
|
||||
match field.ident.as_ref().map(|v| v.to_string()).as_deref() {
|
||||
Some("ident") => {
|
||||
self.ident = field.ident.clone();
|
||||
Ok(())
|
||||
|
|
|
@ -3,8 +3,9 @@
|
|||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{quote, ToTokens, TokenStreamExt};
|
||||
use syn::{parse_quote, Meta, NestedMeta};
|
||||
use syn::{parse_quote, Meta};
|
||||
|
||||
use crate::ast::NestedMeta;
|
||||
use crate::{Error, FromMeta, Result};
|
||||
|
||||
/// Receiver struct for shape validation. Shape validation allows a deriving type
|
||||
|
@ -225,7 +226,7 @@ mod tests {
|
|||
/// parse a string as a syn::Meta instance.
|
||||
fn pm(tokens: TokenStream) -> ::std::result::Result<syn::Meta, String> {
|
||||
let attribute: syn::Attribute = parse_quote!(#[#tokens]);
|
||||
attribute.parse_meta().map_err(|_| "Unable to parse".into())
|
||||
Ok(attribute.meta)
|
||||
}
|
||||
|
||||
fn fm<T: FromMeta>(tokens: TokenStream) -> T {
|
||||
|
|
|
@ -112,20 +112,20 @@ impl UsesLifetimes for Lifetime {
|
|||
}
|
||||
|
||||
uses_lifetimes!(syn::AngleBracketedGenericArguments, args);
|
||||
uses_lifetimes!(syn::AssocType, ty);
|
||||
uses_lifetimes!(syn::BareFnArg, ty);
|
||||
uses_lifetimes!(syn::Binding, ty);
|
||||
uses_lifetimes!(syn::BoundLifetimes, lifetimes);
|
||||
uses_lifetimes!(syn::ConstParam, ty);
|
||||
uses_lifetimes!(syn::Constraint, bounds);
|
||||
uses_lifetimes!(syn::DataEnum, variants);
|
||||
uses_lifetimes!(syn::DataStruct, fields);
|
||||
uses_lifetimes!(syn::DataUnion, fields);
|
||||
uses_lifetimes!(syn::Field, ty);
|
||||
uses_lifetimes!(syn::FieldsNamed, named);
|
||||
uses_lifetimes!(syn::LifetimeDef, lifetime, bounds);
|
||||
uses_lifetimes!(syn::LifetimeParam, lifetime, bounds);
|
||||
uses_lifetimes!(syn::ParenthesizedGenericArguments, inputs, output);
|
||||
uses_lifetimes!(syn::Path, segments);
|
||||
uses_lifetimes!(syn::PathSegment, arguments);
|
||||
uses_lifetimes!(syn::PredicateEq, lhs_ty, rhs_ty);
|
||||
uses_lifetimes!(syn::PredicateLifetime, lifetime, bounds);
|
||||
uses_lifetimes!(syn::PredicateType, lifetimes, bounded_ty, bounds);
|
||||
uses_lifetimes!(syn::QSelf, ty);
|
||||
|
@ -134,6 +134,7 @@ uses_lifetimes!(syn::TypeArray, elem);
|
|||
uses_lifetimes!(syn::TypeBareFn, inputs, output);
|
||||
uses_lifetimes!(syn::TypeGroup, elem);
|
||||
uses_lifetimes!(syn::TypeImplTrait, bounds);
|
||||
uses_lifetimes!(syn::TypeParam, bounds);
|
||||
uses_lifetimes!(syn::TypeParen, elem);
|
||||
uses_lifetimes!(syn::TypePtr, elem);
|
||||
uses_lifetimes!(syn::TypeReference, lifetime, elem);
|
||||
|
@ -245,7 +246,9 @@ impl UsesLifetimes for syn::WherePredicate {
|
|||
match *self {
|
||||
syn::WherePredicate::Type(ref v) => v.uses_lifetimes(options, lifetimes),
|
||||
syn::WherePredicate::Lifetime(ref v) => v.uses_lifetimes(options, lifetimes),
|
||||
syn::WherePredicate::Eq(ref v) => v.uses_lifetimes(options, lifetimes),
|
||||
// non-exhaustive enum
|
||||
// TODO: replace panic with failible function
|
||||
_ => panic!("Unknown syn::WherePredicate: {:?}", self),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -258,10 +261,29 @@ impl UsesLifetimes for syn::GenericArgument {
|
|||
) -> LifetimeRefSet<'a> {
|
||||
match *self {
|
||||
syn::GenericArgument::Type(ref v) => v.uses_lifetimes(options, lifetimes),
|
||||
syn::GenericArgument::Binding(ref v) => v.uses_lifetimes(options, lifetimes),
|
||||
syn::GenericArgument::AssocType(ref v) => v.uses_lifetimes(options, lifetimes),
|
||||
syn::GenericArgument::Lifetime(ref v) => v.uses_lifetimes(options, lifetimes),
|
||||
syn::GenericArgument::Constraint(ref v) => v.uses_lifetimes(options, lifetimes),
|
||||
syn::GenericArgument::Const(_) => Default::default(),
|
||||
syn::GenericArgument::AssocConst(_) | syn::GenericArgument::Const(_) => {
|
||||
Default::default()
|
||||
}
|
||||
// non-exhaustive enum
|
||||
// TODO: replace panic with failible function
|
||||
_ => panic!("Unknown syn::GenericArgument: {:?}", self),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UsesLifetimes for syn::GenericParam {
|
||||
fn uses_lifetimes<'a>(
|
||||
&self,
|
||||
options: &Options,
|
||||
lifetimes: &'a LifetimeSet,
|
||||
) -> LifetimeRefSet<'a> {
|
||||
match *self {
|
||||
syn::GenericParam::Lifetime(ref v) => v.uses_lifetimes(options, lifetimes),
|
||||
syn::GenericParam::Type(ref v) => v.uses_lifetimes(options, lifetimes),
|
||||
syn::GenericParam::Const(ref v) => v.uses_lifetimes(options, lifetimes),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -275,6 +297,9 @@ impl UsesLifetimes for syn::TypeParamBound {
|
|||
match *self {
|
||||
syn::TypeParamBound::Trait(ref v) => v.uses_lifetimes(options, lifetimes),
|
||||
syn::TypeParamBound::Lifetime(ref v) => v.uses_lifetimes(options, lifetimes),
|
||||
// non-exhaustive enum
|
||||
// TODO: replace panic with failible function
|
||||
_ => panic!("Unknown syn::TypeParamBound: {:?}", self),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -282,8 +307,7 @@ impl UsesLifetimes for syn::TypeParamBound {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use proc_macro2::Span;
|
||||
use syn::parse_quote;
|
||||
use syn::DeriveInput;
|
||||
use syn::{parse_quote, DeriveInput};
|
||||
|
||||
use super::UsesLifetimes;
|
||||
use crate::usage::GenericsExt;
|
||||
|
|
|
@ -87,8 +87,8 @@ impl<T: UsesTypeParams, U> UsesTypeParams for Punctuated<T, U> {
|
|||
}
|
||||
|
||||
uses_type_params!(syn::AngleBracketedGenericArguments, args);
|
||||
uses_type_params!(syn::AssocType, ty);
|
||||
uses_type_params!(syn::BareFnArg, ty);
|
||||
uses_type_params!(syn::Binding, ty);
|
||||
uses_type_params!(syn::Constraint, bounds);
|
||||
uses_type_params!(syn::DataEnum, variants);
|
||||
uses_type_params!(syn::DataStruct, fields);
|
||||
|
@ -96,7 +96,6 @@ uses_type_params!(syn::DataUnion, fields);
|
|||
uses_type_params!(syn::Field, ty);
|
||||
uses_type_params!(syn::FieldsNamed, named);
|
||||
uses_type_params!(syn::ParenthesizedGenericArguments, inputs, output);
|
||||
uses_type_params!(syn::PredicateEq, lhs_ty, rhs_ty);
|
||||
uses_type_params!(syn::PredicateType, bounded_ty, bounds);
|
||||
uses_type_params!(syn::QSelf, ty);
|
||||
uses_type_params!(syn::TraitBound, path);
|
||||
|
@ -217,7 +216,9 @@ impl UsesTypeParams for syn::WherePredicate {
|
|||
match *self {
|
||||
syn::WherePredicate::Lifetime(_) => Default::default(),
|
||||
syn::WherePredicate::Type(ref v) => v.uses_type_params(options, type_set),
|
||||
syn::WherePredicate::Eq(ref v) => v.uses_type_params(options, type_set),
|
||||
// non-exhaustive enum
|
||||
// TODO: replace panic with failible function
|
||||
_ => panic!("Unknown syn::WherePredicate: {:?}", self),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -226,11 +227,14 @@ impl UsesTypeParams for syn::GenericArgument {
|
|||
fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
|
||||
match *self {
|
||||
syn::GenericArgument::Type(ref v) => v.uses_type_params(options, type_set),
|
||||
syn::GenericArgument::Binding(ref v) => v.uses_type_params(options, type_set),
|
||||
syn::GenericArgument::AssocType(ref v) => v.uses_type_params(options, type_set),
|
||||
syn::GenericArgument::Constraint(ref v) => v.uses_type_params(options, type_set),
|
||||
syn::GenericArgument::Const(_) | syn::GenericArgument::Lifetime(_) => {
|
||||
Default::default()
|
||||
}
|
||||
syn::GenericArgument::AssocConst(_)
|
||||
| syn::GenericArgument::Const(_)
|
||||
| syn::GenericArgument::Lifetime(_) => Default::default(),
|
||||
// non-exhaustive enum
|
||||
// TODO: replace panic with failible function
|
||||
_ => panic!("Unknown syn::GenericArgument: {:?}", self),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -240,6 +244,9 @@ impl UsesTypeParams for syn::TypeParamBound {
|
|||
match *self {
|
||||
syn::TypeParamBound::Trait(ref v) => v.uses_type_params(options, type_set),
|
||||
syn::TypeParamBound::Lifetime(_) => Default::default(),
|
||||
// non-exhaustive enum
|
||||
// TODO: replace panic with failible function
|
||||
_ => panic!("Unknown syn::TypeParamBound: {:?}", self),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -323,7 +330,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn box_fn_output() {
|
||||
let input: DeriveInput = parse_quote! { struct Foo<T>(Box<Fn() -> T>); };
|
||||
let input: DeriveInput = parse_quote! { struct Foo<T>(Box<dyn Fn() -> T>); };
|
||||
let generics = ident_set(vec!["T"]);
|
||||
let matches = input.data.uses_type_params(&BoundImpl.into(), &generics);
|
||||
assert_eq!(matches.len(), 1);
|
||||
|
@ -332,7 +339,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn box_fn_input() {
|
||||
let input: DeriveInput = parse_quote! { struct Foo<T>(Box<Fn(&T) -> ()>); };
|
||||
let input: DeriveInput = parse_quote! { struct Foo<T>(Box<dyn Fn(&T) -> ()>); };
|
||||
let generics = ident_set(vec!["T"]);
|
||||
let matches = input.data.uses_type_params(&BoundImpl.into(), &generics);
|
||||
assert_eq!(matches.len(), 1);
|
||||
|
|
|
@ -77,12 +77,6 @@ impl FromMeta for Flag {
|
|||
}
|
||||
}
|
||||
|
||||
impl Spanned for Flag {
|
||||
fn span(&self) -> Span {
|
||||
self.0.unwrap_or_else(Span::call_site)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Flag> for bool {
|
||||
fn from(flag: Flag) -> Self {
|
||||
flag.is_present()
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use std::fmt;
|
||||
|
||||
use syn::{Lit, NestedMeta};
|
||||
use syn::Lit;
|
||||
|
||||
use crate::ast::NestedMeta;
|
||||
use crate::{FromMeta, Result};
|
||||
|
||||
use self::Override::*;
|
||||
|
|
|
@ -1,25 +1,34 @@
|
|||
use crate::{util::SpannedValue, Error, Result};
|
||||
use crate::{Error, Result};
|
||||
use std::fmt;
|
||||
use syn::{punctuated::Pair, spanned::Spanned, token, Attribute, Meta, MetaList, Path};
|
||||
use syn::punctuated::Pair;
|
||||
use syn::spanned::Spanned;
|
||||
use syn::{token, Attribute, Meta, MetaList, Path};
|
||||
|
||||
/// Try to parse an attribute into a meta list. Path-type meta values are accepted and returned
|
||||
/// as empty lists with their passed-in path. Name-value meta values and non-meta attributes
|
||||
/// will cause errors to be returned.
|
||||
pub fn parse_attribute_to_meta_list(attr: &Attribute) -> Result<MetaList> {
|
||||
match attr.parse_meta() {
|
||||
Ok(Meta::List(list)) => Ok(list),
|
||||
Ok(Meta::NameValue(nv)) => Err(Error::custom(format!(
|
||||
match &attr.meta {
|
||||
Meta::List(list) => Ok(list.clone()),
|
||||
Meta::NameValue(nv) => Err(Error::custom(format!(
|
||||
"Name-value arguments are not supported. Use #[{}(...)]",
|
||||
DisplayPath(&nv.path)
|
||||
))
|
||||
.with_span(&nv)),
|
||||
Ok(Meta::Path(path)) => Ok(MetaList {
|
||||
path,
|
||||
paren_token: token::Paren(attr.span()),
|
||||
nested: Default::default(),
|
||||
Meta::Path(path) => Ok(MetaList {
|
||||
path: path.clone(),
|
||||
delimiter: syn::MacroDelimiter::Paren(token::Paren {
|
||||
span: {
|
||||
let mut group = proc_macro2::Group::new(
|
||||
proc_macro2::Delimiter::None,
|
||||
proc_macro2::TokenStream::new(),
|
||||
);
|
||||
group.set_span(attr.span());
|
||||
group.delim_span()
|
||||
},
|
||||
}),
|
||||
tokens: Default::default(),
|
||||
}),
|
||||
Err(e) => Err(Error::custom(format!("Unable to parse attribute: {}", e))
|
||||
.with_span(&SpannedValue::new((), e.span()))),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,19 +54,23 @@ impl fmt::Display for DisplayPath<'_> {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::parse_attribute_to_meta_list;
|
||||
use syn::{parse_quote, spanned::Spanned, Ident};
|
||||
use crate::ast::NestedMeta;
|
||||
use syn::spanned::Spanned;
|
||||
use syn::{parse_quote, Ident};
|
||||
|
||||
#[test]
|
||||
fn parse_list() {
|
||||
let meta = parse_attribute_to_meta_list(&parse_quote!(#[bar(baz = 4)])).unwrap();
|
||||
assert_eq!(meta.nested.len(), 1);
|
||||
let nested_meta = NestedMeta::parse_meta_list(meta.tokens).unwrap();
|
||||
assert_eq!(nested_meta.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_path_returns_empty_list() {
|
||||
let meta = parse_attribute_to_meta_list(&parse_quote!(#[bar])).unwrap();
|
||||
let nested_meta = NestedMeta::parse_meta_list(meta.tokens).unwrap();
|
||||
assert!(meta.path.is_ident(&Ident::new("bar", meta.path.span())));
|
||||
assert!(meta.nested.is_empty());
|
||||
assert!(nested_meta.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use std::ops::Deref;
|
||||
|
||||
use syn::{Meta, NestedMeta, Path};
|
||||
use syn::{Meta, Path};
|
||||
|
||||
use crate::ast::NestedMeta;
|
||||
use crate::{Error, FromMeta, Result};
|
||||
|
||||
use super::path_to_string;
|
||||
|
@ -72,7 +73,7 @@ mod tests {
|
|||
/// parse a string as a syn::Meta instance.
|
||||
fn pm(tokens: TokenStream) -> ::std::result::Result<Meta, String> {
|
||||
let attribute: Attribute = parse_quote!(#[#tokens]);
|
||||
attribute.parse_meta().map_err(|_| "Unable to parse".into())
|
||||
Ok(attribute.meta)
|
||||
}
|
||||
|
||||
fn fm<T: FromMeta>(tokens: TokenStream) -> T {
|
||||
|
|
|
@ -67,12 +67,6 @@ impl<T> AsRef<T> for SpannedValue<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> Spanned for SpannedValue<T> {
|
||||
fn span(&self) -> Span {
|
||||
self.span
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! spanned {
|
||||
($trayt:ident, $method:ident, $syn:path) => {
|
||||
impl<T: $trayt> $trayt for SpannedValue<T> {
|
||||
|
@ -95,10 +89,10 @@ impl<T: FromMeta> FromMeta for SpannedValue<T> {
|
|||
syn::Meta::Path(path) => path.span(),
|
||||
// Example: `#[darling(attributes(Value))]` as a SpannedValue<Vec<String>>
|
||||
// should have the span pointing to the list contents.
|
||||
syn::Meta::List(list) => list.nested.span(),
|
||||
syn::Meta::List(list) => list.tokens.span(),
|
||||
// Example: `#[darling(skip = true)]` as SpannedValue<bool>
|
||||
// should have the span pointing to the word `true`.
|
||||
syn::Meta::NameValue(nv) => nv.lit.span(),
|
||||
syn::Meta::NameValue(nv) => nv.value.span(),
|
||||
};
|
||||
|
||||
Ok(Self::new(value, span))
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"f9ca47216222c1dc994ecdd2e10117060f025c1306bc93b93f5b3683c50033cd","LICENSE":"8ea93490d74a5a1b1af3ff71d786271b3f1e5f0bea79ac16e02ec533cef040d6","src/lib.rs":"1dbd1ed31a7db5ff7995bd1dd494e962645e6ff62b0f88807fe1b6025523f187"},"package":"b36230598a2d5de7ec1c6f51f72d8a99a9208daff41de2084d06e3fd3ea56685"}
|
||||
{"files":{"Cargo.toml":"9df103094522de168893c7805273fe0de11ab18405db65d669af71aa6e810955","LICENSE":"8ea93490d74a5a1b1af3ff71d786271b3f1e5f0bea79ac16e02ec533cef040d6","src/lib.rs":"728be3bb12c9cdaaf0520bce87e489a0820e436c78fa3627e238fc1acc11dd7f"},"package":"29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a"}
|
|
@ -12,7 +12,7 @@
|
|||
[package]
|
||||
edition = "2018"
|
||||
name = "darling_macro"
|
||||
version = "0.14.3"
|
||||
version = "0.20.1"
|
||||
authors = ["Ted Driggs <ted.driggs@outlook.com>"]
|
||||
description = """
|
||||
Internal support for a proc-macro library for reading attributes into structs when
|
||||
|
@ -25,10 +25,10 @@ repository = "https://github.com/TedDriggs/darling"
|
|||
proc-macro = true
|
||||
|
||||
[dependencies.darling_core]
|
||||
version = "=0.14.3"
|
||||
version = "=0.20.1"
|
||||
|
||||
[dependencies.quote]
|
||||
version = "1.0.18"
|
||||
|
||||
[dependencies.syn]
|
||||
version = "1.0.91"
|
||||
version = "2.0.15"
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
// This is needed for 1.31.0 to keep compiling
|
||||
extern crate proc_macro;
|
||||
|
||||
use darling_core::{derive, Error};
|
||||
use proc_macro::TokenStream;
|
||||
use syn::parse_macro_input;
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"d0d1787c790db1c5196cb6cc3ce3ed9b670f24d8e10532df4ce1af08b4f446f5","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"79b4502d93c23afe2765054a80d03716d4934eb260cdfbe8c401898df3aa5a8f","README.md":"b4272f04bf647cd6c5d395772a63fa44230df914347ce71f0d2ad7371c7a0bdd","src/lib.rs":"5f0d4ca19512bd146c4d1c1e287385860c7fa4c8906b7eb92843a442c3efea0a","src/repr.rs":"3ec64837ced263c1e2ae3a548b4bed553c85286f6d2b89b4b7dd2358c21d6fcb","tests/compile-fail/variants.rs":"b4b4d8d2a1ea37afb1c478c81a3157d9ea7ab4a2cbf00b7b71386c37f99f2b0b","tests/compile-fail/variants.stderr":"f0d4d9ebcf9cccea04a0796ca2f4a2a81ebaa235f78edf30aeb30a3f232ec1f0","tests/compile-pass/no_imports.rs":"6fa96d43c3970e25dd1e216820dd79f22f5bfe416ce7d6a6df86781797279297","tests/compile-pass/no_std.rs":"e8a402ae12562e6eab4ac531c084278a9dda4ac9b4e64b7a37bb87ce890738e7","tests/ops.rs":"7a00df1ca4a502292f926529d8979e6e3476f48931487088ca50fc869d00e8ff","tests/repr.rs":"a1b792a3b6f71db62b7b26b9b880783b4fd6c905c170c35a0e4b90ccc2a701c9","tests/serde.rs":"8dd9717ee36b26c4b762a258693030ae27e3990625e513694ff3d68af0a131fd","tests/trybuild.rs":"22e03e02452e47976668c3d4ed54322e4d19a95bd1f72647f5d5792b80e8d0f1"},"package":"19be8061a06ab6f3a6cf21106c873578bf01bd42ad15e0311a9c76161cb1c753"}
|
||||
{"files":{"Cargo.toml":"f173fbd9ad98e0eb5077ec8d2fa39321e3c634500c7b1c5f1e7c792e47a590c8","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"7405fdff2ff8682a5384da664436b9b2aca47557572513f5b27686a5347f9b8e","README.md":"d909f3ef0b7937f6d6d0a8171e056671f717561f2679c6da43254a528711d285","src/lib.rs":"3bb0f56eb0e15acc4f99db31f17567c0479d3b364743f94d132d7d4c6c07db5c","src/macros.rs":"01d43156f78cb32caee75263cf613d5018a30fbaafdcd403dd0abb15aa28abca","src/repr/array.rs":"8c082259fcbd0842ea52844b03c88b98f39ed59a381c83b278e574357b972d58","src/repr/mod.rs":"88fa5138818c5246539814da6bd1b27c790abf64f003af4ce05c83f3e8c4e5d2","src/repr/primitive.rs":"1d6e97c7990e7028f6860ac2acf080f247c894d4c050818d36a55f017edaa956","src/set.rs":"7546fd252fbd2b9d1eb6f86b6c0cfcdd69573528335cbcdfe8006de5236ed5bd","src/traits.rs":"533a2e62c09e956538013bc9a852d0ae336d4bf0a0f8f0494fbf5743bbd27da9","tests/compile-fail-serde/deprecation.rs":"fa6be473bffb4f37ad9407f8712a98c30d32ced00f52749a21d127167037a58a","tests/compile-fail-serde/deprecation.stderr":"7639d3599c3bbbbfe7763dac33307037ba8fa7914f2e1c3502271dad4a0663a7","tests/compile-fail/explicit_repr.rs":"8d3afa63274224c8f526df3a1b5a0435de51343fc4e7b5cbf717d15c38f7f5c5","tests/compile-fail/explicit_repr.stderr":"beaf53132cafb18c58ad8f66ff276bb2b51e34bc9c6880dbe5cdb125422c6f2f","tests/compile-fail/syntax.rs":"eab09b5f611c60e086299a5bfb3aa7e6622b736524e4480a5b7c55a6efe2f34f","tests/compile-fail/syntax.stderr":"9892d3eed892a9dadb7abf308257d5b38bf67902430b7cb07aa0c2c61bfba823","tests/compile-fail/variants.rs":"2e9d69cce5f53addda5352588168882cc47833f765e8db631ba1fd9162271e67","tests/compile-fail/variants.stderr":"47f7a545c8bcaea3b8f31a17395abd98c4b4f1799e0d9ad9389e973e26be13e5","tests/compile-pass/no_imports.rs":"6fa96d43c3970e25dd1e216820dd79f22f5bfe416ce7d6a6df86781797279297","tests/compile-pass/no_std.rs":"e8a402ae12562e6eab4ac531c084278a9dda4ac9b4e64b7a37bb87ce890738e7","tests/conversions.rs":"06683f3f89bb580c1feb9e2c3482b5d9df5e18d1f5d0c2fe1abf4ebfcba5d12e","tests/ops.rs":"ce2b5f7979c72c3f2952a2e4cd4c94cc1895100c594ff19c97edd09e631c6cdf","tests/repr.rs":"a1b792a3b6f71db62b7b26b9b880783b4fd6c905c170c35a0e4b90ccc2a701c9","tests/serde.rs":"f8ea3b9a5f944c48a35a2ad68c7b61a7e1ef4ad60dd3f5523feec3a51edfdba5","tests/trybuild.rs":"5bdb2e3777c1162ffb3b00475130c5032c7d34a2c4f822b898bc79eef722ba97"},"package":"e875f1719c16de097dee81ed675e2d9bb63096823ed3f0ca827b7dea3028bbbb"}
|
|
@ -10,9 +10,9 @@
|
|||
# See Cargo.toml.orig for the original contents.
|
||||
|
||||
[package]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
name = "enumset"
|
||||
version = "1.0.12"
|
||||
version = "1.1.2"
|
||||
authors = ["Alissa Rao <lymia@lymiahugs.com>"]
|
||||
description = "A library for creating compact sets of enums."
|
||||
documentation = "https://docs.rs/enumset/"
|
||||
|
@ -24,9 +24,17 @@ keywords = [
|
|||
categories = ["data-structures"]
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/Lymia/enumset"
|
||||
resolver = "1"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = [
|
||||
"--cfg",
|
||||
"docsrs",
|
||||
]
|
||||
|
||||
[dependencies.enumset_derive]
|
||||
version = "0.6.1"
|
||||
version = "0.8.0"
|
||||
|
||||
[dependencies.serde2]
|
||||
version = "1"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2017-2020 Alissa Rao <lymiahugs@gmail.com>
|
||||
Copyright (c) 2017-2023 Alissa Rao <lymiahugs@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
|
|
|
@ -2,11 +2,12 @@
|
|||
|
||||
[![Build Status](https://github.com/Lymia/enumset/actions/workflows/test.yml/badge.svg)](https://github.com/Lymia/enumset/actions/workflows/test.yml)
|
||||
[![Latest Version](https://img.shields.io/crates/v/enumset.svg)](https://crates.io/crates/enumset)
|
||||
![Requires rustc 1.36+](https://img.shields.io/badge/rustc-1.36+-red.svg)
|
||||
![Requires rustc 1.56+](https://img.shields.io/badge/rustc-1.56+-red.svg)
|
||||
[![Rust Documentation](https://img.shields.io/badge/api-rustdoc-blue.svg)](https://docs.rs/enumset)
|
||||
|
||||
A library for defining enums that can be used in compact bit sets.
|
||||
It supports enums up to 128 variants, and has a macro to use these sets in constants.
|
||||
A library for defining enums that can be used in compact bit sets. It supports
|
||||
`serde` and `#[no_std]` environments, and has basic support for using EnumSets
|
||||
in constants.
|
||||
|
||||
See [the documentation](https://docs.rs/enumset) for more information.
|
||||
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
#![no_std]
|
||||
#![forbid(missing_docs)]
|
||||
// The safety requirement is "use the procedural derive".
|
||||
#![allow(clippy::missing_safety_doc)]
|
||||
#![deny(missing_docs)]
|
||||
#![allow(clippy::missing_safety_doc)] // The safety requirement is "use the procedural derive".
|
||||
#![allow(clippy::needless_range_loop)] // range loop style is clearer in most places in enumset
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
|
||||
//! A library for defining enums that can be used in compact bit sets. It supports enums up to 128
|
||||
//! variants, and has a macro to use these sets in constants.
|
||||
//! A library for defining enums that can be used in compact bit sets. It supports arbitrarily
|
||||
//! large enums, and has very basic support for using them in constants.
|
||||
//!
|
||||
//! For serde support, enable the `serde` feature.
|
||||
//! The following feature flags may be used for this crate:
|
||||
//!
|
||||
//! * `serde` enables serialization support for [`EnumSet`].
|
||||
//! * `alloc` enables functions that require allocation.
|
||||
//!
|
||||
//! # Defining enums for use with EnumSet
|
||||
//!
|
||||
|
@ -78,64 +82,32 @@
|
|||
//! assert_eq!(set, Enum::A | Enum::E | Enum::G);
|
||||
//! ```
|
||||
|
||||
use core::cmp::Ordering;
|
||||
use core::fmt;
|
||||
use core::fmt::{Debug, Formatter};
|
||||
use core::hash::{Hash, Hasher};
|
||||
use core::iter::{FromIterator, Sum};
|
||||
use core::ops::*;
|
||||
#[cfg(feature = "alloc")]
|
||||
extern crate alloc;
|
||||
|
||||
#[doc(hidden)]
|
||||
/// Everything in this module is internal API and may change at any time.
|
||||
pub mod __internal {
|
||||
use super::*;
|
||||
|
||||
/// A reexport of core to allow our macros to be generic to std vs core.
|
||||
pub use ::core as core_export;
|
||||
|
||||
/// A reexport of serde so there is no requirement to depend on serde.
|
||||
#[cfg(feature = "serde")]
|
||||
pub use serde2 as serde;
|
||||
|
||||
/// The actual members of EnumSetType. Put here to avoid polluting global namespaces.
|
||||
pub unsafe trait EnumSetTypePrivate {
|
||||
/// The underlying type used to store the bitset.
|
||||
type Repr: EnumSetTypeRepr;
|
||||
/// A mask of bits that are valid in the bitset.
|
||||
const ALL_BITS: Self::Repr;
|
||||
|
||||
/// Converts an enum of this type into its bit position.
|
||||
fn enum_into_u32(self) -> u32;
|
||||
/// Converts a bit position into an enum value.
|
||||
unsafe fn enum_from_u32(val: u32) -> Self;
|
||||
|
||||
/// Serializes the `EnumSet`.
|
||||
///
|
||||
/// This and `deserialize` are part of the `EnumSetType` trait so the procedural derive
|
||||
/// can control how `EnumSet` is serialized.
|
||||
#[cfg(feature = "serde")]
|
||||
fn serialize<S: serde::Serializer>(set: EnumSet<Self>, ser: S) -> Result<S::Ok, S::Error>
|
||||
where Self: EnumSetType;
|
||||
/// Deserializes the `EnumSet`.
|
||||
#[cfg(feature = "serde")]
|
||||
fn deserialize<'de, D: serde::Deserializer<'de>>(de: D) -> Result<EnumSet<Self>, D::Error>
|
||||
where Self: EnumSetType;
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "serde")]
|
||||
use crate::__internal::serde;
|
||||
use crate::__internal::EnumSetTypePrivate;
|
||||
#[cfg(feature = "serde")]
|
||||
use crate::serde::{Deserialize, Serialize};
|
||||
mod macros;
|
||||
|
||||
mod repr;
|
||||
use crate::repr::EnumSetTypeRepr;
|
||||
mod set;
|
||||
mod traits;
|
||||
|
||||
pub use crate::macros::__internal;
|
||||
pub use crate::set::{EnumSet, EnumSetIter};
|
||||
pub use crate::traits::{EnumSetType, EnumSetTypeWithRepr};
|
||||
|
||||
/// The procedural macro used to derive [`EnumSetType`], and allow enums to be used with
|
||||
/// [`EnumSet`].
|
||||
///
|
||||
/// It may be used with any enum with no data fields, at most 127 variants, and no variant
|
||||
/// discriminators larger than 127.
|
||||
/// # Limitations
|
||||
///
|
||||
/// Currently, the following limitations apply to what kinds of enums this macro may be used with:
|
||||
///
|
||||
/// * The enum must have no data fields in any variant.
|
||||
/// * Variant discriminators must be zero or positive.
|
||||
/// * No variant discriminator may be larger than `0xFFFFFFBF`. This is chosen to limit problems
|
||||
/// involving overflow and similar edge cases.
|
||||
/// * Variant discriminators must be defined with integer literals. Expressions like `V = 1 + 1`
|
||||
/// are not currently supported.
|
||||
///
|
||||
/// # Additional Impls
|
||||
///
|
||||
|
@ -173,15 +145,16 @@ use crate::repr::EnumSetTypeRepr;
|
|||
/// “FFI, Safety and `repr`”][EnumSet#ffi-safety-and-repr]. Allowed types are `u8`, `u16`, `u32`,
|
||||
/// `u64` and `u128`. If this is not used, then the derive macro will choose a type to best fit
|
||||
/// the enum, but there are no guarantees about which type will be chosen.
|
||||
/// * `#[enumset(repr = "array")]` forces the `EnumSet` of this type to be backed with an array,
|
||||
/// even if all the variants could fit into a primitive numeric type.
|
||||
///
|
||||
/// When the `serde` feature is used, the following features may also be specified. These options
|
||||
/// may be used (with no effect) when building without the feature enabled:
|
||||
///
|
||||
/// * `#[enumset(serialize_repr = "u8")]` may be used to specify the integer type used to serialize
|
||||
/// the underlying bitset. Any type allowed in the `repr` option may be used in this option.
|
||||
/// * `#[enumset(serialize_as_list)]` may be used to serialize the bitset as a list of enum
|
||||
/// variants instead of an integer. This requires [`Deserialize`] and [`Serialize`] be
|
||||
/// implemented on the enum.
|
||||
/// * `#[enumset(serialize_repr = "…")]` may be used to override the way the `EnumSet` is
|
||||
/// serialized. Valid options are `u8`, `u16`, `u32`, `u64`, `list`, `map` and `array`. For more
|
||||
/// information, see the ["Serialization" section of the `EnumSet` documentation]
|
||||
/// (EnumSet#serialization).
|
||||
/// * `#[enumset(serialize_deny_unknown)]` causes the generated deserializer to return an error
|
||||
/// for unknown bits instead of silently ignoring them.
|
||||
///
|
||||
|
@ -217,755 +190,10 @@ use crate::repr::EnumSetTypeRepr;
|
|||
/// A, B, C, D, E, F, G,
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// [`Sub`]: core::ops::Sub
|
||||
/// [`BitAnd`]: core::ops::BitAnd
|
||||
/// [`BitOr`]: core::ops::BitOr
|
||||
/// [`BitXor`]: core::ops::BitXor
|
||||
/// [`Not`]: core::ops::Not
|
||||
pub use enumset_derive::EnumSetType;
|
||||
|
||||
/// The trait used to define enum types that may be used with [`EnumSet`].
|
||||
///
|
||||
/// This trait must be impelmented using `#[derive(EnumSetType)]`, is not public API, and its
|
||||
/// internal structure may change at any time with no warning.
|
||||
///
|
||||
/// For full documentation on the procedural derive and its options, see
|
||||
/// [`#[derive(EnumSetType)]`](./derive.EnumSetType.html).
|
||||
pub unsafe trait EnumSetType: Copy + Eq + EnumSetTypePrivate {}
|
||||
|
||||
/// An [`EnumSetType`] for which [`EnumSet`]s have a guaranteed in-memory representation.
|
||||
///
|
||||
/// An implementation of this trait is generated by using
|
||||
/// [`#[derive(EnumSetType)]`](./derive.EnumSetType.html) with the annotation
|
||||
/// `#[enumset(repr = "…")]`, where `…` is `u8`, `u16`, `u32`, `u64` or `u128`.
|
||||
///
|
||||
/// For any type `T` that implements this trait, the in-memory representation of `EnumSet<T>`
|
||||
/// is guaranteed to be `Repr`. This guarantee is useful for FFI. See [the `EnumSet` documentation
|
||||
/// under “FFI, Safety and `repr`”][EnumSet#ffi-safety-and-repr] for an example.
|
||||
pub unsafe trait EnumSetTypeWithRepr:
|
||||
EnumSetType + EnumSetTypePrivate<Repr = <Self as EnumSetTypeWithRepr>::Repr>
|
||||
{
|
||||
/// The guaranteed representation.
|
||||
type Repr: EnumSetTypeRepr;
|
||||
}
|
||||
|
||||
/// An efficient set type for enums.
|
||||
///
|
||||
/// It is implemented using a bitset stored using the smallest integer that can fit all bits
|
||||
/// in the underlying enum. In general, an enum variant with a discriminator of `n` is stored in
|
||||
/// the nth least significant bit (corresponding with a mask of, e.g. `1 << enum as u32`).
|
||||
///
|
||||
/// # Numeric representation
|
||||
///
|
||||
/// `EnumSet` is internally implemented using integer types, and as such can be easily converted
|
||||
/// from and to numbers.
|
||||
///
|
||||
/// Each bit of the underlying integer corresponds to at most one particular enum variant. If the
|
||||
/// corresponding bit for a variant is set, it present in the set. Bits that do not correspond to
|
||||
/// any variant are always unset.
|
||||
///
|
||||
/// By default, each enum variant is stored in a bit corresponding to its discriminator. An enum
|
||||
/// variant with a discriminator of `n` is stored in the `n + 1`th least significant bit
|
||||
/// (corresponding to a mask of e.g. `1 << enum as u32`).
|
||||
///
|
||||
/// # Serialization
|
||||
///
|
||||
/// When the `serde` feature is enabled, `EnumSet`s can be serialized and deserialized using
|
||||
/// the `serde` crate. The exact serialization format can be controlled with additional attributes
|
||||
/// on the enum type. These attributes are valid regardless of whether the `serde` feature
|
||||
/// is enabled.
|
||||
///
|
||||
/// By default, `EnumSet`s serialize by directly writing out the underlying bitset as an integer
|
||||
/// of the smallest type that can fit in the underlying enum. You can add a
|
||||
/// `#[enumset(serialize_repr = "u8")]` attribute to your enum to control the integer type used
|
||||
/// for serialization. This can be important for avoiding unintentional breaking changes when
|
||||
/// `EnumSet`s are serialized with formats like `bincode`.
|
||||
///
|
||||
/// By default, unknown bits are ignored and silently removed from the bitset. To override thris
|
||||
/// behavior, you can add a `#[enumset(serialize_deny_unknown)]` attribute. This will cause
|
||||
/// deserialization to fail if an invalid bit is set.
|
||||
///
|
||||
/// In addition, the `#[enumset(serialize_as_list)]` attribute causes the `EnumSet` to be
|
||||
/// instead serialized as a list of enum variants. This requires your enum type implement
|
||||
/// [`Serialize`] and [`Deserialize`]. Note that this is a breaking change.
|
||||
///
|
||||
/// # FFI, Safety and `repr`
|
||||
///
|
||||
/// If an enum type `T` is annotated with [`#[enumset(repr = "R")]`][derive@EnumSetType#options],
|
||||
/// then several things happen:
|
||||
///
|
||||
/// * `T` will implement <code>[EnumSetTypeWithRepr]<Repr = R></code> in addition to
|
||||
/// [`EnumSetType`].
|
||||
/// * The `EnumSet` methods with `repr` in their name, such as [`as_repr`][EnumSet::as_repr] and
|
||||
/// [`from_repr`][EnumSet::from_repr], will be available for `EnumSet<T>`.
|
||||
/// * The in-memory representation of `EnumSet<T>` is guaranteed to be `R`.
|
||||
///
|
||||
/// That last guarantee makes it sound to send `EnumSet<T>` across an FFI boundary. For example:
|
||||
///
|
||||
/// ```
|
||||
/// # use enumset::*;
|
||||
/// #
|
||||
/// # mod ffi_impl {
|
||||
/// # // This example “foreign” function is actually written in Rust, but for the sake
|
||||
/// # // of example, we'll pretend it's written in C.
|
||||
/// # #[no_mangle]
|
||||
/// # extern "C" fn some_foreign_function(set: u32) -> u32 {
|
||||
/// # set & 0b100
|
||||
/// # }
|
||||
/// # }
|
||||
/// #
|
||||
/// extern "C" {
|
||||
/// // This function is written in C like:
|
||||
/// // uint32_t some_foreign_function(uint32_t set) { … }
|
||||
/// fn some_foreign_function(set: EnumSet<MyEnum>) -> EnumSet<MyEnum>;
|
||||
/// }
|
||||
///
|
||||
/// #[derive(Debug, EnumSetType)]
|
||||
/// #[enumset(repr = "u32")]
|
||||
/// enum MyEnum { A, B, C }
|
||||
///
|
||||
/// let set: EnumSet<MyEnum> = enum_set!(MyEnum::A | MyEnum::C);
|
||||
///
|
||||
/// let new_set: EnumSet<MyEnum> = unsafe { some_foreign_function(set) };
|
||||
/// assert_eq!(new_set, enum_set!(MyEnum::C));
|
||||
/// ```
|
||||
///
|
||||
/// When an `EnumSet<T>` is received via FFI, all bits that don't correspond to an enum variant
|
||||
/// of `T` must be set to `0`. Behavior is **undefined** if any of these bits are set to `1`.
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
#[repr(transparent)]
|
||||
pub struct EnumSet<T: EnumSetType> {
|
||||
#[doc(hidden)]
|
||||
/// This is public due to the [`enum_set!`] macro.
|
||||
/// This is **NOT** public API and may change at any time.
|
||||
pub __priv_repr: T::Repr,
|
||||
}
|
||||
impl<T: EnumSetType> EnumSet<T> {
|
||||
// Returns all bits valid for the enum
|
||||
#[inline(always)]
|
||||
fn all_bits() -> T::Repr {
|
||||
T::ALL_BITS
|
||||
}
|
||||
|
||||
/// Creates an empty `EnumSet`.
|
||||
#[inline(always)]
|
||||
pub fn new() -> Self {
|
||||
EnumSet { __priv_repr: T::Repr::empty() }
|
||||
}
|
||||
|
||||
/// Returns an `EnumSet` containing a single element.
|
||||
#[inline(always)]
|
||||
pub fn only(t: T) -> Self {
|
||||
let mut set = Self::new();
|
||||
set.insert(t);
|
||||
set
|
||||
}
|
||||
|
||||
/// Creates an empty `EnumSet`.
|
||||
///
|
||||
/// This is an alias for [`EnumSet::new`].
|
||||
#[inline(always)]
|
||||
pub fn empty() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
|
||||
/// Returns an `EnumSet` containing all valid variants of the enum.
|
||||
#[inline(always)]
|
||||
pub fn all() -> Self {
|
||||
EnumSet { __priv_repr: Self::all_bits() }
|
||||
}
|
||||
|
||||
/// Total number of bits used by this type. Note that the actual amount of space used is
|
||||
/// rounded up to the next highest integer type (`u8`, `u16`, `u32`, `u64`, or `u128`).
|
||||
///
|
||||
/// This is the same as [`EnumSet::variant_count`] except in enums with "sparse" variants.
|
||||
/// (e.g. `enum Foo { A = 10, B = 20 }`)
|
||||
#[inline(always)]
|
||||
pub fn bit_width() -> u32 {
|
||||
T::Repr::WIDTH - T::ALL_BITS.leading_zeros()
|
||||
}
|
||||
|
||||
/// The number of valid variants that this type can contain.
|
||||
///
|
||||
/// This is the same as [`EnumSet::bit_width`] except in enums with "sparse" variants.
|
||||
/// (e.g. `enum Foo { A = 10, B = 20 }`)
|
||||
#[inline(always)]
|
||||
pub fn variant_count() -> u32 {
|
||||
T::ALL_BITS.count_ones()
|
||||
}
|
||||
|
||||
/// Returns the number of elements in this set.
|
||||
#[inline(always)]
|
||||
pub fn len(&self) -> usize {
|
||||
self.__priv_repr.count_ones() as usize
|
||||
}
|
||||
/// Returns `true` if the set contains no elements.
|
||||
#[inline(always)]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.__priv_repr.is_empty()
|
||||
}
|
||||
/// Removes all elements from the set.
|
||||
#[inline(always)]
|
||||
pub fn clear(&mut self) {
|
||||
self.__priv_repr = T::Repr::empty()
|
||||
}
|
||||
|
||||
/// Returns `true` if `self` has no elements in common with `other`. This is equivalent to
|
||||
/// checking for an empty intersection.
|
||||
#[inline(always)]
|
||||
pub fn is_disjoint(&self, other: Self) -> bool {
|
||||
(*self & other).is_empty()
|
||||
}
|
||||
/// Returns `true` if the set is a superset of another, i.e., `self` contains at least all the
|
||||
/// values in `other`.
|
||||
#[inline(always)]
|
||||
pub fn is_superset(&self, other: Self) -> bool {
|
||||
(*self & other).__priv_repr == other.__priv_repr
|
||||
}
|
||||
/// Returns `true` if the set is a subset of another, i.e., `other` contains at least all
|
||||
/// the values in `self`.
|
||||
#[inline(always)]
|
||||
pub fn is_subset(&self, other: Self) -> bool {
|
||||
other.is_superset(*self)
|
||||
}
|
||||
|
||||
/// Returns a set containing any elements present in either set.
|
||||
#[inline(always)]
|
||||
pub fn union(&self, other: Self) -> Self {
|
||||
EnumSet { __priv_repr: self.__priv_repr | other.__priv_repr }
|
||||
}
|
||||
/// Returns a set containing every element present in both sets.
|
||||
#[inline(always)]
|
||||
pub fn intersection(&self, other: Self) -> Self {
|
||||
EnumSet { __priv_repr: self.__priv_repr & other.__priv_repr }
|
||||
}
|
||||
/// Returns a set containing element present in `self` but not in `other`.
|
||||
#[inline(always)]
|
||||
pub fn difference(&self, other: Self) -> Self {
|
||||
EnumSet { __priv_repr: self.__priv_repr.and_not(other.__priv_repr) }
|
||||
}
|
||||
/// Returns a set containing every element present in either `self` or `other`, but is not
|
||||
/// present in both.
|
||||
#[inline(always)]
|
||||
pub fn symmetrical_difference(&self, other: Self) -> Self {
|
||||
EnumSet { __priv_repr: self.__priv_repr ^ other.__priv_repr }
|
||||
}
|
||||
/// Returns a set containing all enum variants not in this set.
|
||||
#[inline(always)]
|
||||
pub fn complement(&self) -> Self {
|
||||
EnumSet { __priv_repr: !self.__priv_repr & Self::all_bits() }
|
||||
}
|
||||
|
||||
/// Checks whether this set contains a value.
|
||||
#[inline(always)]
|
||||
pub fn contains(&self, value: T) -> bool {
|
||||
self.__priv_repr.has_bit(value.enum_into_u32())
|
||||
}
|
||||
|
||||
/// Adds a value to this set.
|
||||
///
|
||||
/// If the set did not have this value present, `true` is returned.
|
||||
///
|
||||
/// If the set did have this value present, `false` is returned.
|
||||
#[inline(always)]
|
||||
pub fn insert(&mut self, value: T) -> bool {
|
||||
let contains = !self.contains(value);
|
||||
self.__priv_repr.add_bit(value.enum_into_u32());
|
||||
contains
|
||||
}
|
||||
/// Removes a value from this set. Returns whether the value was present in the set.
|
||||
#[inline(always)]
|
||||
pub fn remove(&mut self, value: T) -> bool {
|
||||
let contains = self.contains(value);
|
||||
self.__priv_repr.remove_bit(value.enum_into_u32());
|
||||
contains
|
||||
}
|
||||
|
||||
/// Adds all elements in another set to this one.
|
||||
#[inline(always)]
|
||||
pub fn insert_all(&mut self, other: Self) {
|
||||
self.__priv_repr = self.__priv_repr | other.__priv_repr
|
||||
}
|
||||
/// Removes all values in another set from this one.
|
||||
#[inline(always)]
|
||||
pub fn remove_all(&mut self, other: Self) {
|
||||
self.__priv_repr = self.__priv_repr.and_not(other.__priv_repr);
|
||||
}
|
||||
|
||||
/// Iterates the contents of the set in order from the least significant bit to the most
|
||||
/// significant bit.
|
||||
///
|
||||
/// Note that iterator invalidation is impossible as the iterator contains a copy of this type,
|
||||
/// rather than holding a reference to it.
|
||||
pub fn iter(&self) -> EnumSetIter<T> {
|
||||
EnumSetIter::new(*self)
|
||||
}
|
||||
|
||||
/// Returns a `T::Repr` representing the elements of this set.
|
||||
///
|
||||
/// Unlike the other `as_*` methods, this method is zero-cost and guaranteed not to fail,
|
||||
/// panic or truncate any bits.
|
||||
///
|
||||
/// In order to use this method, the definition of `T` must have the `#[enumset(repr = "…")]`
|
||||
/// annotation.
|
||||
#[inline(always)]
|
||||
pub fn as_repr(&self) -> <T as EnumSetTypeWithRepr>::Repr
|
||||
where T: EnumSetTypeWithRepr {
|
||||
self.__priv_repr
|
||||
}
|
||||
|
||||
/// Constructs a bitset from a `T::Repr` without checking for invalid bits.
|
||||
///
|
||||
/// Unlike the other `from_*` methods, this method is zero-cost and guaranteed not to fail,
|
||||
/// panic or truncate any bits, provided the conditions under “Safety” are upheld.
|
||||
///
|
||||
/// In order to use this method, the definition of `T` must have the `#[enumset(repr = "…")]`
|
||||
/// annotation.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// All bits in the provided parameter `bits` that don't correspond to an enum variant of
|
||||
/// `T` must be set to `0`. Behavior is **undefined** if any of these bits are set to `1`.
|
||||
#[inline(always)]
|
||||
pub unsafe fn from_repr_unchecked(bits: <T as EnumSetTypeWithRepr>::Repr) -> Self
|
||||
where T: EnumSetTypeWithRepr {
|
||||
Self { __priv_repr: bits }
|
||||
}
|
||||
|
||||
/// Constructs a bitset from a `T::Repr`.
|
||||
///
|
||||
/// If a bit that doesn't correspond to an enum variant is set, this
|
||||
/// method will panic.
|
||||
///
|
||||
/// In order to use this method, the definition of `T` must have the `#[enumset(repr = "…")]`
|
||||
/// annotation.
|
||||
#[inline(always)]
|
||||
pub fn from_repr(bits: <T as EnumSetTypeWithRepr>::Repr) -> Self
|
||||
where T: EnumSetTypeWithRepr {
|
||||
Self::try_from_repr(bits).expect("Bitset contains invalid variants.")
|
||||
}
|
||||
|
||||
/// Attempts to constructs a bitset from a `T::Repr`.
|
||||
///
|
||||
/// If a bit that doesn't correspond to an enum variant is set, this
|
||||
/// method will return `None`.
|
||||
///
|
||||
/// In order to use this method, the definition of `T` must have the `#[enumset(repr = "…")]`
|
||||
/// annotation.
|
||||
#[inline(always)]
|
||||
pub fn try_from_repr(bits: <T as EnumSetTypeWithRepr>::Repr) -> Option<Self>
|
||||
where T: EnumSetTypeWithRepr {
|
||||
let mask = Self::all().__priv_repr;
|
||||
if bits.and_not(mask).is_empty() {
|
||||
Some(EnumSet { __priv_repr: bits })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs a bitset from a `T::Repr`, ignoring invalid variants.
|
||||
///
|
||||
/// In order to use this method, the definition of `T` must have the `#[enumset(repr = "…")]`
|
||||
/// annotation.
|
||||
#[inline(always)]
|
||||
pub fn from_repr_truncated(bits: <T as EnumSetTypeWithRepr>::Repr) -> Self
|
||||
where T: EnumSetTypeWithRepr {
|
||||
let mask = Self::all().as_repr();
|
||||
let bits = bits & mask;
|
||||
EnumSet { __priv_repr: bits }
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper macro for generating conversion functions.
|
||||
macro_rules! conversion_impls {
|
||||
(
|
||||
$(for_num!(
|
||||
$underlying:ty, $underlying_str:expr,
|
||||
$from_fn:ident $to_fn:ident $from_fn_opt:ident $to_fn_opt:ident,
|
||||
$from:ident $try_from:ident $from_truncated:ident $from_unchecked:ident,
|
||||
$to:ident $try_to:ident $to_truncated:ident
|
||||
);)*
|
||||
) => {
|
||||
impl <T : EnumSetType> EnumSet<T> {$(
|
||||
#[doc = "Returns a `"]
|
||||
#[doc = $underlying_str]
|
||||
#[doc = "` representing the elements of this set.\n\nIf the underlying bitset will \
|
||||
not fit in a `"]
|
||||
#[doc = $underlying_str]
|
||||
#[doc = "` or contains bits that do not correspond to an enum variant, this method \
|
||||
will panic."]
|
||||
#[inline(always)]
|
||||
pub fn $to(&self) -> $underlying {
|
||||
self.$try_to().expect("Bitset will not fit into this type.")
|
||||
}
|
||||
|
||||
#[doc = "Tries to return a `"]
|
||||
#[doc = $underlying_str]
|
||||
#[doc = "` representing the elements of this set.\n\nIf the underlying bitset will \
|
||||
not fit in a `"]
|
||||
#[doc = $underlying_str]
|
||||
#[doc = "` or contains bits that do not correspond to an enum variant, this method \
|
||||
will instead return `None`."]
|
||||
#[inline(always)]
|
||||
pub fn $try_to(&self) -> Option<$underlying> {
|
||||
EnumSetTypeRepr::$to_fn_opt(&self.__priv_repr)
|
||||
}
|
||||
|
||||
#[doc = "Returns a truncated `"]
|
||||
#[doc = $underlying_str]
|
||||
#[doc = "` representing the elements of this set.\n\nIf the underlying bitset will \
|
||||
not fit in a `"]
|
||||
#[doc = $underlying_str]
|
||||
#[doc = "`, this method will truncate any bits that don't fit or do not correspond \
|
||||
to an enum variant."]
|
||||
#[inline(always)]
|
||||
pub fn $to_truncated(&self) -> $underlying {
|
||||
EnumSetTypeRepr::$to_fn(&self.__priv_repr)
|
||||
}
|
||||
|
||||
#[doc = "Constructs a bitset from a `"]
|
||||
#[doc = $underlying_str]
|
||||
#[doc = "`.\n\nIf a bit that doesn't correspond to an enum variant is set, this \
|
||||
method will panic."]
|
||||
#[inline(always)]
|
||||
pub fn $from(bits: $underlying) -> Self {
|
||||
Self::$try_from(bits).expect("Bitset contains invalid variants.")
|
||||
}
|
||||
|
||||
#[doc = "Attempts to constructs a bitset from a `"]
|
||||
#[doc = $underlying_str]
|
||||
#[doc = "`.\n\nIf a bit that doesn't correspond to an enum variant is set, this \
|
||||
method will return `None`."]
|
||||
#[inline(always)]
|
||||
pub fn $try_from(bits: $underlying) -> Option<Self> {
|
||||
let bits = T::Repr::$from_fn_opt(bits);
|
||||
let mask = Self::all().__priv_repr;
|
||||
bits.and_then(|bits| if bits.and_not(mask).is_empty() {
|
||||
Some(EnumSet { __priv_repr: bits })
|
||||
} else {
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
#[doc = "Constructs a bitset from a `"]
|
||||
#[doc = $underlying_str]
|
||||
#[doc = "`, ignoring invalid variants."]
|
||||
#[inline(always)]
|
||||
pub fn $from_truncated(bits: $underlying) -> Self {
|
||||
let mask = Self::all().$to_truncated();
|
||||
let bits = <T::Repr as EnumSetTypeRepr>::$from_fn(bits & mask);
|
||||
EnumSet { __priv_repr: bits }
|
||||
}
|
||||
|
||||
#[doc = "Constructs a bitset from a `"]
|
||||
#[doc = $underlying_str]
|
||||
#[doc = "`, without checking for invalid bits."]
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// All bits in the provided parameter `bits` that don't correspond to an enum variant
|
||||
/// of `T` must be set to `0`. Behavior is **undefined** if any of these bits are set
|
||||
/// to `1`.
|
||||
#[inline(always)]
|
||||
pub unsafe fn $from_unchecked(bits: $underlying) -> Self {
|
||||
EnumSet { __priv_repr: <T::Repr as EnumSetTypeRepr>::$from_fn(bits) }
|
||||
}
|
||||
)*}
|
||||
}
|
||||
}
|
||||
conversion_impls! {
|
||||
for_num!(u8, "u8",
|
||||
from_u8 to_u8 from_u8_opt to_u8_opt,
|
||||
from_u8 try_from_u8 from_u8_truncated from_u8_unchecked,
|
||||
as_u8 try_as_u8 as_u8_truncated);
|
||||
for_num!(u16, "u16",
|
||||
from_u16 to_u16 from_u16_opt to_u16_opt,
|
||||
from_u16 try_from_u16 from_u16_truncated from_u16_unchecked,
|
||||
as_u16 try_as_u16 as_u16_truncated);
|
||||
for_num!(u32, "u32",
|
||||
from_u32 to_u32 from_u32_opt to_u32_opt,
|
||||
from_u32 try_from_u32 from_u32_truncated from_u32_unchecked,
|
||||
as_u32 try_as_u32 as_u32_truncated);
|
||||
for_num!(u64, "u64",
|
||||
from_u64 to_u64 from_u64_opt to_u64_opt,
|
||||
from_u64 try_from_u64 from_u64_truncated from_u64_unchecked,
|
||||
as_u64 try_as_u64 as_u64_truncated);
|
||||
for_num!(u128, "u128",
|
||||
from_u128 to_u128 from_u128_opt to_u128_opt,
|
||||
from_u128 try_from_u128 from_u128_truncated from_u128_unchecked,
|
||||
as_u128 try_as_u128 as_u128_truncated);
|
||||
for_num!(usize, "usize",
|
||||
from_usize to_usize from_usize_opt to_usize_opt,
|
||||
from_usize try_from_usize from_usize_truncated from_usize_unchecked,
|
||||
as_usize try_as_usize as_usize_truncated);
|
||||
}
|
||||
|
||||
impl<T: EnumSetType> Default for EnumSet<T> {
|
||||
/// Returns an empty set.
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EnumSetType> IntoIterator for EnumSet<T> {
|
||||
type Item = T;
|
||||
type IntoIter = EnumSetIter<T>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
impl<T: EnumSetType> Sum for EnumSet<T> {
|
||||
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
|
||||
iter.fold(EnumSet::empty(), |a, v| a | v)
|
||||
}
|
||||
}
|
||||
impl<'a, T: EnumSetType> Sum<&'a EnumSet<T>> for EnumSet<T> {
|
||||
fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
|
||||
iter.fold(EnumSet::empty(), |a, v| a | *v)
|
||||
}
|
||||
}
|
||||
impl<T: EnumSetType> Sum<T> for EnumSet<T> {
|
||||
fn sum<I: Iterator<Item = T>>(iter: I) -> Self {
|
||||
iter.fold(EnumSet::empty(), |a, v| a | v)
|
||||
}
|
||||
}
|
||||
impl<'a, T: EnumSetType> Sum<&'a T> for EnumSet<T> {
|
||||
fn sum<I: Iterator<Item = &'a T>>(iter: I) -> Self {
|
||||
iter.fold(EnumSet::empty(), |a, v| a | *v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EnumSetType, O: Into<EnumSet<T>>> Sub<O> for EnumSet<T> {
|
||||
type Output = Self;
|
||||
#[inline(always)]
|
||||
fn sub(self, other: O) -> Self::Output {
|
||||
self.difference(other.into())
|
||||
}
|
||||
}
|
||||
impl<T: EnumSetType, O: Into<EnumSet<T>>> BitAnd<O> for EnumSet<T> {
|
||||
type Output = Self;
|
||||
#[inline(always)]
|
||||
fn bitand(self, other: O) -> Self::Output {
|
||||
self.intersection(other.into())
|
||||
}
|
||||
}
|
||||
impl<T: EnumSetType, O: Into<EnumSet<T>>> BitOr<O> for EnumSet<T> {
|
||||
type Output = Self;
|
||||
#[inline(always)]
|
||||
fn bitor(self, other: O) -> Self::Output {
|
||||
self.union(other.into())
|
||||
}
|
||||
}
|
||||
impl<T: EnumSetType, O: Into<EnumSet<T>>> BitXor<O> for EnumSet<T> {
|
||||
type Output = Self;
|
||||
#[inline(always)]
|
||||
fn bitxor(self, other: O) -> Self::Output {
|
||||
self.symmetrical_difference(other.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EnumSetType, O: Into<EnumSet<T>>> SubAssign<O> for EnumSet<T> {
|
||||
#[inline(always)]
|
||||
fn sub_assign(&mut self, rhs: O) {
|
||||
*self = *self - rhs;
|
||||
}
|
||||
}
|
||||
impl<T: EnumSetType, O: Into<EnumSet<T>>> BitAndAssign<O> for EnumSet<T> {
|
||||
#[inline(always)]
|
||||
fn bitand_assign(&mut self, rhs: O) {
|
||||
*self = *self & rhs;
|
||||
}
|
||||
}
|
||||
impl<T: EnumSetType, O: Into<EnumSet<T>>> BitOrAssign<O> for EnumSet<T> {
|
||||
#[inline(always)]
|
||||
fn bitor_assign(&mut self, rhs: O) {
|
||||
*self = *self | rhs;
|
||||
}
|
||||
}
|
||||
impl<T: EnumSetType, O: Into<EnumSet<T>>> BitXorAssign<O> for EnumSet<T> {
|
||||
#[inline(always)]
|
||||
fn bitxor_assign(&mut self, rhs: O) {
|
||||
*self = *self ^ rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EnumSetType> Not for EnumSet<T> {
|
||||
type Output = Self;
|
||||
#[inline(always)]
|
||||
fn not(self) -> Self::Output {
|
||||
self.complement()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EnumSetType> From<T> for EnumSet<T> {
|
||||
fn from(t: T) -> Self {
|
||||
EnumSet::only(t)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EnumSetType> PartialEq<T> for EnumSet<T> {
|
||||
fn eq(&self, other: &T) -> bool {
|
||||
self.__priv_repr == EnumSet::only(*other).__priv_repr
|
||||
}
|
||||
}
|
||||
impl<T: EnumSetType + Debug> Debug for EnumSet<T> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
let mut is_first = true;
|
||||
f.write_str("EnumSet(")?;
|
||||
for v in self.iter() {
|
||||
if !is_first {
|
||||
f.write_str(" | ")?;
|
||||
}
|
||||
is_first = false;
|
||||
v.fmt(f)?;
|
||||
}
|
||||
f.write_str(")")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::derive_hash_xor_eq)] // This impl exists to change trait bounds only.
|
||||
impl<T: EnumSetType> Hash for EnumSet<T> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.__priv_repr.hash(state)
|
||||
}
|
||||
}
|
||||
impl<T: EnumSetType> PartialOrd for EnumSet<T> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
self.__priv_repr.partial_cmp(&other.__priv_repr)
|
||||
}
|
||||
}
|
||||
impl<T: EnumSetType> Ord for EnumSet<T> {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.__priv_repr.cmp(&other.__priv_repr)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<T: EnumSetType> Serialize for EnumSet<T> {
|
||||
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
T::serialize(*self, serializer)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de, T: EnumSetType> Deserialize<'de> for EnumSet<T> {
|
||||
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
T::deserialize(deserializer)
|
||||
}
|
||||
}
|
||||
|
||||
/// The iterator used by [`EnumSet`]s.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct EnumSetIter<T: EnumSetType> {
|
||||
set: EnumSet<T>,
|
||||
}
|
||||
impl<T: EnumSetType> EnumSetIter<T> {
|
||||
fn new(set: EnumSet<T>) -> EnumSetIter<T> {
|
||||
EnumSetIter { set }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EnumSetType> Iterator for EnumSetIter<T> {
|
||||
type Item = T;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.set.is_empty() {
|
||||
None
|
||||
} else {
|
||||
let bit = self.set.__priv_repr.trailing_zeros();
|
||||
self.set.__priv_repr.remove_bit(bit);
|
||||
unsafe { Some(T::enum_from_u32(bit)) }
|
||||
}
|
||||
}
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let left = self.set.len();
|
||||
(left, Some(left))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EnumSetType> DoubleEndedIterator for EnumSetIter<T> {
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
if self.set.is_empty() {
|
||||
None
|
||||
} else {
|
||||
let bit = T::Repr::WIDTH - 1 - self.set.__priv_repr.leading_zeros();
|
||||
self.set.__priv_repr.remove_bit(bit);
|
||||
unsafe { Some(T::enum_from_u32(bit)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EnumSetType> ExactSizeIterator for EnumSetIter<T> {}
|
||||
|
||||
impl<T: EnumSetType> Extend<T> for EnumSet<T> {
|
||||
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
|
||||
iter.into_iter().for_each(|v| {
|
||||
self.insert(v);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EnumSetType> FromIterator<T> for EnumSet<T> {
|
||||
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
|
||||
let mut set = EnumSet::default();
|
||||
set.extend(iter);
|
||||
set
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EnumSetType> Extend<EnumSet<T>> for EnumSet<T> {
|
||||
fn extend<I: IntoIterator<Item = EnumSet<T>>>(&mut self, iter: I) {
|
||||
iter.into_iter().for_each(|v| {
|
||||
self.insert_all(v);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EnumSetType> FromIterator<EnumSet<T>> for EnumSet<T> {
|
||||
fn from_iter<I: IntoIterator<Item = EnumSet<T>>>(iter: I) -> Self {
|
||||
let mut set = EnumSet::default();
|
||||
set.extend(iter);
|
||||
set
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a EnumSet literal, which can be used in const contexts.
|
||||
///
|
||||
/// The syntax used is `enum_set!(Type::A | Type::B | Type::C)`. Each variant must be of the same
|
||||
/// type, or a error will occur at compile-time.
|
||||
///
|
||||
/// This macro accepts trailing `|`s to allow easier use in other macros.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use enumset::*;
|
||||
/// # #[derive(EnumSetType, Debug)] enum Enum { A, B, C }
|
||||
/// const CONST_SET: EnumSet<Enum> = enum_set!(Enum::A | Enum::B);
|
||||
/// assert_eq!(CONST_SET, Enum::A | Enum::B);
|
||||
/// ```
|
||||
///
|
||||
/// This macro is strongly typed. For example, the following will not compile:
|
||||
///
|
||||
/// ```compile_fail
|
||||
/// # use enumset::*;
|
||||
/// # #[derive(EnumSetType, Debug)] enum Enum { A, B, C }
|
||||
/// # #[derive(EnumSetType, Debug)] enum Enum2 { A, B, C }
|
||||
/// let type_error = enum_set!(Enum::A | Enum2::B);
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! enum_set {
|
||||
($(|)*) => {
|
||||
$crate::EnumSet { __priv_repr: 0 }
|
||||
};
|
||||
($value:path $(|)*) => {
|
||||
{
|
||||
#[allow(deprecated)] let value = $value.__impl_enumset_internal__const_only();
|
||||
value
|
||||
}
|
||||
};
|
||||
($value:path | $($rest:path)|* $(|)*) => {
|
||||
{
|
||||
#[allow(deprecated)] let value = $value.__impl_enumset_internal__const_only();
|
||||
$(#[allow(deprecated)] let value = $rest.__impl_enumset_internal__const_merge(value);)*
|
||||
value
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
/// Everything in this module is internal API and may change at any time.
|
||||
#[doc(hidden)]
|
||||
pub mod __internal {
|
||||
/// A reexport of core to allow our macros to be generic to std vs core.
|
||||
pub use ::core as core_export;
|
||||
|
||||
/// A reexport of serde so our users don't have to also have a serde dependency.
|
||||
#[cfg(feature = "serde")]
|
||||
pub use serde2 as serde;
|
||||
|
||||
/// Reexports of internal types
|
||||
pub use crate::{
|
||||
repr::{ArrayRepr, EnumSetTypeRepr},
|
||||
traits::EnumSetTypePrivate,
|
||||
};
|
||||
}
|
||||
|
||||
/// Creates a EnumSet literal, which can be used in const contexts.
|
||||
///
|
||||
/// The syntax used is `enum_set!(Type::A | Type::B | Type::C)`. Each variant must be of the same
|
||||
/// type, or a error will occur at compile-time.
|
||||
///
|
||||
/// This macro accepts trailing `|`s to allow easier use in other macros.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use enumset::*;
|
||||
/// # #[derive(EnumSetType, Debug)] enum Enum { A, B, C }
|
||||
/// const CONST_SET: EnumSet<Enum> = enum_set!(Enum::A | Enum::B);
|
||||
/// assert_eq!(CONST_SET, Enum::A | Enum::B);
|
||||
/// ```
|
||||
///
|
||||
/// This macro is strongly typed. For example, the following will not compile:
|
||||
///
|
||||
/// ```compile_fail
|
||||
/// # use enumset::*;
|
||||
/// # #[derive(EnumSetType, Debug)] enum Enum { A, B, C }
|
||||
/// # #[derive(EnumSetType, Debug)] enum Enum2 { A, B, C }
|
||||
/// let type_error = enum_set!(Enum::A | Enum2::B);
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! enum_set {
|
||||
($(|)*) => {
|
||||
EnumSet::EMPTY
|
||||
};
|
||||
($value:path $(|)*) => {
|
||||
{
|
||||
#[allow(deprecated)] let value = $value.__impl_enumset_internal__const_only();
|
||||
value
|
||||
}
|
||||
};
|
||||
($value:path | $($rest:path)|* $(|)*) => {
|
||||
{
|
||||
#[allow(deprecated)] let value = $value.__impl_enumset_internal__const_only();
|
||||
$(#[allow(deprecated)] let value = $rest.__impl_enumset_internal__const_merge(value);)*
|
||||
value
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,225 +0,0 @@
|
|||
use core::convert::TryInto;
|
||||
use core::fmt::Debug;
|
||||
use core::hash::Hash;
|
||||
use core::ops::*;
|
||||
|
||||
/// A trait marking valid underlying bitset storage types and providing the
|
||||
/// operations `EnumSet` and related types use.
|
||||
pub trait EnumSetTypeRepr :
|
||||
// Basic traits used to derive traits
|
||||
Copy +
|
||||
Ord +
|
||||
Eq +
|
||||
Debug +
|
||||
Hash +
|
||||
// Operations used by enumset
|
||||
BitAnd<Output = Self> +
|
||||
BitOr<Output = Self> +
|
||||
BitXor<Output = Self> +
|
||||
Not<Output = Self> +
|
||||
{
|
||||
const WIDTH: u32;
|
||||
|
||||
fn is_empty(&self) -> bool;
|
||||
fn empty() -> Self;
|
||||
|
||||
fn add_bit(&mut self, bit: u32);
|
||||
fn remove_bit(&mut self, bit: u32);
|
||||
fn has_bit(&self, bit: u32) -> bool;
|
||||
|
||||
fn count_ones(&self) -> u32;
|
||||
fn count_remaining_ones(&self, cursor: u32) -> usize;
|
||||
fn leading_zeros(&self) -> u32;
|
||||
fn trailing_zeros(&self) -> u32;
|
||||
|
||||
fn and_not(&self, other: Self) -> Self;
|
||||
|
||||
fn from_u8(v: u8) -> Self;
|
||||
fn from_u16(v: u16) -> Self;
|
||||
fn from_u32(v: u32) -> Self;
|
||||
fn from_u64(v: u64) -> Self;
|
||||
fn from_u128(v: u128) -> Self;
|
||||
fn from_usize(v: usize) -> Self;
|
||||
|
||||
fn to_u8(&self) -> u8;
|
||||
fn to_u16(&self) -> u16;
|
||||
fn to_u32(&self) -> u32;
|
||||
fn to_u64(&self) -> u64;
|
||||
fn to_u128(&self) -> u128;
|
||||
fn to_usize(&self) -> usize;
|
||||
|
||||
fn from_u8_opt(v: u8) -> Option<Self>;
|
||||
fn from_u16_opt(v: u16) -> Option<Self>;
|
||||
fn from_u32_opt(v: u32) -> Option<Self>;
|
||||
fn from_u64_opt(v: u64) -> Option<Self>;
|
||||
fn from_u128_opt(v: u128) -> Option<Self>;
|
||||
fn from_usize_opt(v: usize) -> Option<Self>;
|
||||
|
||||
fn to_u8_opt(&self) -> Option<u8>;
|
||||
fn to_u16_opt(&self) -> Option<u16>;
|
||||
fn to_u32_opt(&self) -> Option<u32>;
|
||||
fn to_u64_opt(&self) -> Option<u64>;
|
||||
fn to_u128_opt(&self) -> Option<u128>;
|
||||
fn to_usize_opt(&self) -> Option<usize>;
|
||||
}
|
||||
macro_rules! prim {
|
||||
($name:ty, $width:expr) => {
|
||||
impl EnumSetTypeRepr for $name {
|
||||
const WIDTH: u32 = $width;
|
||||
|
||||
#[inline(always)]
|
||||
fn is_empty(&self) -> bool {
|
||||
*self == 0
|
||||
}
|
||||
#[inline(always)]
|
||||
fn empty() -> Self {
|
||||
0
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn add_bit(&mut self, bit: u32) {
|
||||
*self |= 1 << bit as $name;
|
||||
}
|
||||
#[inline(always)]
|
||||
fn remove_bit(&mut self, bit: u32) {
|
||||
*self &= !(1 << bit as $name);
|
||||
}
|
||||
#[inline(always)]
|
||||
fn has_bit(&self, bit: u32) -> bool {
|
||||
(self & (1 << bit as $name)) != 0
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn count_ones(&self) -> u32 {
|
||||
(*self).count_ones()
|
||||
}
|
||||
#[inline(always)]
|
||||
fn leading_zeros(&self) -> u32 {
|
||||
(*self).leading_zeros()
|
||||
}
|
||||
#[inline(always)]
|
||||
fn trailing_zeros(&self) -> u32 {
|
||||
(*self).trailing_zeros()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn and_not(&self, other: Self) -> Self {
|
||||
(*self) & !other
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn count_remaining_ones(&self, cursor: u32) -> usize {
|
||||
let left_mask = !((1 as $name)
|
||||
.checked_shl(cursor)
|
||||
.unwrap_or(0)
|
||||
.wrapping_sub(1));
|
||||
(*self & left_mask).count_ones() as usize
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn from_u8(v: u8) -> Self {
|
||||
v as $name
|
||||
}
|
||||
#[inline(always)]
|
||||
fn from_u16(v: u16) -> Self {
|
||||
v as $name
|
||||
}
|
||||
#[inline(always)]
|
||||
fn from_u32(v: u32) -> Self {
|
||||
v as $name
|
||||
}
|
||||
#[inline(always)]
|
||||
fn from_u64(v: u64) -> Self {
|
||||
v as $name
|
||||
}
|
||||
#[inline(always)]
|
||||
fn from_u128(v: u128) -> Self {
|
||||
v as $name
|
||||
}
|
||||
#[inline(always)]
|
||||
fn from_usize(v: usize) -> Self {
|
||||
v as $name
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn to_u8(&self) -> u8 {
|
||||
(*self) as u8
|
||||
}
|
||||
#[inline(always)]
|
||||
fn to_u16(&self) -> u16 {
|
||||
(*self) as u16
|
||||
}
|
||||
#[inline(always)]
|
||||
fn to_u32(&self) -> u32 {
|
||||
(*self) as u32
|
||||
}
|
||||
#[inline(always)]
|
||||
fn to_u64(&self) -> u64 {
|
||||
(*self) as u64
|
||||
}
|
||||
#[inline(always)]
|
||||
fn to_u128(&self) -> u128 {
|
||||
(*self) as u128
|
||||
}
|
||||
#[inline(always)]
|
||||
fn to_usize(&self) -> usize {
|
||||
(*self) as usize
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn from_u8_opt(v: u8) -> Option<Self> {
|
||||
v.try_into().ok()
|
||||
}
|
||||
#[inline(always)]
|
||||
fn from_u16_opt(v: u16) -> Option<Self> {
|
||||
v.try_into().ok()
|
||||
}
|
||||
#[inline(always)]
|
||||
fn from_u32_opt(v: u32) -> Option<Self> {
|
||||
v.try_into().ok()
|
||||
}
|
||||
#[inline(always)]
|
||||
fn from_u64_opt(v: u64) -> Option<Self> {
|
||||
v.try_into().ok()
|
||||
}
|
||||
#[inline(always)]
|
||||
fn from_u128_opt(v: u128) -> Option<Self> {
|
||||
v.try_into().ok()
|
||||
}
|
||||
#[inline(always)]
|
||||
fn from_usize_opt(v: usize) -> Option<Self> {
|
||||
v.try_into().ok()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn to_u8_opt(&self) -> Option<u8> {
|
||||
(*self).try_into().ok()
|
||||
}
|
||||
#[inline(always)]
|
||||
fn to_u16_opt(&self) -> Option<u16> {
|
||||
(*self).try_into().ok()
|
||||
}
|
||||
#[inline(always)]
|
||||
fn to_u32_opt(&self) -> Option<u32> {
|
||||
(*self).try_into().ok()
|
||||
}
|
||||
#[inline(always)]
|
||||
fn to_u64_opt(&self) -> Option<u64> {
|
||||
(*self).try_into().ok()
|
||||
}
|
||||
#[inline(always)]
|
||||
fn to_u128_opt(&self) -> Option<u128> {
|
||||
(*self).try_into().ok()
|
||||
}
|
||||
#[inline(always)]
|
||||
fn to_usize_opt(&self) -> Option<usize> {
|
||||
(*self).try_into().ok()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
prim!(u8, 8);
|
||||
prim!(u16, 16);
|
||||
prim!(u32, 32);
|
||||
prim!(u64, 64);
|
||||
prim!(u128, 128);
|
|
@ -0,0 +1,335 @@
|
|||
use crate::repr::primitive::PrimitiveIter;
|
||||
use crate::repr::EnumSetTypeRepr;
|
||||
use core::ops::*;
|
||||
|
||||
/// An implementation of `EnumSetTypeRepr` based on an arbitrary size array.
|
||||
///
|
||||
/// `N` **must** not be `0`, or else everything will break.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)]
|
||||
pub struct ArrayRepr<const N: usize>(pub [u64; N]);
|
||||
impl<const N: usize> ArrayRepr<N> {
|
||||
fn split_bit(bit: u32) -> (usize, u32) {
|
||||
(bit as usize / 64, bit % 64)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> BitAnd for ArrayRepr<N> {
|
||||
type Output = Self;
|
||||
fn bitand(mut self, rhs: Self) -> Self::Output {
|
||||
for i in 0..N {
|
||||
self.0[i] &= rhs.0[i];
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
impl<const N: usize> BitOr for ArrayRepr<N> {
|
||||
type Output = Self;
|
||||
fn bitor(mut self, rhs: Self) -> Self::Output {
|
||||
for i in 0..N {
|
||||
self.0[i] |= rhs.0[i];
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
impl<const N: usize> BitXor for ArrayRepr<N> {
|
||||
type Output = Self;
|
||||
fn bitxor(mut self, rhs: Self) -> Self::Output {
|
||||
for i in 0..N {
|
||||
self.0[i] ^= rhs.0[i];
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
impl<const N: usize> Not for ArrayRepr<N> {
|
||||
type Output = Self;
|
||||
fn not(mut self) -> Self::Output {
|
||||
for i in 0..N {
|
||||
self.0[i] = !self.0[i];
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> EnumSetTypeRepr for ArrayRepr<N> {
|
||||
const PREFERRED_ARRAY_LEN: usize = N;
|
||||
const WIDTH: u32 = N as u32 * 64;
|
||||
const EMPTY: Self = ArrayRepr([0; N]);
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
self.0.iter().all(|x| *x == 0)
|
||||
}
|
||||
|
||||
fn add_bit(&mut self, bit: u32) {
|
||||
let (idx, bit) = Self::split_bit(bit);
|
||||
self.0[idx].add_bit(bit);
|
||||
}
|
||||
fn remove_bit(&mut self, bit: u32) {
|
||||
let (idx, bit) = Self::split_bit(bit);
|
||||
self.0[idx].remove_bit(bit);
|
||||
}
|
||||
fn has_bit(&self, bit: u32) -> bool {
|
||||
let (idx, bit) = Self::split_bit(bit);
|
||||
self.0[idx].has_bit(bit)
|
||||
}
|
||||
|
||||
fn count_ones(&self) -> u32 {
|
||||
self.0.iter().map(|x| x.count_ones()).sum()
|
||||
}
|
||||
fn leading_zeros(&self) -> u32 {
|
||||
let mut accum = 0;
|
||||
for i in (0..N).rev() {
|
||||
if self.0[i] != 0 {
|
||||
return accum + self.0[i].leading_zeros();
|
||||
}
|
||||
accum += 64;
|
||||
}
|
||||
Self::WIDTH
|
||||
}
|
||||
fn trailing_zeros(&self) -> u32 {
|
||||
let mut accum = 0;
|
||||
for i in 0..N {
|
||||
if self.0[i] != 0 {
|
||||
return accum + self.0[i].trailing_zeros();
|
||||
}
|
||||
accum += 64;
|
||||
}
|
||||
Self::WIDTH
|
||||
}
|
||||
|
||||
fn and_not(&self, other: Self) -> Self {
|
||||
let mut new = Self([0; N]);
|
||||
for i in 0..N {
|
||||
new.0[i] = self.0[i] & !other.0[i];
|
||||
}
|
||||
new
|
||||
}
|
||||
|
||||
type Iter = ArrayIter<N>;
|
||||
fn iter(self) -> Self::Iter {
|
||||
ArrayIter::new(self)
|
||||
}
|
||||
|
||||
fn from_u8(v: u8) -> Self {
|
||||
Self::from_u64(v as u64)
|
||||
}
|
||||
fn from_u16(v: u16) -> Self {
|
||||
Self::from_u64(v as u64)
|
||||
}
|
||||
fn from_u32(v: u32) -> Self {
|
||||
Self::from_u64(v as u64)
|
||||
}
|
||||
fn from_u64(v: u64) -> Self {
|
||||
let mut new = Self([0; N]);
|
||||
new.0[0] = v;
|
||||
new
|
||||
}
|
||||
fn from_u128(v: u128) -> Self {
|
||||
let mut new = Self([0; N]);
|
||||
new.0[0] = v as u64;
|
||||
if N != 1 {
|
||||
new.0[1] = (v >> 64) as u64;
|
||||
}
|
||||
new
|
||||
}
|
||||
fn from_usize(v: usize) -> Self {
|
||||
Self::from_u64(v as u64)
|
||||
}
|
||||
|
||||
fn from_u8_opt(v: u8) -> Option<Self> {
|
||||
Some(Self::from_u8(v))
|
||||
}
|
||||
fn from_u16_opt(v: u16) -> Option<Self> {
|
||||
Some(Self::from_u16(v))
|
||||
}
|
||||
fn from_u32_opt(v: u32) -> Option<Self> {
|
||||
Some(Self::from_u32(v))
|
||||
}
|
||||
fn from_u64_opt(v: u64) -> Option<Self> {
|
||||
Some(Self::from_u64(v))
|
||||
}
|
||||
fn from_u128_opt(v: u128) -> Option<Self> {
|
||||
if N == 1 && (v >> 64) != 0 {
|
||||
None
|
||||
} else {
|
||||
Some(Self::from_u128(v))
|
||||
}
|
||||
}
|
||||
fn from_usize_opt(v: usize) -> Option<Self> {
|
||||
Some(Self::from_usize(v))
|
||||
}
|
||||
|
||||
fn to_u8(&self) -> u8 {
|
||||
self.to_u64().to_u8()
|
||||
}
|
||||
fn to_u16(&self) -> u16 {
|
||||
self.to_u64().to_u16()
|
||||
}
|
||||
fn to_u32(&self) -> u32 {
|
||||
self.to_u64().to_u32()
|
||||
}
|
||||
fn to_u64(&self) -> u64 {
|
||||
self.0[0]
|
||||
}
|
||||
fn to_u128(&self) -> u128 {
|
||||
let hi = if N == 1 { 0 } else { (self.0[1] as u128) << 64 };
|
||||
self.0[0] as u128 | hi
|
||||
}
|
||||
fn to_usize(&self) -> usize {
|
||||
self.to_u64().to_usize()
|
||||
}
|
||||
|
||||
fn to_u8_opt(&self) -> Option<u8> {
|
||||
self.to_u64_opt().and_then(|x| x.to_u8_opt())
|
||||
}
|
||||
fn to_u16_opt(&self) -> Option<u16> {
|
||||
self.to_u64_opt().and_then(|x| x.to_u16_opt())
|
||||
}
|
||||
fn to_u32_opt(&self) -> Option<u32> {
|
||||
self.to_u64_opt().and_then(|x| x.to_u32_opt())
|
||||
}
|
||||
fn to_u64_opt(&self) -> Option<u64> {
|
||||
for i in 1..N {
|
||||
if self.0[i] != 0 {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
Some(self.to_u64())
|
||||
}
|
||||
fn to_u128_opt(&self) -> Option<u128> {
|
||||
for i in 2..N {
|
||||
if self.0[i] != 0 {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
Some(self.to_u128())
|
||||
}
|
||||
fn to_usize_opt(&self) -> Option<usize> {
|
||||
self.to_u64_opt().and_then(|x| x.to_usize_opt())
|
||||
}
|
||||
|
||||
fn to_u64_array<const O: usize>(&self) -> [u64; O] {
|
||||
let mut array = [0; O];
|
||||
let copy_len = if N < O { N } else { O };
|
||||
array[..copy_len].copy_from_slice(&self.0[..copy_len]);
|
||||
array
|
||||
}
|
||||
fn to_u64_array_opt<const O: usize>(&self) -> Option<[u64; O]> {
|
||||
if N > O {
|
||||
for i in O..N {
|
||||
if self.0[i] != 0 {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(self.to_u64_array())
|
||||
}
|
||||
|
||||
fn from_u64_array<const O: usize>(v: [u64; O]) -> Self {
|
||||
ArrayRepr(ArrayRepr::<O>(v).to_u64_array::<N>())
|
||||
}
|
||||
fn from_u64_array_opt<const O: usize>(v: [u64; O]) -> Option<Self> {
|
||||
ArrayRepr::<O>(v).to_u64_array_opt::<N>().map(ArrayRepr)
|
||||
}
|
||||
|
||||
fn to_u64_slice(&self, out: &mut [u64]) {
|
||||
let copy_len = if N < out.len() { N } else { out.len() };
|
||||
out[..copy_len].copy_from_slice(&self.0[..copy_len]);
|
||||
for i in copy_len..out.len() {
|
||||
out[i] = 0;
|
||||
}
|
||||
}
|
||||
#[must_use]
|
||||
fn to_u64_slice_opt(&self, out: &mut [u64]) -> Option<()> {
|
||||
if N > out.len() {
|
||||
for i in out.len()..N {
|
||||
if self.0[i] != 0 {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.to_u64_slice(out);
|
||||
Some(())
|
||||
}
|
||||
|
||||
fn from_u64_slice(v: &[u64]) -> Self {
|
||||
let mut new = ArrayRepr([0; N]);
|
||||
let copy_len = if N < v.len() { N } else { v.len() };
|
||||
new.0[..copy_len].copy_from_slice(&v[..copy_len]);
|
||||
new
|
||||
}
|
||||
fn from_u64_slice_opt(v: &[u64]) -> Option<Self> {
|
||||
if v.len() > N {
|
||||
for i in N..v.len() {
|
||||
if v[i] != 0 {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(Self::from_u64_slice(v))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ArrayIter<const N: usize> {
|
||||
data: [PrimitiveIter<u64>; N],
|
||||
done: bool,
|
||||
idx_f: usize,
|
||||
idx_r: usize,
|
||||
}
|
||||
|
||||
impl<const N: usize> ArrayIter<N> {
|
||||
pub fn new(array: ArrayRepr<N>) -> Self {
|
||||
let mut new = [PrimitiveIter(0); N];
|
||||
for i in 0..N {
|
||||
new[i] = PrimitiveIter(array.0[i])
|
||||
}
|
||||
ArrayIter { data: new, done: false, idx_f: 0, idx_r: N - 1 }
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Iterator for ArrayIter<N> {
|
||||
type Item = u32;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.done {
|
||||
return None;
|
||||
}
|
||||
while self.idx_f <= self.idx_r {
|
||||
if let Some(x) = self.data[self.idx_f].next() {
|
||||
return Some(self.idx_f as u32 * 64 + x);
|
||||
} else {
|
||||
self.idx_f += 1;
|
||||
}
|
||||
}
|
||||
self.done = true;
|
||||
None
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let mut sum = 0;
|
||||
for i in self.idx_f..self.idx_r + 1 {
|
||||
sum += self.data[i].0.count_ones() as usize;
|
||||
}
|
||||
(sum, Some(sum))
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> DoubleEndedIterator for ArrayIter<N> {
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
if self.done {
|
||||
return None;
|
||||
}
|
||||
while self.idx_f <= self.idx_r {
|
||||
if let Some(x) = self.data[self.idx_r].next_back() {
|
||||
return Some(self.idx_r as u32 * 64 + x);
|
||||
} else {
|
||||
if self.idx_r == 0 {
|
||||
break;
|
||||
}
|
||||
self.idx_r -= 1;
|
||||
}
|
||||
}
|
||||
self.done = true;
|
||||
None
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
#![allow(missing_docs)]
|
||||
|
||||
mod array;
|
||||
mod primitive;
|
||||
|
||||
use core::fmt::Debug;
|
||||
use core::hash::Hash;
|
||||
use core::ops::*;
|
||||
|
||||
/// A trait marking valid underlying bitset storage types and providing the
|
||||
/// operations `EnumSet` and related types use.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Note that `iter` *MUST* be implemented correctly and only return bits that
|
||||
/// are actually set in the representation, or else it will cause undefined
|
||||
/// behavior upstream in `EnumSet`.
|
||||
pub trait EnumSetTypeRepr :
|
||||
// Basic traits used to derive traits
|
||||
Copy +
|
||||
Ord +
|
||||
Eq +
|
||||
Debug +
|
||||
Hash +
|
||||
// Operations used by enumset
|
||||
BitAnd<Output = Self> +
|
||||
BitOr<Output = Self> +
|
||||
BitXor<Output = Self> +
|
||||
Not<Output = Self> +
|
||||
{
|
||||
const PREFERRED_ARRAY_LEN: usize;
|
||||
const WIDTH: u32;
|
||||
const EMPTY: Self;
|
||||
|
||||
fn is_empty(&self) -> bool;
|
||||
|
||||
fn add_bit(&mut self, bit: u32);
|
||||
fn remove_bit(&mut self, bit: u32);
|
||||
fn has_bit(&self, bit: u32) -> bool;
|
||||
|
||||
fn count_ones(&self) -> u32;
|
||||
fn leading_zeros(&self) -> u32;
|
||||
fn trailing_zeros(&self) -> u32;
|
||||
|
||||
fn and_not(&self, other: Self) -> Self;
|
||||
|
||||
type Iter: Iterator<Item = u32> + DoubleEndedIterator + Clone + Debug;
|
||||
fn iter(self) -> Self::Iter;
|
||||
|
||||
fn from_u8(v: u8) -> Self;
|
||||
fn from_u16(v: u16) -> Self;
|
||||
fn from_u32(v: u32) -> Self;
|
||||
fn from_u64(v: u64) -> Self;
|
||||
fn from_u128(v: u128) -> Self;
|
||||
fn from_usize(v: usize) -> Self;
|
||||
|
||||
fn to_u8(&self) -> u8;
|
||||
fn to_u16(&self) -> u16;
|
||||
fn to_u32(&self) -> u32;
|
||||
fn to_u64(&self) -> u64;
|
||||
fn to_u128(&self) -> u128;
|
||||
fn to_usize(&self) -> usize;
|
||||
|
||||
fn from_u8_opt(v: u8) -> Option<Self>;
|
||||
fn from_u16_opt(v: u16) -> Option<Self>;
|
||||
fn from_u32_opt(v: u32) -> Option<Self>;
|
||||
fn from_u64_opt(v: u64) -> Option<Self>;
|
||||
fn from_u128_opt(v: u128) -> Option<Self>;
|
||||
fn from_usize_opt(v: usize) -> Option<Self>;
|
||||
|
||||
fn to_u8_opt(&self) -> Option<u8>;
|
||||
fn to_u16_opt(&self) -> Option<u16>;
|
||||
fn to_u32_opt(&self) -> Option<u32>;
|
||||
fn to_u64_opt(&self) -> Option<u64>;
|
||||
fn to_u128_opt(&self) -> Option<u128>;
|
||||
fn to_usize_opt(&self) -> Option<usize>;
|
||||
|
||||
fn to_u64_array<const O: usize>(&self) -> [u64; O];
|
||||
fn to_u64_array_opt<const O: usize>(&self) -> Option<[u64; O]>;
|
||||
|
||||
fn from_u64_array<const O: usize>(v: [u64; O]) -> Self;
|
||||
fn from_u64_array_opt<const O: usize>(v: [u64; O]) -> Option<Self>;
|
||||
|
||||
fn to_u64_slice(&self, out: &mut [u64]);
|
||||
#[must_use]
|
||||
fn to_u64_slice_opt(&self, out: &mut [u64]) -> Option<()>;
|
||||
|
||||
fn from_u64_slice(v: &[u64]) -> Self;
|
||||
fn from_u64_slice_opt(v: &[u64]) -> Option<Self>;
|
||||
}
|
||||
|
||||
pub use array::ArrayRepr;
|
|
@ -0,0 +1,300 @@
|
|||
use crate::repr::EnumSetTypeRepr;
|
||||
|
||||
macro_rules! prim {
|
||||
($name:ty, $width:expr, $preferred_array_len:expr) => {
|
||||
const _: () = {
|
||||
fn lo(v: $name) -> u64 {
|
||||
v as u64
|
||||
}
|
||||
fn hi(v: $name) -> u64 {
|
||||
((v as u128) >> 64) as u64
|
||||
}
|
||||
|
||||
impl EnumSetTypeRepr for $name {
|
||||
const PREFERRED_ARRAY_LEN: usize = $preferred_array_len;
|
||||
const WIDTH: u32 = $width;
|
||||
const EMPTY: Self = 0;
|
||||
|
||||
#[inline(always)]
|
||||
fn is_empty(&self) -> bool {
|
||||
*self == 0
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn add_bit(&mut self, bit: u32) {
|
||||
*self |= 1 << bit as $name;
|
||||
}
|
||||
#[inline(always)]
|
||||
fn remove_bit(&mut self, bit: u32) {
|
||||
*self &= !(1 << bit as $name);
|
||||
}
|
||||
#[inline(always)]
|
||||
fn has_bit(&self, bit: u32) -> bool {
|
||||
(self & (1 << bit as $name)) != 0
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn count_ones(&self) -> u32 {
|
||||
(*self).count_ones()
|
||||
}
|
||||
#[inline(always)]
|
||||
fn leading_zeros(&self) -> u32 {
|
||||
(*self).leading_zeros()
|
||||
}
|
||||
#[inline(always)]
|
||||
fn trailing_zeros(&self) -> u32 {
|
||||
(*self).trailing_zeros()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn and_not(&self, other: Self) -> Self {
|
||||
(*self) & !other
|
||||
}
|
||||
|
||||
type Iter = PrimitiveIter<Self>;
|
||||
fn iter(self) -> Self::Iter {
|
||||
PrimitiveIter(self)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn from_u8(v: u8) -> Self {
|
||||
v as $name
|
||||
}
|
||||
#[inline(always)]
|
||||
fn from_u16(v: u16) -> Self {
|
||||
v as $name
|
||||
}
|
||||
#[inline(always)]
|
||||
fn from_u32(v: u32) -> Self {
|
||||
v as $name
|
||||
}
|
||||
#[inline(always)]
|
||||
fn from_u64(v: u64) -> Self {
|
||||
v as $name
|
||||
}
|
||||
#[inline(always)]
|
||||
fn from_u128(v: u128) -> Self {
|
||||
v as $name
|
||||
}
|
||||
#[inline(always)]
|
||||
fn from_usize(v: usize) -> Self {
|
||||
v as $name
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn to_u8(&self) -> u8 {
|
||||
(*self) as u8
|
||||
}
|
||||
#[inline(always)]
|
||||
fn to_u16(&self) -> u16 {
|
||||
(*self) as u16
|
||||
}
|
||||
#[inline(always)]
|
||||
fn to_u32(&self) -> u32 {
|
||||
(*self) as u32
|
||||
}
|
||||
#[inline(always)]
|
||||
fn to_u64(&self) -> u64 {
|
||||
(*self) as u64
|
||||
}
|
||||
#[inline(always)]
|
||||
fn to_u128(&self) -> u128 {
|
||||
(*self) as u128
|
||||
}
|
||||
#[inline(always)]
|
||||
fn to_usize(&self) -> usize {
|
||||
(*self) as usize
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn from_u8_opt(v: u8) -> Option<Self> {
|
||||
v.try_into().ok()
|
||||
}
|
||||
#[inline(always)]
|
||||
fn from_u16_opt(v: u16) -> Option<Self> {
|
||||
v.try_into().ok()
|
||||
}
|
||||
#[inline(always)]
|
||||
fn from_u32_opt(v: u32) -> Option<Self> {
|
||||
v.try_into().ok()
|
||||
}
|
||||
#[inline(always)]
|
||||
fn from_u64_opt(v: u64) -> Option<Self> {
|
||||
v.try_into().ok()
|
||||
}
|
||||
#[inline(always)]
|
||||
fn from_u128_opt(v: u128) -> Option<Self> {
|
||||
v.try_into().ok()
|
||||
}
|
||||
#[inline(always)]
|
||||
fn from_usize_opt(v: usize) -> Option<Self> {
|
||||
v.try_into().ok()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn to_u8_opt(&self) -> Option<u8> {
|
||||
(*self).try_into().ok()
|
||||
}
|
||||
#[inline(always)]
|
||||
fn to_u16_opt(&self) -> Option<u16> {
|
||||
(*self).try_into().ok()
|
||||
}
|
||||
#[inline(always)]
|
||||
fn to_u32_opt(&self) -> Option<u32> {
|
||||
(*self).try_into().ok()
|
||||
}
|
||||
#[inline(always)]
|
||||
fn to_u64_opt(&self) -> Option<u64> {
|
||||
(*self).try_into().ok()
|
||||
}
|
||||
#[inline(always)]
|
||||
fn to_u128_opt(&self) -> Option<u128> {
|
||||
(*self).try_into().ok()
|
||||
}
|
||||
#[inline(always)]
|
||||
fn to_usize_opt(&self) -> Option<usize> {
|
||||
(*self).try_into().ok()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn to_u64_array<const O: usize>(&self) -> [u64; O] {
|
||||
let mut array = [0; O];
|
||||
if O > 0 {
|
||||
array[0] = lo(*self);
|
||||
}
|
||||
if O > 1 && $preferred_array_len == 2 {
|
||||
array[1] = hi(*self);
|
||||
}
|
||||
array
|
||||
}
|
||||
#[inline(always)]
|
||||
fn to_u64_array_opt<const O: usize>(&self) -> Option<[u64; O]> {
|
||||
if O == 0 && *self != 0 {
|
||||
None
|
||||
} else if O == 1 && hi(*self) != 0 {
|
||||
None
|
||||
} else {
|
||||
Some(self.to_u64_array())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn from_u64_array<const O: usize>(v: [u64; O]) -> Self {
|
||||
if O == 0 {
|
||||
0
|
||||
} else if O > 1 && $preferred_array_len == 2 {
|
||||
Self::from_u128(v[0] as u128 | ((v[1] as u128) << 64))
|
||||
} else {
|
||||
Self::from_u64(v[0])
|
||||
}
|
||||
}
|
||||
#[inline(always)]
|
||||
fn from_u64_array_opt<const O: usize>(v: [u64; O]) -> Option<Self> {
|
||||
if O == 0 {
|
||||
Some(0)
|
||||
} else if O == 1 {
|
||||
Self::from_u64_opt(v[0])
|
||||
} else {
|
||||
for i in 2..O {
|
||||
if v[i] != 0 {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
Self::from_u128_opt(v[0] as u128 | ((v[1] as u128) << 64))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn to_u64_slice(&self, out: &mut [u64]) {
|
||||
if out.len() > 0 {
|
||||
out[0] = lo(*self);
|
||||
}
|
||||
if out.len() > 1 && $preferred_array_len == 2 {
|
||||
out[1] = hi(*self);
|
||||
}
|
||||
for i in $preferred_array_len..out.len() {
|
||||
out[i] = 0;
|
||||
}
|
||||
}
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
fn to_u64_slice_opt(&self, out: &mut [u64]) -> Option<()> {
|
||||
if out.len() == 0 && *self != 0 {
|
||||
None
|
||||
} else if out.len() == 1 && hi(*self) != 0 {
|
||||
None
|
||||
} else {
|
||||
self.to_u64_slice(out);
|
||||
Some(())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn from_u64_slice(v: &[u64]) -> Self {
|
||||
if v.len() == 0 {
|
||||
0
|
||||
} else if v.len() > 1 && $preferred_array_len == 2 {
|
||||
Self::from_u128(v[0] as u128 | ((v[1] as u128) << 64))
|
||||
} else {
|
||||
Self::from_u64(v[0])
|
||||
}
|
||||
}
|
||||
#[inline(always)]
|
||||
fn from_u64_slice_opt(v: &[u64]) -> Option<Self> {
|
||||
if v.len() == 0 {
|
||||
Some(0)
|
||||
} else if v.len() == 1 {
|
||||
Self::from_u64_opt(v[0])
|
||||
} else {
|
||||
for i in 2..v.len() {
|
||||
if v[i] != 0 {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
Self::from_u128_opt(v[0] as u128 | ((v[1] as u128) << 64))
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
prim!(u8, 8, 1);
|
||||
prim!(u16, 16, 1);
|
||||
prim!(u32, 32, 1);
|
||||
prim!(u64, 64, 1);
|
||||
prim!(u128, 128, 2);
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(transparent)]
|
||||
pub struct PrimitiveIter<T: EnumSetTypeRepr>(pub T);
|
||||
|
||||
impl<T: EnumSetTypeRepr> Iterator for PrimitiveIter<T> {
|
||||
type Item = u32;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.0.is_empty() {
|
||||
None
|
||||
} else {
|
||||
let bit = self.0.trailing_zeros();
|
||||
self.0.remove_bit(bit);
|
||||
Some(bit)
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let left = self.0.count_ones() as usize;
|
||||
(left, Some(left))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EnumSetTypeRepr> DoubleEndedIterator for PrimitiveIter<T> {
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
if self.0.is_empty() {
|
||||
None
|
||||
} else {
|
||||
let bit = T::WIDTH - 1 - self.0.leading_zeros();
|
||||
self.0.remove_bit(bit);
|
||||
Some(bit)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,856 @@
|
|||
use crate::repr::EnumSetTypeRepr;
|
||||
use crate::traits::EnumSetType;
|
||||
use crate::EnumSetTypeWithRepr;
|
||||
use core::cmp::Ordering;
|
||||
use core::fmt::{Debug, Formatter};
|
||||
use core::hash::{Hash, Hasher};
|
||||
use core::iter::Sum;
|
||||
use core::ops::{
|
||||
BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not, Sub, SubAssign,
|
||||
};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use {
|
||||
serde2 as serde,
|
||||
serde2::{Deserialize, Serialize},
|
||||
};
|
||||
|
||||
/// An efficient set type for enums.
|
||||
///
|
||||
/// It is implemented using a bitset stored using the smallest integer that can fit all bits
|
||||
/// in the underlying enum. In general, an enum variant with a discriminator of `n` is stored in
|
||||
/// the nth least significant bit (corresponding with a mask of, e.g. `1 << enum as u32`).
|
||||
///
|
||||
/// # Numeric representation
|
||||
///
|
||||
/// `EnumSet` is internally implemented using integer types, and as such can be easily converted
|
||||
/// from and to numbers.
|
||||
///
|
||||
/// Each bit of the underlying integer corresponds to at most one particular enum variant. If the
|
||||
/// corresponding bit for a variant is set, it present in the set. Bits that do not correspond to
|
||||
/// any variant are always unset.
|
||||
///
|
||||
/// By default, each enum variant is stored in a bit corresponding to its discriminator. An enum
|
||||
/// variant with a discriminator of `n` is stored in the `n + 1`th least significant bit
|
||||
/// (corresponding to a mask of e.g. `1 << enum as u32`).
|
||||
///
|
||||
/// # Array representation
|
||||
///
|
||||
/// Sets with more than 128 variants are instead stored with an underlying array of `u64`s. This
|
||||
/// is treated as if it was a single large integer. The `n`th least significant bit of this integer
|
||||
/// is stored in the `n % 64`th least significant bit of the `n / 64`th element in the array.
|
||||
///
|
||||
/// # Serialization
|
||||
///
|
||||
/// When the `serde` feature is enabled, `EnumSet`s can be serialized and deserialized using
|
||||
/// the `serde` crate. The exact serialization format can be controlled with additional attributes
|
||||
/// on the enum type. These attributes are valid regardless of whether the `serde` feature
|
||||
/// is enabled.
|
||||
///
|
||||
/// By default, `EnumSet` is serialized by directly writing out a single integer containing the
|
||||
/// numeric representation of the bitset. The integer type used is the smallest one that can fit
|
||||
/// the largest variant in the enum. If no integer type is large enough, instead the `EnumSet` is
|
||||
/// serialized as an array of `u64`s containing the array representation.
|
||||
///
|
||||
/// The `#[enumset(serialize_repr = "…")]` attribute can be used to override the representation
|
||||
/// used. Valid values are as follows:
|
||||
///
|
||||
/// * `u8`, `u16`, `u32`, `u64`, and `u128` serialize the type as the corresponding integer type.
|
||||
/// * `array` serializes the set as an list of `u64`s corresponding to the array representation.
|
||||
/// * `list` serializes the set as a list of enum variants. This requires your enum type implement
|
||||
/// [`Serialize`] and [`Deserialize`].
|
||||
/// * `map` serializes the set as a map of enum variants to booleans. The set contains a value if
|
||||
/// the boolean is `true`. This requires your enum type implement `Serialize` and `Deserialize`.
|
||||
///
|
||||
/// The representation used is determined statically at compile time, and there is currently no
|
||||
/// support for reading different formats with the same deserializer.
|
||||
///
|
||||
/// By default, unknown bits are ignored and silently removed from the bitset. To override this
|
||||
/// behavior, you can add a `#[enumset(serialize_deny_unknown)]` attribute. This will cause
|
||||
/// deserialization to fail if an invalid bit is set.
|
||||
///
|
||||
/// # FFI, Safety and `repr`
|
||||
///
|
||||
/// If an enum type `T` is annotated with
|
||||
/// [`#[enumset(repr = "…")]`](derive@crate::EnumSetType#options) where `…` is a primitive integer
|
||||
/// type, then several things happen:
|
||||
///
|
||||
/// * `T` will implement
|
||||
/// <code>[EnumSetTypeWithRepr](crate::traits::EnumSetTypeWithRepr)<Repr = R></code> in
|
||||
/// addition to [`EnumSetType`].
|
||||
/// * The `EnumSet` methods with `repr` in their name, such as [`as_repr`][EnumSet::as_repr] and
|
||||
/// [`from_repr`][EnumSet::from_repr], will be available for `EnumSet<T>`.
|
||||
/// * The in-memory representation of `EnumSet<T>` is guaranteed to be `R`.
|
||||
///
|
||||
/// That last guarantee makes it sound to send `EnumSet<T>` across an FFI boundary. For example:
|
||||
///
|
||||
/// ```
|
||||
/// # use enumset::*;
|
||||
/// #
|
||||
/// # mod ffi_impl {
|
||||
/// # // This example “foreign” function is actually written in Rust, but for the sake
|
||||
/// # // of example, we'll pretend it's written in C.
|
||||
/// # #[no_mangle]
|
||||
/// # extern "C" fn some_foreign_function(set: u32) -> u32 {
|
||||
/// # set & 0b100
|
||||
/// # }
|
||||
/// # }
|
||||
/// #
|
||||
/// extern "C" {
|
||||
/// // This function is written in C like:
|
||||
/// // uint32_t some_foreign_function(uint32_t set) { … }
|
||||
/// fn some_foreign_function(set: EnumSet<MyEnum>) -> EnumSet<MyEnum>;
|
||||
/// }
|
||||
///
|
||||
/// #[derive(Debug, EnumSetType)]
|
||||
/// #[enumset(repr = "u32")]
|
||||
/// enum MyEnum { A, B, C }
|
||||
///
|
||||
/// let set: EnumSet<MyEnum> = enum_set!(MyEnum::A | MyEnum::C);
|
||||
///
|
||||
/// let new_set: EnumSet<MyEnum> = unsafe { some_foreign_function(set) };
|
||||
/// assert_eq!(new_set, enum_set!(MyEnum::C));
|
||||
/// ```
|
||||
///
|
||||
/// When an `EnumSet<T>` is received via FFI, all bits that don't correspond to an enum variant
|
||||
/// of `T` must be set to `0`. Behavior is **undefined** if any of these bits are set to `1`.
|
||||
#[cfg_attr(
|
||||
not(feature = "serde"),
|
||||
doc = "\n\n",
|
||||
doc = "[`Serialize`]: https://docs.rs/serde/latest/serde/trait.Serialize.html\n",
|
||||
doc = "[`Deserialize`]: https://docs.rs/serde/latest/serde/trait.Deserialize.html\n"
|
||||
)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
#[repr(transparent)]
|
||||
pub struct EnumSet<T: EnumSetType> {
|
||||
#[doc(hidden)]
|
||||
/// This is public due to the `enum_set!` macro.
|
||||
/// This is **NOT** public API and may change at any time.
|
||||
pub __priv_repr: T::Repr,
|
||||
}
|
||||
|
||||
//region EnumSet operations
|
||||
impl<T: EnumSetType> EnumSet<T> {
|
||||
/// An empty `EnumSet`.
|
||||
///
|
||||
/// This is available as a constant for use in constant expressions.
|
||||
pub const EMPTY: Self = EnumSet { __priv_repr: T::Repr::EMPTY };
|
||||
|
||||
/// An `EnumSet` containing all valid variants of the enum.
|
||||
///
|
||||
/// This is available as a constant for use in constant expressions.
|
||||
pub const ALL: Self = EnumSet { __priv_repr: T::ALL_BITS };
|
||||
|
||||
/// Creates an empty `EnumSet`.
|
||||
#[inline(always)]
|
||||
pub fn new() -> Self {
|
||||
Self::EMPTY
|
||||
}
|
||||
|
||||
/// Returns an `EnumSet` containing a single element.
|
||||
#[inline(always)]
|
||||
pub fn only(t: T) -> Self {
|
||||
let mut set = Self::new();
|
||||
set.insert(t);
|
||||
set
|
||||
}
|
||||
|
||||
/// Creates an empty `EnumSet`.
|
||||
///
|
||||
/// This is an alias for [`EnumSet::new`].
|
||||
#[inline(always)]
|
||||
pub fn empty() -> Self {
|
||||
Self::EMPTY
|
||||
}
|
||||
|
||||
/// Returns an `EnumSet` containing all valid variants of the enum.
|
||||
#[inline(always)]
|
||||
pub fn all() -> Self {
|
||||
Self::ALL
|
||||
}
|
||||
|
||||
/// Total number of bits used by this type. Note that the actual amount of space used is
|
||||
/// rounded up to the next highest integer type (`u8`, `u16`, `u32`, `u64`, or `u128`).
|
||||
///
|
||||
/// This is the same as [`EnumSet::variant_count`] except in enums with "sparse" variants.
|
||||
/// (e.g. `enum Foo { A = 10, B = 20 }`)
|
||||
#[inline(always)]
|
||||
pub fn bit_width() -> u32 {
|
||||
T::BIT_WIDTH
|
||||
}
|
||||
|
||||
/// The number of valid variants that this type can contain.
|
||||
///
|
||||
/// This is the same as [`EnumSet::bit_width`] except in enums with "sparse" variants.
|
||||
/// (e.g. `enum Foo { A = 10, B = 20 }`)
|
||||
#[inline(always)]
|
||||
pub fn variant_count() -> u32 {
|
||||
T::VARIANT_COUNT
|
||||
}
|
||||
|
||||
/// Returns the number of elements in this set.
|
||||
#[inline(always)]
|
||||
pub fn len(&self) -> usize {
|
||||
self.__priv_repr.count_ones() as usize
|
||||
}
|
||||
/// Returns `true` if the set contains no elements.
|
||||
#[inline(always)]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.__priv_repr.is_empty()
|
||||
}
|
||||
/// Removes all elements from the set.
|
||||
#[inline(always)]
|
||||
pub fn clear(&mut self) {
|
||||
self.__priv_repr = T::Repr::EMPTY;
|
||||
}
|
||||
|
||||
/// Returns `true` if `self` has no elements in common with `other`. This is equivalent to
|
||||
/// checking for an empty intersection.
|
||||
#[inline(always)]
|
||||
pub fn is_disjoint(&self, other: Self) -> bool {
|
||||
(*self & other).is_empty()
|
||||
}
|
||||
/// Returns `true` if the set is a superset of another, i.e., `self` contains at least all the
|
||||
/// values in `other`.
|
||||
#[inline(always)]
|
||||
pub fn is_superset(&self, other: Self) -> bool {
|
||||
(*self & other).__priv_repr == other.__priv_repr
|
||||
}
|
||||
/// Returns `true` if the set is a subset of another, i.e., `other` contains at least all
|
||||
/// the values in `self`.
|
||||
#[inline(always)]
|
||||
pub fn is_subset(&self, other: Self) -> bool {
|
||||
other.is_superset(*self)
|
||||
}
|
||||
|
||||
/// Returns a set containing any elements present in either set.
|
||||
#[inline(always)]
|
||||
pub fn union(&self, other: Self) -> Self {
|
||||
EnumSet { __priv_repr: self.__priv_repr | other.__priv_repr }
|
||||
}
|
||||
/// Returns a set containing every element present in both sets.
|
||||
#[inline(always)]
|
||||
pub fn intersection(&self, other: Self) -> Self {
|
||||
EnumSet { __priv_repr: self.__priv_repr & other.__priv_repr }
|
||||
}
|
||||
/// Returns a set containing element present in `self` but not in `other`.
|
||||
#[inline(always)]
|
||||
pub fn difference(&self, other: Self) -> Self {
|
||||
EnumSet { __priv_repr: self.__priv_repr.and_not(other.__priv_repr) }
|
||||
}
|
||||
/// Returns a set containing every element present in either `self` or `other`, but is not
|
||||
/// present in both.
|
||||
#[inline(always)]
|
||||
pub fn symmetrical_difference(&self, other: Self) -> Self {
|
||||
EnumSet { __priv_repr: self.__priv_repr ^ other.__priv_repr }
|
||||
}
|
||||
/// Returns a set containing all enum variants not in this set.
|
||||
#[inline(always)]
|
||||
pub fn complement(&self) -> Self {
|
||||
EnumSet { __priv_repr: !self.__priv_repr & T::ALL_BITS }
|
||||
}
|
||||
|
||||
/// Checks whether this set contains a value.
|
||||
#[inline(always)]
|
||||
pub fn contains(&self, value: T) -> bool {
|
||||
self.__priv_repr.has_bit(value.enum_into_u32())
|
||||
}
|
||||
|
||||
/// Adds a value to this set.
|
||||
///
|
||||
/// If the set did not have this value present, `true` is returned.
|
||||
///
|
||||
/// If the set did have this value present, `false` is returned.
|
||||
#[inline(always)]
|
||||
pub fn insert(&mut self, value: T) -> bool {
|
||||
let contains = !self.contains(value);
|
||||
self.__priv_repr.add_bit(value.enum_into_u32());
|
||||
contains
|
||||
}
|
||||
/// Removes a value from this set. Returns whether the value was present in the set.
|
||||
#[inline(always)]
|
||||
pub fn remove(&mut self, value: T) -> bool {
|
||||
let contains = self.contains(value);
|
||||
self.__priv_repr.remove_bit(value.enum_into_u32());
|
||||
contains
|
||||
}
|
||||
|
||||
/// Adds all elements in another set to this one.
|
||||
#[inline(always)]
|
||||
pub fn insert_all(&mut self, other: Self) {
|
||||
self.__priv_repr = self.__priv_repr | other.__priv_repr
|
||||
}
|
||||
/// Removes all values in another set from this one.
|
||||
#[inline(always)]
|
||||
pub fn remove_all(&mut self, other: Self) {
|
||||
self.__priv_repr = self.__priv_repr.and_not(other.__priv_repr);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EnumSetType> Default for EnumSet<T> {
|
||||
/// Returns an empty set.
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EnumSetType, O: Into<EnumSet<T>>> Sub<O> for EnumSet<T> {
|
||||
type Output = Self;
|
||||
#[inline(always)]
|
||||
fn sub(self, other: O) -> Self::Output {
|
||||
self.difference(other.into())
|
||||
}
|
||||
}
|
||||
impl<T: EnumSetType, O: Into<EnumSet<T>>> BitAnd<O> for EnumSet<T> {
|
||||
type Output = Self;
|
||||
#[inline(always)]
|
||||
fn bitand(self, other: O) -> Self::Output {
|
||||
self.intersection(other.into())
|
||||
}
|
||||
}
|
||||
impl<T: EnumSetType, O: Into<EnumSet<T>>> BitOr<O> for EnumSet<T> {
|
||||
type Output = Self;
|
||||
#[inline(always)]
|
||||
fn bitor(self, other: O) -> Self::Output {
|
||||
self.union(other.into())
|
||||
}
|
||||
}
|
||||
impl<T: EnumSetType, O: Into<EnumSet<T>>> BitXor<O> for EnumSet<T> {
|
||||
type Output = Self;
|
||||
#[inline(always)]
|
||||
fn bitxor(self, other: O) -> Self::Output {
|
||||
self.symmetrical_difference(other.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EnumSetType, O: Into<EnumSet<T>>> SubAssign<O> for EnumSet<T> {
|
||||
#[inline(always)]
|
||||
fn sub_assign(&mut self, rhs: O) {
|
||||
*self = *self - rhs;
|
||||
}
|
||||
}
|
||||
impl<T: EnumSetType, O: Into<EnumSet<T>>> BitAndAssign<O> for EnumSet<T> {
|
||||
#[inline(always)]
|
||||
fn bitand_assign(&mut self, rhs: O) {
|
||||
*self = *self & rhs;
|
||||
}
|
||||
}
|
||||
impl<T: EnumSetType, O: Into<EnumSet<T>>> BitOrAssign<O> for EnumSet<T> {
|
||||
#[inline(always)]
|
||||
fn bitor_assign(&mut self, rhs: O) {
|
||||
*self = *self | rhs;
|
||||
}
|
||||
}
|
||||
impl<T: EnumSetType, O: Into<EnumSet<T>>> BitXorAssign<O> for EnumSet<T> {
|
||||
#[inline(always)]
|
||||
fn bitxor_assign(&mut self, rhs: O) {
|
||||
*self = *self ^ rhs;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EnumSetType> Not for EnumSet<T> {
|
||||
type Output = Self;
|
||||
#[inline(always)]
|
||||
fn not(self) -> Self::Output {
|
||||
self.complement()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EnumSetType> From<T> for EnumSet<T> {
|
||||
fn from(t: T) -> Self {
|
||||
EnumSet::only(t)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EnumSetType> PartialEq<T> for EnumSet<T> {
|
||||
fn eq(&self, other: &T) -> bool {
|
||||
self.__priv_repr == EnumSet::only(*other).__priv_repr
|
||||
}
|
||||
}
|
||||
impl<T: EnumSetType + Debug> Debug for EnumSet<T> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
let mut is_first = true;
|
||||
f.write_str("EnumSet(")?;
|
||||
for v in self.iter() {
|
||||
if !is_first {
|
||||
f.write_str(" | ")?;
|
||||
}
|
||||
is_first = false;
|
||||
v.fmt(f)?;
|
||||
}
|
||||
f.write_str(")")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::derived_hash_with_manual_eq)] // This impl exists to change trait bounds only.
|
||||
impl<T: EnumSetType> Hash for EnumSet<T> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.__priv_repr.hash(state)
|
||||
}
|
||||
}
|
||||
impl<T: EnumSetType> PartialOrd for EnumSet<T> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
self.__priv_repr.partial_cmp(&other.__priv_repr)
|
||||
}
|
||||
}
|
||||
impl<T: EnumSetType> Ord for EnumSet<T> {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.__priv_repr.cmp(&other.__priv_repr)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<T: EnumSetType> Serialize for EnumSet<T> {
|
||||
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
T::serialize(*self, serializer)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de, T: EnumSetType> Deserialize<'de> for EnumSet<T> {
|
||||
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
T::deserialize(deserializer)
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region EnumSet conversions
|
||||
impl<T: EnumSetType + EnumSetTypeWithRepr> EnumSet<T> {
|
||||
/// Returns a `T::Repr` representing the elements of this set.
|
||||
///
|
||||
/// Unlike the other `as_*` methods, this method is zero-cost and guaranteed not to fail,
|
||||
/// panic or truncate any bits.
|
||||
///
|
||||
/// In order to use this method, the definition of `T` must have the `#[enumset(repr = "…")]`
|
||||
/// annotation.
|
||||
#[inline(always)]
|
||||
pub fn as_repr(&self) -> <T as EnumSetTypeWithRepr>::Repr {
|
||||
self.__priv_repr
|
||||
}
|
||||
|
||||
/// Constructs a bitset from a `T::Repr` without checking for invalid bits.
|
||||
///
|
||||
/// Unlike the other `from_*` methods, this method is zero-cost and guaranteed not to fail,
|
||||
/// panic or truncate any bits, provided the conditions under “Safety” are upheld.
|
||||
///
|
||||
/// In order to use this method, the definition of `T` must have the `#[enumset(repr = "…")]`
|
||||
/// annotation.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// All bits in the provided parameter `bits` that don't correspond to an enum variant of
|
||||
/// `T` must be set to `0`. Behavior is **undefined** if any of these bits are set to `1`.
|
||||
#[inline(always)]
|
||||
pub unsafe fn from_repr_unchecked(bits: <T as EnumSetTypeWithRepr>::Repr) -> Self {
|
||||
Self { __priv_repr: bits }
|
||||
}
|
||||
|
||||
/// Constructs a bitset from a `T::Repr`.
|
||||
///
|
||||
/// If a bit that doesn't correspond to an enum variant is set, this
|
||||
/// method will panic.
|
||||
///
|
||||
/// In order to use this method, the definition of `T` must have the `#[enumset(repr = "…")]`
|
||||
/// annotation.
|
||||
#[inline(always)]
|
||||
pub fn from_repr(bits: <T as EnumSetTypeWithRepr>::Repr) -> Self {
|
||||
Self::try_from_repr(bits).expect("Bitset contains invalid variants.")
|
||||
}
|
||||
|
||||
/// Attempts to constructs a bitset from a `T::Repr`.
|
||||
///
|
||||
/// If a bit that doesn't correspond to an enum variant is set, this
|
||||
/// method will return `None`.
|
||||
///
|
||||
/// In order to use this method, the definition of `T` must have the `#[enumset(repr = "…")]`
|
||||
/// annotation.
|
||||
#[inline(always)]
|
||||
pub fn try_from_repr(bits: <T as EnumSetTypeWithRepr>::Repr) -> Option<Self> {
|
||||
let mask = Self::all().__priv_repr;
|
||||
if bits.and_not(mask).is_empty() {
|
||||
Some(EnumSet { __priv_repr: bits })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs a bitset from a `T::Repr`, ignoring invalid variants.
|
||||
///
|
||||
/// In order to use this method, the definition of `T` must have the `#[enumset(repr = "…")]`
|
||||
/// annotation.
|
||||
#[inline(always)]
|
||||
pub fn from_repr_truncated(bits: <T as EnumSetTypeWithRepr>::Repr) -> Self {
|
||||
let mask = Self::all().as_repr();
|
||||
let bits = bits & mask;
|
||||
EnumSet { __priv_repr: bits }
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper macro for generating conversion functions.
|
||||
macro_rules! conversion_impls {
|
||||
(
|
||||
$(for_num!(
|
||||
$underlying:ty, $underlying_str:expr,
|
||||
$from_fn:ident $to_fn:ident $from_fn_opt:ident $to_fn_opt:ident,
|
||||
$from:ident $try_from:ident $from_truncated:ident $from_unchecked:ident,
|
||||
$to:ident $try_to:ident $to_truncated:ident
|
||||
);)*
|
||||
) => {
|
||||
impl<T: EnumSetType> EnumSet<T> {$(
|
||||
#[doc = "Returns a `"]
|
||||
#[doc = $underlying_str]
|
||||
#[doc = "` representing the elements of this set.\n\nIf the underlying bitset will \
|
||||
not fit in a `"]
|
||||
#[doc = $underlying_str]
|
||||
#[doc = "`, this method will panic."]
|
||||
#[inline(always)]
|
||||
pub fn $to(&self) -> $underlying {
|
||||
self.$try_to().expect("Bitset will not fit into this type.")
|
||||
}
|
||||
|
||||
#[doc = "Tries to return a `"]
|
||||
#[doc = $underlying_str]
|
||||
#[doc = "` representing the elements of this set.\n\nIf the underlying bitset will \
|
||||
not fit in a `"]
|
||||
#[doc = $underlying_str]
|
||||
#[doc = "`, this method will panic."]
|
||||
#[inline(always)]
|
||||
pub fn $try_to(&self) -> Option<$underlying> {
|
||||
EnumSetTypeRepr::$to_fn_opt(&self.__priv_repr)
|
||||
}
|
||||
|
||||
#[doc = "Returns a truncated `"]
|
||||
#[doc = $underlying_str]
|
||||
#[doc = "` representing the elements of this set.\n\nIf the underlying bitset will \
|
||||
not fit in a `"]
|
||||
#[doc = $underlying_str]
|
||||
#[doc = "`, this method will truncate any bits that don't fit."]
|
||||
#[inline(always)]
|
||||
pub fn $to_truncated(&self) -> $underlying {
|
||||
EnumSetTypeRepr::$to_fn(&self.__priv_repr)
|
||||
}
|
||||
|
||||
#[doc = "Constructs a bitset from a `"]
|
||||
#[doc = $underlying_str]
|
||||
#[doc = "`.\n\nIf a bit that doesn't correspond to an enum variant is set, this \
|
||||
method will panic."]
|
||||
#[inline(always)]
|
||||
pub fn $from(bits: $underlying) -> Self {
|
||||
Self::$try_from(bits).expect("Bitset contains invalid variants.")
|
||||
}
|
||||
|
||||
#[doc = "Attempts to constructs a bitset from a `"]
|
||||
#[doc = $underlying_str]
|
||||
#[doc = "`.\n\nIf a bit that doesn't correspond to an enum variant is set, this \
|
||||
method will return `None`."]
|
||||
#[inline(always)]
|
||||
pub fn $try_from(bits: $underlying) -> Option<Self> {
|
||||
let bits = T::Repr::$from_fn_opt(bits);
|
||||
let mask = T::ALL_BITS;
|
||||
bits.and_then(|bits| if bits.and_not(mask).is_empty() {
|
||||
Some(EnumSet { __priv_repr: bits })
|
||||
} else {
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
#[doc = "Constructs a bitset from a `"]
|
||||
#[doc = $underlying_str]
|
||||
#[doc = "`, ignoring bits that do not correspond to a variant."]
|
||||
#[inline(always)]
|
||||
pub fn $from_truncated(bits: $underlying) -> Self {
|
||||
let mask = Self::all().$to_truncated();
|
||||
let bits = <T::Repr as EnumSetTypeRepr>::$from_fn(bits & mask);
|
||||
EnumSet { __priv_repr: bits }
|
||||
}
|
||||
|
||||
#[doc = "Constructs a bitset from a `"]
|
||||
#[doc = $underlying_str]
|
||||
#[doc = "`, without checking for invalid bits."]
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// All bits in the provided parameter `bits` that don't correspond to an enum variant
|
||||
/// of `T` must be set to `0`. Behavior is **undefined** if any of these bits are set
|
||||
/// to `1`.
|
||||
#[inline(always)]
|
||||
pub unsafe fn $from_unchecked(bits: $underlying) -> Self {
|
||||
EnumSet { __priv_repr: <T::Repr as EnumSetTypeRepr>::$from_fn(bits) }
|
||||
}
|
||||
)*}
|
||||
}
|
||||
}
|
||||
conversion_impls! {
|
||||
for_num!(u8, "u8",
|
||||
from_u8 to_u8 from_u8_opt to_u8_opt,
|
||||
from_u8 try_from_u8 from_u8_truncated from_u8_unchecked,
|
||||
as_u8 try_as_u8 as_u8_truncated);
|
||||
for_num!(u16, "u16",
|
||||
from_u16 to_u16 from_u16_opt to_u16_opt,
|
||||
from_u16 try_from_u16 from_u16_truncated from_u16_unchecked,
|
||||
as_u16 try_as_u16 as_u16_truncated);
|
||||
for_num!(u32, "u32",
|
||||
from_u32 to_u32 from_u32_opt to_u32_opt,
|
||||
from_u32 try_from_u32 from_u32_truncated from_u32_unchecked,
|
||||
as_u32 try_as_u32 as_u32_truncated);
|
||||
for_num!(u64, "u64",
|
||||
from_u64 to_u64 from_u64_opt to_u64_opt,
|
||||
from_u64 try_from_u64 from_u64_truncated from_u64_unchecked,
|
||||
as_u64 try_as_u64 as_u64_truncated);
|
||||
for_num!(u128, "u128",
|
||||
from_u128 to_u128 from_u128_opt to_u128_opt,
|
||||
from_u128 try_from_u128 from_u128_truncated from_u128_unchecked,
|
||||
as_u128 try_as_u128 as_u128_truncated);
|
||||
for_num!(usize, "usize",
|
||||
from_usize to_usize from_usize_opt to_usize_opt,
|
||||
from_usize try_from_usize from_usize_truncated from_usize_unchecked,
|
||||
as_usize try_as_usize as_usize_truncated);
|
||||
}
|
||||
|
||||
impl<T: EnumSetType> EnumSet<T> {
|
||||
/// Returns an `[u64; O]` representing the elements of this set.
|
||||
///
|
||||
/// If the underlying bitset will not fit in a `[u64; O]`, this method will panic.
|
||||
pub fn as_array<const O: usize>(&self) -> [u64; O] {
|
||||
self.try_as_array()
|
||||
.expect("Bitset will not fit into this type.")
|
||||
}
|
||||
|
||||
/// Returns an `[u64; O]` representing the elements of this set.
|
||||
///
|
||||
/// If the underlying bitset will not fit in a `[u64; O]`, this method will instead return
|
||||
/// `None`.
|
||||
pub fn try_as_array<const O: usize>(&self) -> Option<[u64; O]> {
|
||||
self.__priv_repr.to_u64_array_opt()
|
||||
}
|
||||
|
||||
/// Returns an `[u64; O]` representing the elements of this set.
|
||||
///
|
||||
/// If the underlying bitset will not fit in a `[u64; O]`, this method will truncate any bits
|
||||
/// that don't fit.
|
||||
pub fn as_array_truncated<const O: usize>(&self) -> [u64; O] {
|
||||
self.__priv_repr.to_u64_array()
|
||||
}
|
||||
|
||||
/// Attempts to constructs a bitset from a `[u64; O]`.
|
||||
///
|
||||
/// If a bit that doesn't correspond to an enum variant is set, this method will panic.
|
||||
pub fn from_array<const O: usize>(v: [u64; O]) -> Self {
|
||||
Self::try_from_array(v).expect("Bitset contains invalid variants.")
|
||||
}
|
||||
|
||||
/// Attempts to constructs a bitset from a `[u64; O]`.
|
||||
///
|
||||
/// If a bit that doesn't correspond to an enum variant is set, this method will return `None`.
|
||||
pub fn try_from_array<const O: usize>(bits: [u64; O]) -> Option<Self> {
|
||||
let bits = T::Repr::from_u64_array_opt::<O>(bits);
|
||||
let mask = T::ALL_BITS;
|
||||
bits.and_then(|bits| {
|
||||
if bits.and_not(mask).is_empty() {
|
||||
Some(EnumSet { __priv_repr: bits })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Constructs a bitset from a `[u64; O]`, ignoring bits that do not correspond to a variant.
|
||||
pub fn from_array_truncated<const O: usize>(bits: [u64; O]) -> Self {
|
||||
let bits = T::Repr::from_u64_array(bits) & T::ALL_BITS;
|
||||
EnumSet { __priv_repr: bits }
|
||||
}
|
||||
|
||||
/// Constructs a bitset from a `[u64; O]`, without checking for invalid bits.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// All bits in the provided parameter `bits` that don't correspond to an enum variant
|
||||
/// of `T` must be set to `0`. Behavior is **undefined** if any of these bits are set
|
||||
/// to `1`.
|
||||
#[inline(always)]
|
||||
pub unsafe fn from_array_unchecked<const O: usize>(bits: [u64; O]) -> Self {
|
||||
EnumSet { __priv_repr: T::Repr::from_u64_array(bits) }
|
||||
}
|
||||
|
||||
/// Returns a `Vec<u64>` representing the elements of this set.
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
|
||||
pub fn to_vec(&self) -> alloc::vec::Vec<u64> {
|
||||
let mut vec = alloc::vec![0; T::Repr::PREFERRED_ARRAY_LEN];
|
||||
self.__priv_repr.to_u64_slice(&mut vec);
|
||||
vec
|
||||
}
|
||||
|
||||
/// Copies the elements of this set into a `&mut [u64]`.
|
||||
///
|
||||
/// If the underlying bitset will not fit in the provided slice, this method will panic.
|
||||
pub fn copy_into_slice(&self, data: &mut [u64]) {
|
||||
self.try_copy_into_slice(data)
|
||||
.expect("Bitset will not fit into slice.")
|
||||
}
|
||||
|
||||
/// Copies the elements of this set into a `&mut [u64]`.
|
||||
///
|
||||
/// If the underlying bitset will not fit in the provided slice, this method will return
|
||||
/// `None`. Otherwise, it will return `Some(())`.
|
||||
#[must_use]
|
||||
pub fn try_copy_into_slice(&self, data: &mut [u64]) -> Option<()> {
|
||||
self.__priv_repr.to_u64_slice_opt(data)
|
||||
}
|
||||
|
||||
/// Copies the elements of this set into a `&mut [u64]`.
|
||||
///
|
||||
/// If the underlying bitset will not fit in the provided slice, this method will truncate any
|
||||
/// bits that don't fit.
|
||||
pub fn copy_into_slice_truncated(&self, data: &mut [u64]) {
|
||||
self.__priv_repr.to_u64_slice(data)
|
||||
}
|
||||
|
||||
/// Attempts to constructs a bitset from a `&[u64]`.
|
||||
///
|
||||
/// If a bit that doesn't correspond to an enum variant is set, this method will panic.
|
||||
pub fn from_slice(v: &[u64]) -> Self {
|
||||
Self::try_from_slice(v).expect("Bitset contains invalid variants.")
|
||||
}
|
||||
|
||||
/// Attempts to constructs a bitset from a `&[u64]`.
|
||||
///
|
||||
/// If a bit that doesn't correspond to an enum variant is set, this method will return `None`.
|
||||
pub fn try_from_slice(bits: &[u64]) -> Option<Self> {
|
||||
let bits = T::Repr::from_u64_slice_opt(bits);
|
||||
let mask = T::ALL_BITS;
|
||||
bits.and_then(|bits| {
|
||||
if bits.and_not(mask).is_empty() {
|
||||
Some(EnumSet { __priv_repr: bits })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Constructs a bitset from a `&[u64]`, ignoring bits that do not correspond to a variant.
|
||||
pub fn from_slice_truncated(bits: &[u64]) -> Self {
|
||||
let bits = T::Repr::from_u64_slice(bits) & T::ALL_BITS;
|
||||
EnumSet { __priv_repr: bits }
|
||||
}
|
||||
|
||||
/// Constructs a bitset from a `&[u64]`, without checking for invalid bits.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// All bits in the provided parameter `bits` that don't correspond to an enum variant
|
||||
/// of `T` must be set to `0`. Behavior is **undefined** if any of these bits are set
|
||||
/// to `1`.
|
||||
#[inline(always)]
|
||||
pub unsafe fn from_slice_unchecked(bits: &[u64]) -> Self {
|
||||
EnumSet { __priv_repr: T::Repr::from_u64_slice(bits) }
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region EnumSet iter
|
||||
/// The iterator used by [`EnumSet`]s.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct EnumSetIter<T: EnumSetType> {
|
||||
iter: <T::Repr as EnumSetTypeRepr>::Iter,
|
||||
}
|
||||
impl<T: EnumSetType> EnumSetIter<T> {
|
||||
fn new(set: EnumSet<T>) -> EnumSetIter<T> {
|
||||
EnumSetIter { iter: set.__priv_repr.iter() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EnumSetType> EnumSet<T> {
|
||||
/// Iterates the contents of the set in order from the least significant bit to the most
|
||||
/// significant bit.
|
||||
///
|
||||
/// Note that iterator invalidation is impossible as the iterator contains a copy of this type,
|
||||
/// rather than holding a reference to it.
|
||||
pub fn iter(&self) -> EnumSetIter<T> {
|
||||
EnumSetIter::new(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EnumSetType> Iterator for EnumSetIter<T> {
|
||||
type Item = T;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.iter.next().map(|x| unsafe { T::enum_from_u32(x) })
|
||||
}
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.iter.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EnumSetType> DoubleEndedIterator for EnumSetIter<T> {
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
self.iter
|
||||
.next_back()
|
||||
.map(|x| unsafe { T::enum_from_u32(x) })
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EnumSetType> ExactSizeIterator for EnumSetIter<T> {}
|
||||
|
||||
impl<T: EnumSetType> Extend<T> for EnumSet<T> {
|
||||
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
|
||||
iter.into_iter().for_each(|v| {
|
||||
self.insert(v);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EnumSetType> FromIterator<T> for EnumSet<T> {
|
||||
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
|
||||
let mut set = EnumSet::default();
|
||||
set.extend(iter);
|
||||
set
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EnumSetType> Extend<EnumSet<T>> for EnumSet<T> {
|
||||
fn extend<I: IntoIterator<Item = EnumSet<T>>>(&mut self, iter: I) {
|
||||
iter.into_iter().for_each(|v| {
|
||||
self.insert_all(v);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EnumSetType> FromIterator<EnumSet<T>> for EnumSet<T> {
|
||||
fn from_iter<I: IntoIterator<Item = EnumSet<T>>>(iter: I) -> Self {
|
||||
let mut set = EnumSet::default();
|
||||
set.extend(iter);
|
||||
set
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EnumSetType> IntoIterator for EnumSet<T> {
|
||||
type Item = T;
|
||||
type IntoIter = EnumSetIter<T>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
impl<T: EnumSetType> Sum for EnumSet<T> {
|
||||
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
|
||||
iter.fold(EnumSet::empty(), |a, v| a | v)
|
||||
}
|
||||
}
|
||||
impl<'a, T: EnumSetType> Sum<&'a EnumSet<T>> for EnumSet<T> {
|
||||
fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
|
||||
iter.fold(EnumSet::empty(), |a, v| a | *v)
|
||||
}
|
||||
}
|
||||
impl<T: EnumSetType> Sum<T> for EnumSet<T> {
|
||||
fn sum<I: Iterator<Item = T>>(iter: I) -> Self {
|
||||
iter.fold(EnumSet::empty(), |a, v| a | v)
|
||||
}
|
||||
}
|
||||
impl<'a, T: EnumSetType> Sum<&'a T> for EnumSet<T> {
|
||||
fn sum<I: Iterator<Item = &'a T>>(iter: I) -> Self {
|
||||
iter.fold(EnumSet::empty(), |a, v| a | *v)
|
||||
}
|
||||
}
|
||||
//endregion
|
|
@ -0,0 +1,62 @@
|
|||
use crate::repr::EnumSetTypeRepr;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use {crate::EnumSet, serde2 as serde};
|
||||
|
||||
/// The trait used to define enum types that may be used with [`EnumSet`].
|
||||
///
|
||||
/// This trait must be impelmented using `#[derive(EnumSetType)]`, is not public API, and its
|
||||
/// internal structure may change at any time with no warning.
|
||||
///
|
||||
/// For full documentation on the procedural derive and its options, see
|
||||
/// [`#[derive(EnumSetType)]`](derive@crate::EnumSetType).
|
||||
///
|
||||
/// [`EnumSet`]: crate::set::EnumSet
|
||||
pub unsafe trait EnumSetType: Copy + Eq + EnumSetTypePrivate {}
|
||||
|
||||
/// An [`EnumSetType`] for which [`EnumSet`]s have a guaranteed in-memory representation.
|
||||
///
|
||||
/// An implementation of this trait is generated by using
|
||||
/// [`#[derive(EnumSetType)]`](derive@crate::EnumSetType) with the annotation
|
||||
/// `#[enumset(repr = "…")]`, where `…` is `u8`, `u16`, `u32`, `u64` or `u128`.
|
||||
///
|
||||
/// For any type `T` that implements this trait, the in-memory representation of `EnumSet<T>`
|
||||
/// is guaranteed to be `Repr`. This guarantee is useful for FFI. See [the `EnumSet` documentation
|
||||
/// under “FFI, Safety and `repr`”][crate::set::EnumSet#ffi-safety-and-repr] for an example.
|
||||
///
|
||||
/// [`EnumSet`]: crate::set::EnumSet
|
||||
pub unsafe trait EnumSetTypeWithRepr:
|
||||
EnumSetType + EnumSetTypePrivate<Repr = <Self as EnumSetTypeWithRepr>::Repr>
|
||||
{
|
||||
/// The guaranteed representation.
|
||||
type Repr: EnumSetTypeRepr;
|
||||
}
|
||||
|
||||
/// The actual members of EnumSetType. Put here to avoid polluting global namespaces.
|
||||
pub unsafe trait EnumSetTypePrivate {
|
||||
/// The underlying type used to store the bitset.
|
||||
type Repr: EnumSetTypeRepr;
|
||||
/// A mask of bits that are valid in the bitset.
|
||||
const ALL_BITS: Self::Repr;
|
||||
/// The largest bit used in the bitset.
|
||||
const BIT_WIDTH: u32;
|
||||
/// The number of variants in the bitset.
|
||||
const VARIANT_COUNT: u32;
|
||||
|
||||
/// Converts an enum of this type into its bit position.
|
||||
fn enum_into_u32(self) -> u32;
|
||||
/// Converts a bit position into an enum value.
|
||||
unsafe fn enum_from_u32(val: u32) -> Self;
|
||||
|
||||
/// Serializes the `EnumSet`.
|
||||
///
|
||||
/// This and `deserialize` are part of the `EnumSetType` trait so the procedural derive
|
||||
/// can control how `EnumSet` is serialized.
|
||||
#[cfg(feature = "serde")]
|
||||
fn serialize<S: serde::Serializer>(set: EnumSet<Self>, ser: S) -> Result<S::Ok, S::Error>
|
||||
where Self: EnumSetType;
|
||||
/// Deserializes the `EnumSet`.
|
||||
#[cfg(feature = "serde")]
|
||||
fn deserialize<'de, D: serde::Deserializer<'de>>(de: D) -> Result<EnumSet<Self>, D::Error>
|
||||
where Self: EnumSetType;
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
#![deny(deprecated)]
|
||||
|
||||
use enumset::*;
|
||||
use serde_derive::*;
|
||||
|
||||
#[derive(Serialize, Deserialize, EnumSetType, Debug)]
|
||||
#[enumset(serialize_as_map)]
|
||||
#[serde(crate="enumset::__internal::serde")]
|
||||
pub enum MapEnum {
|
||||
A, B, C, D, E, F, G, H,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, EnumSetType, Debug)]
|
||||
#[enumset(serialize_as_list)]
|
||||
#[serde(crate="enumset::__internal::serde")]
|
||||
pub enum ListEnum {
|
||||
A, B, C, D, E, F, G, H,
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,17 @@
|
|||
error: use of deprecated constant `_::__enumset_derive__generated_warnings::_w`: #[enumset(serialize_as_map)] is deprecated. Use `#[enumset(serialize_repr = "map")]` instead.
|
||||
--> tests/compile-fail-serde/deprecation.rs:7:11
|
||||
|
|
||||
7 | #[enumset(serialize_as_map)]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> tests/compile-fail-serde/deprecation.rs:1:9
|
||||
|
|
||||
1 | #![deny(deprecated)]
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: use of deprecated constant `_::__enumset_derive__generated_warnings::_w`: #[enumset(serialize_as_list)] is deprecated. Use `#[enumset(serialize_repr = "list")]` instead.
|
||||
--> tests/compile-fail-serde/deprecation.rs:14:11
|
||||
|
|
||||
14 | #[enumset(serialize_as_list)]
|
||||
| ^^^^^^^^^^^^^^^^^
|
|
@ -0,0 +1,17 @@
|
|||
use enumset::*;
|
||||
|
||||
#[derive(EnumSetType)]
|
||||
enum OkayEnumButCantUseFromRepr {
|
||||
Variant,
|
||||
}
|
||||
|
||||
#[derive(EnumSetType)]
|
||||
#[enumset(repr = "array")]
|
||||
enum OkayEnumButCantUseFromReprArray {
|
||||
Variant,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
EnumSet::<OkayEnumButCantUseFromRepr>::from_repr(1);
|
||||
EnumSet::<OkayEnumButCantUseFromReprArray>::from_repr([1]);
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
error[E0599]: the function or associated item `from_repr` exists for struct `EnumSet<OkayEnumButCantUseFromRepr>`, but its trait bounds were not satisfied
|
||||
--> tests/compile-fail/explicit_repr.rs:15:44
|
||||
|
|
||||
4 | enum OkayEnumButCantUseFromRepr {
|
||||
| -------------------------------
|
||||
| |
|
||||
| doesn't satisfy `<_ as EnumSetTypePrivate>::Repr = <OkayEnumButCantUseFromRepr as EnumSetTypeWithRepr>::Repr`
|
||||
| doesn't satisfy `OkayEnumButCantUseFromRepr: EnumSetTypeWithRepr`
|
||||
...
|
||||
15 | EnumSet::<OkayEnumButCantUseFromRepr>::from_repr(1);
|
||||
| ^^^^^^^^^ function or associated item cannot be called on `EnumSet<OkayEnumButCantUseFromRepr>` due to unsatisfied trait bounds
|
||||
|
|
||||
= note: the following trait bounds were not satisfied:
|
||||
`OkayEnumButCantUseFromRepr: EnumSetTypeWithRepr`
|
||||
`<OkayEnumButCantUseFromRepr as EnumSetTypePrivate>::Repr = <OkayEnumButCantUseFromRepr as EnumSetTypeWithRepr>::Repr`
|
||||
which is required by `OkayEnumButCantUseFromRepr: EnumSetTypeWithRepr`
|
||||
note: the trait `EnumSetTypeWithRepr` must be implemented
|
||||
--> src/traits.rs
|
||||
|
|
||||
| / pub unsafe trait EnumSetTypeWithRepr:
|
||||
| | EnumSetType + EnumSetTypePrivate<Repr = <Self as EnumSetTypeWithRepr>::Repr>
|
||||
| |________________________________________________________________________________^
|
||||
|
||||
error[E0599]: the function or associated item `from_repr` exists for struct `EnumSet<OkayEnumButCantUseFromReprArray>`, but its trait bounds were not satisfied
|
||||
--> tests/compile-fail/explicit_repr.rs:16:49
|
||||
|
|
||||
10 | enum OkayEnumButCantUseFromReprArray {
|
||||
| ------------------------------------
|
||||
| |
|
||||
| doesn't satisfy `<_ as EnumSetTypePrivate>::Repr = <OkayEnumButCantUseFromReprArray as EnumSetTypeWithRepr>::Repr`
|
||||
| doesn't satisfy `_: EnumSetTypeWithRepr`
|
||||
...
|
||||
16 | EnumSet::<OkayEnumButCantUseFromReprArray>::from_repr([1]);
|
||||
| ^^^^^^^^^ function or associated item cannot be called on `EnumSet<OkayEnumButCantUseFromReprArray>` due to unsatisfied trait bounds
|
||||
|
|
||||
= note: the following trait bounds were not satisfied:
|
||||
`OkayEnumButCantUseFromReprArray: EnumSetTypeWithRepr`
|
||||
`<OkayEnumButCantUseFromReprArray as EnumSetTypePrivate>::Repr = <OkayEnumButCantUseFromReprArray as EnumSetTypeWithRepr>::Repr`
|
||||
which is required by `OkayEnumButCantUseFromReprArray: EnumSetTypeWithRepr`
|
||||
note: the trait `EnumSetTypeWithRepr` must be implemented
|
||||
--> src/traits.rs
|
||||
|
|
||||
| / pub unsafe trait EnumSetTypeWithRepr:
|
||||
| | EnumSetType + EnumSetTypePrivate<Repr = <Self as EnumSetTypeWithRepr>::Repr>
|
||||
| |________________________________________________________________________________^
|
|
@ -0,0 +1,39 @@
|
|||
use enumset::*;
|
||||
|
||||
#[derive(EnumSetType)]
|
||||
#[repr(usize)]
|
||||
enum BadRepr {
|
||||
Variant,
|
||||
}
|
||||
|
||||
#[derive(EnumSetType)]
|
||||
#[repr(usize)]
|
||||
enum GenericEnum<T> {
|
||||
Variant,
|
||||
FieldBlah(T),
|
||||
}
|
||||
|
||||
#[derive(EnumSetType)]
|
||||
struct BadItemType {
|
||||
|
||||
}
|
||||
|
||||
#[derive(EnumSetType)]
|
||||
#[enumset(repr = "u8", repr = "u16")]
|
||||
enum MultipleReprs {
|
||||
Variant,
|
||||
}
|
||||
|
||||
#[derive(EnumSetType)]
|
||||
#[enumset(repr = "abcdef")]
|
||||
enum InvalidRepr {
|
||||
Variant,
|
||||
}
|
||||
|
||||
#[derive(EnumSetType)]
|
||||
#[enumset(serialize_repr = "abcdef")]
|
||||
enum InvalidSerdeRepr {
|
||||
Variant,
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,31 @@
|
|||
error: `#[derive(EnumSetType)]` cannot be used on enums with type parameters.
|
||||
--> tests/compile-fail/syntax.rs:11:17
|
||||
|
|
||||
11 | enum GenericEnum<T> {
|
||||
| ^^^
|
||||
|
||||
error: `#[derive(EnumSetType)]` may only be used on enums
|
||||
--> tests/compile-fail/syntax.rs:17:1
|
||||
|
|
||||
17 | / struct BadItemType {
|
||||
18 | |
|
||||
19 | | }
|
||||
| |_^
|
||||
|
||||
error: Duplicate field `repr`
|
||||
--> tests/compile-fail/syntax.rs:22:24
|
||||
|
|
||||
22 | #[enumset(repr = "u8", repr = "u16")]
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error: `abcdef` is not a valid internal enumset representation.
|
||||
--> tests/compile-fail/syntax.rs:28:18
|
||||
|
|
||||
28 | #[enumset(repr = "abcdef")]
|
||||
| ^^^^^^^^
|
||||
|
||||
error: `abcdef` is not a valid serialized representation.
|
||||
--> tests/compile-fail/syntax.rs:34:28
|
||||
|
|
||||
34 | #[enumset(serialize_repr = "abcdef")]
|
||||
| ^^^^^^^^
|
|
@ -1,41 +1,19 @@
|
|||
use enumset::*;
|
||||
|
||||
#[derive(EnumSetType)]
|
||||
enum VariantOver127 {
|
||||
Variant = 128,
|
||||
}
|
||||
|
||||
#[derive(EnumSetType)]
|
||||
#[repr(i64)]
|
||||
enum VariantOverU32 {
|
||||
Variant = 0x100000000,
|
||||
}
|
||||
|
||||
#[derive(EnumSetType)]
|
||||
enum TooManyVariants {
|
||||
_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20,
|
||||
_21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39,
|
||||
_40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58,
|
||||
_59, _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, _70, _71, _72, _73, _74, _75, _76, _77,
|
||||
_78, _79, _80, _81, _82, _83, _84, _85, _86, _87, _88, _89, _90, _91, _92, _93, _94, _95, _96,
|
||||
_97, _98, _99, _100, _101, _102, _103, _104, _105, _106, _107, _108, _109, _110, _111, _112,
|
||||
_113, _114, _115, _116, _117, _118, _119, _120, _121, _122, _123, _124, _125, _126, _127, _128,
|
||||
}
|
||||
|
||||
#[derive(EnumSetType)]
|
||||
enum NegativeVariant {
|
||||
Variant = -1,
|
||||
}
|
||||
|
||||
#[derive(EnumSetType)]
|
||||
#[repr(usize)]
|
||||
enum BadRepr {
|
||||
Variant,
|
||||
enum HasFields {
|
||||
Variant(u32),
|
||||
}
|
||||
|
||||
#[derive(EnumSetType)]
|
||||
enum HasFields {
|
||||
Variant(u32),
|
||||
#[enumset(repr = "u128")]
|
||||
enum BadExplicitRepr {
|
||||
Variant = 128,
|
||||
}
|
||||
|
||||
#[derive(EnumSetType)]
|
||||
|
@ -45,8 +23,8 @@ enum BadSerializationRepr {
|
|||
}
|
||||
|
||||
#[derive(EnumSetType)]
|
||||
struct BadItemType {
|
||||
|
||||
enum NonLiteralRepr {
|
||||
Variant = 1 + 1,
|
||||
}
|
||||
|
||||
#[derive(EnumSetType)]
|
||||
|
@ -56,10 +34,8 @@ enum BadMemRepr {
|
|||
}
|
||||
|
||||
#[derive(EnumSetType)]
|
||||
enum OkayEnumButCantUseFromRepr {
|
||||
Variant,
|
||||
enum ExcessiveReprSize {
|
||||
Variant = 0xFFFFFFD0,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
EnumSet::<OkayEnumButCantUseFromRepr>::from_repr(1);
|
||||
}
|
||||
fn main() {}
|
||||
|
|
|
@ -1,77 +1,41 @@
|
|||
error: `#[derive(EnumSetType)]` currently only supports discriminants up to 127.
|
||||
--> tests/compile-fail/variants.rs:5:5
|
||||
error: Enum discriminants must not be negative.
|
||||
--> tests/compile-fail/variants.rs:5:15
|
||||
|
|
||||
5 | Variant = 128,
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error: Enum set discriminants must be `u32`s. (larger discrimiants are still unsupported with reprs that allow them.)
|
||||
--> tests/compile-fail/variants.rs:11:15
|
||||
|
|
||||
11 | Variant = 0x100000000,
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
error: `#[derive(EnumSetType)]` currently only supports enums up to 128 variants.
|
||||
--> tests/compile-fail/variants.rs:22:95
|
||||
|
|
||||
22 | _113, _114, _115, _116, _117, _118, _119, _120, _121, _122, _123, _124, _125, _126, _127, _128,
|
||||
| ^^^^
|
||||
|
||||
error: Enum set discriminants must be `u32`s.
|
||||
--> tests/compile-fail/variants.rs:27:5
|
||||
|
|
||||
27 | Variant = -1,
|
||||
| ^^^^^^^^^^^^
|
||||
5 | Variant = -1,
|
||||
| ^^
|
||||
|
||||
error: `#[derive(EnumSetType)]` can only be used on fieldless enums.
|
||||
--> tests/compile-fail/variants.rs:38:5
|
||||
--> tests/compile-fail/variants.rs:10:5
|
||||
|
|
||||
38 | Variant(u32),
|
||||
10 | Variant(u32),
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error: serialize_repr cannot be smaller than bitset.
|
||||
--> tests/compile-fail/variants.rs:41:10
|
||||
error: `repr` is too small to contain the largest discriminant.
|
||||
--> tests/compile-fail/variants.rs:16:5
|
||||
|
|
||||
41 | #[derive(EnumSetType)]
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= note: this error originates in the derive macro `EnumSetType` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
16 | Variant = 128,
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error: `#[derive(EnumSetType)]` may only be used on enums
|
||||
--> tests/compile-fail/variants.rs:48:1
|
||||
error: `serialize_repr` is too small to contain the largest discriminant.
|
||||
--> tests/compile-fail/variants.rs:22:5
|
||||
|
|
||||
48 | / struct BadItemType {
|
||||
49 | |
|
||||
50 | | }
|
||||
| |_^
|
||||
22 | Variant = 8,
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
error: repr cannot be smaller than bitset.
|
||||
--> tests/compile-fail/variants.rs:52:10
|
||||
error: Enum discriminants must be literal expressions.
|
||||
--> tests/compile-fail/variants.rs:27:5
|
||||
|
|
||||
52 | #[derive(EnumSetType)]
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= note: this error originates in the derive macro `EnumSetType` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
27 | Variant = 1 + 1,
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error[E0277]: the trait bound `OkayEnumButCantUseFromRepr: EnumSetTypeWithRepr` is not satisfied
|
||||
--> tests/compile-fail/variants.rs:64:5
|
||||
|
|
||||
64 | EnumSet::<OkayEnumButCantUseFromRepr>::from_repr(1);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `EnumSetTypeWithRepr` is not implemented for `OkayEnumButCantUseFromRepr`
|
||||
|
|
||||
note: required by a bound in `enumset::EnumSet::<T>::from_repr`
|
||||
--> src/lib.rs
|
||||
|
|
||||
| where T: EnumSetTypeWithRepr {
|
||||
| ^^^^^^^^^^^^^^^^^^^ required by this bound in `enumset::EnumSet::<T>::from_repr`
|
||||
|
||||
error[E0277]: the trait bound `OkayEnumButCantUseFromRepr: EnumSetTypeWithRepr` is not satisfied
|
||||
--> tests/compile-fail/variants.rs:64:5
|
||||
error: `repr` is too small to contain the largest discriminant.
|
||||
--> tests/compile-fail/variants.rs:33:5
|
||||
|
|
||||
64 | EnumSet::<OkayEnumButCantUseFromRepr>::from_repr(1);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `EnumSetTypeWithRepr` is not implemented for `OkayEnumButCantUseFromRepr`
|
||||
33 | Variant = 16,
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error[E0277]: the trait bound `OkayEnumButCantUseFromRepr: EnumSetTypeWithRepr` is not satisfied
|
||||
--> tests/compile-fail/variants.rs:64:54
|
||||
error: Maximum discriminant allowed is `0xFFFFFFBF`.
|
||||
--> tests/compile-fail/variants.rs:38:5
|
||||
|
|
||||
64 | EnumSet::<OkayEnumButCantUseFromRepr>::from_repr(1);
|
||||
| ^ the trait `EnumSetTypeWithRepr` is not implemented for `OkayEnumButCantUseFromRepr`
|
||||
38 | Variant = 0xFFFFFFD0,
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
|
@ -0,0 +1,360 @@
|
|||
use enumset::*;
|
||||
|
||||
macro_rules! read_slice {
|
||||
($set:expr, $size:expr) => {{
|
||||
let mut arr = [0; $size];
|
||||
$set.copy_into_slice(&mut arr);
|
||||
arr
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! try_read_slice {
|
||||
($set:expr, $size:expr) => {{
|
||||
let mut arr = [0; $size];
|
||||
match $set.try_copy_into_slice(&mut arr) {
|
||||
Some(()) => Some(arr),
|
||||
None => None,
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! read_slice_truncated {
|
||||
($set:expr, $size:expr) => {{
|
||||
let mut arr = [0; $size];
|
||||
$set.copy_into_slice_truncated(&mut arr);
|
||||
arr
|
||||
}};
|
||||
}
|
||||
|
||||
#[derive(EnumSetType, Debug)]
|
||||
pub enum Enum8 {
|
||||
A, B, C, D, E, F, G,
|
||||
// H omitted for non-existent bit test
|
||||
}
|
||||
|
||||
#[derive(EnumSetType, Debug)]
|
||||
pub enum Enum16 {
|
||||
A, B, C, D, E=8, F, G, H,
|
||||
}
|
||||
|
||||
#[derive(EnumSetType, Debug)]
|
||||
pub enum Enum32 {
|
||||
A, B, C, D, E=16, F, G, H,
|
||||
}
|
||||
|
||||
#[derive(EnumSetType, Debug)]
|
||||
pub enum Enum64 {
|
||||
A, B, C, D, E=32, F, G, H,
|
||||
}
|
||||
|
||||
#[derive(EnumSetType, Debug)]
|
||||
pub enum Enum128 {
|
||||
A, B, C, D, E=64, F, G, H,
|
||||
}
|
||||
|
||||
#[derive(EnumSetType, Debug)]
|
||||
pub enum Enum192 {
|
||||
A, B, C, D, E=128, F, G, H,
|
||||
}
|
||||
|
||||
#[derive(EnumSetType, Debug)]
|
||||
pub enum Enum256 {
|
||||
A, B, C, D, E=192, F, G, H,
|
||||
}
|
||||
|
||||
macro_rules! check_simple_conversion {
|
||||
($mod:ident, $e:ident) => {
|
||||
mod $mod {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn to_integer() {
|
||||
assert_eq!(7,
|
||||
($e::A | $e::B | $e::C).as_u8());
|
||||
assert_eq!(7,
|
||||
($e::A | $e::B | $e::C).as_u16());
|
||||
assert_eq!(7,
|
||||
($e::A | $e::B | $e::C).as_u32());
|
||||
assert_eq!(7,
|
||||
($e::A | $e::B | $e::C).as_u64());
|
||||
assert_eq!(7,
|
||||
($e::A | $e::B | $e::C).as_u128());
|
||||
assert_eq!(7,
|
||||
($e::A | $e::B | $e::C).as_usize());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn try_from_integer() {
|
||||
assert_eq!(Some($e::A | $e::B | $e::C),
|
||||
EnumSet::try_from_u8(7));
|
||||
assert_eq!(None,
|
||||
EnumSet::<$e>::try_from_u8(7 | (1 << 7)));
|
||||
assert_eq!(None,
|
||||
EnumSet::<$e>::try_from_u16(7 | (1 << 15)));
|
||||
assert_eq!(None,
|
||||
EnumSet::<$e>::try_from_u32(7 | (1 << 31)));
|
||||
assert_eq!(None,
|
||||
EnumSet::<$e>::try_from_usize(7 | (1 << 31)));
|
||||
assert_eq!(None,
|
||||
EnumSet::<$e>::try_from_u64(7 | (1 << 63)));
|
||||
assert_eq!(None,
|
||||
EnumSet::<$e>::try_from_u128(7 | (1 << 127)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_integer_truncated() {
|
||||
assert_eq!($e::A | $e::B | $e::C,
|
||||
EnumSet::from_u8_truncated(7));
|
||||
assert_eq!($e::A | $e::B | $e::C,
|
||||
EnumSet::from_u8_truncated(7 | (1 << 7)));
|
||||
assert_eq!($e::A | $e::B | $e::C,
|
||||
EnumSet::from_u16_truncated(7 | (1 << 15)));
|
||||
assert_eq!($e::A | $e::B | $e::C,
|
||||
EnumSet::from_u32_truncated(7 | (1 << 31)));
|
||||
assert_eq!($e::A | $e::B | $e::C,
|
||||
EnumSet::from_usize_truncated(7 | (1 << 31)));
|
||||
assert_eq!($e::A | $e::B | $e::C,
|
||||
EnumSet::from_u64_truncated(7 | (1 << 63)));
|
||||
assert_eq!($e::A | $e::B | $e::C,
|
||||
EnumSet::from_u128_truncated(7 | (1 << 127)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic_to_array() {
|
||||
// array tests
|
||||
assert_eq!(($e::A | $e::B | $e::C).as_array_truncated(),
|
||||
[]);
|
||||
assert_eq!(EnumSet::<$e>::EMPTY.as_array_truncated(),
|
||||
[]);
|
||||
assert_eq!(($e::A | $e::B | $e::C).as_array(),
|
||||
[7]);
|
||||
assert_eq!(($e::A | $e::B | $e::C).as_array(),
|
||||
[7, 0]);
|
||||
assert_eq!(($e::A | $e::B | $e::C).as_array(),
|
||||
[7, 0, 0]);
|
||||
assert_eq!(($e::A | $e::B | $e::C).as_array(),
|
||||
[7, 0, 0, 0]);
|
||||
assert_eq!(($e::A | $e::B | $e::C).as_array(),
|
||||
[7, 0, 0, 0, 0]);
|
||||
|
||||
// slice tests
|
||||
assert_eq!(read_slice!($e::A | $e::B | $e::C, 1),
|
||||
[7]);
|
||||
assert_eq!(read_slice!($e::A | $e::B | $e::C, 2),
|
||||
[7, 0]);
|
||||
assert_eq!(read_slice!($e::A | $e::B | $e::C, 3),
|
||||
[7, 0, 0]);
|
||||
assert_eq!(read_slice!($e::A | $e::B | $e::C, 4),
|
||||
[7, 0, 0, 0]);
|
||||
assert_eq!(read_slice!($e::A | $e::B | $e::C, 5),
|
||||
[7, 0, 0, 0, 0]);
|
||||
|
||||
// slice tests truncated
|
||||
assert_eq!(read_slice_truncated!($e::A | $e::B | $e::C, 1),
|
||||
[7]);
|
||||
assert_eq!(read_slice_truncated!($e::A | $e::B | $e::C, 2),
|
||||
[7, 0]);
|
||||
assert_eq!(read_slice_truncated!($e::A | $e::B | $e::C, 3),
|
||||
[7, 0, 0]);
|
||||
assert_eq!(read_slice_truncated!($e::A | $e::B | $e::C, 4),
|
||||
[7, 0, 0, 0]);
|
||||
assert_eq!(read_slice_truncated!($e::A | $e::B | $e::C, 5),
|
||||
[7, 0, 0, 0, 0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic_from_array() {
|
||||
// array tests
|
||||
assert_eq!(EnumSet::<$e>::EMPTY,
|
||||
EnumSet::<$e>::from_array([]));
|
||||
assert_eq!($e::A | $e::B | $e::C,
|
||||
EnumSet::<$e>::from_array([7]));
|
||||
assert_eq!($e::A | $e::B | $e::C,
|
||||
EnumSet::<$e>::from_array([7, 0, 0]));
|
||||
assert_eq!($e::A | $e::B | $e::C,
|
||||
EnumSet::<$e>::from_array([7, 0, 0, 0]));
|
||||
assert_eq!($e::A | $e::B | $e::C,
|
||||
EnumSet::<$e>::from_array([7, 0, 0, 0, 0]));
|
||||
|
||||
// array tests
|
||||
assert_eq!(EnumSet::<$e>::EMPTY,
|
||||
EnumSet::<$e>::from_slice(&[]));
|
||||
assert_eq!($e::A | $e::B | $e::C,
|
||||
EnumSet::<$e>::from_slice(&[7]));
|
||||
assert_eq!($e::A | $e::B | $e::C,
|
||||
EnumSet::<$e>::from_slice(&[7, 0, 0]));
|
||||
assert_eq!($e::A | $e::B | $e::C,
|
||||
EnumSet::<$e>::from_slice(&[7, 0, 0, 0]));
|
||||
assert_eq!($e::A | $e::B | $e::C,
|
||||
EnumSet::<$e>::from_slice(&[7, 0, 0, 0, 0]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic_from_array_truncated() {
|
||||
// array tests
|
||||
assert_eq!(EnumSet::<$e>::EMPTY,
|
||||
EnumSet::<$e>::from_array_truncated([]));
|
||||
assert_eq!($e::A | $e::B | $e::C,
|
||||
EnumSet::<$e>::from_array_truncated([7 | (1 << 31)]));
|
||||
assert_eq!($e::A | $e::B | $e::C,
|
||||
EnumSet::<$e>::from_array_truncated([7, 0, 16]));
|
||||
assert_eq!($e::A | $e::B | $e::C,
|
||||
EnumSet::<$e>::from_array_truncated([7, 0, 0, 16]));
|
||||
assert_eq!($e::A | $e::B | $e::C,
|
||||
EnumSet::<$e>::from_array_truncated([7, 0, 0, 0, 16]));
|
||||
|
||||
// array tests
|
||||
assert_eq!(EnumSet::<$e>::EMPTY,
|
||||
EnumSet::<$e>::from_slice_truncated(&[]));
|
||||
assert_eq!($e::A | $e::B | $e::C,
|
||||
EnumSet::<$e>::from_slice_truncated(&[7 | (1 << 31)]));
|
||||
assert_eq!($e::A | $e::B | $e::C,
|
||||
EnumSet::<$e>::from_slice_truncated(&[7, 0, 16]));
|
||||
assert_eq!($e::A | $e::B | $e::C,
|
||||
EnumSet::<$e>::from_slice_truncated(&[7, 0, 0, 16]));
|
||||
assert_eq!($e::A | $e::B | $e::C,
|
||||
EnumSet::<$e>::from_slice_truncated(&[7, 0, 0, 0, 16]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn fail_from_u8() {
|
||||
EnumSet::<$e>::from_u8(7 | (1 << 7));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn fail_from_u16() {
|
||||
EnumSet::<$e>::from_u16(7 | (1 << 15));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn fail_from_u32() {
|
||||
EnumSet::<$e>::from_u32(7 | (1 << 31));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn fail_from_usize() {
|
||||
EnumSet::<$e>::from_usize(7 | (1 << 31));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn fail_from_u64() {
|
||||
EnumSet::<$e>::from_u64(7 | (1 << 63));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn fail_from_u128() {
|
||||
EnumSet::<$e>::from_u128(7 | (1 << 127));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn fail_to_array() {
|
||||
($e::A | $e::B | $e::C).as_array::<0>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn fail_to_slice() {
|
||||
read_slice!($e::A | $e::B | $e::C, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn fail_from_array_1() {
|
||||
EnumSet::<$e>::from_array([7 | (1 << 63), 0, 0, 0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn fail_from_slice_1() {
|
||||
EnumSet::<$e>::from_slice(&[7 | (1 << 63), 0, 0, 0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn fail_from_array_2() {
|
||||
EnumSet::<$e>::from_array([7, 0, 0, 0, 1]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn fail_from_slice_2() {
|
||||
EnumSet::<$e>::from_slice(&[7, 0, 0, 0, 1]);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
check_simple_conversion!(enum_8_simple, Enum8);
|
||||
check_simple_conversion!(enum_16_simple, Enum16);
|
||||
check_simple_conversion!(enum_32_simple, Enum32);
|
||||
check_simple_conversion!(enum_64_simple, Enum64);
|
||||
check_simple_conversion!(enum_128_simple, Enum128);
|
||||
check_simple_conversion!(enum_192_simple, Enum192);
|
||||
check_simple_conversion!(enum_256_simple, Enum256);
|
||||
|
||||
macro_rules! check_oversized_64 {
|
||||
($mod:ident, $e:ident) => {
|
||||
mod $mod {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn downcast_to_u64() {
|
||||
assert_eq!(Some(7), ($e::A | $e::B | $e::C).try_as_u64());
|
||||
assert_eq!(Some([7]), ($e::A | $e::B | $e::C).try_as_array());
|
||||
assert_eq!(Some([7]), try_read_slice!($e::A | $e::B | $e::C, 1));
|
||||
|
||||
assert_eq!(None, ($e::E | $e::F | $e::G).try_as_u64());
|
||||
assert_eq!(None, ($e::E | $e::F | $e::G).try_as_array::<1>());
|
||||
assert_eq!(None, try_read_slice!($e::E | $e::F | $e::G, 1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn downcast_to_u64_truncated() {
|
||||
assert_eq!(0, ($e::E | $e::F | $e::G).as_u64_truncated());
|
||||
assert_eq!([0], ($e::E | $e::F | $e::G).as_array_truncated());
|
||||
assert_eq!([0], read_slice_truncated!($e::E | $e::F | $e::G, 1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn fail_to_u64() {
|
||||
($e::E | $e::F | $e::G).as_u64();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
check_oversized_64!(enum_128_oversized_64, Enum128);
|
||||
check_oversized_64!(enum_192_oversized_64, Enum192);
|
||||
check_oversized_64!(enum_256_oversized_64, Enum256);
|
||||
|
||||
macro_rules! check_oversized_128 {
|
||||
($mod:ident, $e:ident) => {
|
||||
mod $mod {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn downcast_to_u128() {
|
||||
assert_eq!(Some(7), ($e::A | $e::B | $e::C).try_as_u128());
|
||||
assert_eq!(None, ($e::E | $e::F | $e::G).try_as_u128());
|
||||
assert_eq!(0, ($e::E | $e::F | $e::G).as_u128_truncated());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn fail_to_u128() {
|
||||
($e::E | $e::F | $e::G).as_u128();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
check_oversized_128!(enum_192_oversized_128, Enum192);
|
||||
check_oversized_128!(enum_256_oversized_128, Enum256);
|
|
@ -21,6 +21,7 @@ pub enum SmallEnumExplicitDerive {
|
|||
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
|
||||
}
|
||||
#[derive(EnumSetType, Debug)]
|
||||
#[enumset(repr = "u128")]
|
||||
pub enum LargeEnum {
|
||||
_00, _01, _02, _03, _04, _05, _06, _07,
|
||||
_10, _11, _12, _13, _14, _15, _16, _17,
|
||||
|
@ -73,6 +74,30 @@ pub enum ReprEnum3 {
|
|||
pub enum ReprEnum4 {
|
||||
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
|
||||
}
|
||||
#[derive(EnumSetType, Debug)]
|
||||
pub enum GiantEnum {
|
||||
A = 100, B = 200, C = 300, D = 400, E = 500, F = 600, G = 700, H = 800,
|
||||
}
|
||||
#[derive(EnumSetType, Debug)]
|
||||
#[enumset(repr = "array")]
|
||||
pub enum SmallArrayEnum {
|
||||
A, B, C, D, E, F, G, H
|
||||
}
|
||||
#[derive(EnumSetType, Debug)]
|
||||
#[enumset(repr = "array")]
|
||||
pub enum MarginalArrayEnumS2 {
|
||||
A, B, C, D, E, F, G, H, Marginal = 64,
|
||||
}
|
||||
#[derive(EnumSetType, Debug)]
|
||||
#[enumset(repr = "array")]
|
||||
pub enum MarginalArrayEnumS2H {
|
||||
A = 64, B, C, D, E, F, G, H, Marginal = 127,
|
||||
}
|
||||
#[derive(EnumSetType, Debug)]
|
||||
#[enumset(repr = "array")]
|
||||
pub enum MarginalArrayEnumS3 {
|
||||
A, B, C, D, E, F, G, H, Marginal = 128,
|
||||
}
|
||||
|
||||
macro_rules! test_variants {
|
||||
($enum_name:ident $all_empty_test:ident $($variant:ident,)*) => {
|
||||
|
@ -113,7 +138,7 @@ macro_rules! test_enum {
|
|||
($e:ident, $mem_size:expr) => {
|
||||
const CONST_SET: EnumSet<$e> = enum_set!($e::A | $e::C);
|
||||
const CONST_1_SET: EnumSet<$e> = enum_set!($e::A);
|
||||
const EMPTY_SET: EnumSet<$e> = enum_set!();
|
||||
const EMPTY_SET: EnumSet<$e> = EnumSet::EMPTY;
|
||||
#[test]
|
||||
fn const_set() {
|
||||
assert_eq!(CONST_SET.len(), 2);
|
||||
|
@ -201,6 +226,13 @@ macro_rules! test_enum {
|
|||
assert_eq!(set, set_5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_iter_test() {
|
||||
for _ in EnumSet::<$e>::new() {
|
||||
panic!("should not happen");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iter_ordering_test() {
|
||||
let set_a = $e::A | $e::B | $e::E;
|
||||
|
@ -218,17 +250,34 @@ macro_rules! test_enum {
|
|||
|
||||
fn check_iter_size_hint(set: EnumSet<$e>) {
|
||||
let count = set.len();
|
||||
let mut itr = set.iter();
|
||||
for idx in 0 .. count {
|
||||
assert_eq!(itr.size_hint(), (count-idx, Some(count-idx)));
|
||||
assert_eq!(itr.len(), count-idx);
|
||||
assert!(itr.next().is_some());
|
||||
|
||||
// check for forward iteration
|
||||
{
|
||||
let mut itr = set.iter();
|
||||
for idx in 0 .. count {
|
||||
assert_eq!(itr.size_hint(), (count-idx, Some(count-idx)));
|
||||
assert_eq!(itr.len(), count-idx);
|
||||
assert!(itr.next().is_some());
|
||||
}
|
||||
assert_eq!(itr.size_hint(), (0, Some(0)));
|
||||
assert_eq!(itr.len(), 0);
|
||||
}
|
||||
|
||||
// check for backwards iteration
|
||||
{
|
||||
let mut itr = set.iter().rev();
|
||||
for idx in 0 .. count {
|
||||
assert_eq!(itr.size_hint(), (count-idx, Some(count-idx)));
|
||||
assert_eq!(itr.len(), count-idx);
|
||||
assert!(itr.next().is_some());
|
||||
}
|
||||
assert_eq!(itr.size_hint(), (0, Some(0)));
|
||||
assert_eq!(itr.len(), 0);
|
||||
}
|
||||
assert_eq!(itr.size_hint(), (0, Some(0)));
|
||||
assert_eq!(itr.len(), 0);
|
||||
}
|
||||
#[test]
|
||||
fn test_iter_size_hint() {
|
||||
check_iter_size_hint(EnumSet::<$e>::new());
|
||||
check_iter_size_hint(EnumSet::<$e>::all());
|
||||
let mut set = EnumSet::new();
|
||||
set.insert($e::A);
|
||||
|
@ -283,7 +332,21 @@ macro_rules! test_enum {
|
|||
#[test]
|
||||
fn to_from_bits() {
|
||||
let value = $e::A | $e::C | $e::D | $e::F | $e::E | $e::G;
|
||||
assert_eq!(EnumSet::from_u128(value.as_u128()), value);
|
||||
if EnumSet::<$e>::bit_width() < 128 {
|
||||
assert_eq!(EnumSet::from_u128(value.as_u128()), value);
|
||||
}
|
||||
if EnumSet::<$e>::bit_width() < 64 {
|
||||
assert_eq!(EnumSet::from_u64(value.as_u64()), value);
|
||||
}
|
||||
if EnumSet::<$e>::bit_width() < 32 {
|
||||
assert_eq!(EnumSet::from_u32(value.as_u32()), value);
|
||||
}
|
||||
if EnumSet::<$e>::bit_width() < 16 {
|
||||
assert_eq!(EnumSet::from_u16(value.as_u16()), value);
|
||||
}
|
||||
if EnumSet::<$e>::bit_width() < 8 {
|
||||
assert_eq!(EnumSet::from_u8(value.as_u8()), value);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -385,6 +448,11 @@ tests!(repr_enum_u32, test_enum!(ReprEnum, 4));
|
|||
tests!(repr_enum_u64, test_enum!(ReprEnum2, 4));
|
||||
tests!(repr_enum_isize, test_enum!(ReprEnum3, 4));
|
||||
tests!(repr_enum_c, test_enum!(ReprEnum4, 4));
|
||||
tests!(giant_enum, test_enum!(GiantEnum, 104));
|
||||
tests!(small_array_enum, test_enum!(SmallArrayEnum, 8));
|
||||
tests!(marginal_array_enum_s2, test_enum!(MarginalArrayEnumS2, 16));
|
||||
tests!(marginal_array_enum_s2h, test_enum!(MarginalArrayEnumS2H, 16));
|
||||
tests!(marginal_array_enum_s3, test_enum!(MarginalArrayEnumS3, 24));
|
||||
|
||||
#[derive(EnumSetType, Debug)]
|
||||
pub enum ThresholdEnum {
|
||||
|
@ -457,6 +525,6 @@ bits_tests!(test_u64_bits, U64, (U128), u64,
|
|||
as_u64 try_as_u64 as_u64_truncated from_u64 try_from_u64 from_u64_truncated);
|
||||
bits_tests!(test_u128_bits, U128, (), u128,
|
||||
as_u128 try_as_u128 as_u128_truncated from_u128 try_from_u128 from_u128_truncated);
|
||||
bits_tests!(test_uize_bits, U32, (U128), usize,
|
||||
bits_tests!(test_usize_bits, U32, (U128), usize,
|
||||
as_usize try_as_usize as_usize_truncated
|
||||
from_usize try_from_usize from_usize_truncated);
|
|
@ -10,12 +10,30 @@ type None = ();
|
|||
type Result = ();
|
||||
|
||||
#[derive(Serialize, Deserialize, EnumSetType, Debug)]
|
||||
#[enumset(serialize_as_list)]
|
||||
#[enumset(serialize_repr = "list")]
|
||||
#[serde(crate="serde2")]
|
||||
pub enum ListEnum {
|
||||
A, B, C, D, E, F, G, H,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, EnumSetType, Debug)]
|
||||
#[enumset(serialize_repr = "map")]
|
||||
#[serde(crate="serde2")]
|
||||
pub enum MapEnum {
|
||||
A, B, C, D, E, F, G, H,
|
||||
}
|
||||
|
||||
#[derive(EnumSetType, Debug)]
|
||||
#[enumset(serialize_repr = "array")]
|
||||
pub enum ArrayEnum {
|
||||
A, B, C, D, E, F, G, H,
|
||||
}
|
||||
|
||||
#[derive(EnumSetType, Debug)]
|
||||
pub enum LargeEnum {
|
||||
A, B, C, D, E=200, F, G, H,
|
||||
}
|
||||
|
||||
#[derive(EnumSetType, Debug)]
|
||||
#[enumset(serialize_repr = "u128")]
|
||||
pub enum ReprEnum {
|
||||
|
@ -30,23 +48,37 @@ pub enum DenyUnknownEnum {
|
|||
|
||||
macro_rules! serde_test_simple {
|
||||
($e:ident, $ser_size:expr) => {
|
||||
const VALUES: &[EnumSet<$e>] = &[
|
||||
enum_set!(),
|
||||
enum_set!($e::A | $e::C | $e::D | $e::F | $e::E | $e::G),
|
||||
enum_set!($e::A),
|
||||
enum_set!($e::H),
|
||||
enum_set!($e::A | $e::B),
|
||||
enum_set!($e::A | $e::B | $e::C | $e::D),
|
||||
enum_set!($e::A | $e::B | $e::C | $e::D | $e::F | $e::G | $e::H),
|
||||
enum_set!($e::G | $e::H),
|
||||
enum_set!($e::E | $e::F | $e::G | $e::H),
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn serialize_deserialize_test_bincode() {
|
||||
let value = $e::A | $e::C | $e::D | $e::F | $e::E | $e::G;
|
||||
let serialized = bincode::serialize(&value).unwrap();
|
||||
let deserialized = bincode::deserialize::<EnumSet<$e>>(&serialized).unwrap();
|
||||
assert_eq!(value, deserialized);
|
||||
if $ser_size != !0 {
|
||||
assert_eq!(serialized.len(), $ser_size);
|
||||
for &value in VALUES {
|
||||
let serialized = bincode::serialize(&value).unwrap();
|
||||
let deserialized = bincode::deserialize::<EnumSet<$e>>(&serialized).unwrap();
|
||||
assert_eq!(value, deserialized);
|
||||
if $ser_size != !0 {
|
||||
assert_eq!(serialized.len(), $ser_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_deserialize_test_json() {
|
||||
let value = $e::A | $e::C | $e::D | $e::F | $e::E | $e::G;
|
||||
let serialized = serde_json::to_string(&value).unwrap();
|
||||
let deserialized = serde_json::from_str::<EnumSet<$e>>(&serialized).unwrap();
|
||||
assert_eq!(value, deserialized);
|
||||
for &value in VALUES {
|
||||
let serialized = serde_json::to_string(&value).unwrap();
|
||||
let deserialized = serde_json::from_str::<EnumSet<$e>>(&serialized).unwrap();
|
||||
assert_eq!(value, deserialized);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -74,17 +106,32 @@ fn test_deny_unknown() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_reprs() {
|
||||
fn test_json_reprs_basic() {
|
||||
assert_eq!(ListEnum::A | ListEnum::C | ListEnum::F,
|
||||
serde_json::from_str::<EnumSet<ListEnum>>(r#"["A","C","F"]"#).unwrap());
|
||||
assert_eq!(MapEnum::A | MapEnum::C | MapEnum::F,
|
||||
serde_json::from_str::<EnumSet<MapEnum>>(r#"{"A":true,"C":true,"F":true}"#).unwrap());
|
||||
assert_eq!(ReprEnum::A | ReprEnum::C | ReprEnum::D,
|
||||
serde_json::from_str::<EnumSet<ReprEnum>>("13").unwrap());
|
||||
assert_eq!(r#"["A","C","F"]"#,
|
||||
serde_json::to_string(&(ListEnum::A | ListEnum::C | ListEnum::F)).unwrap());
|
||||
assert_eq!(r#"{"A":true,"C":true,"F":true}"#,
|
||||
serde_json::to_string(&(MapEnum::A | MapEnum::C | MapEnum::F)).unwrap());
|
||||
assert_eq!("13",
|
||||
serde_json::to_string(&(ReprEnum::A | ReprEnum::C | ReprEnum::D)).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_reprs_edge_cases() {
|
||||
assert_eq!(MapEnum::A | MapEnum::C | MapEnum::F,
|
||||
serde_json::from_str::<EnumSet<MapEnum>>(r#"{"D":false,"A":true,"E":false,"C":true,"F":true}"#).unwrap());
|
||||
assert_eq!(LargeEnum::A | LargeEnum::B | LargeEnum::C | LargeEnum::D,
|
||||
serde_json::from_str::<EnumSet<LargeEnum>>(r#"[15]"#).unwrap());
|
||||
}
|
||||
|
||||
tests!(list_enum, serde_test_simple!(ListEnum, !0));
|
||||
tests!(map_enum, serde_test_simple!(MapEnum, !0));
|
||||
tests!(array_enum, serde_test_simple!(ArrayEnum, !0));
|
||||
tests!(large_enum, serde_test_simple!(LargeEnum, !0));
|
||||
tests!(repr_enum, serde_test!(ReprEnum, 16));
|
||||
tests!(deny_unknown_enum, serde_test_simple!(DenyUnknownEnum, 16));
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
#[test]
|
||||
fn ui() {
|
||||
let t = trybuild::TestCases::new();
|
||||
|
||||
t.compile_fail("tests/compile-fail/*.rs");
|
||||
t.pass("tests/compile-pass/*.rs");
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
t.compile_fail("tests/compile-fail-serde/*.rs");
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"2de22e952501bb15d5b13fef8ead3f53d6a40af1c1a284d332bb4a090ba59a3c","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"79b4502d93c23afe2765054a80d03716d4934eb260cdfbe8c401898df3aa5a8f","README.md":"d45f82ad73b39aad91f85d0688bc6a94402293c40c295d0cab7b2a9b7225677b","src/lib.rs":"149dbabe0beb802bb41a560cd03f95c4992600ac07fdf57a3b1610a557bc10f4"},"package":"03e7b551eba279bf0fa88b83a46330168c1560a52a94f5126f892f0b364ab3e0"}
|
||||
{"files":{"Cargo.toml":"b22d675f79e7c888bd013b3beff0335cdfde340351dd2c46b9b2fd55156d0fac","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"7405fdff2ff8682a5384da664436b9b2aca47557572513f5b27686a5347f9b8e","README.md":"d45f82ad73b39aad91f85d0688bc6a94402293c40c295d0cab7b2a9b7225677b","src/lib.rs":"ad2d7adf74ded236b268e4c3ba9e18260527323d1f030c7a4c39fdb550c0634a"},"package":"e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af"}
|
|
@ -10,21 +10,22 @@
|
|||
# See Cargo.toml.orig for the original contents.
|
||||
|
||||
[package]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
name = "enumset_derive"
|
||||
version = "0.6.1"
|
||||
version = "0.8.1"
|
||||
authors = ["Alissa Rao <lymia@lymiahugs.com>"]
|
||||
description = "An internal helper crate for enumset. Not public API."
|
||||
documentation = "https://lymia.moe/doc/enumset/enumset/"
|
||||
readme = "README.md"
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/Lymia/enumset"
|
||||
resolver = "1"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies.darling]
|
||||
version = "0.14"
|
||||
version = "0.20"
|
||||
default-features = false
|
||||
|
||||
[dependencies.proc-macro-crate]
|
||||
|
@ -38,7 +39,7 @@ version = "1"
|
|||
version = "1"
|
||||
|
||||
[dependencies.syn]
|
||||
version = "1"
|
||||
version = "2"
|
||||
|
||||
[features]
|
||||
serde = []
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2017-2020 Alissa Rao <lymiahugs@gmail.com>
|
||||
Copyright (c) 2017-2023 Alissa Rao <lymiahugs@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -7,6 +7,353 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
## [3.0.0] - 2023-05-01
|
||||
|
||||
This breaking release should not impact most users.
|
||||
It only affects custom character sets used for base64 of which there are no instances of on GitHub.
|
||||
|
||||
### Changed
|
||||
|
||||
* Upgrade base64 to v0.21 (#543)
|
||||
Thanks to @jeff-hiner for submitting the PR.
|
||||
|
||||
Remove support for custom character sets.
|
||||
This is technically a breaking change.
|
||||
A code search on GitHub revealed no instances of anyone using that, and `serde_with` ships with many predefined character sets.
|
||||
The removal means that future base64 upgrade will no longer be breaking changes.
|
||||
|
||||
## [2.3.3] - 2023-04-27
|
||||
|
||||
### Changed
|
||||
|
||||
* Update `syn` to v2 and `darling` to v0.20 (#578)
|
||||
Update proc-macro dependencies.
|
||||
This change should have no impact on users, but now uses the same dependency as `serde_derive`.
|
||||
|
||||
## [2.3.2] - 2023-04-05
|
||||
|
||||
### Changed
|
||||
|
||||
* Improve the error message when deserializing `OneOrMany` or `PickFirst` fails.
|
||||
It now includes the original error message for each of the individual variants.
|
||||
This is possible by dropping untagged enums as the internal implementations, since they will likely never support this, as these old PRs show [serde#2376](https://github.com/serde-rs/serde/pull/2376) and [serde#1544](https://github.com/serde-rs/serde/pull/1544).
|
||||
|
||||
The new errors look like:
|
||||
|
||||
```text
|
||||
OneOrMany could not deserialize any variant:
|
||||
One: invalid type: map, expected u32
|
||||
Many: invalid type: map, expected a sequence
|
||||
```
|
||||
|
||||
```text
|
||||
PickFirst could not deserialize any variant:
|
||||
First: invalid type: string "Abc", expected u32
|
||||
Second: invalid digit found in string
|
||||
```
|
||||
|
||||
### Fixed
|
||||
|
||||
* Specify the correct minimum serde version as dependency. (#588)
|
||||
Thanks to @nox for submitting a PR.
|
||||
|
||||
## [2.3.1] - 2023-03-10
|
||||
|
||||
### Fixed
|
||||
|
||||
* Undo the changes to the trait bound for `Seq`. (#570, #571)
|
||||
The new implementation caused issues with serialization formats that require the sequence length beforehand.
|
||||
It also caused problems, that certain attributes which worked before no longer worked, due to mismatching number of references.
|
||||
|
||||
Thanks to @stefunctional for reporting and for @stephaneyfx for providing a test case.
|
||||
|
||||
## [2.3.0] - 2023-03-09
|
||||
|
||||
### Added
|
||||
|
||||
* Add `serde_as` compatible versions for the existing duplicate key and value handling. (#534)
|
||||
The new types `MapPreventDuplicates`, `MapFirstKeyWins`, `SetPreventDuplicates`, and `SetLastValueWins` can replace the existing modules `maps_duplicate_key_is_error`, `maps_first_key_wins`, `sets_duplicate_value_is_error`, and `sets_last_value_wins`.
|
||||
* Added a new `KeyValueMap` type using the map key as a struct field. (#341)
|
||||
This conversion is useful for maps, where an ID value is the map key, but the ID should become part of a single struct.
|
||||
The conversion allows this, by using a special field named `$key$`.
|
||||
|
||||
This conversion is possible for structs and maps, using the `$key$` field.
|
||||
Tuples, tuple structs, and sequences are supported by turning the first value into the map key.
|
||||
|
||||
Each of the `SimpleStruct`s
|
||||
|
||||
```rust
|
||||
// Somewhere there is a collection:
|
||||
// #[serde_as(as = "KeyValueMap<_>")]
|
||||
// Vec<SimpleStruct>,
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct SimpleStruct {
|
||||
b: bool,
|
||||
// The field named `$key$` will become the map key
|
||||
#[serde(rename = "$key$")]
|
||||
id: String,
|
||||
i: i32,
|
||||
}
|
||||
```
|
||||
|
||||
will turn into a JSON snippet like this.
|
||||
|
||||
```json
|
||||
"id-0000": {
|
||||
"b": false,
|
||||
"i": 123
|
||||
},
|
||||
```
|
||||
|
||||
### Changed
|
||||
|
||||
* Relax the trait bounds of `Seq` to allow for more custom types. (#565)
|
||||
This extends the support beyond tuples.
|
||||
|
||||
### Fixed
|
||||
|
||||
* `EnumMap` passes the `human_readable` status of the `Serializer` to more places.
|
||||
* Support `alloc` on targets without `target_has_atomic = "ptr"`. (#560)
|
||||
Thanks to @vembacher for reporting and fixing the issue.
|
||||
|
||||
## [2.2.0] - 2023-01-09
|
||||
|
||||
### Added
|
||||
|
||||
* Add new `Map` and `Seq` types for converting between maps and tuple lists. (#527)
|
||||
|
||||
The behavior is not new, but already present using `BTreeMap`/`HashMap` or `Vec`.
|
||||
However, the new types `Map` and `Seq` are also available on `no_std`, even without the `alloc` feature.
|
||||
|
||||
### Changed
|
||||
|
||||
* Pin the `serde_with_macros` dependency to the same version as the main crate.
|
||||
This simplifies publishing and ensures that always a compatible version is picked.
|
||||
|
||||
### Fixed
|
||||
|
||||
* `serde_with::apply` had an issue matching types when invisible token groups where in use (#538)
|
||||
The token groups can stem from macro_rules expansion, but should be treated mostly transparent.
|
||||
The old code required a group to match a group, while now groups are silently removed when checking for type patterns.
|
||||
|
||||
## [2.1.0] - 2022-11-16
|
||||
|
||||
### Added
|
||||
|
||||
* Add new `apply` attribute to simplify repetitive attributes over many fields.
|
||||
Multiple rules and multiple attributes can be provided each.
|
||||
|
||||
```rust
|
||||
#[serde_with::apply(
|
||||
Option => #[serde(default)] #[serde(skip_serializing_if = "Option::is_none")],
|
||||
Option<bool> => #[serde(rename = "bool")],
|
||||
)]
|
||||
#[derive(serde::Serialize)]
|
||||
struct Data {
|
||||
a: Option<String>,
|
||||
b: Option<u64>,
|
||||
c: Option<String>,
|
||||
d: Option<bool>,
|
||||
}
|
||||
```
|
||||
|
||||
The `apply` attribute will expand into this, applying the attributs to the matching fields:
|
||||
|
||||
```rust
|
||||
#[derive(serde::Serialize)]
|
||||
struct Data {
|
||||
#[serde(default)]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
a: Option<String>,
|
||||
#[serde(default)]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
b: Option<u64>,
|
||||
#[serde(default)]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
c: Option<String>,
|
||||
#[serde(default)]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(rename = "bool")]
|
||||
d: Option<bool>,
|
||||
}
|
||||
```
|
||||
|
||||
The attribute supports field matching using many rules, such as `_` to apply to all fields and partial generics like `Option` to match any `Option` be it `Option<String>`, `Option<bool>`, or `Option<T>`.
|
||||
|
||||
### Fixed
|
||||
|
||||
* The derive macros `SerializeDisplay` and `DeserializeFromStr` now take better care not to use conflicting names for generic values. (#526)
|
||||
All used generics now start with `__` to make conflicts with manually written code unlikely.
|
||||
|
||||
Thanks to @Elrendio for submitting a PR fixing the issue.
|
||||
|
||||
## [2.0.1] - 2022-09-09
|
||||
|
||||
### Added
|
||||
|
||||
* `time` added support for the well-known `Iso8601` format.
|
||||
This extends the existing support of `Rfc2822` and `Rfc3339`.
|
||||
|
||||
### Changed
|
||||
|
||||
* Warn if `serde_as` is used on an enum variant.
|
||||
Attributes on enum variants were never supported.
|
||||
But `#[serde(with = "...")]` can be added on variants, such that some confusion can occur when migration ([#499](https://github.com/jonasbb/serde_with/issues/499)).
|
||||
|
||||
### Note
|
||||
|
||||
A cargo bug ([cargo#10801](https://github.com/rust-lang/cargo/issues/10801)) means that upgrading from v1 to v2 may add unnecessary crates to the `Cargo.lock` file.
|
||||
A diff of the lock-file makes it seem that `serde_with` depends on new crates, even though these crates are unused and will not get compiled or linked.
|
||||
However, tools consuming `Cargo.lock` or `cargo metadata` might give wrong results.
|
||||
|
||||
## [2.0.0] - 2022-07-17
|
||||
|
||||
### Added
|
||||
|
||||
* Make `JsonString<T>` smarter by allowing nesting `serde_as` definitions.
|
||||
This allows applying custom serialization logic, before the value gets converted into a JSON string.
|
||||
|
||||
```rust
|
||||
// Rust
|
||||
#[serde_as(as = "JsonString<Vec<(JsonString, _)>>")]
|
||||
value: BTreeMap<[u8; 2], u32>,
|
||||
|
||||
// JSON
|
||||
{"value":"[[\"[1,2]\",3],[\"[4,5]\",6]]"}
|
||||
```
|
||||
|
||||
### Changed
|
||||
|
||||
* Make `#[serde_as]` behave more intuitive on `Option<T>` fields.
|
||||
|
||||
The `#[serde_as]` macro now detects if a `#[serde_as(as = "Option<S>")]` is used on a field of type `Option<T>` and applies `#[serde(default)]` to the field.
|
||||
This restores the ability to deserialize with missing fields and fixes a common annoyance (#183, #185, #311, #417).
|
||||
This is a breaking change, since now deserialization will pass where it did not before and this might be undesired.
|
||||
|
||||
The `Option` field and transformation are detected by directly matching on the type name.
|
||||
These variants are detected as `Option`.
|
||||
* `Option`
|
||||
* `std::option::Option`, with or without leading `::`
|
||||
* `core::option::Option`, with or without leading `::`
|
||||
|
||||
If an existing `default` attribute is detected, the attribute is not applied again.
|
||||
This behavior can be suppressed by using `#[serde_as(no_default)]` or `#[serde_as(as = "Option<S>", no_default)]`.
|
||||
* `NoneAsEmptyString` and `string_empty_as_none` use a different serialization bound (#388).
|
||||
|
||||
Both types used `AsRef<str>` as the serialization bound.
|
||||
This is limiting for non-string types like `Option<i32>`.
|
||||
The deserialization often was already more flexible, due to the `FromStr` bound.
|
||||
|
||||
For most std types this should have little impact, as the types implementing `AsRef<str>` mostly implement `Display`, too, such as `String`, `Cow<str>`, or `Rc<str>`.
|
||||
* Bump MSRV to 1.60. This is required for the optional dependency feature syntax in cargo.
|
||||
|
||||
### Removed
|
||||
|
||||
* Remove old module based conversions.
|
||||
|
||||
The newer `serde_as` based conversions are preferred.
|
||||
|
||||
* `seq_display_fromstr`: Use `DisplayFromStr` in combination with your container type:
|
||||
|
||||
```rust
|
||||
#[serde_as(as = "BTreeSet<DisplayFromStr>")]
|
||||
addresses: BTreeSet<Ipv4Addr>,
|
||||
#[serde_as(as = "Vec<DisplayFromStr>")]
|
||||
bools: Vec<bool>,
|
||||
```
|
||||
|
||||
* `tuple_list_as_map`: Use `BTreeMap` on a `Vec` of tuples:
|
||||
|
||||
```rust
|
||||
#[serde_as(as = "BTreeMap<_, _>")] // HashMap will also work
|
||||
s: Vec<(i32, String)>,
|
||||
```
|
||||
|
||||
* `map_as_tuple_list` can be replaced with `#[serde_as(as = "Vec<(_, _)>")]`.
|
||||
* `display_fromstr` can be replaced with `#[serde_as(as = "DisplayFromStr")]`.
|
||||
* `bytes_or_string` can be replaced with `#[serde_as(as = "BytesOrString")]`.
|
||||
* `default_on_error` can be replaced with `#[serde_as(as = "DefaultOnError")]`.
|
||||
* `default_on_null` can be replaced with `#[serde_as(as = "DefaultOnNull")]`.
|
||||
* `string_empty_as_none` can be replaced with `#[serde_as(as = "NoneAsEmptyString")]`.
|
||||
* `StringWithSeparator` can now only be used in `serde_as`.
|
||||
The definition of the `Separator` trait and its implementations have been moved to the `formats` module.
|
||||
* `json::nested` can be replaced with `#[serde_as(as = "json::JsonString")]`.
|
||||
|
||||
* Remove previously deprecated modules.
|
||||
|
||||
* `sets_first_value_wins`
|
||||
* `btreemap_as_tuple_list` and `hashmap_as_tuple_list` can be replaced with `#[serde_as(as = "Vec<(_, _)>")]`.
|
||||
|
||||
### Note
|
||||
|
||||
A cargo bug ([cargo#10801](https://github.com/rust-lang/cargo/issues/10801)) means that upgrading from v1 to v2 may add unnecessary crates to the `Cargo.lock` file.
|
||||
A diff of the lock-file makes it seem that `serde_with` depends on new crates, even though these crates are unused and will not get compiled or linked.
|
||||
|
||||
## [2.0.0-rc.0] - 2022-06-29
|
||||
|
||||
### Changed
|
||||
|
||||
* Make `#[serde_as]` behave more intuitive on `Option<T>` fields.
|
||||
|
||||
The `#[serde_as]` macro now detects if a `#[serde_as(as = "Option<S>")]` is used on a field of type `Option<T>` and applies `#[serde(default)]` to the field.
|
||||
This restores the ability to deserialize with missing fields and fixes a common annoyance (#183, #185, #311, #417).
|
||||
This is a breaking change, since now deserialization will pass where it did not before and this might be undesired.
|
||||
|
||||
The `Option` field and transformation are detected by directly matching on the type name.
|
||||
These variants are detected as `Option`.
|
||||
* `Option`
|
||||
* `std::option::Option`, with or without leading `::`
|
||||
* `core::option::Option`, with or without leading `::`
|
||||
|
||||
If an existing `default` attribute is detected, the attribute is not applied again.
|
||||
This behavior can be suppressed by using `#[serde_as(no_default)]` or `#[serde_as(as = "Option<S>", no_default)]`.
|
||||
* `NoneAsEmptyString` and `string_empty_as_none` use a different serialization bound (#388).
|
||||
|
||||
Both types used `AsRef<str>` as the serialization bound.
|
||||
This is limiting for non-string types like `Option<i32>`.
|
||||
The deserialization often was already more flexible, due to the `FromStr` bound.
|
||||
|
||||
For most std types this should have little impact, as the types implementing `AsRef<str>` mostly implement `Display`, too, such as `String`, `Cow<str>`, or `Rc<str>`.
|
||||
* Bump MSRV to 1.60. This is required for the optional dependency feature syntax in cargo.
|
||||
|
||||
### Removed
|
||||
|
||||
* Remove old module based conversions.
|
||||
|
||||
The newer `serde_as` based conversions are preferred.
|
||||
|
||||
* `seq_display_fromstr`: Use `DisplayFromStr` in combination with your container type:
|
||||
|
||||
```rust
|
||||
#[serde_as(as = "BTreeSet<DisplayFromStr>")]
|
||||
addresses: BTreeSet<Ipv4Addr>,
|
||||
#[serde_as(as = "Vec<DisplayFromStr>")]
|
||||
bools: Vec<bool>,
|
||||
```
|
||||
|
||||
* `tuple_list_as_map`: Use `BTreeMap` on a `Vec` of tuples:
|
||||
|
||||
```rust
|
||||
#[serde_as(as = "BTreeMap<_, _>")] // HashMap will also work
|
||||
s: Vec<(i32, String)>,
|
||||
```
|
||||
|
||||
* `map_as_tuple_list` can be replaced with `#[serde_as(as = "Vec<(_, _)>")]`.
|
||||
* `display_fromstr` can be replaced with `#[serde_as(as = "DisplayFromStr")]`.
|
||||
* `bytes_or_string` can be replaced with `#[serde_as(as = "BytesOrString")]`.
|
||||
* `default_on_error` can be replaced with `#[serde_as(as = "DefaultOnError")]`.
|
||||
* `default_on_null` can be replaced with `#[serde_as(as = "DefaultOnNull")]`.
|
||||
* `string_empty_as_none` can be replaced with `#[serde_as(as = "NoneAsEmptyString")]`.
|
||||
* `StringWithSeparator` can now only be used in `serde_as`.
|
||||
The definition of the `Separator` trait and its implementations have been moved to the `formats` module.
|
||||
* `json::nested` can be replaced with `#[serde_as(as = "json::JsonString")]`.
|
||||
|
||||
* Remove previously deprecated modules.
|
||||
|
||||
* `sets_first_value_wins`
|
||||
* `btreemap_as_tuple_list` and `hashmap_as_tuple_list` can be replaced with `#[serde_as(as = "Vec<(_, _)>")]`.
|
||||
|
||||
## [1.14.0] - 2022-05-29
|
||||
|
||||
### Added
|
||||
|
@ -96,7 +443,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|||
* Account for generics when deriving implementations with `SerializeDisplay` and `DeserializeFromStr` #413
|
||||
* Provide better error messages when parsing types fails #423
|
||||
|
||||
|
||||
## [1.12.0] - 2022-02-07
|
||||
|
||||
### Added
|
||||
|
|
|
@ -10,10 +10,10 @@
|
|||
# See Cargo.toml.orig for the original contents.
|
||||
|
||||
[package]
|
||||
edition = "2018"
|
||||
rust-version = "1.53"
|
||||
edition = "2021"
|
||||
rust-version = "1.60"
|
||||
name = "serde_with"
|
||||
version = "1.14.0"
|
||||
version = "3.0.0"
|
||||
authors = [
|
||||
"Jonas Bushart",
|
||||
"Marcin Kaźmierczak",
|
||||
|
@ -34,10 +34,12 @@ keywords = [
|
|||
"serialization",
|
||||
"deserialization",
|
||||
]
|
||||
categories = ["encoding"]
|
||||
categories = [
|
||||
"encoding",
|
||||
"no-std",
|
||||
]
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/jonasbb/serde_with"
|
||||
resolver = "2"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
@ -47,6 +49,29 @@ rustdoc-args = [
|
|||
"--generate-link-to-definition",
|
||||
]
|
||||
|
||||
[package.metadata.release]
|
||||
tag = true
|
||||
tag-message = "{{crate_name}} v{{version}}"
|
||||
tag-name = "v{{version}}"
|
||||
|
||||
[[package.metadata.release.pre-release-replacements]]
|
||||
file = "CHANGELOG.md"
|
||||
replace = """
|
||||
[Unreleased]
|
||||
|
||||
## [{{version}}] - {{date}}"""
|
||||
search = '\[Unreleased\]'
|
||||
|
||||
[[package.metadata.release.pre-release-replacements]]
|
||||
file = "src/lib.rs"
|
||||
replace = "https://docs.rs/serde_with/{{version}}/"
|
||||
search = 'https://docs\.rs/serde_with/[\d.]+/'
|
||||
|
||||
[[package.metadata.release.pre-release-replacements]]
|
||||
file = "README.md"
|
||||
replace = "https://docs.rs/serde_with/{{version}}/"
|
||||
search = 'https://docs\.rs/serde_with/[\d.]+/'
|
||||
|
||||
[[test]]
|
||||
name = "base64"
|
||||
path = "tests/base64.rs"
|
||||
|
@ -56,10 +81,10 @@ required-features = [
|
|||
]
|
||||
|
||||
[[test]]
|
||||
name = "chrono"
|
||||
path = "tests/chrono.rs"
|
||||
name = "chrono_0_4"
|
||||
path = "tests/chrono_0_4.rs"
|
||||
required-features = [
|
||||
"chrono",
|
||||
"chrono_0_4",
|
||||
"macros",
|
||||
]
|
||||
|
||||
|
@ -72,10 +97,10 @@ required-features = [
|
|||
]
|
||||
|
||||
[[test]]
|
||||
name = "indexmap"
|
||||
path = "tests/indexmap.rs"
|
||||
name = "indexmap_1"
|
||||
path = "tests/indexmap_1.rs"
|
||||
required-features = [
|
||||
"indexmap",
|
||||
"indexmap_1",
|
||||
"macros",
|
||||
]
|
||||
|
||||
|
@ -105,18 +130,24 @@ name = "derives"
|
|||
path = "tests/derives/lib.rs"
|
||||
required-features = ["macros"]
|
||||
|
||||
[dependencies.base64_crate]
|
||||
version = "0.13.0"
|
||||
optional = true
|
||||
package = "base64"
|
||||
[[test]]
|
||||
name = "with_prefix"
|
||||
path = "tests/with_prefix.rs"
|
||||
required-features = ["macros"]
|
||||
|
||||
[dependencies.chrono_crate]
|
||||
version = "0.4.1"
|
||||
features = [
|
||||
"clock",
|
||||
"serde",
|
||||
"std",
|
||||
]
|
||||
[[test]]
|
||||
name = "rust"
|
||||
path = "tests/rust.rs"
|
||||
required-features = ["alloc"]
|
||||
|
||||
[dependencies.base64]
|
||||
version = "0.21.0"
|
||||
optional = true
|
||||
default-features = false
|
||||
|
||||
[dependencies.chrono_0_4]
|
||||
version = "0.4.20"
|
||||
features = ["serde"]
|
||||
optional = true
|
||||
default-features = false
|
||||
package = "chrono"
|
||||
|
@ -126,35 +157,39 @@ version = "0.3.3"
|
|||
optional = true
|
||||
|
||||
[dependencies.hex]
|
||||
version = "0.4.2"
|
||||
version = "0.4.3"
|
||||
optional = true
|
||||
default-features = false
|
||||
|
||||
[dependencies.indexmap_crate]
|
||||
[dependencies.indexmap_1]
|
||||
version = "1.8"
|
||||
features = ["serde-1"]
|
||||
optional = true
|
||||
default-features = false
|
||||
package = "indexmap"
|
||||
|
||||
[dependencies.serde]
|
||||
version = "1.0.122"
|
||||
version = "1.0.157"
|
||||
features = ["derive"]
|
||||
default-features = false
|
||||
|
||||
[dependencies.serde_json]
|
||||
version = "1.0.1"
|
||||
version = "1.0.45"
|
||||
optional = true
|
||||
default-features = false
|
||||
|
||||
[dependencies.serde_with_macros]
|
||||
version = "1.5.2"
|
||||
version = "=3.0.0"
|
||||
optional = true
|
||||
|
||||
[dependencies.time_0_3]
|
||||
version = "~0.3"
|
||||
features = ["serde-well-known"]
|
||||
version = "~0.3.11"
|
||||
optional = true
|
||||
default-features = false
|
||||
package = "time"
|
||||
|
||||
[dev-dependencies.expect-test]
|
||||
version = "1.0.0"
|
||||
version = "1.3.0"
|
||||
|
||||
[dev-dependencies.fnv]
|
||||
version = "1.0.6"
|
||||
|
@ -169,7 +204,7 @@ version = "0.3.16"
|
|||
version = "1.0.0"
|
||||
|
||||
[dev-dependencies.regex]
|
||||
version = "1.3.9"
|
||||
version = "1.8.1"
|
||||
features = ["std"]
|
||||
default-features = false
|
||||
|
||||
|
@ -177,13 +212,13 @@ default-features = false
|
|||
version = "1.1.0"
|
||||
|
||||
[dev-dependencies.ron]
|
||||
version = "0.7"
|
||||
version = "0.8"
|
||||
|
||||
[dev-dependencies.rustversion]
|
||||
version = "1.0.0"
|
||||
|
||||
[dev-dependencies.serde-xml-rs]
|
||||
version = "0.5.0"
|
||||
version = "0.6.0"
|
||||
|
||||
[dev-dependencies.serde_json]
|
||||
version = "1.0.25"
|
||||
|
@ -193,22 +228,59 @@ features = ["preserve_order"]
|
|||
version = "1.0.124"
|
||||
|
||||
[dev-dependencies.serde_yaml]
|
||||
version = "0.8.21"
|
||||
version = "0.9.2"
|
||||
|
||||
[dev-dependencies.version-sync]
|
||||
version = "0.9.1"
|
||||
|
||||
[features]
|
||||
base64 = ["base64_crate"]
|
||||
chrono = ["chrono_crate"]
|
||||
default = ["macros"]
|
||||
guide = [
|
||||
"doc-comment",
|
||||
alloc = [
|
||||
"serde/alloc",
|
||||
"base64?/alloc",
|
||||
"chrono_0_4?/alloc",
|
||||
"hex?/alloc",
|
||||
"serde_json?/alloc",
|
||||
"time_0_3?/alloc",
|
||||
]
|
||||
base64 = [
|
||||
"dep:base64",
|
||||
"alloc",
|
||||
]
|
||||
chrono = ["chrono_0_4"]
|
||||
chrono_0_4 = ["dep:chrono_0_4"]
|
||||
default = [
|
||||
"std",
|
||||
"macros",
|
||||
]
|
||||
indexmap = ["indexmap_crate"]
|
||||
json = ["serde_json"]
|
||||
macros = ["serde_with_macros"]
|
||||
guide = [
|
||||
"dep:doc-comment",
|
||||
"macros",
|
||||
"std",
|
||||
]
|
||||
hex = [
|
||||
"dep:hex",
|
||||
"alloc",
|
||||
]
|
||||
indexmap = ["indexmap_1"]
|
||||
indexmap_1 = [
|
||||
"dep:indexmap_1",
|
||||
"alloc",
|
||||
]
|
||||
json = [
|
||||
"dep:serde_json",
|
||||
"alloc",
|
||||
]
|
||||
macros = ["dep:serde_with_macros"]
|
||||
std = [
|
||||
"alloc",
|
||||
"serde/std",
|
||||
"chrono_0_4?/clock",
|
||||
"chrono_0_4?/std",
|
||||
"indexmap_1?/std",
|
||||
"time_0_3?/serde-well-known",
|
||||
"time_0_3?/std",
|
||||
]
|
||||
time_0_3 = ["dep:time_0_3"]
|
||||
|
||||
[badges.maintenance]
|
||||
status = "actively-developed"
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
[![Build Status](https://github.com/jonasbb/serde_with/workflows/Rust%20CI/badge.svg)](https://github.com/jonasbb/serde_with)
|
||||
[![codecov](https://codecov.io/gh/jonasbb/serde_with/branch/master/graph/badge.svg)](https://codecov.io/gh/jonasbb/serde_with)
|
||||
[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/4322/badge)](https://bestpractices.coreinfrastructure.org/projects/4322)
|
||||
[![Binder](https://img.shields.io/badge/Try%20on%20-binder-579ACA.svg?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFkAAABZCAMAAABi1XidAAAB8lBMVEX///9XmsrmZYH1olJXmsr1olJXmsrmZYH1olJXmsr1olJXmsrmZYH1olL1olJXmsr1olJXmsrmZYH1olL1olJXmsrmZYH1olJXmsr1olL1olJXmsrmZYH1olL1olJXmsrmZYH1olL1olL0nFf1olJXmsrmZYH1olJXmsq8dZb1olJXmsrmZYH1olJXmspXmspXmsr1olL1olJXmsrmZYH1olJXmsr1olL1olJXmsrmZYH1olL1olLeaIVXmsrmZYH1olL1olL1olJXmsrmZYH1olLna31Xmsr1olJXmsr1olJXmsrmZYH1olLqoVr1olJXmsr1olJXmsrmZYH1olL1olKkfaPobXvviGabgadXmsqThKuofKHmZ4Dobnr1olJXmsr1olJXmspXmsr1olJXmsrfZ4TuhWn1olL1olJXmsqBi7X1olJXmspZmslbmMhbmsdemsVfl8ZgmsNim8Jpk8F0m7R4m7F5nLB6jbh7jbiDirOEibOGnKaMhq+PnaCVg6qWg6qegKaff6WhnpKofKGtnomxeZy3noG6dZi+n3vCcpPDcpPGn3bLb4/Mb47UbIrVa4rYoGjdaIbeaIXhoWHmZYHobXvpcHjqdHXreHLroVrsfG/uhGnuh2bwj2Hxk17yl1vzmljzm1j0nlX1olL3AJXWAAAAbXRSTlMAEBAQHx8gICAuLjAwMDw9PUBAQEpQUFBXV1hgYGBkcHBwcXl8gICAgoiIkJCQlJicnJ2goKCmqK+wsLC4usDAwMjP0NDQ1NbW3Nzg4ODi5+3v8PDw8/T09PX29vb39/f5+fr7+/z8/Pz9/v7+zczCxgAABC5JREFUeAHN1ul3k0UUBvCb1CTVpmpaitAGSLSpSuKCLWpbTKNJFGlcSMAFF63iUmRccNG6gLbuxkXU66JAUef/9LSpmXnyLr3T5AO/rzl5zj137p136BISy44fKJXuGN/d19PUfYeO67Znqtf2KH33Id1psXoFdW30sPZ1sMvs2D060AHqws4FHeJojLZqnw53cmfvg+XR8mC0OEjuxrXEkX5ydeVJLVIlV0e10PXk5k7dYeHu7Cj1j+49uKg7uLU61tGLw1lq27ugQYlclHC4bgv7VQ+TAyj5Zc/UjsPvs1sd5cWryWObtvWT2EPa4rtnWW3JkpjggEpbOsPr7F7EyNewtpBIslA7p43HCsnwooXTEc3UmPmCNn5lrqTJxy6nRmcavGZVt/3Da2pD5NHvsOHJCrdc1G2r3DITpU7yic7w/7Rxnjc0kt5GC4djiv2Sz3Fb2iEZg41/ddsFDoyuYrIkmFehz0HR2thPgQqMyQYb2OtB0WxsZ3BeG3+wpRb1vzl2UYBog8FfGhttFKjtAclnZYrRo9ryG9uG/FZQU4AEg8ZE9LjGMzTmqKXPLnlWVnIlQQTvxJf8ip7VgjZjyVPrjw1te5otM7RmP7xm+sK2Gv9I8Gi++BRbEkR9EBw8zRUcKxwp73xkaLiqQb+kGduJTNHG72zcW9LoJgqQxpP3/Tj//c3yB0tqzaml05/+orHLksVO+95kX7/7qgJvnjlrfr2Ggsyx0eoy9uPzN5SPd86aXggOsEKW2Prz7du3VID3/tzs/sSRs2w7ovVHKtjrX2pd7ZMlTxAYfBAL9jiDwfLkq55Tm7ifhMlTGPyCAs7RFRhn47JnlcB9RM5T97ASuZXIcVNuUDIndpDbdsfrqsOppeXl5Y+XVKdjFCTh+zGaVuj0d9zy05PPK3QzBamxdwtTCrzyg/2Rvf2EstUjordGwa/kx9mSJLr8mLLtCW8HHGJc2R5hS219IiF6PnTusOqcMl57gm0Z8kanKMAQg0qSyuZfn7zItsbGyO9QlnxY0eCuD1XL2ys/MsrQhltE7Ug0uFOzufJFE2PxBo/YAx8XPPdDwWN0MrDRYIZF0mSMKCNHgaIVFoBbNoLJ7tEQDKxGF0kcLQimojCZopv0OkNOyWCCg9XMVAi7ARJzQdM2QUh0gmBozjc3Skg6dSBRqDGYSUOu66Zg+I2fNZs/M3/f/Grl/XnyF1Gw3VKCez0PN5IUfFLqvgUN4C0qNqYs5YhPL+aVZYDE4IpUk57oSFnJm4FyCqqOE0jhY2SMyLFoo56zyo6becOS5UVDdj7Vih0zp+tcMhwRpBeLyqtIjlJKAIZSbI8SGSF3k0pA3mR5tHuwPFoa7N7reoq2bqCsAk1HqCu5uvI1n6JuRXI+S1Mco54YmYTwcn6Aeic+kssXi8XpXC4V3t7/ADuTNKaQJdScAAAAAElFTkSuQmCC)](https://mybinder.org/v2/gist/jonasbb/18b9aece4c17f617b1c2b3946d29eeb0/HEAD?filepath=serde-with-demo.ipynb)
|
||||
[![Rustexplorer](https://img.shields.io/badge/Try%20on-rustexplorer-lightgrey?logo=rust&logoColor=orange)](https://www.rustexplorer.com/b/py7ida)
|
||||
|
||||
---
|
||||
|
||||
|
@ -12,14 +12,14 @@ This crate provides custom de/serialization helpers to use in combination with [
|
|||
Some common use cases are:
|
||||
|
||||
* De/Serializing a type using the `Display` and `FromStr` traits, e.g., for `u8`, `url::Url`, or `mime::Mime`.
|
||||
Check [`DisplayFromStr`][] or [`serde_with::rust::display_fromstr`][display_fromstr] for details.
|
||||
Check [`DisplayFromStr`] for details.
|
||||
* Support for arrays larger than 32 elements or using const generics.
|
||||
With `serde_as` large arrays are supported, even if they are nested in other types.
|
||||
`[bool; 64]`, `Option<[u8; M]>`, and `Box<[[u8; 64]; N]>` are all supported, as [this examples shows](#large-and-const-generic-arrays).
|
||||
* Skip serializing all empty `Option` types with [`#[skip_serializing_none]`][skip_serializing_none].
|
||||
* Apply a prefix to each field name of a struct, without changing the de/serialize implementations of the struct using [`with_prefix!`][].
|
||||
* Deserialize a comma separated list like `#hash,#tags,#are,#great` into a `Vec<String>`.
|
||||
Check the documentation for [`serde_with::rust::StringWithSeparator::<CommaSeparator>`][StringWithSeparator].
|
||||
Check the documentation for [`serde_with::StringWithSeparator::<CommaSeparator, T>`][StringWithSeparator].
|
||||
|
||||
### Getting Help
|
||||
|
||||
|
@ -30,12 +30,9 @@ For bugs, please open a [new issue](https://github.com/jonasbb/serde_with/issues
|
|||
|
||||
## Use `serde_with` in your Project
|
||||
|
||||
Add this to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dependencies.serde_with]
|
||||
version = "1.14.0"
|
||||
features = [ "..." ]
|
||||
```bash
|
||||
# Add the current version to your Cargo.toml
|
||||
cargo add serde_with
|
||||
```
|
||||
|
||||
The crate contains different features for integration with other common crates.
|
||||
|
@ -44,10 +41,16 @@ Check the [feature flags][] section for information about all available features
|
|||
## Examples
|
||||
|
||||
Annotate your struct or enum to enable the custom de/serializer.
|
||||
The `#[serde_as]` attribute must be place *before* the `#[derive]`.
|
||||
The `#[serde_as]` attribute must be placed *before* the `#[derive]`.
|
||||
|
||||
The `as` is analogous to the `with` attribute of serde.
|
||||
You mirror the type structure of the field you want to de/serialize.
|
||||
You can specify converters for the inner types of a field, e.g., `Vec<DisplayFromStr>`.
|
||||
The default de/serialization behavior can be restored by using `_` as a placeholder, e.g., `BTreeMap<_, DisplayFromStr>`.
|
||||
|
||||
### `DisplayFromStr`
|
||||
|
||||
[![Rustexplorer](https://img.shields.io/badge/Try%20on-rustexplorer-lightgrey?logo=rust&logoColor=orange)](https://www.rustexplorer.com/b/py7ida)
|
||||
```rust
|
||||
#[serde_as]
|
||||
#[derive(Deserialize, Serialize)]
|
||||
|
@ -67,8 +70,11 @@ Foo {bar: 12}
|
|||
### Large and const-generic arrays
|
||||
|
||||
serde does not support arrays with more than 32 elements or using const-generics.
|
||||
The `serde_as` attribute allows to circumvent this restriction, even for nested types and nested arrays.
|
||||
The `serde_as` attribute allows circumventing this restriction, even for nested types and nested arrays.
|
||||
|
||||
On top of it, `[u8; N]` (aka, bytes) can use the specialized `"Bytes"` for efficiency much like the `serde_bytes` crate.
|
||||
|
||||
[![Rustexplorer](https://img.shields.io/badge/Try%20on-rustexplorer-lightgrey?logo=rust&logoColor=orange)](https://www.rustexplorer.com/b/um0xyi)
|
||||
```rust
|
||||
#[serde_as]
|
||||
#[derive(Deserialize, Serialize)]
|
||||
|
@ -81,13 +87,17 @@ struct Arrays<const N: usize, const M: usize> {
|
|||
|
||||
#[serde_as(as = "Option<[_; M]>")]
|
||||
optional: Option<[u8; M]>,
|
||||
|
||||
#[serde_as(as = "Bytes")]
|
||||
bytes: [u8; M],
|
||||
}
|
||||
|
||||
// This allows us to serialize a struct like this
|
||||
let arrays: Arrays<100, 128> = Arrays {
|
||||
constgeneric: [true; 100],
|
||||
nested: Box::new([[111; 64]; 100]),
|
||||
optional: Some([222; 128])
|
||||
optional: Some([222; 128]),
|
||||
bytes: [0x42; 128],
|
||||
};
|
||||
assert!(serde_json::to_string(&arrays).is_ok());
|
||||
```
|
||||
|
@ -96,8 +106,9 @@ assert!(serde_json::to_string(&arrays).is_ok());
|
|||
|
||||
This situation often occurs with JSON, but other formats also support optional fields.
|
||||
If many fields are optional, putting the annotations on the structs can become tedious.
|
||||
The `#[skip_serializing_none]` attribute must be place *before* the `#[derive]`.
|
||||
The `#[skip_serializing_none]` attribute must be placed *before* the `#[derive]`.
|
||||
|
||||
[![Rustexplorer](https://img.shields.io/badge/Try%20on-rustexplorer-lightgrey?logo=rust&logoColor=orange)](https://www.rustexplorer.com/b/xr1tm0)
|
||||
```rust
|
||||
#[skip_serializing_none]
|
||||
#[derive(Deserialize, Serialize)]
|
||||
|
@ -121,52 +132,65 @@ Foo {a: None, b: None, c: None, d: Some(4), e: None, f: None, g: Some(7)}
|
|||
### Advanced `serde_as` usage
|
||||
|
||||
This example is mainly supposed to highlight the flexibility of the `serde_as`-annotation compared to [serde's with-annotation][with-annotation].
|
||||
More details about `serde_as` can be found in the [user guide][].
|
||||
More details about `serde_as` can be found in the [user guide].
|
||||
|
||||
```rust
|
||||
use std::time::Duration;
|
||||
|
||||
#[serde_as]
|
||||
#[derive(Deserialize, Serialize)]
|
||||
struct Foo {
|
||||
// Serialize them into a list of number as seconds
|
||||
#[serde_as(as = "Vec<DurationSeconds>")]
|
||||
durations: Vec<Duration>,
|
||||
// We can treat a Vec like a map with duplicates.
|
||||
// JSON only allows string keys, so convert i32 to strings
|
||||
// The bytes will be hex encoded
|
||||
#[serde_as(as = "BTreeMap<DisplayFromStr, Hex>")]
|
||||
bytes: Vec<(i32, Vec<u8>)>,
|
||||
enum Foo {
|
||||
Durations(
|
||||
// Serialize them into a list of number as seconds
|
||||
#[serde_as(as = "Vec<DurationSeconds>")]
|
||||
Vec<Duration>,
|
||||
),
|
||||
Bytes {
|
||||
// We can treat a Vec like a map with duplicates.
|
||||
// JSON only allows string keys, so convert i32 to strings
|
||||
// The bytes will be hex encoded
|
||||
#[serde_as(as = "Map<DisplayFromStr, Hex>")]
|
||||
bytes: Vec<(i32, Vec<u8>)>,
|
||||
}
|
||||
}
|
||||
|
||||
// This will serialize
|
||||
Foo {
|
||||
durations: vec![Duration::new(5, 0), Duration::new(3600, 0), Duration::new(0, 0)],
|
||||
Foo::Durations(
|
||||
vec![Duration::new(5, 0), Duration::new(3600, 0), Duration::new(0, 0)]
|
||||
)
|
||||
// into this JSON
|
||||
{
|
||||
"Durations": [5, 3600, 0]
|
||||
}
|
||||
|
||||
// and serializes
|
||||
Foo::Bytes {
|
||||
bytes: vec![
|
||||
(1, vec![0, 1, 2]),
|
||||
(-100, vec![100, 200, 255]),
|
||||
(1, vec![0, 111, 222]),
|
||||
],
|
||||
}
|
||||
|
||||
// into this JSON
|
||||
{
|
||||
"durations": [5, 3600, 0],
|
||||
"bytes": {
|
||||
"1": "000102",
|
||||
"-100": "64c8ff",
|
||||
"1": "006fde"
|
||||
"Bytes": {
|
||||
"bytes": {
|
||||
"1": "000102",
|
||||
"-100": "64c8ff",
|
||||
"1": "006fde"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
[`DisplayFromStr`]: https://docs.rs/serde_with/1.14.0/serde_with/struct.DisplayFromStr.html
|
||||
[`with_prefix!`]: https://docs.rs/serde_with/1.14.0/serde_with/macro.with_prefix.html
|
||||
[display_fromstr]: https://docs.rs/serde_with/1.14.0/serde_with/rust/display_fromstr/index.html
|
||||
[feature flags]: https://docs.rs/serde_with/1.14.0/serde_with/guide/feature_flags/index.html
|
||||
[skip_serializing_none]: https://docs.rs/serde_with/1.14.0/serde_with/attr.skip_serializing_none.html
|
||||
[StringWithSeparator]: https://docs.rs/serde_with/1.14.0/serde_with/rust/struct.StringWithSeparator.html
|
||||
[user guide]: https://docs.rs/serde_with/1.14.0/serde_with/guide/index.html
|
||||
[`DisplayFromStr`]: https://docs.rs/serde_with/3.0.0/serde_with/struct.DisplayFromStr.html
|
||||
[`with_prefix!`]: https://docs.rs/serde_with/3.0.0/serde_with/macro.with_prefix.html
|
||||
[feature flags]: https://docs.rs/serde_with/3.0.0/serde_with/guide/feature_flags/index.html
|
||||
[skip_serializing_none]: https://docs.rs/serde_with/3.0.0/serde_with/attr.skip_serializing_none.html
|
||||
[StringWithSeparator]: https://docs.rs/serde_with/3.0.0/serde_with/struct.StringWithSeparator.html
|
||||
[user guide]: https://docs.rs/serde_with/3.0.0/serde_with/guide/index.html
|
||||
[with-annotation]: https://serde.rs/field-attrs.html#with
|
||||
[as-annotation]: https://docs.rs/serde_with/1.14.0/serde_with/guide/serde_as/index.html
|
||||
[as-annotation]: https://docs.rs/serde_with/3.0.0/serde_with/guide/serde_as/index.html
|
||||
|
||||
## License
|
||||
|
||||
|
|
|
@ -4,14 +4,7 @@
|
|||
//!
|
||||
//! Please check the documentation on the [`Base64`] type for details.
|
||||
|
||||
use crate::{formats, DeserializeAs, SerializeAs};
|
||||
use alloc::{format, string::String, vec::Vec};
|
||||
use core::{
|
||||
convert::{TryFrom, TryInto},
|
||||
default::Default,
|
||||
marker::PhantomData,
|
||||
};
|
||||
use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer};
|
||||
use crate::prelude::*;
|
||||
|
||||
/// Serialize bytes with base64
|
||||
///
|
||||
|
@ -19,7 +12,7 @@ use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer};
|
|||
/// It works on any type implementing `AsRef<[u8]>` for serialization and `TryFrom<Vec<u8>>` for deserialization.
|
||||
///
|
||||
/// The type allows customizing the character set and the padding behavior.
|
||||
/// The `CHARSET` is a type implementing [`CharacterSet`].
|
||||
/// The `ALPHABET` is a type implementing [`Alphabet`].
|
||||
/// `PADDING` specifies if serializing should emit padding.
|
||||
/// Deserialization always supports padded and unpadded formats.
|
||||
/// [`formats::Padded`] emits padding and [`formats::Unpadded`] leaves it off.
|
||||
|
@ -69,137 +62,171 @@ use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer};
|
|||
|
||||
// The padding might be better as `const PADDING: bool = true`
|
||||
// https://blog.rust-lang.org/inside-rust/2021/09/06/Splitting-const-generics.html#featureconst_generics_default/
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct Base64<CHARSET: CharacterSet = Standard, PADDING: formats::Format = formats::Padded>(
|
||||
PhantomData<(CHARSET, PADDING)>,
|
||||
pub struct Base64<ALPHABET: Alphabet = Standard, PADDING: formats::Format = formats::Padded>(
|
||||
PhantomData<(ALPHABET, PADDING)>,
|
||||
);
|
||||
|
||||
impl<T, CHARSET> SerializeAs<T> for Base64<CHARSET, formats::Padded>
|
||||
impl<T, ALPHABET> SerializeAs<T> for Base64<ALPHABET, formats::Padded>
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
CHARSET: CharacterSet,
|
||||
ALPHABET: Alphabet,
|
||||
{
|
||||
fn serialize_as<S>(source: &T, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
base64_crate::encode_config(source, base64_crate::Config::new(CHARSET::charset(), true))
|
||||
.serialize(serializer)
|
||||
use ::base64::Engine as _;
|
||||
|
||||
::base64::engine::GeneralPurpose::new(
|
||||
&ALPHABET::charset(),
|
||||
::base64::engine::general_purpose::PAD,
|
||||
)
|
||||
.encode(source)
|
||||
.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, CHARSET> SerializeAs<T> for Base64<CHARSET, formats::Unpadded>
|
||||
impl<T, ALPHABET> SerializeAs<T> for Base64<ALPHABET, formats::Unpadded>
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
CHARSET: CharacterSet,
|
||||
ALPHABET: Alphabet,
|
||||
{
|
||||
fn serialize_as<S>(source: &T, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
base64_crate::encode_config(source, base64_crate::Config::new(CHARSET::charset(), false))
|
||||
.serialize(serializer)
|
||||
use ::base64::Engine as _;
|
||||
|
||||
::base64::engine::GeneralPurpose::new(
|
||||
&ALPHABET::charset(),
|
||||
::base64::engine::general_purpose::NO_PAD,
|
||||
)
|
||||
.encode(source)
|
||||
.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, T, CHARSET, FORMAT> DeserializeAs<'de, T> for Base64<CHARSET, FORMAT>
|
||||
// Our decoders uniformly do not care about padding.
|
||||
const PAD_INDIFFERENT: ::base64::engine::GeneralPurposeConfig =
|
||||
::base64::engine::GeneralPurposeConfig::new()
|
||||
.with_decode_padding_mode(::base64::engine::DecodePaddingMode::Indifferent);
|
||||
|
||||
impl<'de, T, ALPHABET, FORMAT> DeserializeAs<'de, T> for Base64<ALPHABET, FORMAT>
|
||||
where
|
||||
T: TryFrom<Vec<u8>>,
|
||||
CHARSET: CharacterSet,
|
||||
ALPHABET: Alphabet,
|
||||
FORMAT: formats::Format,
|
||||
{
|
||||
fn deserialize_as<D>(deserializer: D) -> Result<T, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
String::deserialize(deserializer)
|
||||
.and_then(|s| {
|
||||
base64_crate::decode_config(
|
||||
&*s,
|
||||
base64_crate::Config::new(CHARSET::charset(), false),
|
||||
)
|
||||
.map_err(Error::custom)
|
||||
})
|
||||
.and_then(|vec: Vec<u8>| {
|
||||
let length = vec.len();
|
||||
vec.try_into().map_err(|_e: T::Error| {
|
||||
Error::custom(format!(
|
||||
"Can't convert a Byte Vector of length {} to the output type.",
|
||||
length
|
||||
struct Helper<T, ALPHABET>(PhantomData<(T, ALPHABET)>);
|
||||
|
||||
impl<'de, T, ALPHABET> Visitor<'de> for Helper<T, ALPHABET>
|
||||
where
|
||||
T: TryFrom<Vec<u8>>,
|
||||
ALPHABET: Alphabet,
|
||||
{
|
||||
type Value = T;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
formatter.write_str("a base64 encoded string")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: DeError,
|
||||
{
|
||||
use ::base64::Engine as _;
|
||||
|
||||
let bytes =
|
||||
::base64::engine::GeneralPurpose::new(&ALPHABET::charset(), PAD_INDIFFERENT)
|
||||
.decode(value)
|
||||
.map_err(DeError::custom)?;
|
||||
|
||||
let length = bytes.len();
|
||||
bytes.try_into().map_err(|_e: T::Error| {
|
||||
DeError::custom(format_args!(
|
||||
"Can't convert a Byte Vector of length {length} to the output type."
|
||||
))
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_str(Helper::<T, ALPHABET>(PhantomData))
|
||||
}
|
||||
}
|
||||
|
||||
/// A base64 character set from [this list](base64_crate::CharacterSet).
|
||||
pub trait CharacterSet {
|
||||
/// Return a specific character set.
|
||||
///
|
||||
/// Return one enum variant of the [`base64::CharacterSet`](base64_crate::CharacterSet) enum.
|
||||
fn charset() -> base64_crate::CharacterSet;
|
||||
mod sealed {
|
||||
pub trait Sealed {}
|
||||
impl Sealed for super::Standard {}
|
||||
impl Sealed for super::UrlSafe {}
|
||||
impl Sealed for super::Crypt {}
|
||||
impl Sealed for super::Bcrypt {}
|
||||
impl Sealed for super::ImapMutf7 {}
|
||||
impl Sealed for super::BinHex {}
|
||||
}
|
||||
|
||||
/// A base64 alphabet
|
||||
pub trait Alphabet: sealed::Sealed {
|
||||
/// Return a specific alphabet.
|
||||
fn charset() -> ::base64::alphabet::Alphabet;
|
||||
}
|
||||
/// The standard character set (uses `+` and `/`).
|
||||
///
|
||||
/// See [RFC 3548](https://tools.ietf.org/html/rfc3548#section-3).
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct Standard;
|
||||
impl CharacterSet for Standard {
|
||||
fn charset() -> base64_crate::CharacterSet {
|
||||
base64_crate::CharacterSet::Standard
|
||||
impl Alphabet for Standard {
|
||||
fn charset() -> ::base64::alphabet::Alphabet {
|
||||
::base64::alphabet::STANDARD
|
||||
}
|
||||
}
|
||||
|
||||
/// The URL safe character set (uses `-` and `_`).
|
||||
///
|
||||
/// See [RFC 3548](https://tools.ietf.org/html/rfc3548#section-3).
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct UrlSafe;
|
||||
impl CharacterSet for UrlSafe {
|
||||
fn charset() -> base64_crate::CharacterSet {
|
||||
base64_crate::CharacterSet::UrlSafe
|
||||
impl Alphabet for UrlSafe {
|
||||
fn charset() -> ::base64::alphabet::Alphabet {
|
||||
::base64::alphabet::URL_SAFE
|
||||
}
|
||||
}
|
||||
|
||||
/// The `crypt(3)` character set (uses `./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`).
|
||||
///
|
||||
/// Not standardized, but folk wisdom on the net asserts that this alphabet is what crypt uses.
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct Crypt;
|
||||
impl CharacterSet for Crypt {
|
||||
fn charset() -> base64_crate::CharacterSet {
|
||||
base64_crate::CharacterSet::Crypt
|
||||
impl Alphabet for Crypt {
|
||||
fn charset() -> ::base64::alphabet::Alphabet {
|
||||
::base64::alphabet::CRYPT
|
||||
}
|
||||
}
|
||||
|
||||
/// The bcrypt character set (uses `./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789`).
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct Bcrypt;
|
||||
impl CharacterSet for Bcrypt {
|
||||
fn charset() -> base64_crate::CharacterSet {
|
||||
base64_crate::CharacterSet::Bcrypt
|
||||
impl Alphabet for Bcrypt {
|
||||
fn charset() -> ::base64::alphabet::Alphabet {
|
||||
::base64::alphabet::BCRYPT
|
||||
}
|
||||
}
|
||||
|
||||
/// The character set used in IMAP-modified UTF-7 (uses `+` and `,`).
|
||||
///
|
||||
/// See [RFC 3501](https://tools.ietf.org/html/rfc3501#section-5.1.3).
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct ImapMutf7;
|
||||
impl CharacterSet for ImapMutf7 {
|
||||
fn charset() -> base64_crate::CharacterSet {
|
||||
base64_crate::CharacterSet::ImapMutf7
|
||||
impl Alphabet for ImapMutf7 {
|
||||
fn charset() -> ::base64::alphabet::Alphabet {
|
||||
::base64::alphabet::IMAP_MUTF7
|
||||
}
|
||||
}
|
||||
|
||||
/// The character set used in BinHex 4.0 files.
|
||||
///
|
||||
/// See [BinHex 4.0 Definition](http://files.stairways.com/other/binhex-40-specs-info.txt).
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct BinHex;
|
||||
impl CharacterSet for BinHex {
|
||||
fn charset() -> base64_crate::CharacterSet {
|
||||
base64_crate::CharacterSet::BinHex
|
||||
impl Alphabet for BinHex {
|
||||
fn charset() -> ::base64::alphabet::Alphabet {
|
||||
::base64::alphabet::BIN_HEX
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,38 +1,31 @@
|
|||
//! De/Serialization of [chrono][] types
|
||||
//! De/Serialization of [chrono] types
|
||||
//!
|
||||
//! This modules is only available if using the `chrono` feature of the crate.
|
||||
//! This modules is only available if using the `chrono_0_4` feature of the crate.
|
||||
//!
|
||||
//! [chrono]: https://docs.rs/chrono/
|
||||
|
||||
use crate::{
|
||||
de::DeserializeAs,
|
||||
formats::{Flexible, Format, Strict, Strictness},
|
||||
ser::SerializeAs,
|
||||
utils::duration::{DurationSigned, Sign},
|
||||
DurationMicroSeconds, DurationMicroSecondsWithFrac, DurationMilliSeconds,
|
||||
DurationMilliSecondsWithFrac, DurationNanoSeconds, DurationNanoSecondsWithFrac,
|
||||
DurationSeconds, DurationSecondsWithFrac, TimestampMicroSeconds, TimestampMicroSecondsWithFrac,
|
||||
TimestampMilliSeconds, TimestampMilliSecondsWithFrac, TimestampNanoSeconds,
|
||||
TimestampNanoSecondsWithFrac, TimestampSeconds, TimestampSecondsWithFrac,
|
||||
prelude::*,
|
||||
};
|
||||
use alloc::{format, string::String, vec::Vec};
|
||||
use chrono_crate::{DateTime, Duration, Local, NaiveDateTime, TimeZone, Utc};
|
||||
use core::fmt;
|
||||
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
||||
#[cfg(feature = "std")]
|
||||
use ::chrono_0_4::Local;
|
||||
use ::chrono_0_4::{DateTime, Duration, NaiveDateTime, TimeZone, Utc};
|
||||
|
||||
/// Create a [`DateTime`] for the Unix Epoch using the [`Utc`] timezone
|
||||
fn unix_epoch_utc() -> DateTime<Utc> {
|
||||
DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(0, 0), Utc)
|
||||
DateTime::<Utc>::from_utc(unix_epoch_naive(), Utc)
|
||||
}
|
||||
|
||||
/// Create a [`DateTime`] for the Unix Epoch using the [`Local`] timezone
|
||||
#[cfg(feature = "std")]
|
||||
fn unix_epoch_local() -> DateTime<Local> {
|
||||
DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(0, 0), Utc).with_timezone(&Local)
|
||||
unix_epoch_utc().with_timezone(&Local)
|
||||
}
|
||||
|
||||
/// Create a [`NaiveDateTime`] for the Unix Epoch
|
||||
fn unix_epoch_naive() -> NaiveDateTime {
|
||||
NaiveDateTime::from_timestamp(0, 0)
|
||||
NaiveDateTime::from_timestamp_opt(0, 0).unwrap()
|
||||
}
|
||||
|
||||
/// Deserialize a Unix timestamp with optional subsecond precision into a `DateTime<Utc>`.
|
||||
|
@ -42,12 +35,12 @@ fn unix_epoch_naive() -> NaiveDateTime {
|
|||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use chrono_crate::{DateTime, Utc};
|
||||
/// # use chrono_0_4::{DateTime, Utc};
|
||||
/// # use serde::Deserialize;
|
||||
/// #
|
||||
/// #[derive(Debug, Deserialize)]
|
||||
/// struct S {
|
||||
/// #[serde(with = "serde_with::chrono::datetime_utc_ts_seconds_from_any")]
|
||||
/// #[serde(with = "serde_with::chrono_0_4::datetime_utc_ts_seconds_from_any")]
|
||||
/// date: DateTime<Utc>,
|
||||
/// }
|
||||
///
|
||||
|
@ -58,10 +51,10 @@ fn unix_epoch_naive() -> NaiveDateTime {
|
|||
/// // and strings with numbers, for high-precision values
|
||||
/// assert!(serde_json::from_str::<S>(r#"{ "date": "1478563200.123" }"#).is_ok());
|
||||
/// ```
|
||||
// Requires float operations from std
|
||||
#[cfg(feature = "std")]
|
||||
pub mod datetime_utc_ts_seconds_from_any {
|
||||
use super::*;
|
||||
use chrono_crate::{DateTime, NaiveDateTime, Utc};
|
||||
use serde::de::{Deserializer, Error, Unexpected, Visitor};
|
||||
|
||||
/// Deserialize a Unix timestamp with optional subsecond precision into a `DateTime<Utc>`.
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
|
||||
|
@ -79,37 +72,35 @@ pub mod datetime_utc_ts_seconds_from_any {
|
|||
|
||||
fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
E: DeError,
|
||||
{
|
||||
let ndt = NaiveDateTime::from_timestamp_opt(value, 0);
|
||||
if let Some(ndt) = ndt {
|
||||
Ok(DateTime::<Utc>::from_utc(ndt, Utc))
|
||||
} else {
|
||||
Err(Error::custom(format!(
|
||||
"a timestamp which can be represented in a DateTime but received '{}'",
|
||||
value
|
||||
Err(DeError::custom(format_args!(
|
||||
"a timestamp which can be represented in a DateTime but received '{value}'"
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
E: DeError,
|
||||
{
|
||||
let ndt = NaiveDateTime::from_timestamp_opt(value as i64, 0);
|
||||
if let Some(ndt) = ndt {
|
||||
Ok(DateTime::<Utc>::from_utc(ndt, Utc))
|
||||
} else {
|
||||
Err(Error::custom(format!(
|
||||
"a timestamp which can be represented in a DateTime but received '{}'",
|
||||
value
|
||||
Err(DeError::custom(format_args!(
|
||||
"a timestamp which can be represented in a DateTime but received '{value}'"
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_f64<E>(self, value: f64) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
E: DeError,
|
||||
{
|
||||
let seconds = value.trunc() as i64;
|
||||
let nsecs = (value.fract() * 1_000_000_000_f64).abs() as u32;
|
||||
|
@ -117,16 +108,15 @@ pub mod datetime_utc_ts_seconds_from_any {
|
|||
if let Some(ndt) = ndt {
|
||||
Ok(DateTime::<Utc>::from_utc(ndt, Utc))
|
||||
} else {
|
||||
Err(Error::custom(format!(
|
||||
"a timestamp which can be represented in a DateTime but received '{}'",
|
||||
value
|
||||
Err(DeError::custom(format_args!(
|
||||
"a timestamp which can be represented in a DateTime but received '{value}'"
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
E: DeError,
|
||||
{
|
||||
let parts: Vec<_> = value.split('.').collect();
|
||||
|
||||
|
@ -137,22 +127,20 @@ pub mod datetime_utc_ts_seconds_from_any {
|
|||
if let Some(ndt) = ndt {
|
||||
Ok(DateTime::<Utc>::from_utc(ndt, Utc))
|
||||
} else {
|
||||
Err(Error::custom(format!(
|
||||
"a timestamp which can be represented in a DateTime but received '{}'",
|
||||
value
|
||||
Err(DeError::custom(format_args!(
|
||||
"a timestamp which can be represented in a DateTime but received '{value}'"
|
||||
)))
|
||||
}
|
||||
} else {
|
||||
Err(Error::invalid_value(Unexpected::Str(value), &self))
|
||||
Err(DeError::invalid_value(Unexpected::Str(value), &self))
|
||||
}
|
||||
}
|
||||
[seconds, subseconds] => {
|
||||
if let Ok(seconds) = seconds.parse() {
|
||||
let subseclen = subseconds.chars().count() as u32;
|
||||
if subseclen > 9 {
|
||||
return Err(Error::custom(format!(
|
||||
"DateTimes only support nanosecond precision but '{}' has more than 9 digits.",
|
||||
value
|
||||
return Err(DeError::custom(format_args!(
|
||||
"DateTimes only support nanosecond precision but '{value}' has more than 9 digits."
|
||||
)));
|
||||
}
|
||||
|
||||
|
@ -163,20 +151,19 @@ pub mod datetime_utc_ts_seconds_from_any {
|
|||
if let Some(ndt) = ndt {
|
||||
Ok(DateTime::<Utc>::from_utc(ndt, Utc))
|
||||
} else {
|
||||
Err(Error::custom(format!(
|
||||
"a timestamp which can be represented in a DateTime but received '{}'",
|
||||
value
|
||||
Err(DeError::custom(format_args!(
|
||||
"a timestamp which can be represented in a DateTime but received '{value}'"
|
||||
)))
|
||||
}
|
||||
} else {
|
||||
Err(Error::invalid_value(Unexpected::Str(value), &self))
|
||||
Err(DeError::invalid_value(Unexpected::Str(value), &self))
|
||||
}
|
||||
} else {
|
||||
Err(Error::invalid_value(Unexpected::Str(value), &self))
|
||||
Err(DeError::invalid_value(Unexpected::Str(value), &self))
|
||||
}
|
||||
}
|
||||
|
||||
_ => Err(Error::invalid_value(Unexpected::Str(value), &self)),
|
||||
_ => Err(DeError::invalid_value(Unexpected::Str(value), &self)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -204,7 +191,7 @@ impl<'de> DeserializeAs<'de, NaiveDateTime> for DateTime<Utc> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Convert a [`chrono::Duration`] into a [`DurationSigned`]
|
||||
/// Convert a [`chrono_0_4::Duration`] into a [`DurationSigned`]
|
||||
fn duration_into_duration_signed(dur: &Duration) -> DurationSigned {
|
||||
match dur.to_std() {
|
||||
Ok(dur) => DurationSigned::with_duration(Sign::Positive, dur),
|
||||
|
@ -218,7 +205,7 @@ fn duration_into_duration_signed(dur: &Duration) -> DurationSigned {
|
|||
}
|
||||
}
|
||||
|
||||
/// Convert a [`DurationSigned`] into a [`chrono::Duration`]
|
||||
/// Convert a [`DurationSigned`] into a [`chrono_0_4::Duration`]
|
||||
fn duration_from_duration_signed<'de, D>(dur: DurationSigned) -> Result<Duration, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
|
@ -226,9 +213,8 @@ where
|
|||
let mut chrono_dur = match Duration::from_std(dur.duration) {
|
||||
Ok(dur) => dur,
|
||||
Err(msg) => {
|
||||
return Err(de::Error::custom(format!(
|
||||
"Duration is outside of the representable range: {}",
|
||||
msg
|
||||
return Err(DeError::custom(format_args!(
|
||||
"Duration is outside of the representable range: {msg}"
|
||||
)))
|
||||
}
|
||||
};
|
||||
|
@ -294,6 +280,16 @@ use_duration_signed_ser!(
|
|||
Duration; duration_into_duration_signed =>
|
||||
{i64, STRICTNESS => STRICTNESS: Strictness}
|
||||
{f64, STRICTNESS => STRICTNESS: Strictness}
|
||||
}
|
||||
);
|
||||
#[cfg(feature = "alloc")]
|
||||
use_duration_signed_ser!(
|
||||
DurationSeconds DurationSeconds,
|
||||
DurationMilliSeconds DurationMilliSeconds,
|
||||
DurationMicroSeconds DurationMicroSeconds,
|
||||
DurationNanoSeconds DurationNanoSeconds,
|
||||
=> {
|
||||
Duration; duration_into_duration_signed =>
|
||||
{String, STRICTNESS => STRICTNESS: Strictness}
|
||||
}
|
||||
);
|
||||
|
@ -306,6 +302,16 @@ use_duration_signed_ser!(
|
|||
DateTime<TZ>; datetime_to_duration =>
|
||||
{i64, STRICTNESS => TZ: TimeZone, STRICTNESS: Strictness}
|
||||
{f64, STRICTNESS => TZ: TimeZone, STRICTNESS: Strictness}
|
||||
}
|
||||
);
|
||||
#[cfg(feature = "alloc")]
|
||||
use_duration_signed_ser!(
|
||||
TimestampSeconds DurationSeconds,
|
||||
TimestampMilliSeconds DurationMilliSeconds,
|
||||
TimestampMicroSeconds DurationMicroSeconds,
|
||||
TimestampNanoSeconds DurationNanoSeconds,
|
||||
=> {
|
||||
DateTime<TZ>; datetime_to_duration =>
|
||||
{String, STRICTNESS => TZ: TimeZone, STRICTNESS: Strictness}
|
||||
}
|
||||
);
|
||||
|
@ -318,6 +324,16 @@ use_duration_signed_ser!(
|
|||
NaiveDateTime; naive_datetime_to_duration =>
|
||||
{i64, STRICTNESS => STRICTNESS: Strictness}
|
||||
{f64, STRICTNESS => STRICTNESS: Strictness}
|
||||
}
|
||||
);
|
||||
#[cfg(feature = "alloc")]
|
||||
use_duration_signed_ser!(
|
||||
TimestampSeconds DurationSeconds,
|
||||
TimestampMilliSeconds DurationMilliSeconds,
|
||||
TimestampMicroSeconds DurationMicroSeconds,
|
||||
TimestampNanoSeconds DurationNanoSeconds,
|
||||
=> {
|
||||
NaiveDateTime; naive_datetime_to_duration =>
|
||||
{String, STRICTNESS => STRICTNESS: Strictness}
|
||||
}
|
||||
);
|
||||
|
@ -331,6 +347,16 @@ use_duration_signed_ser!(
|
|||
=> {
|
||||
Duration; duration_into_duration_signed =>
|
||||
{f64, STRICTNESS => STRICTNESS: Strictness}
|
||||
}
|
||||
);
|
||||
#[cfg(feature = "alloc")]
|
||||
use_duration_signed_ser!(
|
||||
DurationSecondsWithFrac DurationSecondsWithFrac,
|
||||
DurationMilliSecondsWithFrac DurationMilliSecondsWithFrac,
|
||||
DurationMicroSecondsWithFrac DurationMicroSecondsWithFrac,
|
||||
DurationNanoSecondsWithFrac DurationNanoSecondsWithFrac,
|
||||
=> {
|
||||
Duration; duration_into_duration_signed =>
|
||||
{String, STRICTNESS => STRICTNESS: Strictness}
|
||||
}
|
||||
);
|
||||
|
@ -342,6 +368,16 @@ use_duration_signed_ser!(
|
|||
=> {
|
||||
DateTime<TZ>; datetime_to_duration =>
|
||||
{f64, STRICTNESS => TZ: TimeZone, STRICTNESS: Strictness}
|
||||
}
|
||||
);
|
||||
#[cfg(feature = "alloc")]
|
||||
use_duration_signed_ser!(
|
||||
TimestampSecondsWithFrac DurationSecondsWithFrac,
|
||||
TimestampMilliSecondsWithFrac DurationMilliSecondsWithFrac,
|
||||
TimestampMicroSecondsWithFrac DurationMicroSecondsWithFrac,
|
||||
TimestampNanoSecondsWithFrac DurationNanoSecondsWithFrac,
|
||||
=> {
|
||||
DateTime<TZ>; datetime_to_duration =>
|
||||
{String, STRICTNESS => TZ: TimeZone, STRICTNESS: Strictness}
|
||||
}
|
||||
);
|
||||
|
@ -353,6 +389,16 @@ use_duration_signed_ser!(
|
|||
=> {
|
||||
NaiveDateTime; naive_datetime_to_duration =>
|
||||
{f64, STRICTNESS => STRICTNESS: Strictness}
|
||||
}
|
||||
);
|
||||
#[cfg(feature = "alloc")]
|
||||
use_duration_signed_ser!(
|
||||
TimestampSecondsWithFrac DurationSecondsWithFrac,
|
||||
TimestampMilliSecondsWithFrac DurationMilliSecondsWithFrac,
|
||||
TimestampMicroSecondsWithFrac DurationMicroSecondsWithFrac,
|
||||
TimestampNanoSecondsWithFrac DurationNanoSecondsWithFrac,
|
||||
=> {
|
||||
NaiveDateTime; naive_datetime_to_duration =>
|
||||
{String, STRICTNESS => STRICTNESS: Strictness}
|
||||
}
|
||||
);
|
||||
|
@ -397,6 +443,7 @@ where
|
|||
Ok(unix_epoch_utc() + duration_from_duration_signed::<D>(dur)?)
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
fn duration_to_datetime_local<'de, D>(dur: DurationSigned) -> Result<DateTime<Local>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
|
@ -420,11 +467,31 @@ use_duration_signed_de!(
|
|||
=> {
|
||||
Duration; duration_from_duration_signed =>
|
||||
{i64, Strict =>}
|
||||
{f64, Strict =>}
|
||||
{String, Strict =>}
|
||||
{FORMAT, Flexible => FORMAT: Format}
|
||||
}
|
||||
);
|
||||
#[cfg(feature = "alloc")]
|
||||
use_duration_signed_de!(
|
||||
DurationSeconds DurationSeconds,
|
||||
DurationMilliSeconds DurationMilliSeconds,
|
||||
DurationMicroSeconds DurationMicroSeconds,
|
||||
DurationNanoSeconds DurationNanoSeconds,
|
||||
=> {
|
||||
Duration; duration_from_duration_signed =>
|
||||
{String, Strict =>}
|
||||
}
|
||||
);
|
||||
#[cfg(feature = "std")]
|
||||
use_duration_signed_de!(
|
||||
DurationSeconds DurationSeconds,
|
||||
DurationMilliSeconds DurationMilliSeconds,
|
||||
DurationMicroSeconds DurationMicroSeconds,
|
||||
DurationNanoSeconds DurationNanoSeconds,
|
||||
=> {
|
||||
Duration; duration_from_duration_signed =>
|
||||
{f64, Strict =>}
|
||||
}
|
||||
);
|
||||
use_duration_signed_de!(
|
||||
TimestampSeconds DurationSeconds,
|
||||
TimestampMilliSeconds DurationMilliSeconds,
|
||||
|
@ -433,11 +500,32 @@ use_duration_signed_de!(
|
|||
=> {
|
||||
DateTime<Utc>; duration_to_datetime_utc =>
|
||||
{i64, Strict =>}
|
||||
{f64, Strict =>}
|
||||
{String, Strict =>}
|
||||
{FORMAT, Flexible => FORMAT: Format}
|
||||
}
|
||||
);
|
||||
#[cfg(feature = "alloc")]
|
||||
use_duration_signed_de!(
|
||||
TimestampSeconds DurationSeconds,
|
||||
TimestampMilliSeconds DurationMilliSeconds,
|
||||
TimestampMicroSeconds DurationMicroSeconds,
|
||||
TimestampNanoSeconds DurationNanoSeconds,
|
||||
=> {
|
||||
DateTime<Utc>; duration_to_datetime_utc =>
|
||||
{String, Strict =>}
|
||||
}
|
||||
);
|
||||
#[cfg(feature = "std")]
|
||||
use_duration_signed_de!(
|
||||
TimestampSeconds DurationSeconds,
|
||||
TimestampMilliSeconds DurationMilliSeconds,
|
||||
TimestampMicroSeconds DurationMicroSeconds,
|
||||
TimestampNanoSeconds DurationNanoSeconds,
|
||||
=> {
|
||||
DateTime<Utc>; duration_to_datetime_utc =>
|
||||
{f64, Strict =>}
|
||||
}
|
||||
);
|
||||
#[cfg(feature = "std")]
|
||||
use_duration_signed_de!(
|
||||
TimestampSeconds DurationSeconds,
|
||||
TimestampMilliSeconds DurationMilliSeconds,
|
||||
|
@ -459,11 +547,31 @@ use_duration_signed_de!(
|
|||
=> {
|
||||
NaiveDateTime; duration_to_naive_datetime =>
|
||||
{i64, Strict =>}
|
||||
{f64, Strict =>}
|
||||
{String, Strict =>}
|
||||
{FORMAT, Flexible => FORMAT: Format}
|
||||
}
|
||||
);
|
||||
#[cfg(feature = "alloc")]
|
||||
use_duration_signed_de!(
|
||||
TimestampSeconds DurationSeconds,
|
||||
TimestampMilliSeconds DurationMilliSeconds,
|
||||
TimestampMicroSeconds DurationMicroSeconds,
|
||||
TimestampNanoSeconds DurationNanoSeconds,
|
||||
=> {
|
||||
NaiveDateTime; duration_to_naive_datetime =>
|
||||
{String, Strict =>}
|
||||
}
|
||||
);
|
||||
#[cfg(feature = "std")]
|
||||
use_duration_signed_de!(
|
||||
TimestampSeconds DurationSeconds,
|
||||
TimestampMilliSeconds DurationMilliSeconds,
|
||||
TimestampMicroSeconds DurationMicroSeconds,
|
||||
TimestampNanoSeconds DurationNanoSeconds,
|
||||
=> {
|
||||
NaiveDateTime; duration_to_naive_datetime =>
|
||||
{f64, Strict =>}
|
||||
}
|
||||
);
|
||||
|
||||
// Duration/Timestamp WITH FRACTIONS
|
||||
use_duration_signed_de!(
|
||||
|
@ -474,10 +582,20 @@ use_duration_signed_de!(
|
|||
=> {
|
||||
Duration; duration_from_duration_signed =>
|
||||
{f64, Strict =>}
|
||||
{String, Strict =>}
|
||||
{FORMAT, Flexible => FORMAT: Format}
|
||||
}
|
||||
);
|
||||
#[cfg(feature = "alloc")]
|
||||
use_duration_signed_de!(
|
||||
DurationSecondsWithFrac DurationSecondsWithFrac,
|
||||
DurationMilliSecondsWithFrac DurationMilliSecondsWithFrac,
|
||||
DurationMicroSecondsWithFrac DurationMicroSecondsWithFrac,
|
||||
DurationNanoSecondsWithFrac DurationNanoSecondsWithFrac,
|
||||
=> {
|
||||
Duration; duration_from_duration_signed =>
|
||||
{String, Strict =>}
|
||||
}
|
||||
);
|
||||
use_duration_signed_de!(
|
||||
TimestampSecondsWithFrac DurationSecondsWithFrac,
|
||||
TimestampMilliSecondsWithFrac DurationMilliSecondsWithFrac,
|
||||
|
@ -486,10 +604,21 @@ use_duration_signed_de!(
|
|||
=> {
|
||||
DateTime<Utc>; duration_to_datetime_utc =>
|
||||
{f64, Strict =>}
|
||||
{String, Strict =>}
|
||||
{FORMAT, Flexible => FORMAT: Format}
|
||||
}
|
||||
);
|
||||
#[cfg(feature = "alloc")]
|
||||
use_duration_signed_de!(
|
||||
TimestampSecondsWithFrac DurationSecondsWithFrac,
|
||||
TimestampMilliSecondsWithFrac DurationMilliSecondsWithFrac,
|
||||
TimestampMicroSecondsWithFrac DurationMicroSecondsWithFrac,
|
||||
TimestampNanoSecondsWithFrac DurationNanoSecondsWithFrac,
|
||||
=> {
|
||||
DateTime<Utc>; duration_to_datetime_utc =>
|
||||
{String, Strict =>}
|
||||
}
|
||||
);
|
||||
#[cfg(feature = "std")]
|
||||
use_duration_signed_de!(
|
||||
TimestampSecondsWithFrac DurationSecondsWithFrac,
|
||||
TimestampMilliSecondsWithFrac DurationMilliSecondsWithFrac,
|
||||
|
@ -510,7 +639,17 @@ use_duration_signed_de!(
|
|||
=> {
|
||||
NaiveDateTime; duration_to_naive_datetime =>
|
||||
{f64, Strict =>}
|
||||
{String, Strict =>}
|
||||
{FORMAT, Flexible => FORMAT: Format}
|
||||
}
|
||||
);
|
||||
#[cfg(feature = "alloc")]
|
||||
use_duration_signed_de!(
|
||||
TimestampSecondsWithFrac DurationSecondsWithFrac,
|
||||
TimestampMilliSecondsWithFrac DurationMilliSecondsWithFrac,
|
||||
TimestampMicroSecondsWithFrac DurationMicroSecondsWithFrac,
|
||||
TimestampNanoSecondsWithFrac DurationNanoSecondsWithFrac,
|
||||
=> {
|
||||
NaiveDateTime; duration_to_naive_datetime =>
|
||||
{String, Strict =>}
|
||||
}
|
||||
);
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -2,4 +2,7 @@
|
|||
//!
|
||||
//! <https://github.com/serde-rs/serde/blob/55a7cedd737278a9d75a2efd038c6f38b8c38bd6/serde/src/private/ser.rs#L338-L997>
|
||||
|
||||
#![cfg(not(tarpaulin_include))]
|
||||
|
||||
pub(crate) mod de;
|
||||
pub(crate) mod ser;
|
||||
|
|
|
@ -11,11 +11,8 @@
|
|||
//! The types support 128-bit integers, which is supported for all targets in Rust 1.40+.
|
||||
//! The [`ContentSerializer`] can also be configured to human readable or compact representation.
|
||||
|
||||
use alloc::{borrow::ToOwned, boxed::Box, string::String, vec::Vec};
|
||||
use core::marker::PhantomData;
|
||||
use serde::ser::{self, Serialize, Serializer};
|
||||
use crate::prelude::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum Content {
|
||||
Bool(bool),
|
||||
|
||||
|
@ -61,6 +58,16 @@ pub(crate) enum Content {
|
|||
),
|
||||
}
|
||||
|
||||
impl Content {
|
||||
pub(crate) fn as_str(&self) -> Option<&str> {
|
||||
match self {
|
||||
Self::String(ref x) => Some(x),
|
||||
Self::Bytes(x) => core::str::from_utf8(x).ok(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Content {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
|
@ -94,7 +101,6 @@ impl Serialize for Content {
|
|||
}
|
||||
Content::Seq(ref elements) => elements.serialize(serializer),
|
||||
Content::Tuple(ref elements) => {
|
||||
use serde::ser::SerializeTuple;
|
||||
let mut tuple = serializer.serialize_tuple(elements.len())?;
|
||||
for e in elements {
|
||||
tuple.serialize_element(e)?;
|
||||
|
@ -102,7 +108,6 @@ impl Serialize for Content {
|
|||
tuple.end()
|
||||
}
|
||||
Content::TupleStruct(n, ref fields) => {
|
||||
use serde::ser::SerializeTupleStruct;
|
||||
let mut ts = serializer.serialize_tuple_struct(n, fields.len())?;
|
||||
for f in fields {
|
||||
ts.serialize_field(f)?;
|
||||
|
@ -110,7 +115,6 @@ impl Serialize for Content {
|
|||
ts.end()
|
||||
}
|
||||
Content::TupleVariant(n, i, v, ref fields) => {
|
||||
use serde::ser::SerializeTupleVariant;
|
||||
let mut tv = serializer.serialize_tuple_variant(n, i, v, fields.len())?;
|
||||
for f in fields {
|
||||
tv.serialize_field(f)?;
|
||||
|
@ -118,25 +122,22 @@ impl Serialize for Content {
|
|||
tv.end()
|
||||
}
|
||||
Content::Map(ref entries) => {
|
||||
use serde::ser::SerializeMap;
|
||||
let mut map = serializer.serialize_map(Some(entries.len()))?;
|
||||
for &(ref k, ref v) in entries {
|
||||
for (k, v) in entries {
|
||||
map.serialize_entry(k, v)?;
|
||||
}
|
||||
map.end()
|
||||
}
|
||||
Content::Struct(n, ref fields) => {
|
||||
use serde::ser::SerializeStruct;
|
||||
let mut s = serializer.serialize_struct(n, fields.len())?;
|
||||
for &(k, ref v) in fields {
|
||||
for (k, v) in fields {
|
||||
s.serialize_field(k, v)?;
|
||||
}
|
||||
s.end()
|
||||
}
|
||||
Content::StructVariant(n, i, v, ref fields) => {
|
||||
use serde::ser::SerializeStructVariant;
|
||||
let mut sv = serializer.serialize_struct_variant(n, i, v, fields.len())?;
|
||||
for &(k, ref v) in fields {
|
||||
for (k, v) in fields {
|
||||
sv.serialize_field(k, v)?;
|
||||
}
|
||||
sv.end()
|
||||
|
@ -167,18 +168,18 @@ impl<E> Default for ContentSerializer<E> {
|
|||
|
||||
impl<E> Serializer for ContentSerializer<E>
|
||||
where
|
||||
E: ser::Error,
|
||||
E: SerError,
|
||||
{
|
||||
type Ok = Content;
|
||||
type Error = E;
|
||||
|
||||
type SerializeSeq = SerializeSeq<E>;
|
||||
type SerializeTuple = SerializeTuple<E>;
|
||||
type SerializeTupleStruct = SerializeTupleStruct<E>;
|
||||
type SerializeTupleVariant = SerializeTupleVariant<E>;
|
||||
type SerializeMap = SerializeMap<E>;
|
||||
type SerializeStruct = SerializeStruct<E>;
|
||||
type SerializeStructVariant = SerializeStructVariant<E>;
|
||||
type SerializeSeq = SeqSerialize<E>;
|
||||
type SerializeTuple = TupleSerialize<E>;
|
||||
type SerializeTupleStruct = TupleStructSerialize<E>;
|
||||
type SerializeTupleVariant = TupleVariantSerialize<E>;
|
||||
type SerializeMap = MapSerialize<E>;
|
||||
type SerializeStruct = StructSerialize<E>;
|
||||
type SerializeStructVariant = StructVariantSerialize<E>;
|
||||
|
||||
fn is_human_readable(&self) -> bool {
|
||||
self.is_human_readable
|
||||
|
@ -309,7 +310,7 @@ where
|
|||
}
|
||||
|
||||
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, E> {
|
||||
Ok(SerializeSeq {
|
||||
Ok(SeqSerialize {
|
||||
is_human_readable: self.is_human_readable,
|
||||
elements: Vec::with_capacity(len.unwrap_or(0)),
|
||||
error: PhantomData,
|
||||
|
@ -317,7 +318,7 @@ where
|
|||
}
|
||||
|
||||
fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, E> {
|
||||
Ok(SerializeTuple {
|
||||
Ok(TupleSerialize {
|
||||
is_human_readable: self.is_human_readable,
|
||||
elements: Vec::with_capacity(len),
|
||||
error: PhantomData,
|
||||
|
@ -329,7 +330,7 @@ where
|
|||
name: &'static str,
|
||||
len: usize,
|
||||
) -> Result<Self::SerializeTupleStruct, E> {
|
||||
Ok(SerializeTupleStruct {
|
||||
Ok(TupleStructSerialize {
|
||||
is_human_readable: self.is_human_readable,
|
||||
name,
|
||||
fields: Vec::with_capacity(len),
|
||||
|
@ -344,7 +345,7 @@ where
|
|||
variant: &'static str,
|
||||
len: usize,
|
||||
) -> Result<Self::SerializeTupleVariant, E> {
|
||||
Ok(SerializeTupleVariant {
|
||||
Ok(TupleVariantSerialize {
|
||||
is_human_readable: self.is_human_readable,
|
||||
name,
|
||||
variant_index,
|
||||
|
@ -355,7 +356,7 @@ where
|
|||
}
|
||||
|
||||
fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap, E> {
|
||||
Ok(SerializeMap {
|
||||
Ok(MapSerialize {
|
||||
is_human_readable: self.is_human_readable,
|
||||
entries: Vec::with_capacity(len.unwrap_or(0)),
|
||||
key: None,
|
||||
|
@ -364,7 +365,7 @@ where
|
|||
}
|
||||
|
||||
fn serialize_struct(self, name: &'static str, len: usize) -> Result<Self::SerializeStruct, E> {
|
||||
Ok(SerializeStruct {
|
||||
Ok(StructSerialize {
|
||||
is_human_readable: self.is_human_readable,
|
||||
name,
|
||||
fields: Vec::with_capacity(len),
|
||||
|
@ -379,7 +380,7 @@ where
|
|||
variant: &'static str,
|
||||
len: usize,
|
||||
) -> Result<Self::SerializeStructVariant, E> {
|
||||
Ok(SerializeStructVariant {
|
||||
Ok(StructVariantSerialize {
|
||||
is_human_readable: self.is_human_readable,
|
||||
name,
|
||||
variant_index,
|
||||
|
@ -390,15 +391,15 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) struct SerializeSeq<E> {
|
||||
pub(crate) struct SeqSerialize<E> {
|
||||
is_human_readable: bool,
|
||||
elements: Vec<Content>,
|
||||
error: PhantomData<E>,
|
||||
}
|
||||
|
||||
impl<E> ser::SerializeSeq for SerializeSeq<E>
|
||||
impl<E> SerializeSeq for SeqSerialize<E>
|
||||
where
|
||||
E: ser::Error,
|
||||
E: SerError,
|
||||
{
|
||||
type Ok = Content;
|
||||
type Error = E;
|
||||
|
@ -417,15 +418,15 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) struct SerializeTuple<E> {
|
||||
pub(crate) struct TupleSerialize<E> {
|
||||
is_human_readable: bool,
|
||||
elements: Vec<Content>,
|
||||
error: PhantomData<E>,
|
||||
}
|
||||
|
||||
impl<E> ser::SerializeTuple for SerializeTuple<E>
|
||||
impl<E> SerializeTuple for TupleSerialize<E>
|
||||
where
|
||||
E: ser::Error,
|
||||
E: SerError,
|
||||
{
|
||||
type Ok = Content;
|
||||
type Error = E;
|
||||
|
@ -444,16 +445,16 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) struct SerializeTupleStruct<E> {
|
||||
pub(crate) struct TupleStructSerialize<E> {
|
||||
is_human_readable: bool,
|
||||
name: &'static str,
|
||||
fields: Vec<Content>,
|
||||
error: PhantomData<E>,
|
||||
}
|
||||
|
||||
impl<E> ser::SerializeTupleStruct for SerializeTupleStruct<E>
|
||||
impl<E> SerializeTupleStruct for TupleStructSerialize<E>
|
||||
where
|
||||
E: ser::Error,
|
||||
E: SerError,
|
||||
{
|
||||
type Ok = Content;
|
||||
type Error = E;
|
||||
|
@ -472,7 +473,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) struct SerializeTupleVariant<E> {
|
||||
pub(crate) struct TupleVariantSerialize<E> {
|
||||
is_human_readable: bool,
|
||||
name: &'static str,
|
||||
variant_index: u32,
|
||||
|
@ -481,9 +482,9 @@ pub(crate) struct SerializeTupleVariant<E> {
|
|||
error: PhantomData<E>,
|
||||
}
|
||||
|
||||
impl<E> ser::SerializeTupleVariant for SerializeTupleVariant<E>
|
||||
impl<E> SerializeTupleVariant for TupleVariantSerialize<E>
|
||||
where
|
||||
E: ser::Error,
|
||||
E: SerError,
|
||||
{
|
||||
type Ok = Content;
|
||||
type Error = E;
|
||||
|
@ -507,16 +508,16 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) struct SerializeMap<E> {
|
||||
pub(crate) struct MapSerialize<E> {
|
||||
is_human_readable: bool,
|
||||
entries: Vec<(Content, Content)>,
|
||||
key: Option<Content>,
|
||||
error: PhantomData<E>,
|
||||
}
|
||||
|
||||
impl<E> ser::SerializeMap for SerializeMap<E>
|
||||
impl<E> SerializeMap for MapSerialize<E>
|
||||
where
|
||||
E: ser::Error,
|
||||
E: SerError,
|
||||
{
|
||||
type Ok = Content;
|
||||
type Error = E;
|
||||
|
@ -559,16 +560,16 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) struct SerializeStruct<E> {
|
||||
pub(crate) struct StructSerialize<E> {
|
||||
is_human_readable: bool,
|
||||
name: &'static str,
|
||||
fields: Vec<(&'static str, Content)>,
|
||||
error: PhantomData<E>,
|
||||
}
|
||||
|
||||
impl<E> ser::SerializeStruct for SerializeStruct<E>
|
||||
impl<E> SerializeStruct for StructSerialize<E>
|
||||
where
|
||||
E: ser::Error,
|
||||
E: SerError,
|
||||
{
|
||||
type Ok = Content;
|
||||
type Error = E;
|
||||
|
@ -587,7 +588,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) struct SerializeStructVariant<E> {
|
||||
pub(crate) struct StructVariantSerialize<E> {
|
||||
is_human_readable: bool,
|
||||
name: &'static str,
|
||||
variant_index: u32,
|
||||
|
@ -596,9 +597,9 @@ pub(crate) struct SerializeStructVariant<E> {
|
|||
error: PhantomData<E>,
|
||||
}
|
||||
|
||||
impl<E> ser::SerializeStructVariant for SerializeStructVariant<E>
|
||||
impl<E> SerializeStructVariant for StructVariantSerialize<E>
|
||||
where
|
||||
E: ser::Error,
|
||||
E: SerError,
|
||||
{
|
||||
type Ok = Content;
|
||||
type Error = E;
|
||||
|
|
|
@ -1,338 +0,0 @@
|
|||
use super::*;
|
||||
use crate::utils::{MapIter, SeqIter};
|
||||
use alloc::{borrow::Cow, boxed::Box, collections::BTreeMap, string::String, vec::Vec};
|
||||
use core::{convert::TryInto, fmt, mem::MaybeUninit};
|
||||
use serde::de::*;
|
||||
use std::collections::HashMap;
|
||||
|
||||
// TODO this should probably be moved into the utils module when const generics are available for MSRV
|
||||
|
||||
/// # Safety
|
||||
/// The code follow exactly the pattern of initializing an array element-by-element from the standard library.
|
||||
/// <https://doc.rust-lang.org/nightly/std/mem/union.MaybeUninit.html#initializing-an-array-element-by-element>
|
||||
fn array_from_iterator<I, T, E, const N: usize>(
|
||||
mut iter: I,
|
||||
expected: &dyn Expected,
|
||||
) -> Result<[T; N], E>
|
||||
where
|
||||
I: Iterator<Item = Result<T, E>>,
|
||||
E: Error,
|
||||
{
|
||||
fn drop_array_elems<T, const N: usize>(num: usize, mut arr: [MaybeUninit<T>; N]) {
|
||||
arr[..num].iter_mut().for_each(|elem| {
|
||||
// TODO This would be better with assume_init_drop nightly function
|
||||
// https://github.com/rust-lang/rust/issues/63567
|
||||
unsafe { core::ptr::drop_in_place(elem.as_mut_ptr()) };
|
||||
});
|
||||
}
|
||||
|
||||
// Create an uninitialized array of `MaybeUninit`. The `assume_init` is
|
||||
// safe because the type we are claiming to have initialized here is a
|
||||
// bunch of `MaybeUninit`s, which do not require initialization.
|
||||
//
|
||||
// TODO could be simplified with nightly maybe_uninit_uninit_array feature
|
||||
// https://doc.rust-lang.org/nightly/std/mem/union.MaybeUninit.html#method.uninit_array
|
||||
let mut arr: [MaybeUninit<T>; N] = unsafe { MaybeUninit::uninit().assume_init() };
|
||||
|
||||
// Dropping a `MaybeUninit` does nothing. Thus using raw pointer
|
||||
// assignment instead of `ptr::write` does not cause the old
|
||||
// uninitialized value to be dropped. Also if there is a panic during
|
||||
// this loop, we have a memory leak, but there is no memory safety
|
||||
// issue.
|
||||
for (idx, elem) in arr[..].iter_mut().enumerate() {
|
||||
*elem = match iter.next() {
|
||||
Some(Ok(value)) => MaybeUninit::new(value),
|
||||
Some(Err(err)) => {
|
||||
drop_array_elems(idx, arr);
|
||||
return Err(err);
|
||||
}
|
||||
None => {
|
||||
drop_array_elems(idx, arr);
|
||||
return Err(Error::invalid_length(idx, expected));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Everything is initialized. Transmute the array to the
|
||||
// initialized type.
|
||||
// A normal transmute is not possible because of:
|
||||
// https://github.com/rust-lang/rust/issues/61956
|
||||
Ok(unsafe { core::mem::transmute_copy::<_, [T; N]>(&arr) })
|
||||
}
|
||||
|
||||
impl<'de, T, As, const N: usize> DeserializeAs<'de, [T; N]> for [As; N]
|
||||
where
|
||||
As: DeserializeAs<'de, T>,
|
||||
{
|
||||
fn deserialize_as<D>(deserializer: D) -> Result<[T; N], D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct ArrayVisitor<T, const M: usize>(PhantomData<T>);
|
||||
|
||||
impl<'de, T, As, const M: usize> Visitor<'de> for ArrayVisitor<DeserializeAsWrap<T, As>, M>
|
||||
where
|
||||
As: DeserializeAs<'de, T>,
|
||||
{
|
||||
type Value = [T; M];
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
formatter.write_fmt(format_args!("an array of size {}", M))
|
||||
}
|
||||
|
||||
fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
|
||||
where
|
||||
A: SeqAccess<'de>,
|
||||
{
|
||||
array_from_iterator(
|
||||
SeqIter::new(seq).map(|res: Result<DeserializeAsWrap<T, As>, A::Error>| {
|
||||
res.map(|t| t.into_inner())
|
||||
}),
|
||||
&self,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_tuple(N, ArrayVisitor::<DeserializeAsWrap<T, As>, N>(PhantomData))
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! tuple_seq_as_map_impl_intern {
|
||||
($tyorig:ty, $ty:ident <KAs, VAs>) => {
|
||||
#[allow(clippy::implicit_hasher)]
|
||||
impl<'de, K, KAs, V, VAs, const N: usize> DeserializeAs<'de, $tyorig> for $ty<KAs, VAs>
|
||||
where
|
||||
KAs: DeserializeAs<'de, K>,
|
||||
VAs: DeserializeAs<'de, V>,
|
||||
{
|
||||
fn deserialize_as<D>(deserializer: D) -> Result<$tyorig, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct MapVisitor<K, KAs, V, VAs, const M: usize> {
|
||||
marker: PhantomData<(K, KAs, V, VAs)>,
|
||||
}
|
||||
|
||||
impl<'de, K, KAs, V, VAs, const M: usize> Visitor<'de> for MapVisitor<K, KAs, V, VAs, M>
|
||||
where
|
||||
KAs: DeserializeAs<'de, K>,
|
||||
VAs: DeserializeAs<'de, V>,
|
||||
{
|
||||
type Value = [(K, V); M];
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
formatter.write_fmt(format_args!("a map of length {}", M))
|
||||
}
|
||||
|
||||
fn visit_map<A>(self, access: A) -> Result<Self::Value, A::Error>
|
||||
where
|
||||
A: MapAccess<'de>,
|
||||
{
|
||||
array_from_iterator(MapIter::new(access).map(
|
||||
|res: Result<(DeserializeAsWrap<K, KAs>, DeserializeAsWrap<V, VAs>), A::Error>| {
|
||||
res.map(|(k, v)| (k.into_inner(), v.into_inner()))
|
||||
}
|
||||
), &self)
|
||||
}
|
||||
}
|
||||
|
||||
let visitor = MapVisitor::<K, KAs, V, VAs, N> {
|
||||
marker: PhantomData,
|
||||
};
|
||||
deserializer.deserialize_map(visitor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
tuple_seq_as_map_impl_intern!([(K, V); N], BTreeMap<KAs, VAs>);
|
||||
tuple_seq_as_map_impl_intern!([(K, V); N], HashMap<KAs, VAs>);
|
||||
|
||||
impl<'de, const N: usize> DeserializeAs<'de, [u8; N]> for Bytes {
|
||||
fn deserialize_as<D>(deserializer: D) -> Result<[u8; N], D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct ArrayVisitor<const M: usize>;
|
||||
|
||||
impl<'de, const M: usize> Visitor<'de> for ArrayVisitor<M> {
|
||||
type Value = [u8; M];
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
formatter.write_fmt(format_args!("an byte array of size {}", M))
|
||||
}
|
||||
|
||||
fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
|
||||
where
|
||||
A: SeqAccess<'de>,
|
||||
{
|
||||
array_from_iterator(SeqIter::new(seq), &self)
|
||||
}
|
||||
|
||||
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
v.try_into()
|
||||
.map_err(|_| Error::invalid_length(v.len(), &self))
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
v.as_bytes()
|
||||
.try_into()
|
||||
.map_err(|_| Error::invalid_length(v.len(), &self))
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_bytes(ArrayVisitor::<N>)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, const N: usize> DeserializeAs<'de, &'de [u8; N]> for Bytes {
|
||||
fn deserialize_as<D>(deserializer: D) -> Result<&'de [u8; N], D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct ArrayVisitor<const M: usize>;
|
||||
|
||||
impl<'de, const M: usize> Visitor<'de> for ArrayVisitor<M> {
|
||||
type Value = &'de [u8; M];
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
formatter.write_fmt(format_args!("a borrowed byte array of size {}", M))
|
||||
}
|
||||
|
||||
fn visit_borrowed_bytes<E>(self, v: &'de [u8]) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
v.try_into()
|
||||
.map_err(|_| Error::invalid_length(v.len(), &self))
|
||||
}
|
||||
|
||||
fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
v.as_bytes()
|
||||
.try_into()
|
||||
.map_err(|_| Error::invalid_length(v.len(), &self))
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_bytes(ArrayVisitor::<N>)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, const N: usize> DeserializeAs<'de, Cow<'de, [u8; N]>> for Bytes {
|
||||
fn deserialize_as<D>(deserializer: D) -> Result<Cow<'de, [u8; N]>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct CowVisitor<const M: usize>;
|
||||
|
||||
impl<'de, const M: usize> Visitor<'de> for CowVisitor<M> {
|
||||
type Value = Cow<'de, [u8; M]>;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
formatter.write_str("a byte array")
|
||||
}
|
||||
|
||||
fn visit_borrowed_bytes<E>(self, v: &'de [u8]) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
Ok(Cow::Borrowed(
|
||||
v.try_into()
|
||||
.map_err(|_| Error::invalid_length(v.len(), &self))?,
|
||||
))
|
||||
}
|
||||
|
||||
fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
Ok(Cow::Borrowed(
|
||||
v.as_bytes()
|
||||
.try_into()
|
||||
.map_err(|_| Error::invalid_length(v.len(), &self))?,
|
||||
))
|
||||
}
|
||||
|
||||
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
Ok(Cow::Owned(
|
||||
v.to_vec()
|
||||
.try_into()
|
||||
.map_err(|_| Error::invalid_length(v.len(), &self))?,
|
||||
))
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
Ok(Cow::Owned(
|
||||
v.as_bytes()
|
||||
.to_vec()
|
||||
.try_into()
|
||||
.map_err(|_| Error::invalid_length(v.len(), &self))?,
|
||||
))
|
||||
}
|
||||
|
||||
fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
let len = v.len();
|
||||
Ok(Cow::Owned(
|
||||
v.try_into()
|
||||
.map_err(|_| Error::invalid_length(len, &self))?,
|
||||
))
|
||||
}
|
||||
|
||||
fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
let len = v.len();
|
||||
Ok(Cow::Owned(
|
||||
v.into_bytes()
|
||||
.try_into()
|
||||
.map_err(|_| Error::invalid_length(len, &self))?,
|
||||
))
|
||||
}
|
||||
|
||||
fn visit_seq<V>(self, seq: V) -> Result<Self::Value, V::Error>
|
||||
where
|
||||
V: SeqAccess<'de>,
|
||||
{
|
||||
Ok(Cow::Owned(array_from_iterator(SeqIter::new(seq), &self)?))
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_bytes(CowVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, const N: usize> DeserializeAs<'de, Box<[u8; N]>> for Bytes {
|
||||
fn deserialize_as<D>(deserializer: D) -> Result<Box<[u8; N]>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Bytes::deserialize_as(deserializer).map(Box::new)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, const N: usize> DeserializeAs<'de, Cow<'de, [u8; N]>> for BorrowCow {
|
||||
fn deserialize_as<D>(deserializer: D) -> Result<Cow<'de, [u8; N]>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Bytes::deserialize_as(deserializer)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,223 @@
|
|||
use super::impls::{foreach_map_create, foreach_set_create};
|
||||
use crate::{
|
||||
duplicate_key_impls::{
|
||||
DuplicateInsertsFirstWinsMap, DuplicateInsertsLastWinsSet, PreventDuplicateInsertsMap,
|
||||
PreventDuplicateInsertsSet,
|
||||
},
|
||||
prelude::*,
|
||||
MapFirstKeyWins, MapPreventDuplicates, SetLastValueWins, SetPreventDuplicates,
|
||||
};
|
||||
#[cfg(feature = "indexmap_1")]
|
||||
use indexmap_1::{IndexMap, IndexSet};
|
||||
|
||||
struct SetPreventDuplicatesVisitor<SET, T, TAs>(PhantomData<(SET, T, TAs)>);
|
||||
|
||||
impl<'de, SET, T, TAs> Visitor<'de> for SetPreventDuplicatesVisitor<SET, T, TAs>
|
||||
where
|
||||
SET: PreventDuplicateInsertsSet<T>,
|
||||
TAs: DeserializeAs<'de, T>,
|
||||
{
|
||||
type Value = SET;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
formatter.write_str("a sequence")
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_seq<A>(self, mut access: A) -> Result<Self::Value, A::Error>
|
||||
where
|
||||
A: SeqAccess<'de>,
|
||||
{
|
||||
let mut values = Self::Value::new(access.size_hint());
|
||||
|
||||
while let Some(value) = access.next_element::<DeserializeAsWrap<T, TAs>>()? {
|
||||
if !values.insert(value.into_inner()) {
|
||||
return Err(DeError::custom("invalid entry: found duplicate value"));
|
||||
};
|
||||
}
|
||||
|
||||
Ok(values)
|
||||
}
|
||||
}
|
||||
|
||||
struct SetLastValueWinsVisitor<SET, T, TAs>(PhantomData<(SET, T, TAs)>);
|
||||
|
||||
impl<'de, SET, T, TAs> Visitor<'de> for SetLastValueWinsVisitor<SET, T, TAs>
|
||||
where
|
||||
SET: DuplicateInsertsLastWinsSet<T>,
|
||||
TAs: DeserializeAs<'de, T>,
|
||||
{
|
||||
type Value = SET;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
formatter.write_str("a sequence")
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_seq<A>(self, mut access: A) -> Result<Self::Value, A::Error>
|
||||
where
|
||||
A: SeqAccess<'de>,
|
||||
{
|
||||
let mut values = Self::Value::new(access.size_hint());
|
||||
|
||||
while let Some(value) = access.next_element::<DeserializeAsWrap<T, TAs>>()? {
|
||||
values.replace(value.into_inner());
|
||||
}
|
||||
|
||||
Ok(values)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
macro_rules! set_impl {
|
||||
(
|
||||
$ty:ident < T $(: $tbound1:ident $(+ $tbound2:ident)*)* $(, $typaram:ident : $bound1:ident $(+ $bound2:ident)* )* >,
|
||||
$with_capacity:expr,
|
||||
$append:ident
|
||||
) => {
|
||||
impl<'de, T, TAs $(, $typaram)*> DeserializeAs<'de, $ty<T $(, $typaram)*>> for SetPreventDuplicates<TAs>
|
||||
where
|
||||
TAs: DeserializeAs<'de, T>,
|
||||
$(T: $tbound1 $(+ $tbound2)*,)*
|
||||
$($typaram: $bound1 $(+ $bound2)*),*
|
||||
{
|
||||
fn deserialize_as<D>(deserializer: D) -> Result<$ty<T $(, $typaram)*>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_seq(SetPreventDuplicatesVisitor::<$ty<T $(, $typaram)*>, T, TAs>(
|
||||
PhantomData,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, T, TAs $(, $typaram)*> DeserializeAs<'de, $ty<T $(, $typaram)*>> for SetLastValueWins<TAs>
|
||||
where
|
||||
TAs: DeserializeAs<'de, T>,
|
||||
$(T: $tbound1 $(+ $tbound2)*,)*
|
||||
$($typaram: $bound1 $(+ $bound2)*),*
|
||||
{
|
||||
fn deserialize_as<D>(deserializer: D) -> Result<$ty<T $(, $typaram)*>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserializer
|
||||
.deserialize_seq(SetLastValueWinsVisitor::<$ty<T $(, $typaram)*>, T, TAs>(PhantomData))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach_set_create!(set_impl);
|
||||
|
||||
struct MapPreventDuplicatesVisitor<MAP, K, KAs, V, VAs>(PhantomData<(MAP, K, KAs, V, VAs)>);
|
||||
|
||||
impl<'de, MAP, K, KAs, V, VAs> Visitor<'de> for MapPreventDuplicatesVisitor<MAP, K, KAs, V, VAs>
|
||||
where
|
||||
MAP: PreventDuplicateInsertsMap<K, V>,
|
||||
KAs: DeserializeAs<'de, K>,
|
||||
VAs: DeserializeAs<'de, V>,
|
||||
{
|
||||
type Value = MAP;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
formatter.write_str("a map")
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_map<A>(self, mut access: A) -> Result<Self::Value, A::Error>
|
||||
where
|
||||
A: MapAccess<'de>,
|
||||
{
|
||||
let mut values = Self::Value::new(access.size_hint());
|
||||
|
||||
while let Some((key, value)) =
|
||||
access.next_entry::<DeserializeAsWrap<K, KAs>, DeserializeAsWrap<V, VAs>>()?
|
||||
{
|
||||
if !values.insert(key.into_inner(), value.into_inner()) {
|
||||
return Err(DeError::custom("invalid entry: found duplicate key"));
|
||||
};
|
||||
}
|
||||
|
||||
Ok(values)
|
||||
}
|
||||
}
|
||||
|
||||
struct MapFirstKeyWinsVisitor<MAP, K, KAs, V, VAs>(PhantomData<(MAP, K, KAs, V, VAs)>);
|
||||
|
||||
impl<'de, MAP, K, KAs, V, VAs> Visitor<'de> for MapFirstKeyWinsVisitor<MAP, K, KAs, V, VAs>
|
||||
where
|
||||
MAP: DuplicateInsertsFirstWinsMap<K, V>,
|
||||
KAs: DeserializeAs<'de, K>,
|
||||
VAs: DeserializeAs<'de, V>,
|
||||
{
|
||||
type Value = MAP;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
formatter.write_str("a map")
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_map<A>(self, mut access: A) -> Result<Self::Value, A::Error>
|
||||
where
|
||||
A: MapAccess<'de>,
|
||||
{
|
||||
let mut values = Self::Value::new(access.size_hint());
|
||||
|
||||
while let Some((key, value)) =
|
||||
access.next_entry::<DeserializeAsWrap<K, KAs>, DeserializeAsWrap<V, VAs>>()?
|
||||
{
|
||||
values.insert(key.into_inner(), value.into_inner());
|
||||
}
|
||||
|
||||
Ok(values)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
macro_rules! map_impl {
|
||||
(
|
||||
$ty:ident < K $(: $kbound1:ident $(+ $kbound2:ident)*)*, V $(, $typaram:ident : $bound1:ident $(+ $bound2:ident)*)* >,
|
||||
$with_capacity:expr
|
||||
) => {
|
||||
impl<'de, K, V, KAs, VAs $(, $typaram)*> DeserializeAs<'de, $ty<K, V $(, $typaram)*>>
|
||||
for MapPreventDuplicates<KAs, VAs>
|
||||
where
|
||||
KAs: DeserializeAs<'de, K>,
|
||||
VAs: DeserializeAs<'de, V>,
|
||||
$(K: $kbound1 $(+ $kbound2)*,)*
|
||||
$($typaram: $bound1 $(+ $bound2)*),*
|
||||
{
|
||||
fn deserialize_as<D>(deserializer: D) -> Result<$ty<K, V $(, $typaram)*>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_map(MapPreventDuplicatesVisitor::<
|
||||
$ty<K, V $(, $typaram)*>,
|
||||
K,
|
||||
KAs,
|
||||
V,
|
||||
VAs,
|
||||
>(PhantomData))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, K, V, KAs, VAs $(, $typaram)*> DeserializeAs<'de, $ty<K, V $(, $typaram)*>>
|
||||
for MapFirstKeyWins<KAs, VAs>
|
||||
where
|
||||
KAs: DeserializeAs<'de, K>,
|
||||
VAs: DeserializeAs<'de, V>,
|
||||
$(K: $kbound1 $(+ $kbound2)*,)*
|
||||
$($typaram: $bound1 $(+ $bound2)*),*
|
||||
{
|
||||
fn deserialize_as<D>(deserializer: D) -> Result<$ty<K, V $(, $typaram)*>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_map(MapFirstKeyWinsVisitor::<$ty<K, V $(, $typaram)*>, K, KAs, V, VAs>(
|
||||
PhantomData,
|
||||
))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
foreach_map_create!(map_impl);
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,85 +0,0 @@
|
|||
use super::*;
|
||||
use core::fmt;
|
||||
use serde::de::*;
|
||||
|
||||
macro_rules! array_impl {
|
||||
($len:literal $($idx:tt)*) => {
|
||||
impl<'de, T, As> DeserializeAs<'de, [T; $len]> for [As; $len]
|
||||
where
|
||||
As: DeserializeAs<'de, T>,
|
||||
{
|
||||
fn deserialize_as<D>(deserializer: D) -> Result<[T; $len], D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct ArrayVisitor<T>(PhantomData<T>);
|
||||
|
||||
impl<'de, T, As> Visitor<'de>
|
||||
for ArrayVisitor<DeserializeAsWrap<T, As>>
|
||||
where
|
||||
As: DeserializeAs<'de, T>,
|
||||
{
|
||||
type Value = [T; $len];
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
formatter.write_str(concat!("an array of size ", $len))
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
// Because of 0-size arrays
|
||||
#[allow(unused_variables, unused_mut)]
|
||||
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||
where
|
||||
A: SeqAccess<'de>,
|
||||
{
|
||||
Ok([$(
|
||||
match seq.next_element::<DeserializeAsWrap<T, As>>()? {
|
||||
Some(value) => value.into_inner(),
|
||||
None => return Err(Error::invalid_length($idx, &self)),
|
||||
},
|
||||
)*])
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_tuple(
|
||||
$len,
|
||||
ArrayVisitor::<DeserializeAsWrap<T, As>>(PhantomData),
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
array_impl!(0);
|
||||
array_impl!(1 0);
|
||||
array_impl!(2 0 1);
|
||||
array_impl!(3 0 1 2);
|
||||
array_impl!(4 0 1 2 3);
|
||||
array_impl!(5 0 1 2 3 4);
|
||||
array_impl!(6 0 1 2 3 4 5);
|
||||
array_impl!(7 0 1 2 3 4 5 6);
|
||||
array_impl!(8 0 1 2 3 4 5 6 7);
|
||||
array_impl!(9 0 1 2 3 4 5 6 7 8);
|
||||
array_impl!(10 0 1 2 3 4 5 6 7 8 9);
|
||||
array_impl!(11 0 1 2 3 4 5 6 7 8 9 10);
|
||||
array_impl!(12 0 1 2 3 4 5 6 7 8 9 10 11);
|
||||
array_impl!(13 0 1 2 3 4 5 6 7 8 9 10 11 12);
|
||||
array_impl!(14 0 1 2 3 4 5 6 7 8 9 10 11 12 13);
|
||||
array_impl!(15 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14);
|
||||
array_impl!(16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15);
|
||||
array_impl!(17 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16);
|
||||
array_impl!(18 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17);
|
||||
array_impl!(19 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18);
|
||||
array_impl!(20 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19);
|
||||
array_impl!(21 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20);
|
||||
array_impl!(22 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21);
|
||||
array_impl!(23 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22);
|
||||
array_impl!(24 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23);
|
||||
array_impl!(25 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24);
|
||||
array_impl!(26 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25);
|
||||
array_impl!(27 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26);
|
||||
array_impl!(28 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27);
|
||||
array_impl!(29 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28);
|
||||
array_impl!(30 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29);
|
||||
array_impl!(31 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30);
|
||||
array_impl!(32 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31);
|
|
@ -7,15 +7,16 @@
|
|||
//!
|
||||
//! [user guide]: crate::guide
|
||||
|
||||
mod const_arrays;
|
||||
#[cfg(feature = "alloc")]
|
||||
mod duplicates;
|
||||
mod impls;
|
||||
|
||||
use super::*;
|
||||
use crate::prelude::*;
|
||||
|
||||
/// A **data structure** that can be deserialized from any data format supported by Serde, analogue to [`Deserialize`].
|
||||
///
|
||||
/// The trait is analogue to the [`serde::Deserialize`][`Deserialize`] trait, with the same meaning of input and output arguments.
|
||||
/// It can and should the implemented using the same code structure as the [`Deserialize`] trait.
|
||||
/// It can and should be implemented using the same code structure as the [`Deserialize`] trait.
|
||||
/// As such, the same advice for [implementing `Deserialize`][impl-deserialize] applies here.
|
||||
///
|
||||
/// # Differences to [`Deserialize`]
|
||||
|
@ -74,7 +75,7 @@ use super::*;
|
|||
/// # #[cfg(all(feature = "macros"))] {
|
||||
/// # use serde::Deserialize;
|
||||
/// # use serde::de::Error;
|
||||
/// # use serde_with::DeserializeAs;
|
||||
/// # use serde_with::{serde_as, DeserializeAs};
|
||||
/// # use std::str::FromStr;
|
||||
/// # use std::fmt::Display;
|
||||
/// struct DisplayFromStr;
|
||||
|
@ -93,7 +94,7 @@ use super::*;
|
|||
/// }
|
||||
/// }
|
||||
/// #
|
||||
/// # #[serde_with::serde_as]
|
||||
/// # #[serde_as]
|
||||
/// # #[derive(serde::Deserialize)]
|
||||
/// # struct S (#[serde_as(as = "DisplayFromStr")] bool);
|
||||
/// #
|
||||
|
@ -114,7 +115,6 @@ pub trait DeserializeAs<'de, T>: Sized {
|
|||
}
|
||||
|
||||
/// Helper type to implement [`DeserializeAs`] for container-like types.
|
||||
#[derive(Debug)]
|
||||
pub struct DeserializeAsWrap<T, U> {
|
||||
value: T,
|
||||
marker: PhantomData<U>,
|
||||
|
@ -141,3 +141,18 @@ where
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> As<T> {
|
||||
/// Deserialize type `T` using [`DeserializeAs`][]
|
||||
///
|
||||
/// The function signature is compatible with [serde's with-annotation][with-annotation].
|
||||
///
|
||||
/// [with-annotation]: https://serde.rs/field-attrs.html#with
|
||||
pub fn deserialize<'de, D, I>(deserializer: D) -> Result<I, D::Error>
|
||||
where
|
||||
T: DeserializeAs<'de, I>,
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
T::deserialize_as(deserializer)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
use alloc::collections::{BTreeMap, BTreeSet};
|
||||
use core::hash::{BuildHasher, Hash};
|
||||
#[cfg(feature = "indexmap")]
|
||||
use indexmap_crate::{IndexMap, IndexSet};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use crate::prelude::*;
|
||||
#[cfg(feature = "indexmap_1")]
|
||||
use indexmap_1::{IndexMap, IndexSet};
|
||||
|
||||
pub trait PreventDuplicateInsertsSet<T> {
|
||||
fn new(size_hint: Option<usize>) -> Self;
|
||||
|
@ -18,6 +16,7 @@ pub trait PreventDuplicateInsertsMap<K, V> {
|
|||
fn insert(&mut self, key: K, value: V) -> bool;
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<T, S> PreventDuplicateInsertsSet<T> for HashSet<T, S>
|
||||
where
|
||||
T: Eq + Hash,
|
||||
|
@ -37,7 +36,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "indexmap")]
|
||||
#[cfg(feature = "indexmap_1")]
|
||||
impl<T, S> PreventDuplicateInsertsSet<T> for IndexSet<T, S>
|
||||
where
|
||||
T: Eq + Hash,
|
||||
|
@ -72,6 +71,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<K, V, S> PreventDuplicateInsertsMap<K, V> for HashMap<K, V, S>
|
||||
where
|
||||
K: Eq + Hash,
|
||||
|
@ -91,7 +91,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "indexmap")]
|
||||
#[cfg(feature = "indexmap_1")]
|
||||
impl<K, V, S> PreventDuplicateInsertsMap<K, V> for IndexMap<K, V, S>
|
||||
where
|
||||
K: Eq + Hash,
|
||||
|
|
|
@ -1,16 +1,6 @@
|
|||
use alloc::collections::{BTreeMap, BTreeSet};
|
||||
use core::hash::{BuildHasher, Hash};
|
||||
#[cfg(feature = "indexmap")]
|
||||
use indexmap_crate::IndexMap;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
#[deprecated = "This is serde's default behavior."]
|
||||
pub trait DuplicateInsertsFirstWinsSet<T> {
|
||||
fn new(size_hint: Option<usize>) -> Self;
|
||||
|
||||
/// Insert the value into the set, if there is not already an existing value
|
||||
fn insert(&mut self, value: T);
|
||||
}
|
||||
use crate::prelude::*;
|
||||
#[cfg(feature = "indexmap_1")]
|
||||
use indexmap_1::IndexMap;
|
||||
|
||||
pub trait DuplicateInsertsFirstWinsMap<K, V> {
|
||||
fn new(size_hint: Option<usize>) -> Self;
|
||||
|
@ -19,44 +9,7 @@ pub trait DuplicateInsertsFirstWinsMap<K, V> {
|
|||
fn insert(&mut self, key: K, value: V);
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl<T, S> DuplicateInsertsFirstWinsSet<T> for HashSet<T, S>
|
||||
where
|
||||
T: Eq + Hash,
|
||||
S: BuildHasher + Default,
|
||||
{
|
||||
#[inline]
|
||||
fn new(size_hint: Option<usize>) -> Self {
|
||||
match size_hint {
|
||||
Some(size) => Self::with_capacity_and_hasher(size, S::default()),
|
||||
None => Self::with_hasher(S::default()),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn insert(&mut self, value: T) {
|
||||
// Hashset already fulfils the contract and always keeps the first value
|
||||
self.insert(value);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl<T> DuplicateInsertsFirstWinsSet<T> for BTreeSet<T>
|
||||
where
|
||||
T: Ord,
|
||||
{
|
||||
#[inline]
|
||||
fn new(_size_hint: Option<usize>) -> Self {
|
||||
Self::new()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn insert(&mut self, value: T) {
|
||||
// BTreeSet already fulfils the contract and always keeps the first value
|
||||
self.insert(value);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<K, V, S> DuplicateInsertsFirstWinsMap<K, V> for HashMap<K, V, S>
|
||||
where
|
||||
K: Eq + Hash,
|
||||
|
@ -84,7 +37,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "indexmap")]
|
||||
#[cfg(feature = "indexmap_1")]
|
||||
impl<K, V, S> DuplicateInsertsFirstWinsMap<K, V> for IndexMap<K, V, S>
|
||||
where
|
||||
K: Eq + Hash,
|
||||
|
@ -100,7 +53,7 @@ where
|
|||
|
||||
#[inline]
|
||||
fn insert(&mut self, key: K, value: V) {
|
||||
use indexmap_crate::map::Entry;
|
||||
use indexmap_1::map::Entry;
|
||||
|
||||
match self.entry(key) {
|
||||
// we want to keep the first value, so do nothing
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
use alloc::collections::BTreeSet;
|
||||
use core::hash::{BuildHasher, Hash};
|
||||
#[cfg(feature = "indexmap")]
|
||||
use indexmap_crate::IndexSet;
|
||||
use std::collections::HashSet;
|
||||
use crate::prelude::*;
|
||||
#[cfg(feature = "indexmap_1")]
|
||||
use indexmap_1::IndexSet;
|
||||
|
||||
pub trait DuplicateInsertsLastWinsSet<T> {
|
||||
fn new(size_hint: Option<usize>) -> Self;
|
||||
|
@ -11,6 +9,7 @@ pub trait DuplicateInsertsLastWinsSet<T> {
|
|||
fn replace(&mut self, value: T);
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<T, S> DuplicateInsertsLastWinsSet<T> for HashSet<T, S>
|
||||
where
|
||||
T: Eq + Hash,
|
||||
|
@ -31,7 +30,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "indexmap")]
|
||||
#[cfg(feature = "indexmap_1")]
|
||||
impl<T, S> DuplicateInsertsLastWinsSet<T> for IndexSet<T, S>
|
||||
where
|
||||
T: Eq + Hash,
|
||||
|
|
|
@ -2,9 +2,8 @@ mod error_on_duplicate;
|
|||
mod first_value_wins;
|
||||
mod last_value_wins;
|
||||
|
||||
#[allow(deprecated)]
|
||||
pub use self::{
|
||||
error_on_duplicate::{PreventDuplicateInsertsMap, PreventDuplicateInsertsSet},
|
||||
first_value_wins::{DuplicateInsertsFirstWinsMap, DuplicateInsertsFirstWinsSet},
|
||||
first_value_wins::DuplicateInsertsFirstWinsMap,
|
||||
last_value_wins::DuplicateInsertsLastWinsSet,
|
||||
};
|
||||
|
|
|
@ -1,14 +1,6 @@
|
|||
use crate::{
|
||||
content::ser::{Content, ContentSerializer},
|
||||
DeserializeAs, SerializeAs,
|
||||
};
|
||||
use alloc::{string::ToString, vec::Vec};
|
||||
use core::{fmt, marker::PhantomData};
|
||||
use serde::{
|
||||
de::{DeserializeSeed, EnumAccess, Error, MapAccess, SeqAccess, VariantAccess, Visitor},
|
||||
ser,
|
||||
ser::{Impossible, SerializeMap, SerializeSeq, SerializeStructVariant, SerializeTupleVariant},
|
||||
Deserialize, Deserializer, Serialize, Serializer,
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
/// Represent a list of enum values as a map.
|
||||
|
@ -27,7 +19,7 @@ use serde::{
|
|||
/// ```rust
|
||||
/// # #[cfg(feature = "macros")] {
|
||||
/// # use serde::{Deserialize, Serialize};
|
||||
/// use serde_with::EnumMap;
|
||||
/// use serde_with::{serde_as, EnumMap};
|
||||
///
|
||||
/// # #[derive(Debug, Clone, PartialEq, Eq)]
|
||||
/// #[derive(Serialize, Deserialize)]
|
||||
|
@ -43,7 +35,7 @@ use serde::{
|
|||
/// },
|
||||
/// }
|
||||
///
|
||||
/// #[serde_with::serde_as]
|
||||
/// #[serde_as]
|
||||
/// # #[derive(Debug, Clone, PartialEq, Eq)]
|
||||
/// #[derive(Serialize, Deserialize)]
|
||||
/// struct VecEnumValues (
|
||||
|
@ -105,7 +97,7 @@ use serde::{
|
|||
/// ```
|
||||
/// # #[cfg(feature = "macros")] {
|
||||
/// # use serde::{Deserialize, Serialize};
|
||||
/// use serde_with::EnumMap;
|
||||
/// use serde_with::{serde_as, EnumMap};
|
||||
///
|
||||
/// # #[derive(Debug, Clone, PartialEq, Eq)]
|
||||
/// #[derive(Serialize, Deserialize)]
|
||||
|
@ -115,7 +107,7 @@ use serde::{
|
|||
/// Unit,
|
||||
/// }
|
||||
///
|
||||
/// #[serde_with::serde_as]
|
||||
/// #[serde_as]
|
||||
/// # #[derive(Debug, Clone, PartialEq, Eq)]
|
||||
/// #[derive(Serialize, Deserialize)]
|
||||
/// struct VecEnumValues {
|
||||
|
@ -139,17 +131,18 @@ use serde::{
|
|||
/// // into this XML document
|
||||
/// // Duplicate keys are emitted for identical enum variants.
|
||||
/// let expected = r#"
|
||||
/// <?xml version="1.0" encoding="UTF-8"?>
|
||||
/// <VecEnumValues>
|
||||
/// <vec>
|
||||
/// <Int>123</Int>
|
||||
/// <String>FooBar</String>
|
||||
/// <Int>456</Int>
|
||||
/// <String>XXX</String>
|
||||
/// <Unit></Unit>
|
||||
/// <Unit />
|
||||
/// </vec>
|
||||
/// </VecEnumValues>"#
|
||||
/// // Remove whitespace
|
||||
/// .replace(' ', "")
|
||||
/// .replace(" ", "")
|
||||
/// .replace('\n', "");
|
||||
///
|
||||
/// // Both serialization and deserialization work flawlessly.
|
||||
|
@ -159,7 +152,6 @@ use serde::{
|
|||
/// assert_eq!(values, deserialized);
|
||||
/// # }
|
||||
/// ```
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct EnumMap;
|
||||
|
||||
impl<T> SerializeAs<Vec<T>> for EnumMap
|
||||
|
@ -182,7 +174,10 @@ where
|
|||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct EnumMapVisitor<T>(PhantomData<T>);
|
||||
struct EnumMapVisitor<T> {
|
||||
is_human_readable: bool,
|
||||
phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<'de, T> Visitor<'de> for EnumMapVisitor<T>
|
||||
where
|
||||
|
@ -191,15 +186,22 @@ where
|
|||
type Value = Vec<T>;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(formatter, "a map or enum values")
|
||||
write!(formatter, "a map of enum values")
|
||||
}
|
||||
|
||||
fn visit_map<A: MapAccess<'de>>(self, map: A) -> Result<Self::Value, A::Error> {
|
||||
Vec::deserialize(SeqDeserializer(map))
|
||||
Vec::deserialize(SeqDeserializer {
|
||||
delegate: map,
|
||||
is_human_readable: self.is_human_readable,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_map(EnumMapVisitor(PhantomData))
|
||||
let is_human_readable = deserializer.is_human_readable();
|
||||
deserializer.deserialize_map(EnumMapVisitor {
|
||||
is_human_readable,
|
||||
phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -228,87 +230,91 @@ where
|
|||
type SerializeStruct = Impossible<S::Ok, S::Error>;
|
||||
type SerializeStructVariant = Impossible<S::Ok, S::Error>;
|
||||
|
||||
fn is_human_readable(&self) -> bool {
|
||||
self.0.is_human_readable()
|
||||
}
|
||||
|
||||
fn serialize_bool(self, _v: bool) -> Result<Self::Ok, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_i8(self, _v: i8) -> Result<Self::Ok, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_i16(self, _v: i16) -> Result<Self::Ok, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_i32(self, _v: i32) -> Result<Self::Ok, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_i64(self, _v: i64) -> Result<Self::Ok, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_i128(self, _v: i128) -> Result<Self::Ok, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_u8(self, _v: u8) -> Result<Self::Ok, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_u16(self, _v: u16) -> Result<Self::Ok, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_u32(self, _v: u32) -> Result<Self::Ok, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_u64(self, _v: u64) -> Result<Self::Ok, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_u128(self, _v: u128) -> Result<Self::Ok, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_f32(self, _v: f32) -> Result<Self::Ok, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_f64(self, _v: f64) -> Result<Self::Ok, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_char(self, _v: char) -> Result<Self::Ok, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_str(self, _v: &str) -> Result<Self::Ok, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_bytes(self, _v: &[u8]) -> Result<Self::Ok, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_some<T: ?Sized>(self, _value: &T) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_unit_variant(
|
||||
|
@ -317,7 +323,7 @@ where
|
|||
_variant_index: u32,
|
||||
_variant: &'static str,
|
||||
) -> Result<Self::Ok, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_newtype_struct<T: ?Sized>(
|
||||
|
@ -328,7 +334,7 @@ where
|
|||
where
|
||||
T: Serialize,
|
||||
{
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_newtype_variant<T: ?Sized>(
|
||||
|
@ -341,7 +347,7 @@ where
|
|||
where
|
||||
T: Serialize,
|
||||
{
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
|
||||
|
@ -355,7 +361,7 @@ where
|
|||
}
|
||||
|
||||
fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_tuple_struct(
|
||||
|
@ -363,7 +369,7 @@ where
|
|||
_name: &'static str,
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeTupleStruct, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_tuple_variant(
|
||||
|
@ -373,11 +379,11 @@ where
|
|||
_variant: &'static str,
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeTupleVariant, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_struct(
|
||||
|
@ -385,7 +391,7 @@ where
|
|||
_name: &'static str,
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeStruct, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_struct_variant(
|
||||
|
@ -395,13 +401,13 @@ where
|
|||
_variant: &'static str,
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeStructVariant, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Serialize a single element but turn the sequence into a map logic.
|
||||
///
|
||||
/// It uses [`SerializeEnumAsMapElement`] for the map element serialization.
|
||||
/// It uses [`EnumAsMapElementSerializer`] for the map element serialization.
|
||||
///
|
||||
/// The [`Serializer`] implementation handles all the `serialize_*_variant` functions and defers to [`SerializeVariant`] for the more complicated tuple and struct variants.
|
||||
struct SerializeSeqElement<M> {
|
||||
|
@ -452,87 +458,91 @@ where
|
|||
type SerializeStruct = Impossible<Self::Ok, Self::Error>;
|
||||
type SerializeStructVariant = SerializeVariant<'a, M>;
|
||||
|
||||
fn is_human_readable(&self) -> bool {
|
||||
self.is_human_readable
|
||||
}
|
||||
|
||||
fn serialize_bool(self, _v: bool) -> Result<Self::Ok, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_i8(self, _v: i8) -> Result<Self::Ok, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_i16(self, _v: i16) -> Result<Self::Ok, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_i32(self, _v: i32) -> Result<Self::Ok, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_i64(self, _v: i64) -> Result<Self::Ok, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_i128(self, _v: i128) -> Result<Self::Ok, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_u8(self, _v: u8) -> Result<Self::Ok, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_u16(self, _v: u16) -> Result<Self::Ok, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_u32(self, _v: u32) -> Result<Self::Ok, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_u64(self, _v: u64) -> Result<Self::Ok, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_u128(self, _v: u128) -> Result<Self::Ok, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_f32(self, _v: f32) -> Result<Self::Ok, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_f64(self, _v: f64) -> Result<Self::Ok, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_char(self, _v: char) -> Result<Self::Ok, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_str(self, _v: &str) -> Result<Self::Ok, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_bytes(self, _v: &[u8]) -> Result<Self::Ok, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_some<T: ?Sized>(self, _value: &T) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_unit_variant(
|
||||
|
@ -553,7 +563,7 @@ where
|
|||
where
|
||||
T: Serialize,
|
||||
{
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_newtype_variant<T: ?Sized>(
|
||||
|
@ -571,11 +581,11 @@ where
|
|||
}
|
||||
|
||||
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_tuple_struct(
|
||||
|
@ -583,7 +593,7 @@ where
|
|||
_name: &'static str,
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeTupleStruct, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_tuple_variant(
|
||||
|
@ -602,7 +612,7 @@ where
|
|||
}
|
||||
|
||||
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_struct(
|
||||
|
@ -610,7 +620,7 @@ where
|
|||
_name: &'static str,
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeStruct, Self::Error> {
|
||||
Err(ser::Error::custom("wrong type for EnumMap"))
|
||||
Err(SerError::custom("wrong type for EnumMap"))
|
||||
}
|
||||
|
||||
fn serialize_struct_variant(
|
||||
|
@ -698,7 +708,10 @@ where
|
|||
/// Deserialize the sequence of enum instances.
|
||||
///
|
||||
/// The main [`Deserializer`] implementation handles the outer sequence (e.g., `Vec`), while the [`SeqAccess`] implementation is responsible for the inner elements.
|
||||
struct SeqDeserializer<M>(M);
|
||||
struct SeqDeserializer<M> {
|
||||
delegate: M,
|
||||
is_human_readable: bool,
|
||||
}
|
||||
|
||||
impl<'de, M> Deserializer<'de> for SeqDeserializer<M>
|
||||
where
|
||||
|
@ -706,6 +719,10 @@ where
|
|||
{
|
||||
type Error = M::Error;
|
||||
|
||||
fn is_human_readable(&self) -> bool {
|
||||
self.is_human_readable
|
||||
}
|
||||
|
||||
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
|
@ -737,7 +754,15 @@ where
|
|||
where
|
||||
T: DeserializeSeed<'de>,
|
||||
{
|
||||
match seed.deserialize(EnumDeserializer(&mut self.0)) {
|
||||
// We need to check if the map is done, or if there are still remaining elements.
|
||||
// But we cannot ask the MapAccess directly.
|
||||
// The only way is trying to deserialize, but we don't know the type yet.
|
||||
// So we assume there is a value and try to deserialize it.
|
||||
// If we later on find out that there is no value, we return a special error value, which we turn into `None`.
|
||||
match seed.deserialize(EnumDeserializer {
|
||||
delegate: &mut self.delegate,
|
||||
is_human_readable: self.is_human_readable,
|
||||
}) {
|
||||
Ok(value) => Ok(Some(value)),
|
||||
Err(err) => {
|
||||
// Unfortunately we loose the optional aspect of MapAccess, so we need to special case an error value to mark the end of the map.
|
||||
|
@ -751,7 +776,7 @@ where
|
|||
}
|
||||
|
||||
fn size_hint(&self) -> Option<usize> {
|
||||
self.0.size_hint()
|
||||
self.delegate.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -760,7 +785,10 @@ where
|
|||
/// The [`Deserializer`] implementation is the starting point, which first calls the [`EnumAccess`] methods.
|
||||
/// The [`EnumAccess`] is used to deserialize the enum variant type of the enum.
|
||||
/// The [`VariantAccess`] is used to deserialize the value part of the enum.
|
||||
struct EnumDeserializer<M>(M);
|
||||
struct EnumDeserializer<M> {
|
||||
delegate: M,
|
||||
is_human_readable: bool,
|
||||
}
|
||||
|
||||
impl<'de, M> Deserializer<'de> for EnumDeserializer<M>
|
||||
where
|
||||
|
@ -768,6 +796,10 @@ where
|
|||
{
|
||||
type Error = M::Error;
|
||||
|
||||
fn is_human_readable(&self) -> bool {
|
||||
self.is_human_readable
|
||||
}
|
||||
|
||||
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
|
@ -805,11 +837,11 @@ where
|
|||
where
|
||||
T: DeserializeSeed<'de>,
|
||||
{
|
||||
match self.0.next_key_seed(seed)? {
|
||||
match self.delegate.next_key_seed(seed)? {
|
||||
Some(key) => Ok((key, self)),
|
||||
|
||||
// Unfortunately we loose the optional aspect of MapAccess, so we need to special case an error value to mark the end of the map.
|
||||
None => Err(Error::custom(END_OF_MAP_IDENTIFIER)),
|
||||
None => Err(DeError::custom(END_OF_MAP_IDENTIFIER)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -821,21 +853,22 @@ where
|
|||
type Error = M::Error;
|
||||
|
||||
fn unit_variant(mut self) -> Result<(), Self::Error> {
|
||||
self.0.next_value()
|
||||
self.delegate.next_value()
|
||||
}
|
||||
|
||||
fn newtype_variant_seed<T>(mut self, seed: T) -> Result<T::Value, Self::Error>
|
||||
where
|
||||
T: DeserializeSeed<'de>,
|
||||
{
|
||||
self.0.next_value_seed(seed)
|
||||
self.delegate.next_value_seed(seed)
|
||||
}
|
||||
|
||||
fn tuple_variant<V>(mut self, len: usize, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.0.next_value_seed(SeedTupleVariant { len, visitor })
|
||||
self.delegate
|
||||
.next_value_seed(SeedTupleVariant { len, visitor })
|
||||
}
|
||||
|
||||
fn struct_variant<V>(
|
||||
|
@ -846,7 +879,7 @@ where
|
|||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.0.next_value_seed(SeedStructVariant { visitor })
|
||||
self.delegate.next_value_seed(SeedStructVariant { visitor })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -50,17 +50,19 @@
|
|||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! flattened_maybe {
|
||||
($fn:ident, $field:literal) => {
|
||||
fn $fn<'de, T, D>(deserializer: D) -> ::std::result::Result<T, D::Error>
|
||||
($fn:ident, $field:tt) => {
|
||||
fn $fn<'de, T, D>(deserializer: D) -> $crate::__private__::Result<T, D::Error>
|
||||
where
|
||||
T: $crate::serde::Deserialize<'de>,
|
||||
D: $crate::serde::Deserializer<'de>,
|
||||
{
|
||||
use ::std::{
|
||||
option::Option::{self, None, Some},
|
||||
result::Result::{self, Err, Ok},
|
||||
use $crate::{
|
||||
__private__::{
|
||||
Option::{self, None, Some},
|
||||
Result::{self, Err, Ok},
|
||||
},
|
||||
serde,
|
||||
};
|
||||
use $crate::serde;
|
||||
|
||||
#[derive($crate::serde::Deserialize)]
|
||||
#[serde(crate = "serde")]
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче