serde_with-3.12.0/.cargo_vcs_info.json0000644000000001500000000000100132500ustar { "git": { "sha1": "5de3400f9cd459abb91cd021ba29c5fd5716c520" }, "path_in_vcs": "serde_with" }serde_with-3.12.0/CHANGELOG.md000064400000000000000000001270071046102023000136640ustar 00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). ## [Unreleased] ## [3.12.0] - 2024-12-25 ### Added * Add `with_suffix!` macro, which puts a suffix on every struct field ## [3.11.0] - 2024-10-05 ### Added * Add support for `hashbrown` v0.15 (#787/#790) This extends the existing support for `hashbrown` v0.14 to the newly released version. ## [3.10.0] - 2024-10-01 ### Added * Add newline separator by @jayvdb (#777) The `UnixLineSeparator` and `DosLineSeparator` can be used together with `StringWithSeparator`. ### Fixed * Proper handling of `cfg_attr` in the `serde_as` macro by @sivizius (#782) This allows to parse more valid forms of the `cfg_attr` macro, including multiple values and attribute that do not follow the `key = value` schema. ## [3.9.0] - 2024-07-14 ### Added * Deserialize a map` and skip all elements failing to deserialize by @johnmave126 (#763) `MapSkipError` acts like a map (`HashMap`/`BTreeMap`), but keys or values that fail to deserialize, like are ignored. For formats with heterogeneously typed maps, we can collect only the elements where both key and value are deserializable. This is also useful in conjunction to `#[serde(flatten)]` to ignore some entries when capturing additional fields. ```text // JSON "value": {"0": "v0", "5": "v5", "str": "str", "10": 2}, // Rust #[serde_as(as = "MapSkipError")] value: BTreeMap, // Only deserializes entries with a numerical key and a string value, i.e., {0 => "v0", 5 => "v5"} ``` ## [3.8.3] - 2024-07-03 ### Fixed * Fix compile issues when dependency `schemars_0_8` is used with the `preserve_order` features (#762) ## [3.8.2] - 2024-06-30 ### Changed * Bump MSRV to 1.67, since that is required for the `time` dependency. The `time` version needed to be updated for nightly compatibility. ### Fixed * Implement `JsonSchemaAs` for `OneOrMany` instead of `JsonSchema` by @swlynch99 (#760) ## [3.8.1] - 2024-04-28 ### Fixed * Do not emit `schemars(deserialize_with = "...")` annotations, as `schemars` does not support them (#735) Thanks to @sivizius for reporting the issue. ## [3.8.0] - 2024-04-24 ### Added * Implement (De)Serialization for Pinned Smart Pointers by @Astralchroma (#733) * Implement `JsonSchemaAs` for `PickFirst` by @swlynch99 (#721) ### Changed * Bump `base64` dependency to v0.22 (#724) * Update dev dependencies ### Fixed * `serde_conv` regressed and triggered `clippy::ptr_arg` and add test to prevent future problems. (#731) ## [3.7.0] - 2024-03-11 ### Added * Implement `JsonSchemaAs` for `EnumMap` by @swlynch99 (#697) * Implement `JsonSchemaAs` for `IfIsHumanReadable` by @swlynch99 (#717) * Implement `JsonSchemaAs` for `KeyValueMap` by @swlynch99 (#713) * Implement `JsonSchemaAs` for `OneOrMany` by @swlynch99 (#719) ### Fixed * Detect conflicting `schema_with` attributes on fields with `schemars` annotations by @swlynch99 (#715) This extends the existing avoidance mechanism to a new variant fixing #712. ## [3.6.1] - 2024-02-08 ### Changed * Eliminate dependency on serde's "derive" feature by @dtolnay (#694) This allows parallel compilation of `serde` and `serde_derive` which can speed up the wallclock time. It requires that downstream crates do not use the "derive" feature either. ## [3.6.0] - 2024-01-30 ### Added * Add `IfIsHumanReadable` for conditional implementation by @irriden (#690) Used to specify different transformations for text-based and binary formats. * Add more `JsonSchemaAs` impls for all `Duration*` and `Timestamp*` adaptors by @swlynch99 (#685) ### Changed * Bump MSRV to 1.65, since that is required for the `regex` dependency. ## [3.5.1] - 2024-01-23 ### Fixed * The `serde_as` macro now better detects existing `schemars` attributes on fields and incorporates them (#682) This avoids errors on existing `#[schemars(with = ...)]` annotations. ## [3.5.0] - 2024-01-20 ### Added * Support for `schemars` integration added by @swlynch99 (#666) The support uses a new `Schema` top-level item which implements `JsonSchema` The `serde_as` macro can now detect `schemars` usage and emits matching annotations for all fields with `serde_as` attribute. Many types of this crate come already with support for the `schemars`, but support is not complete and will be extended over time. ## [3.4.0] - 2023-10-17 * Lower minimum required serde version to 1.0.152 (#653) Thanks to @banool for submitting the PR. This allows people that have a problem with 1.0.153 to still use `serde_with`. * Add support for `core::ops::Bound` (#655) Thanks to @qsantos for submitting the PR. ## [3.3.0] - 2023-08-19 ### Added * Support the `hashbrown` type `HashMap` and `HashSet` (#636, #637) Thanks to @OliverNChalk for raising the issue and submitting a PR. This extends the existing support for `HashMap`s and `HashSet`s to the `hashbrown` crate v0.14. The same conversions as for the `std` and `indexmap` types are available, like general support for `#[serde_as]` and converting it to/from sequences or maps. ### Changed * Generalize some trait bounds for `DeserializeAs` implementations While working on #637, it came to light that some macros for generating `DeserializeAs` implementations were not as generic as they could. This means they didn't work with custom hasher types, but only the default hashers. This has now been fixed and custom hashers should work better, as long as they implement `BuildHasher + Default`. * (internal) Change how features are documented (#639) This change moves the feature documentation into `Cargo.toml` in a format that can be read by lib.rs. It will improve the generated features documentation there. The page with all features remains in the guide but is now generated from the `Cargo.toml` information. ## [3.2.0] - 2023-08-04 ### Added * Add optional support for indexmap v2 (#621) Support for v1 is already available using the `indexmap_1` feature. This adds identical support for v2 of indexmap using the `indexmap_2` feature. ### Changed * Bump MSRV to 1.64, since that is required for the indexmap v2 dependency. ### Fixed * Prevent panics when deserializing `i64::MIN` using `TimestampSeconds` (#632, #633) Thanks to @hollmmax for reporting and fixing the issue. ## [3.1.0] - 2023-07-17 ### Added * Add `FromIntoRef` and `TryFromIntoRef` (#618) Thanks to @oblique for submitting the PR. The new types are similar to the existing `FromInto` and `TryFromInto` types. They behave different during serialization, allowing the removal of the `Clone` bound on their `SerializeAs` trait implementation ### Changed * Improve documentation about cfg-gating `serde_as` (#607) * Bump MSRV to 1.61 because that is required by the crate `cfg_eval`. ## [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 such as that certain attributes which worked before no longer worked, due to a 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, #[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 a compatible version is always 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 => #[serde(rename = "bool")], )] #[derive(serde::Serialize)] struct Data { a: Option, b: Option, c: Option, d: Option, } ``` The `apply` attribute will expand into this, applying the attributes to the matching fields: ```rust #[derive(serde::Serialize)] struct Data { #[serde(default)] #[serde(skip_serializing_if = "Option::is_none")] a: Option, #[serde(default)] #[serde(skip_serializing_if = "Option::is_none")] b: Option, #[serde(default)] #[serde(skip_serializing_if = "Option::is_none")] c: Option, #[serde(default)] #[serde(skip_serializing_if = "Option::is_none")] #[serde(rename = "bool")] d: Option, } ``` 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`, `Option`, or `Option`. ### 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` 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>")] value: BTreeMap<[u8; 2], u32>, // JSON {"value":"[[\"[1,2]\",3],[\"[4,5]\",6]]"} ``` ### Changed * Make `#[serde_as]` behave more intuitive on `Option` fields. The `#[serde_as]` macro now detects if a `#[serde_as(as = "Option")]` is used on a field of type `Option` 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", no_default)]`. * `NoneAsEmptyString` and `string_empty_as_none` use a different serialization bound (#388). Both types used `AsRef` as the serialization bound. This is limiting for non-string types like `Option`. 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` mostly implement `Display`, too, such as `String`, `Cow`, or `Rc`. * 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")] addresses: BTreeSet, #[serde_as(as = "Vec")] bools: Vec, ``` * `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` fields. The `#[serde_as]` macro now detects if a `#[serde_as(as = "Option")]` is used on a field of type `Option` 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", no_default)]`. * `NoneAsEmptyString` and `string_empty_as_none` use a different serialization bound (#388). Both types used `AsRef` as the serialization bound. This is limiting for non-string types like `Option`. 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` mostly implement `Display`, too, such as `String`, `Cow`, or `Rc`. * 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")] addresses: BTreeSet, #[serde_as(as = "Vec")] bools: Vec, ``` * `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 * Add support for `time` crate v0.3 #450 `time::Duration` can now be serialized with the `DurationSeconds` and related converters. ```rust // Rust #[serde_as(as = "serde_with::DurationSeconds")] value: Duration, // JSON "value": 86400, ``` `time::OffsetDateTime` and `time::PrimitiveDateTime` can now be serialized with the `TimestampSeconds` and related converters. ```rust // Rust #[serde_as(as = "serde_with::TimestampMicroSecondsWithFrac")] value: time::PrimitiveDateTime, // JSON "value": "1000000", ``` `time::OffsetDateTime` can be serialized in string format in different well-known formats. Two formats are supported, `time::format_description::well_known::Rfc2822` and `time::format_description::well_known::Rfc3339`. ```rust // Rust #[serde_as(as = "time::format_description::well_known::Rfc2822")] rfc_2822: OffsetDateTime, #[serde_as(as = "Vec")] rfc_3339: Vec, // JSON "rfc_2822": "Fri, 21 Nov 1997 09:55:06 -0600", "rfc_3339": ["1997-11-21T09:55:06-06:00"], ``` * Deserialize `bool` from integers #456 462 Deserialize an integer and convert it into a `bool`. `BoolFromInt` (default) deserializes 0 to `false` and `1` to `true`, other numbers are errors. `BoolFromInt` deserializes any non-zero as `true`. Serialization only emits 0/1. ```rust // Rust #[serde_as(as = "BoolFromInt")] // BoolFromInt b: bool, // JSON "b": 1, ``` ### Changed * Bump MSRV to 1.53, since the new dependency `time` requires that version. ### Fixed * Make the documentation clearer by stating that the `#[serde_as]` and `#[skip_serializing_none]` attributes must always be places before `#[derive]`. ## [1.13.0] - 2022-04-23 ### Added * Added support for `indexmap::IndexMap` and `indexmap::IndexSet` types. #431, #436 Both types are now compatible with these functions: `maps_duplicate_key_is_error`, `maps_first_key_wins`, `sets_duplicate_value_is_error`, `sets_last_value_wins`. `serde_as` integration is provided by implementing both `SerializeAs` and `DeserializeAs` for both types. `IndexMap`s can also be serialized as a list of types via the `serde_as(as = "Vec<(_, _)>")` annotation. All implementations are gated behind the `indexmap` feature. Thanks to @jgrund for providing parts of the implementation. ## [1.12.1] - 2022-04-07 ### Fixed * Depend on a newer `serde_with_macros` version to pull in some fixes. * 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 * Deserialize a `Vec` and skip all elements failing to deserialize #383 `VecSkipError` acts like a `Vec`, but elements which fail to deserialize, like the `"Yellow"` are ignored. ```rust #[derive(serde::Deserialize)] enum Color { Red, Green, Blue, } // JSON "colors": ["Blue", "Yellow", "Green"], // Rust #[serde_as(as = "VecSkipError<_>")] colors: Vec, // => vec![Blue, Green] ``` Thanks to @hdhoang for creating the PR. * Transform between maps and `Vec` #375 The new `EnumMap` type converts `Vec` of enums into a single map. The key is the enum variant name, and the value is the variant value. ```rust // Rust VecEnumValues(vec![ EnumValue::Int(123), EnumValue::String("Foo".to_string()), EnumValue::Unit, EnumValue::Tuple(1, "Bar".to_string()), EnumValue::Struct { a: 666, b: "Baz".to_string(), }, ] // JSON { "Int": 123, "String": "Foo", "Unit": null, "Tuple": [ 1, "Bar", ], "Struct": { "a": 666, "b": "Baz", } } ``` ### Changed * The `Timestamp*Seconds` and `Timestamp*SecondsWithFrac` types can now be used with `chrono::NaiveDateTime`. #389 ## [1.11.0] - 2021-10-18 ### Added * Serialize bytes as base64 encoded strings. The character set and padding behavior can be configured. ```rust // Rust #[serde_as(as = "serde_with::base64::Base64")] value: Vec, #[serde_as(as = "Base64")] bcrypt_unpadded: Vec, // JSON "value": "SGVsbG8gV29ybGQ=", "bcrypt_unpadded": "QETqZE6eT07wZEO", ``` * The minimal supported Rust version (MSRV) is now specified in the `Cargo.toml` via the `rust-version` field. The field is supported in Rust 1.56 and has no effect on versions before. More details: https://doc.rust-lang.org/nightly/cargo/reference/manifest.html#the-rust-version-field ### Fixed * Fixed RUSTSEC-2020-0071 in the `time` v0.1 dependency, but changing the feature flags of the `chrono` dependency. This should not change anything. Crates requiring the `oldtime` feature of `chrono` can enable it separately. * Allow `HashSet`s with custom hashers to be deserialized when used in combination with `serde_as`. #408 ## [1.10.0] - 2021-09-04 ### Added * Add `BorrowCow` which instructs serde to borrow data during deserialization of `Cow<'_, str>`, `Cow<'_, [u8]>`, or `Cow<'_, [u8; N]>`. (#347) The implementation is for [serde#2072](https://github.com/serde-rs/serde/pull/2072#pullrequestreview-735511713) and [serde#2016](https://github.com/serde-rs/serde/issues/2016), about `#[serde(borrow)]` not working for `Option>`. ```rust #[serde_as] #[derive(Deserialize, Serialize)] struct Data<'a> { #[serde_as(as = "Option<[BorrowCow; 1]>")] nested: Option<[Cow<'a, str>; 1]>, } ``` The `#[serde(borrow)]` annotation is automatically added by the `#[serde_as]` attribute. ### Changed * Bump MSRV to 1.46, since the dev-dependency `bitflags` requires that version now. * `flattened_maybe!` no longer requires the `serde_with` crate to be available with a specific name. This allows renaming the crate or using `flattened_maybe!` through a re-export without any complications. ## [1.9.4] - 2021-06-18 ### Fixed * `with_prefix!` now supports an optional visibility modifier. (#327, #328) If not specified `pub(self)` is assumed. ```rust with_prefix!(prefix_active "active_"); // => mod {...} with_prefix!(pub prefix_active "active_"); // => pub mod {...} with_prefix!(pub(crate) prefix_active "active_"); // => pub(crate) mod {...} with_prefix!(pub(in other_mod) prefix_active "active_"); // => pub(in other_mod) mod {...} ``` Thanks to @elpiel for raising and fixing the issue. ## [1.9.3] - 2021-06-14 ### Added * The `Bytes` type now supports borrowed and Cow arrays of fixed size (requires Rust 1.51+) ```rust #[serde_as(as = "Bytes")] #[serde(borrow)] borrowed_array: &'a [u8; 15], #[serde_as(as = "Bytes")] #[serde(borrow)] cow_array: Cow<'a, [u8; 15]>, ``` Note: For borrowed arrays, the used Deserializer needs to support Serde's 0-copy deserialization. ## [1.9.2] - 2021-06-07 ### Fixed * Suppress clippy warnings, which can occur while using `serde_conv` (#320) Thanks to @mkroening for reporting and fixing the issue. ## [1.9.1] - 2021-05-15 ### Changed * `NoneAsEmptyString`: Deserialize using `FromStr` instead of using `for<'a> From<&'a str>` (#316) This will *not* change any behavior when applied to a field of type `Option` as used in the documentation. Thanks to @mkroening for finding and fixing the issue. ## [1.9.0] - 2021-05-09 ### Added * Added `FromInto` and `TryFromInto` adapters, which enable serialization by converting into a proxy type. ```rust // Rust #[serde_as(as = "FromInto<(u8, u8, u8)>")] value: Rgb, impl From<(u8, u8, u8)> for Rgb { ... } impl From for (u8, u8, u8) { ... } // JSON "value": [128, 64, 32], ``` * New `serde_conv!` macro to create conversion types with reduced boilerplate. The generated types can be used with `#[serde_as]` or serde's with-attribute. ```rust serde_with::serde_conv!( RgbAsArray, Rgb, |rgb: &Rgb| [rgb.red, rgb.green, rgb.blue], |value: [u8; 3]| -> Result<_, std::convert::Infallible> { Ok(Rgb { red: value[0], green: value[1], blue: value[2], }) } ); ``` ## [1.8.1] - 2021-04-19 ### Added * The `hex::Hex` type also works for u8-arrays on Rust 1.48. Thanks to @TheAlgorythm for raising and fixing the issue. ## [1.8.0] - 2021-03-30 ### Added * Added `PickFirst` adapter for `serde_as`. [#291] It allows deserializing from multiple different forms. Deserializing a number from either a number or string can be implemented like: ```rust #[serde_as(as = "PickFirst<(_, DisplayFromStr)>")] value: u32, ``` * Implement `SerializeAs`/`DeserializeAs` for more wrapper types. [#288], [#293] This now supports: * `Arc`, `sync::Weak` * `Rc`, `rc::Weak` * `Cell`, `RefCell` * `Mutex`, `RwLock` * `Result` [#288]: https://github.com/jonasbb/serde_with/issues/288 [#291]: https://github.com/jonasbb/serde_with/issues/291 [#293]: https://github.com/jonasbb/serde_with/issues/293 ### Changed * Add a new `serde_with::rust::map_as_tuple_list` module as a replacement for `serde_with::rust::btreemap_as_tuple_list` and `serde_with::rust::hashmap_as_tuple_list`. The new module uses `IntoIterator` and `FromIterator` as trait bound making it usable in more situations. The old names continue to exist but are marked as deprecated. ### Deprecated * Deprecated the module names `serde_with::rust::btreemap_as_tuple_list` and `serde_with::rust::hashmap_as_tuple_list`. You can use `serde_with::rust::map_as_tuple_list` as a replacement. ### Fixed * Implement `Timestamp*Seconds` and `Duration*Seconds` also for chrono types. This closes [#194]. This was incompletely implemented in [#199]. [#194]: https://github.com/jonasbb/serde_with/issues/194 [#199]: https://github.com/jonasbb/serde_with/issues/199 ## [1.7.0] - 2021-03-24 ### Added * Add support for arrays of arbitrary size. ([#272]) This feature requires Rust 1.51+. ```rust // Rust #[serde_as(as = "[[_; 64]; 33]")] value: [[u8; 64]; 33], // JSON "value": [[0,0,0,0,0,...], [0,0,0,...], ...], ``` Mapping of arrays was available before, but limited to arrays of length 32. All conversion methods are available for the array elements. This is similar to the existing [`serde-big-array`] crate with three important improvements: 1. Support for the `serde_as` annotation. 2. Supports non-copy elements (see [serde-big-array#6][serde-big-array-copy]). 3. Supports arbitrary nestings of arrays (see [serde-big-array#7][serde-big-array-nested]). [#272]: https://github.com/jonasbb/serde_with/pull/272 [`serde-big-array`]: https://crates.io/crates/serde-big-array [serde-big-array-copy]: https://github.com/est31/serde-big-array/issues/6 [serde-big-array-nested]: https://github.com/est31/serde-big-array/issues/7 * Arrays with tuple elements can now be deserialized from a map. ([#272]) This feature requires Rust 1.51+. ```rust // Rust #[serde_as(as = "BTreeMap<_, _>")] value: [(String, u16); 3], // JSON "value": { "a": 1, "b": 2, "c": 3 }, ``` * The `Bytes` type is heavily inspired by `serde_bytes` and ports it to the `serde_as` system. ([#277]) ```rust #[serde_as(as = "Bytes")] value: Vec, ``` Compared to `serde_bytes` these improvements are available 1. Integration with the `serde_as` annotation (see [serde-bytes#14][serde-bytes-complex]). 2. Implementation for arrays of arbitrary size (Rust 1.51+) (see [serde-bytes#26][serde-bytes-arrays]). [#277]: https://github.com/jonasbb/serde_with/pull/277 [serde-bytes-complex]: https://github.com/serde-rs/bytes/issues/14 [serde-bytes-arrays]: https://github.com/serde-rs/bytes/issues/26 * The `OneOrMany` type allows deserializing a `Vec` from either a single element or a sequence. ([#281]) ```rust #[serde_as(as = "OneOrMany<_>")] cities: Vec, ``` This allows deserializing from either `cities: "Berlin"` or `cities: ["Berlin", "Paris"]`. The serialization can be configured to always emit a list with `PreferMany` or emit a single element with `PreferOne`. [#281]: https://github.com/jonasbb/serde_with/pull/281 ## [1.6.4] - 2021-02-16 ### Fixed * Fix compiling when having a struct field without the `serde_as` annotation by updating `serde_with_macros`. This broke in 1.4.0 of `serde_with_macros`. [#267](https://github.com/jonasbb/serde_with/issues/267) ## [1.6.3] - 2021-02-15 ### Changed * Bump macro crate dependency (`serde_with_macros`) to 1.4.0 to pull in those improvements. ## [1.6.2] - 2021-01-30 ### Added * New function `serde_with::rust::deserialize_ignore_any`. This function allows deserializing any data and returns the default value of the type. This can be used in conjunction with `#[serde(other)]` to allow deserialization of unknown data carrying enum variants. Thanks to @lovasoa for suggesting and implementing it. ## [1.6.1] - 2021-01-24 ### Added * Add new types similar to `DurationSeconds` and `TimestampSeconds` but for base units of milliseconds, microseconds, and nanoseconds. The `*WithFrac` variants also exist. * Add `SerializeAs` implementation for references. ### Changed * Release `Sized` trait bound from `As`, `Same`, `SerializeAs`, and `SerializeAsWrap`. Only the `serialize` part is relaxed. ## [1.6.0] - 2020-11-22 ### Added * Add `DefaultOnNull` as the equivalent for `rust::default_on_null` but for the `serde_as` system. * Support specifying a path to the `serde_with` crate for the `serde_as` and derive macros. This is useful when using crate renaming in Cargo.toml or while re-exporting the macros. Many thanks to @tobz1000 for raising the issue and contributing fixes. ### Changed * Bump minimum supported rust version to 1.40.0 ## [1.5.1] - 2020-10-07 ### Fixed * Depend on serde with the `derive` feature enabled. The `derive` feature is required to deserialize untagged enums which are used in the `DefaultOnError` helpers. This fixes compilation of `serde_with` in scenarios where no other crate enables the `derive` feature. ## [1.5.0] - 2020-10-01 ### Added * The largest addition to this release is the addition of the `serde_as` de/serialization scheme. Its goal is to be a more flexible replacement to serde's `with` annotation, by being more composable than before. No longer is it a problem to add a custom de/serialization adapter is the type is within an `Option` or a `Vec`. Thanks to `@markazmierczak` for the design of the trait without whom this wouldn't be possible. More details about this new scheme can be found in the also new [user guide](https://docs.rs/serde_with/1.5.0/serde_with/guide/index.html) * This release also features a detailed user guide. The guide focuses more on how to use this crate by providing examples. For example, it includes a section about the available feature flags of this crate and how you can migrate to the shiny new `serde_as` scheme. * The crate now features de/serialization adaptors for the std and `chrono` `Duration` types. #56 #104 * Add a `hex` module, which allows formatting bytes (i.e. `Vec`) as a hexadecimal string. The formatting supports different arguments how the formatting is happening. * Add two derive macros, `SerializeDisplay` and `DeserializeFromStr`, which implement the `Serialize`/`Deserialize` traits based on `Display` and `FromStr`. This is in addition to the already existing methods like `DisplayFromStr`, which act locally, whereas the derive macros provide the traits expected by the rest of the ecosystem. This is part of `serde_with_macros` v1.2.0. * Added some `serialize` functions to modules which previously had none. This makes it easier to use the conversion when also deriving `Serialize`. The functions simply pass through to the underlying `Serialize` implementation. This affects `sets_duplicate_value_is_error`, `maps_duplicate_key_is_error`, `maps_first_key_wins`, `default_on_error`, and `default_on_null`. * Added `sets_last_value_wins` as a replacement for `sets_first_value_wins` which is deprecated now. The default behavior of serde is to prefer the first value of a set, so the opposite is taking the last value. * Added `#[serde_as]` compatible conversion methods for serializing durations and timestamps as numbers. The four types `DurationSeconds`, `DurationSecondsWithFrac`, `TimestampSeconds`, `TimestampSecondsWithFrac` provide the serialization conversion with optional sub-second precision. There is support for `std::time::Duration`, `chrono::Duration`, `std::time::SystemTime` and `chrono::DateTime`. Timestamps are serialized as durations since the UNIX epoch. The serialization can be customized. It supports multiple formats, such as `i64`, `f64`, or `String`, and the deserialization can be tweaked if it should be strict or lenient when accepting formats. ### Changed * Convert the code to use 2018 edition. * @peterjoel improved the performance of `with_prefix!`. #101 ### Fixed * The `with_prefix!` macro, to add a string prefixes during serialization, now also works with unit variant enum types. #115 #116 * The `serde_as` macro now supports serde attributes and no longer panic on unrecognized values in the attribute. This is part of `serde_with_macros` v1.2.0. ### Deprecated * Deprecate `sets_first_value_wins`. The default behavior of serde is to take the first value, so this module is not necessary. ## [1.5.0-alpha.2] - 2020-08-16 ### Added * Add a `hex` module, which allows formatting bytes (i.e. `Vec`) as a hexadecimal string. The formatting supports different arguments how the formatting is happening. * Add two derive macros, `SerializeDisplay` and `DeserializeFromStr`, which implement the `Serialize`/`Deserialize` traits based on `Display` and `FromStr`. This is in addition to the already existing methods like `DisplayFromStr`, which act locally, whereas the derive macros provide the traits expected by the rest of the ecosystem. This is part of `serde_with_macros` v1.2.0-alpha.3. ### Fixed * The `serde_as` macro now supports serde attributes and no longer panic on unrecognized values in the attribute. This is part of `serde_with_macros` v1.2.0-alpha.2. ## [1.5.0-alpha.1] - 2020-06-27 ### Added * The largest addition to this release is the addition of the `serde_as` de/serialization scheme. Its goal is to be a more flexible replacement to serde's with annotation, by being more composable than before. No longer is it a problem to add a custom de/serialization adapter is the type is within an `Option` or a `Vec`. Thanks to `@markazmierczak` for the design of the trait without whom this wouldn't be possible. More details about this new scheme can be found in the also new [user guide](https://docs.rs/serde_with/1.5.0-alpha.1/serde_with/guide/index.html) * This release also features a detailed user guide. The guide focuses more on how to use this crate by providing examples. For example, it includes a section about the available feature flags of this crate and how you can migrate to the shiny new `serde_as` scheme. * The crate now features de/serialization adaptors for the std and `chrono`'s `Duration` types. #56 #104 ### Changed * Convert the code to use 2018 edition. * @peterjoel improved the performance of `with_prefix!`. #101 ### Fixed * The `with_prefix!` macro, to add a string prefixes during serialization, now also works with unit variant enum types. #115 #116 ## [1.4.0] - 2020-01-16 ### Added * Add a helper to deserialize a `Vec` from `String` (#35) * Add `default_on_error` helper, which turns errors into `Default`s of the type * Add `default_on_null` helper, which turns `null` values into `Default`s of the type ### Changed * Bump minimal Rust version to 1.36.0 * Supports Rust Edition 2018 * version-sync depends on smallvec, which requires 1.36 * Improved CI pipeline by running `cargo audit` and `tarpaulin` in all configurations now. ## [1.3.1] - 2019-04-09 ### Fixed * Use `serde_with_macros` with proper dependencies specified. ## [1.3.0] - 2019-04-02 ### Added * Add `skip_serializing_none` attribute, which adds `#[serde(skip_serializing_if = "Option::is_none")]` for each Option in a struct. This is helpful for APIs which have many optional fields. The effect of can be negated by adding `serialize_always` on those fields, which should always be serialized. Existing `skip_serializing_if` will never be modified and those fields keep their behavior. ## [1.2.0] - 2019-03-04 ### Added * Add macro helper to support deserializing values with nested or flattened syntax #38 * Serialize tuple list as map helper ### Changed * Bumped minimal Rust version to 1.30.0 ## [1.1.0] - 2019-02-18 ### Added * Serialize HashMap/BTreeMap as a list of tuples ## [1.0.0] - 2019-01-17 ### Added * No changes in this release. * Bumped version number to indicate the stability of the library. ## [0.2.5] - 2018-11-29 ### Added * Helper which deserializes an empty string as `None` and otherwise uses `FromStr` and `AsRef`. ## [0.2.4] - 2018-11-24 ### Added * De/Serialize sequences by using `Display` and `FromStr` implementations on each element. Contributed by @katyo ## [0.2.3] - 2018-11-08 ### Added * Add missing docs and enable deny missing_docs * Add badges to Cargo.toml and crates.io ### Changed * Improve Travis configuration * Various clippy improvements ## [0.2.2] - 2018-08-05 ### Added * `unwrap_or_skip` allows to transparently serialize the inner part of a `Some(T)` * Add deserialization helper for sets and maps, inspired by [comment](https://github.com/serde-rs/serde/issues/553#issuecomment-299711855) * Create an error if duplicate values for a set are detected * Create an error if duplicate keys for a map are detected * Implement a "first value wins" strategy for sets/maps. This is different to serde's default, which implements a "last value wins" strategy. ## [0.2.1] - 2018-06-05 ### Added * Double Option pattern to differentiate between missing, unset, or existing value * `with_prefix!` macro, which puts a prefix on every struct field ## [0.2.0] - 2018-05-31 ### Added * Add chrono support: Deserialize timestamps from int, float, and string * Serialization of embedded JSON strings * De/Serialization using `Display` and `FromStr` implementations * String-based collections using `Display` and `FromStr`, allows deserializing "#foo,#bar" ## [0.1.0] - 2017-08-17 ### Added * Reserve name on crates.io serde_with-3.12.0/Cargo.toml0000644000000172770000000000100112700ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.70" name = "serde_with" version = "3.12.0" authors = [ "Jonas Bushart", "Marcin Kaźmierczak", ] build = false include = [ "src/**/*", "tests/**/*", "LICENSE-*", "README.md", "CHANGELOG.md", ] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Custom de/serialization functions for Rust's serde" documentation = "https://docs.rs/serde_with/" readme = "README.md" keywords = [ "serde", "utilities", "serialization", "deserialization", ] categories = [ "encoding", "no-std", "no-std::no-alloc", ] license = "MIT OR Apache-2.0" repository = "https://github.com/jonasbb/serde_with/" [package.metadata.docs.rs] all-features = true rustdoc-args = [ "--cfg=docsrs", "-Zunstable-options", "--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.]+/' [lib] name = "serde_with" path = "src/lib.rs" [[test]] name = "base64" path = "tests/base64.rs" required-features = [ "base64", "macros", ] [[test]] name = "chrono_0_4" path = "tests/chrono_0_4.rs" required-features = [ "chrono_0_4", "macros", ] [[test]] name = "derives" path = "tests/derives/lib.rs" required-features = ["macros"] [[test]] name = "hashbrown_0_14" path = "tests/hashbrown_0_14.rs" required-features = [ "hashbrown_0_14", "macros", ] [[test]] name = "hashbrown_0_15" path = "tests/hashbrown_0_15.rs" required-features = [ "hashbrown_0_15", "macros", ] [[test]] name = "hex" path = "tests/hex.rs" required-features = [ "hex", "macros", ] [[test]] name = "indexmap_1" path = "tests/indexmap_1.rs" required-features = [ "indexmap_1", "macros", ] [[test]] name = "indexmap_2" path = "tests/indexmap_2.rs" required-features = [ "indexmap_2", "macros", ] [[test]] name = "json" path = "tests/json.rs" required-features = [ "json", "macros", ] [[test]] name = "rust" path = "tests/rust.rs" required-features = ["alloc"] [[test]] name = "schemars_0_8" path = "tests/schemars_0_8.rs" required-features = ["schemars_0_8"] [[test]] name = "serde_as" path = "tests/serde_as/lib.rs" required-features = ["macros"] [[test]] name = "time_0_3" path = "tests/time_0_3.rs" required-features = [ "macros", "time_0_3", ] [[test]] name = "utils" path = "tests/utils.rs" [[test]] name = "version_numbers" path = "tests/version_numbers.rs" [[test]] name = "with_prefix" path = "tests/with_prefix.rs" required-features = ["macros"] [[test]] name = "with_suffix" path = "tests/with_suffix.rs" [dependencies.base64] version = "0.22.1" optional = true default-features = false [dependencies.chrono_0_4] version = "0.4.20" features = ["serde"] optional = true default-features = false package = "chrono" [dependencies.doc-comment] version = "0.3.3" optional = true [dependencies.document-features] version = "0.2.7" optional = true [dependencies.hashbrown_0_14] version = "0.14.0" features = ["serde"] optional = true default-features = false package = "hashbrown" [dependencies.hashbrown_0_15] version = "0.15.0" features = ["serde"] optional = true default-features = false package = "hashbrown" [dependencies.hex] version = "0.4.3" optional = true default-features = false [dependencies.indexmap_1] version = "1.8" features = ["serde-1"] optional = true default-features = false package = "indexmap" [dependencies.indexmap_2] version = "2.0" features = ["serde"] optional = true default-features = false package = "indexmap" [dependencies.schemars_0_8] version = "0.8.16" optional = true default-features = false package = "schemars" [dependencies.serde] version = "1.0.152" default-features = false [dependencies.serde_derive] version = "1.0.152" [dependencies.serde_json] version = "1.0.45" optional = true default-features = false [dependencies.serde_with_macros] version = "=3.12.0" optional = true [dependencies.time_0_3] version = "~0.3.36" optional = true default-features = false package = "time" [dev-dependencies.expect-test] version = "1.5.0" [dev-dependencies.fnv] version = "1.0.6" [dev-dependencies.glob] version = "0.3.0" [dev-dependencies.jsonschema] version = "0.26.1" features = ["resolve-file"] default-features = false [dev-dependencies.mime] version = "0.3.16" [dev-dependencies.pretty_assertions] version = "1.4.0" [dev-dependencies.regex] version = "1.11.0" features = ["std"] default-features = false [dev-dependencies.rmp-serde] version = "1.3.0" [dev-dependencies.ron] version = "0.8" [dev-dependencies.rustversion] version = "1.0.0" [dev-dependencies.schemars_0_8] version = "0.8.16" package = "schemars" [dev-dependencies.serde] version = "1.0.152" features = ["derive"] default-features = false [dev-dependencies.serde-xml-rs] version = "0.6.0" [dev-dependencies.serde_json] version = "1.0.25" features = ["preserve_order"] [dev-dependencies.serde_test] version = "1.0.124" [dev-dependencies.serde_yaml] version = "0.9.2" [dev-dependencies.version-sync] version = "0.9.1" [features] 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", ] guide = [ "dep:doc-comment", "dep:document-features", "macros", "std", ] hashbrown_0_14 = [ "dep:hashbrown_0_14", "alloc", ] hashbrown_0_15 = [ "dep:hashbrown_0_15", "alloc", ] hex = [ "dep:hex", "alloc", ] indexmap = ["indexmap_1"] indexmap_1 = [ "dep:indexmap_1", "alloc", ] indexmap_2 = [ "dep:indexmap_2", "alloc", ] json = [ "dep:serde_json", "alloc", ] macros = ["dep:serde_with_macros"] schemars_0_8 = [ "dep:schemars_0_8", "std", "serde_with_macros?/schemars_0_8", ] std = [ "alloc", "serde/std", "chrono_0_4?/clock", "chrono_0_4?/std", "indexmap_1?/std", "indexmap_2?/std", "time_0_3?/serde-well-known", "time_0_3?/std", ] time_0_3 = ["dep:time_0_3"] [badges.maintenance] status = "actively-developed" [lints.clippy] cloned_instead_of_copied = "warn" default_trait_access = "warn" doc_markdown = "warn" explicit_auto_deref = "allow" manual-unwrap-or-default = "allow" redundant_closure_for_method_calls = "warn" semicolon_if_nothing_returned = "warn" [lints.rust] missing_docs = "warn" trivial_casts = "warn" trivial_numeric_casts = "warn" unused_extern_crates = "warn" unused_import_braces = "warn" variant_size_differences = "warn" [lints.rust.rust_2018_idioms] level = "warn" priority = -1 [lints.rust.unexpected_cfgs] level = "warn" priority = 0 check-cfg = [ "cfg(tarpaulin)", "cfg(tarpaulin_include)", ] [lints.rustdoc] missing_crate_level_docs = "warn" serde_with-3.12.0/Cargo.toml.orig000064400000000000000000000232441046102023000147400ustar 00000000000000lints.workspace = true [package] authors = [ "Jonas Bushart", # For creation of the SerializeAs and DeserializeAs traits. "Marcin Kaźmierczak", ] name = "serde_with" categories = ["encoding", "no-std", "no-std::no-alloc"] description = "Custom de/serialization functions for Rust's serde" documentation = "https://docs.rs/serde_with/" keywords = ["serde", "utilities", "serialization", "deserialization"] edition.workspace = true license.workspace = true readme.workspace = true repository.workspace = true rust-version.workspace = true version.workspace = true include = ["src/**/*", "tests/**/*", "LICENSE-*", "README.md", "CHANGELOG.md"] [badges] maintenance = { status = "actively-developed" } # When adding new features update the documentation in feature-flags.md # The features are documented using https://docs.rs/document-features # lib.rs has support for generating the documentation from the feature flags. # https://users.rust-lang.org/t/new-features-on-lib-rs/98560 [features] default = ["std", "macros"] #! `serde_with` is fully `no_std` compatible, by depending on it with `default-features = false`. #! Support for `alloc` and `std` can be enabled with the respective features. #! Some features require `alloc` or `std` support and might not work in a `no_std` environment. ## Enable support for types from the `alloc` crate when running in a `no_std` environment. alloc = ["serde/alloc", "base64?/alloc", "chrono_0_4?/alloc", "hex?/alloc", "serde_json?/alloc", "time_0_3?/alloc"] ## Enables support for various types from the std library. ## This will enable `std` support in all dependencies too. ## The feature enabled by default and also enables `alloc`. std = ["alloc", "serde/std", "chrono_0_4?/clock", "chrono_0_4?/std", "indexmap_1?/std", "indexmap_2?/std", "time_0_3?/serde-well-known", "time_0_3?/std"] #! # Documentation #! #! The following features enhance the documentation of `serde_with`. ## The `guide` feature enables inclusion of this user guide. ## The feature only changes the rustdoc output and enables no other effects. guide = ["dep:doc-comment", "dep:document-features", "macros", "std"] #! # Features #! #! The following features enable support for types from other crates or enable additional functionality that requires further dependencies to be pulled in. #! These features are disabled by default to minimize the number of required dependencies. ## The feature enables serializing data in base64 format. base64 = ["dep:base64", "alloc"] ## Deprecated feature name. Use `chrono_0_4` instead. chrono = ["chrono_0_4"] ## The feature enables integration of `chrono` v0.4 specific conversions. ## This includes support for the timestamp and duration types. ## More features are available in combination with `alloc` or `std`. ## The legacy feature name `chrono` is still available for v1 compatibility. ## ## This pulls in `chrono` v0.4 as a dependency. chrono_0_4 = ["dep:chrono_0_4"] ## The feature enables `hashbrown::{HashMap, HashSet}` as supported containers. ## ## This pulls in `hashbrown` v0.14 as a dependency. ## It enables the `alloc` feature. ## Some functionality is only available when `std` is enabled too. hashbrown_0_14 = ["dep:hashbrown_0_14", "alloc"] ## The feature enables `hashbrown::{HashMap, HashSet}` as supported containers. ## ## This pulls in `hashbrown` v0.15 as a dependency. ## It enables the `alloc` feature. ## Some functionality is only available when `std` is enabled too. hashbrown_0_15 = ["dep:hashbrown_0_15", "alloc"] ## The feature enables serializing data in hex format. ## ## This pulls in `hex` as a dependency. ## It enables the `alloc` feature. hex = ["dep:hex", "alloc"] ## Deprecated feature name. Use `indexmap_1` instead. indexmap = ["indexmap_1"] ## The feature enables implementations of `indexmap` v1 specific checks. ## This includes support for checking duplicate keys and duplicate values. ## The legacy feature name `indexmap` is still available for v1 compatibility. ## ## This pulls in `indexmap` v1 as a dependency. ## It enables the `alloc` feature. ## Some functionality is only available when `std` is enabled too. indexmap_1 = ["dep:indexmap_1", "alloc"] ## The feature enables implementations of `indexmap` v2 specific checks. ## This includes support for checking duplicate keys and duplicate values. ## ## This pulls in `indexmap` v2 as a dependency. ## It enables the `alloc` feature. ## Some functionality is only available when `std` is enabled too. indexmap_2 = ["dep:indexmap_2", "alloc"] ## The feature enables JSON conversions from the `json` module. ## ## This pulls in `serde_json` as a dependency. ## It enables the `alloc` feature. json = ["dep:serde_json", "alloc"] ## The feature enables all helper macros and derives. ## It is enabled by default, since the macros provide a usability benefit, especially for `serde_as`. ## ## This pulls in `serde_with_macros` as a dependency. macros = ["dep:serde_with_macros"] ## This feature enables integration with `schemars` 0.8. ## This makes `#[derive(JsonSchema)]` pick up the correct schema for the type ## used within `#[serde_as(as = ...)]`. ## ## This pulls in `schemars` v0.8 as a dependency. It will also implicitly enable ## the `std` feature as `schemars` is not `#[no_std]`. schemars_0_8 = ["dep:schemars_0_8", "std", "serde_with_macros?/schemars_0_8"] ## The feature enables integration of `time` v0.3 specific conversions. ## This includes support for the timestamp and duration types. ## ## This pulls in `time` v0.3 as a dependency. ## Some functionality is only available when `alloc` or `std` is enabled too. time_0_3 = ["dep:time_0_3"] # When adding new optional dependencies, update the documentation in feature-flags.md [dependencies] base64 = { version = "0.22.1", optional = true, default-features = false } chrono_0_4 = { package = "chrono", version = "0.4.20", optional = true, default-features = false, features = ["serde"] } doc-comment = { version = "0.3.3", optional = true } document-features = { version = "0.2.7", optional = true } hashbrown_0_14 = { package = "hashbrown", version = "0.14.0", optional = true, default-features = false, features = ["serde"] } hashbrown_0_15 = { package = "hashbrown", version = "0.15.0", optional = true, default-features = false, features = ["serde"] } hex = { version = "0.4.3", optional = true, default-features = false } indexmap_1 = { package = "indexmap", version = "1.8", optional = true, default-features = false, features = ["serde-1"] } indexmap_2 = { package = "indexmap", version = "2.0", optional = true, default-features = false, features = ["serde"] } schemars_0_8 = { package = "schemars", version = "0.8.16", optional = true, default-features = false } serde = { version = "1.0.152", default-features = false } serde_derive = "1.0.152" serde_json = { version = "1.0.45", optional = true, default-features = false } serde_with_macros = { path = "../serde_with_macros", version = "=3.12.0", optional = true } time_0_3 = { package = "time", version = "~0.3.36", optional = true, default-features = false } [dev-dependencies] expect-test = "1.5.0" fnv = "1.0.6" glob = "0.3.0" jsonschema = { version = "0.26.1", default-features = false, features = ["resolve-file"] } mime = "0.3.16" pretty_assertions = "1.4.0" regex = { version = "1.11.0", default-features = false, features = ["std"] } rmp-serde = "1.3.0" ron = "0.8" rustversion = "1.0.0" schemars_0_8 = { package = "schemars", version = "0.8.16" } serde = { version = "1.0.152", default-features = false, features = ["derive"] } serde_json = { version = "1.0.25", features = ["preserve_order"] } serde_test = "1.0.124" serde_yaml = "0.9.2" serde-xml-rs = "0.6.0" version-sync = "0.9.1" [[test]] name = "base64" path = "tests/base64.rs" required-features = ["base64", "macros"] [[test]] name = "chrono_0_4" path = "tests/chrono_0_4.rs" required-features = ["chrono_0_4", "macros"] [[test]] name = "hex" path = "tests/hex.rs" required-features = ["hex", "macros"] [[test]] name = "hashbrown_0_14" path = "tests/hashbrown_0_14.rs" required-features = ["hashbrown_0_14", "macros"] [[test]] name = "hashbrown_0_15" path = "tests/hashbrown_0_15.rs" required-features = ["hashbrown_0_15", "macros"] [[test]] name = "indexmap_1" path = "tests/indexmap_1.rs" required-features = ["indexmap_1", "macros"] [[test]] name = "indexmap_2" path = "tests/indexmap_2.rs" required-features = ["indexmap_2", "macros"] [[test]] name = "json" path = "tests/json.rs" required-features = ["json", "macros"] [[test]] name = "serde_as" path = "tests/serde_as/lib.rs" required-features = ["macros"] [[test]] name = "time_0_3" path = "tests/time_0_3.rs" required-features = ["macros", "time_0_3"] [[test]] name = "derives" path = "tests/derives/lib.rs" required-features = ["macros"] [[test]] name = "with_prefix" path = "tests/with_prefix.rs" required-features = ["macros"] [[test]] name = "rust" path = "tests/rust.rs" required-features = ["alloc"] [[test]] name = "schemars_0_8" path = "tests/schemars_0_8.rs" required-features = ["schemars_0_8"] [package.metadata.docs.rs] all-features = true rustdoc-args = [ # Enable doc_cfg showing the required features. "--cfg=docsrs", # Generate links to definition in rustdoc source code pages # https://github.com/rust-lang/rust/pull/84176 "-Zunstable-options", "--generate-link-to-definition" ] [package.metadata.release] pre-release-replacements = [ { file = "CHANGELOG.md", search = "\\[Unreleased\\]", replace = "[Unreleased]\n\n## [{{version}}] - {{date}}" }, { file = "src/lib.rs", search = "https://docs\\.rs/serde_with/[\\d.]+/", replace = "https://docs.rs/serde_with/{{version}}/" }, { file = "README.md", search = "https://docs\\.rs/serde_with/[\\d.]+/", replace = "https://docs.rs/serde_with/{{version}}/" }, ] tag = true tag-message = "{{crate_name}} v{{version}}" tag-name = "v{{version}}" serde_with-3.12.0/LICENSE-APACHE000064400000000000000000000251371046102023000140000ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. serde_with-3.12.0/LICENSE-MIT000064400000000000000000000020231046102023000134750ustar 00000000000000Copyright (c) 2015 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. serde_with-3.12.0/README.md000064400000000000000000000176141046102023000133340ustar 00000000000000# Custom de/serialization functions for Rust's [serde](https://serde.rs) [![crates.io badge](https://img.shields.io/crates/v/serde_with.svg)](https://crates.io/crates/serde_with/) [![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) [![Rustexplorer](https://img.shields.io/badge/Try%20on-rustexplorer-lightgrey?logo=rust&logoColor=orange)](https://www.rustexplorer.com/b/py7ida) --- This crate provides custom de/serialization helpers to use in combination with [serde's `with` annotation][with-annotation] and with the improved [`serde_as`][as-annotation]-annotation. 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`] 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 / suffix to each field name of a struct, without changing the de/serialize implementations of the struct using [`with_prefix!`][] / [`with_suffix!`][]. * Deserialize a comma separated list like `#hash,#tags,#are,#great` into a `Vec`. Check the documentation for [`serde_with::StringWithSeparator::`][StringWithSeparator]. ### Getting Help **Check out the [user guide][user guide] to find out more tips and tricks about this crate.** For further help using this crate, you can [open a new discussion](https://github.com/jonasbb/serde_with/discussions/new) or ask on [users.rust-lang.org](https://users.rust-lang.org/). For bugs, please open a [new issue](https://github.com/jonasbb/serde_with/issues/new) on GitHub. ## Use `serde_with` in your Project ```bash # Add the current version to your Cargo.toml cargo add serde_with ``` The crate contains different features for integration with other common crates. 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 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`. 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)] struct Foo { // Serialize with Display, deserialize with FromStr #[serde_as(as = "DisplayFromStr")] bar: u8, } // This will serialize Foo {bar: 12} // into this JSON {"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 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)] struct Arrays { #[serde_as(as = "[_; N]")] constgeneric: [bool; N], #[serde_as(as = "Box<[[_; 64]; N]>")] nested: Box<[[u8; 64]; N]>, #[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]), bytes: [0x42; 128], }; assert!(serde_json::to_string(&arrays).is_ok()); ``` ### `skip_serializing_none` 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 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)] struct Foo { a: Option, b: Option, c: Option, d: Option, e: Option, f: Option, g: Option, } // This will serialize Foo {a: None, b: None, c: None, d: Some(4), e: None, f: None, g: Some(7)} // into this JSON {"d": 4, "g": 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]. ```rust use std::time::Duration; #[serde_as] #[derive(Deserialize, Serialize)] enum Foo { Durations( // Serialize them into a list of number as seconds #[serde_as(as = "Vec")] Vec, ), 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")] bytes: Vec<(i32, Vec)>, } } // This will serialize 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 { "Bytes": { "bytes": { "1": "000102", "-100": "64c8ff", "1": "006fde" } } } ``` [`DisplayFromStr`]: https://docs.rs/serde_with/3.12.0/serde_with/struct.DisplayFromStr.html [`with_prefix!`]: https://docs.rs/serde_with/3.12.0/serde_with/macro.with_prefix.html [`with_suffix!`]: https://docs.rs/serde_with/3.12.0/serde_with/macro.with_suffix.html [feature flags]: https://docs.rs/serde_with/3.12.0/serde_with/guide/feature_flags/index.html [skip_serializing_none]: https://docs.rs/serde_with/3.12.0/serde_with/attr.skip_serializing_none.html [StringWithSeparator]: https://docs.rs/serde_with/3.12.0/serde_with/struct.StringWithSeparator.html [user guide]: https://docs.rs/serde_with/3.12.0/serde_with/guide/index.html [with-annotation]: https://serde.rs/field-attrs.html#with [as-annotation]: https://docs.rs/serde_with/3.12.0/serde_with/guide/serde_as/index.html ## License Licensed under either of * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) at your option. ## Contribution For detailed contribution instructions please read [`CONTRIBUTING.md`]. Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual-licensed as above, without any additional terms or conditions. [`CONTRIBUTING.md`]: https://github.com/jonasbb/serde_with/blob/master/CONTRIBUTING.md serde_with-3.12.0/src/base64.rs000064400000000000000000000162511046102023000142720ustar 00000000000000//! De/Serialization of base64 encoded bytes //! //! This modules is only available when using the `base64` feature of the crate. //! //! Please check the documentation on the [`Base64`] type for details. use crate::prelude::*; /// Serialize bytes with base64 /// /// The type serializes a sequence of bytes as a base64 string. /// It works on any type implementing `AsRef<[u8]>` for serialization and `TryFrom>` for deserialization. /// /// The type allows customizing the character set and the padding behavior. /// 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. /// /// ```rust /// # #[cfg(feature = "macros")] { /// # use serde::{Deserialize, Serialize}; /// # use serde_with::serde_as; /// use serde_with::base64::{Base64, Bcrypt, BinHex, Standard}; /// use serde_with::formats::{Padded, Unpadded}; /// /// #[serde_as] /// # #[derive(Debug, PartialEq, Eq)] /// #[derive(Serialize, Deserialize)] /// struct B64 { /// // The default is the same as Standard character set with padding /// #[serde_as(as = "Base64")] /// default: Vec, /// // Only change the character set, implies padding /// #[serde_as(as = "Base64")] /// charset_binhex: Vec, /// /// #[serde_as(as = "Base64")] /// explicit_padding: Vec, /// #[serde_as(as = "Base64")] /// no_padding: Vec, /// } /// /// let b64 = B64 { /// default: b"Hello World".to_vec(), /// charset_binhex: b"Hello World".to_vec(), /// explicit_padding: b"Hello World".to_vec(), /// no_padding: b"Hello World".to_vec(), /// }; /// let json = serde_json::json!({ /// "default": "SGVsbG8gV29ybGQ=", /// "charset_binhex": "5'9XE'mJ9fpbE'3=", /// "explicit_padding": "SGVsbG8gV29ybGQ=", /// "no_padding": "QETqZE6eT07wZEO", /// }); /// /// // Test serialization and deserialization /// assert_eq!(json, serde_json::to_value(&b64).unwrap()); /// assert_eq!(b64, serde_json::from_value(json).unwrap()); /// # } /// ``` // 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/ pub struct Base64( PhantomData<(ALPHABET, PADDING)>, ); impl SerializeAs for Base64 where T: AsRef<[u8]>, ALPHABET: Alphabet, { fn serialize_as(source: &T, serializer: S) -> Result where S: Serializer, { use ::base64::Engine as _; ::base64::engine::GeneralPurpose::new( &ALPHABET::charset(), ::base64::engine::general_purpose::PAD, ) .encode(source) .serialize(serializer) } } impl SerializeAs for Base64 where T: AsRef<[u8]>, ALPHABET: Alphabet, { fn serialize_as(source: &T, serializer: S) -> Result where S: Serializer, { use ::base64::Engine as _; ::base64::engine::GeneralPurpose::new( &ALPHABET::charset(), ::base64::engine::general_purpose::NO_PAD, ) .encode(source) .serialize(serializer) } } // 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 where T: TryFrom>, ALPHABET: Alphabet, FORMAT: formats::Format, { fn deserialize_as(deserializer: D) -> Result where D: Deserializer<'de>, { struct Helper(PhantomData<(T, ALPHABET)>); impl Visitor<'_> for Helper where T: TryFrom>, ALPHABET: Alphabet, { type Value = T; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("a base64 encoded string") } fn visit_str(self, value: &str) -> Result 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::(PhantomData)) } } 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). pub struct 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). pub struct 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. pub struct Crypt; impl Alphabet for Crypt { fn charset() -> ::base64::alphabet::Alphabet { ::base64::alphabet::CRYPT } } /// The bcrypt character set (uses `./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789`). pub struct 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). pub struct 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). pub struct BinHex; impl Alphabet for BinHex { fn charset() -> ::base64::alphabet::Alphabet { ::base64::alphabet::BIN_HEX } } serde_with-3.12.0/src/chrono_0_4.rs000064400000000000000000000543061046102023000151430ustar 00000000000000//! De/Serialization of [chrono] types //! //! This modules is only available if using the `chrono_0_4` feature of the crate. //! //! [chrono]: https://docs.rs/chrono/ // Serialization of large numbers can result in overflows // The time calculations are prone to this, so lint here extra // https://github.com/jonasbb/serde_with/issues/771 #![warn(clippy::as_conversions)] use crate::{ formats::{Flexible, Format, Strict, Strictness}, prelude::*, }; #[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.from_utc_datetime(&unix_epoch_naive()) } /// Create a [`DateTime`] for the Unix Epoch using the [`Local`] timezone #[cfg(feature = "std")] fn unix_epoch_local() -> DateTime { Local.from_utc_datetime(&unix_epoch_naive()) } /// Create a [`NaiveDateTime`] for the Unix Epoch fn unix_epoch_naive() -> NaiveDateTime { DateTime::from_timestamp(0, 0).unwrap().naive_utc() } /// Deserialize a Unix timestamp with optional sub-second precision into a `DateTime`. /// /// The `DateTime` can be serialized from an integer, a float, or a string representing a number. /// /// # Examples /// /// ``` /// # use chrono_0_4::{DateTime, Utc}; /// # use serde::Deserialize; /// # /// #[derive(Debug, Deserialize)] /// struct S { /// #[serde(with = "serde_with::chrono_0_4::datetime_utc_ts_seconds_from_any")] /// date: DateTime, /// } /// /// // Deserializes integers /// assert!(serde_json::from_str::(r#"{ "date": 1478563200 }"#).is_ok()); /// # // Ensure the date field is not dead code /// # assert_eq!(serde_json::from_str::(r#"{ "date": 1478563200 }"#).unwrap().date.timestamp(), 1478563200); /// // floats /// assert!(serde_json::from_str::(r#"{ "date": 1478563200.123 }"#).is_ok()); /// // and strings with numbers, for high-precision values /// assert!(serde_json::from_str::(r#"{ "date": "1478563200.123" }"#).is_ok()); /// ``` // Requires float operations from std #[cfg(feature = "std")] pub mod datetime_utc_ts_seconds_from_any { use super::*; /// Deserialize a Unix timestamp with optional subsecond precision into a `DateTime`. pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { struct Helper; impl Visitor<'_> for Helper { type Value = DateTime; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter .write_str("an integer, float, or string with optional subsecond precision.") } fn visit_i64(self, value: i64) -> Result where E: DeError, { DateTime::from_timestamp(value, 0).ok_or_else(|| { DeError::custom(format_args!( "a timestamp which can be represented in a DateTime but received '{value}'" )) }) } fn visit_u64(self, value: u64) -> Result where E: DeError, { let value = i64::try_from(value).map_err(|_| { DeError::custom(format_args!( "a timestamp which can be represented in a DateTime but received '{value}'" )) })?; DateTime::from_timestamp(value, 0).ok_or_else(|| { DeError::custom(format_args!( "a timestamp which can be represented in a DateTime but received '{value}'" )) }) } // as conversions are necessary for floats #[allow(clippy::as_conversions)] fn visit_f64(self, value: f64) -> Result where E: DeError, { let seconds = value.trunc() as i64; let nsecs = (value.fract() * 1_000_000_000_f64).abs() as u32; DateTime::from_timestamp(seconds, nsecs).ok_or_else(|| { DeError::custom(format_args!( "a timestamp which can be represented in a DateTime but received '{value}'" )) }) } fn visit_str(self, value: &str) -> Result where E: DeError, { let parts: Vec<_> = value.split('.').collect(); match *parts.as_slice() { [seconds] => { if let Ok(seconds) = seconds.parse() { DateTime::from_timestamp(seconds, 0).ok_or_else(|| { DeError::custom(format_args!( "a timestamp which can be represented in a DateTime but received '{value}'" )) }) } else { Err(DeError::invalid_value(Unexpected::Str(value), &self)) } } [seconds, subseconds] => { if let Ok(seconds) = seconds.parse() { let subseclen = match u32::try_from(subseconds.chars().count()) { Ok(subseclen) if subseclen <= 9 => subseclen, _ => return Err(DeError::custom(format_args!( "DateTimes only support nanosecond precision but '{value}' has more than 9 digits." ))), }; if let Ok(mut subseconds) = subseconds.parse() { // convert subseconds to nanoseconds (10^-9), require 9 places for nanoseconds subseconds *= 10u32.pow(9 - subseclen); DateTime::from_timestamp(seconds, subseconds).ok_or_else(|| { DeError::custom(format_args!( "a timestamp which can be represented in a DateTime but received '{value}'" )) }) } else { Err(DeError::invalid_value(Unexpected::Str(value), &self)) } } else { Err(DeError::invalid_value(Unexpected::Str(value), &self)) } } _ => Err(DeError::invalid_value(Unexpected::Str(value), &self)), } } } deserializer.deserialize_any(Helper) } } impl SerializeAs for DateTime { fn serialize_as(source: &NaiveDateTime, serializer: S) -> Result where S: Serializer, { let datetime = Utc.from_utc_datetime(source); datetime.serialize(serializer) } } impl<'de> DeserializeAs<'de, NaiveDateTime> for DateTime { fn deserialize_as(deserializer: D) -> Result where D: Deserializer<'de>, { DateTime::::deserialize(deserializer).map(|datetime| datetime.naive_utc()) } } /// 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), Err(_) => { if let Ok(dur) = (-*dur).to_std() { DurationSigned::with_duration(Sign::Negative, dur) } else { panic!("A chrono Duration should be convertible to a DurationSigned") } } } } /// Convert a [`DurationSigned`] into a [`chrono_0_4::Duration`] fn duration_from_duration_signed<'de, D>(dur: DurationSigned) -> Result where D: Deserializer<'de>, { let mut chrono_dur = match Duration::from_std(dur.duration) { Ok(dur) => dur, Err(msg) => { return Err(DeError::custom(format_args!( "Duration is outside of the representable range: {msg}" ))) } }; if dur.sign.is_negative() { chrono_dur = -chrono_dur; } Ok(chrono_dur) } macro_rules! use_duration_signed_ser { ( $main_trait:ident $internal_trait:ident => { $ty:ty; $converter:ident => $({ $format:ty, $strictness:ty => $($tbound:ident: $bound:ident $(,)?)* })* } ) => { $( impl<$($tbound ,)*> SerializeAs<$ty> for $main_trait<$format, $strictness> where $($tbound: $bound,)* { fn serialize_as(source: &$ty, serializer: S) -> Result where S: Serializer, { let dur: DurationSigned = $converter(source); $internal_trait::<$format, $strictness>::serialize_as( &dur, serializer, ) } } )* }; ( $( $main_trait:ident $internal_trait:ident, )+ => $rest:tt ) => { $( use_duration_signed_ser!($main_trait $internal_trait => $rest); )+ }; } fn datetime_to_duration(source: &DateTime) -> DurationSigned where TZ: TimeZone, { duration_into_duration_signed(&source.clone().signed_duration_since(unix_epoch_utc())) } fn naive_datetime_to_duration(source: &NaiveDateTime) -> DurationSigned { duration_into_duration_signed(&source.signed_duration_since(unix_epoch_naive())) } use_duration_signed_ser!( DurationSeconds DurationSeconds, DurationMilliSeconds DurationMilliSeconds, DurationMicroSeconds DurationMicroSeconds, DurationNanoSeconds DurationNanoSeconds, => { 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} } ); use_duration_signed_ser!( TimestampSeconds DurationSeconds, TimestampMilliSeconds DurationMilliSeconds, TimestampMicroSeconds DurationMicroSeconds, TimestampNanoSeconds DurationNanoSeconds, => { DateTime; 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; datetime_to_duration => {String, STRICTNESS => TZ: TimeZone, STRICTNESS: Strictness} } ); use_duration_signed_ser!( TimestampSeconds DurationSeconds, TimestampMilliSeconds DurationMilliSeconds, TimestampMicroSeconds DurationMicroSeconds, TimestampNanoSeconds DurationNanoSeconds, => { 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} } ); // Duration/Timestamp WITH FRACTIONS use_duration_signed_ser!( DurationSecondsWithFrac DurationSecondsWithFrac, DurationMilliSecondsWithFrac DurationMilliSecondsWithFrac, DurationMicroSecondsWithFrac DurationMicroSecondsWithFrac, DurationNanoSecondsWithFrac DurationNanoSecondsWithFrac, => { 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} } ); use_duration_signed_ser!( TimestampSecondsWithFrac DurationSecondsWithFrac, TimestampMilliSecondsWithFrac DurationMilliSecondsWithFrac, TimestampMicroSecondsWithFrac DurationMicroSecondsWithFrac, TimestampNanoSecondsWithFrac DurationNanoSecondsWithFrac, => { DateTime; 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; datetime_to_duration => {String, STRICTNESS => TZ: TimeZone, STRICTNESS: Strictness} } ); use_duration_signed_ser!( TimestampSecondsWithFrac DurationSecondsWithFrac, TimestampMilliSecondsWithFrac DurationMilliSecondsWithFrac, TimestampMicroSecondsWithFrac DurationMicroSecondsWithFrac, TimestampNanoSecondsWithFrac DurationNanoSecondsWithFrac, => { 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} } ); macro_rules! use_duration_signed_de { ( $main_trait:ident $internal_trait:ident => { $ty:ty; $converter:ident => $({ $format:ty, $strictness:ty => $($tbound:ident: $bound:ident)* })* } ) =>{ $( impl<'de, $($tbound,)*> DeserializeAs<'de, $ty> for $main_trait<$format, $strictness> where $($tbound: $bound,)* { fn deserialize_as(deserializer: D) -> Result<$ty, D::Error> where D: Deserializer<'de>, { let dur: DurationSigned = $internal_trait::<$format, $strictness>::deserialize_as(deserializer)?; $converter::(dur) } } )* }; ( $( $main_trait:ident $internal_trait:ident, )+ => $rest:tt ) => { $( use_duration_signed_de!($main_trait $internal_trait => $rest); )+ }; } fn duration_to_datetime_utc<'de, D>(dur: DurationSigned) -> Result, D::Error> where D: Deserializer<'de>, { Ok(unix_epoch_utc() + duration_from_duration_signed::(dur)?) } #[cfg(feature = "std")] fn duration_to_datetime_local<'de, D>(dur: DurationSigned) -> Result, D::Error> where D: Deserializer<'de>, { Ok(unix_epoch_local() + duration_from_duration_signed::(dur)?) } fn duration_to_naive_datetime<'de, D>(dur: DurationSigned) -> Result where D: Deserializer<'de>, { Ok(unix_epoch_naive() + duration_from_duration_signed::(dur)?) } // No subsecond precision use_duration_signed_de!( DurationSeconds DurationSeconds, DurationMilliSeconds DurationMilliSeconds, DurationMicroSeconds DurationMicroSeconds, DurationNanoSeconds DurationNanoSeconds, => { Duration; duration_from_duration_signed => {i64, 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, TimestampMicroSeconds DurationMicroSeconds, TimestampNanoSeconds DurationNanoSeconds, => { DateTime; duration_to_datetime_utc => {i64, Strict =>} {FORMAT, Flexible => FORMAT: Format} } ); #[cfg(feature = "alloc")] use_duration_signed_de!( TimestampSeconds DurationSeconds, TimestampMilliSeconds DurationMilliSeconds, TimestampMicroSeconds DurationMicroSeconds, TimestampNanoSeconds DurationNanoSeconds, => { DateTime; duration_to_datetime_utc => {String, Strict =>} } ); #[cfg(feature = "std")] use_duration_signed_de!( TimestampSeconds DurationSeconds, TimestampMilliSeconds DurationMilliSeconds, TimestampMicroSeconds DurationMicroSeconds, TimestampNanoSeconds DurationNanoSeconds, => { DateTime; duration_to_datetime_utc => {f64, Strict =>} } ); #[cfg(feature = "std")] use_duration_signed_de!( TimestampSeconds DurationSeconds, TimestampMilliSeconds DurationMilliSeconds, TimestampMicroSeconds DurationMicroSeconds, TimestampNanoSeconds DurationNanoSeconds, => { DateTime; duration_to_datetime_local => {i64, Strict =>} {f64, Strict =>} {String, Strict =>} {FORMAT, Flexible => FORMAT: Format} } ); use_duration_signed_de!( TimestampSeconds DurationSeconds, TimestampMilliSeconds DurationMilliSeconds, TimestampMicroSeconds DurationMicroSeconds, TimestampNanoSeconds DurationNanoSeconds, => { NaiveDateTime; duration_to_naive_datetime => {i64, 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!( DurationSecondsWithFrac DurationSecondsWithFrac, DurationMilliSecondsWithFrac DurationMilliSecondsWithFrac, DurationMicroSecondsWithFrac DurationMicroSecondsWithFrac, DurationNanoSecondsWithFrac DurationNanoSecondsWithFrac, => { Duration; duration_from_duration_signed => {f64, 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, TimestampMicroSecondsWithFrac DurationMicroSecondsWithFrac, TimestampNanoSecondsWithFrac DurationNanoSecondsWithFrac, => { DateTime; duration_to_datetime_utc => {f64, Strict =>} {FORMAT, Flexible => FORMAT: Format} } ); #[cfg(feature = "alloc")] use_duration_signed_de!( TimestampSecondsWithFrac DurationSecondsWithFrac, TimestampMilliSecondsWithFrac DurationMilliSecondsWithFrac, TimestampMicroSecondsWithFrac DurationMicroSecondsWithFrac, TimestampNanoSecondsWithFrac DurationNanoSecondsWithFrac, => { DateTime; duration_to_datetime_utc => {String, Strict =>} } ); #[cfg(feature = "std")] use_duration_signed_de!( TimestampSecondsWithFrac DurationSecondsWithFrac, TimestampMilliSecondsWithFrac DurationMilliSecondsWithFrac, TimestampMicroSecondsWithFrac DurationMicroSecondsWithFrac, TimestampNanoSecondsWithFrac DurationNanoSecondsWithFrac, => { DateTime; duration_to_datetime_local => {f64, Strict =>} {String, Strict =>} {FORMAT, Flexible => FORMAT: Format} } ); use_duration_signed_de!( TimestampSecondsWithFrac DurationSecondsWithFrac, TimestampMilliSecondsWithFrac DurationMilliSecondsWithFrac, TimestampMicroSecondsWithFrac DurationMicroSecondsWithFrac, TimestampNanoSecondsWithFrac DurationNanoSecondsWithFrac, => { NaiveDateTime; duration_to_naive_datetime => {f64, 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 =>} } ); serde_with-3.12.0/src/content/de.rs000064400000000000000000001523321046102023000152510ustar 00000000000000//! Buffer for deserializing data. //! //! This is a copy and improvement of the `serde` private type: //! //! The code is very stable in the `serde` crate, so no maintainability problem is expected. //! //! Since the type is private we copy the type here. //! `serde` is licensed as MIT+Apache2, the same as this crate. //! //! This version carries improvements compared to `serde`'s version. //! The types support 128-bit integers, which is supported for all targets in Rust 1.40+. //! A value for `is_human_readable` is passed through all types, to preserve the information. //! //! In the future this can hopefully be replaced by a public type in `serde` itself. //! use self::utils::{get_unexpected_i128, get_unexpected_u128}; use crate::{ prelude::*, utils::{size_hint_cautious, size_hint_from_bounds}, }; /// Used from generated code to buffer the contents of the Deserializer when /// deserializing untagged enums and internally tagged enums. /// /// Not public API. Use serde-value instead. #[derive(Debug, Clone)] pub(crate) enum Content<'de> { Bool(bool), U8(u8), U16(u16), U32(u32), U64(u64), U128(u128), I8(i8), I16(i16), I32(i32), I64(i64), I128(i128), F32(f32), F64(f64), Char(char), String(String), Str(&'de str), ByteBuf(Vec), Bytes(&'de [u8]), None, Some(Box>), Unit, Newtype(Box>), Seq(Vec>), Map(Vec<(Content<'de>, Content<'de>)>), } impl Content<'_> { #[cold] fn unexpected<'a>(&'a self, buf: &'a mut [u8; 58]) -> Unexpected<'a> { match *self { Content::Bool(b) => Unexpected::Bool(b), Content::U8(n) => Unexpected::Unsigned(u64::from(n)), Content::U16(n) => Unexpected::Unsigned(u64::from(n)), Content::U32(n) => Unexpected::Unsigned(u64::from(n)), Content::U64(n) => Unexpected::Unsigned(n), Content::U128(n) => get_unexpected_u128(n, buf), Content::I8(n) => Unexpected::Signed(i64::from(n)), Content::I16(n) => Unexpected::Signed(i64::from(n)), Content::I32(n) => Unexpected::Signed(i64::from(n)), Content::I64(n) => Unexpected::Signed(n), Content::I128(n) => get_unexpected_i128(n, buf), Content::F32(f) => Unexpected::Float(f64::from(f)), Content::F64(f) => Unexpected::Float(f), Content::Char(c) => Unexpected::Char(c), Content::String(ref s) => Unexpected::Str(s), Content::Str(s) => Unexpected::Str(s), Content::ByteBuf(ref b) => Unexpected::Bytes(b), Content::Bytes(b) => Unexpected::Bytes(b), Content::None | Content::Some(_) => Unexpected::Option, Content::Unit => Unexpected::Unit, Content::Newtype(_) => Unexpected::NewtypeStruct, Content::Seq(_) => Unexpected::Seq, Content::Map(_) => Unexpected::Map, } } } impl<'de> Deserialize<'de> for Content<'de> { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { // Untagged and internally tagged enums are only supported in // self-describing formats. let visitor = ContentVisitor { value: PhantomData }; deserializer.deserialize_any(visitor) } } struct ContentVisitor<'de> { value: PhantomData>, } impl<'de> Visitor<'de> for ContentVisitor<'de> { type Value = Content<'de>; fn expecting(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt.write_str("any value") } fn visit_bool(self, value: bool) -> Result where F: DeError, { Ok(Content::Bool(value)) } fn visit_i8(self, value: i8) -> Result where F: DeError, { Ok(Content::I8(value)) } fn visit_i16(self, value: i16) -> Result where F: DeError, { Ok(Content::I16(value)) } fn visit_i32(self, value: i32) -> Result where F: DeError, { Ok(Content::I32(value)) } fn visit_i64(self, value: i64) -> Result where F: DeError, { Ok(Content::I64(value)) } fn visit_i128(self, value: i128) -> Result where F: DeError, { Ok(Content::I128(value)) } fn visit_u8(self, value: u8) -> Result where F: DeError, { Ok(Content::U8(value)) } fn visit_u16(self, value: u16) -> Result where F: DeError, { Ok(Content::U16(value)) } fn visit_u32(self, value: u32) -> Result where F: DeError, { Ok(Content::U32(value)) } fn visit_u64(self, value: u64) -> Result where F: DeError, { Ok(Content::U64(value)) } fn visit_u128(self, value: u128) -> Result where F: DeError, { Ok(Content::U128(value)) } fn visit_f32(self, value: f32) -> Result where F: DeError, { Ok(Content::F32(value)) } fn visit_f64(self, value: f64) -> Result where F: DeError, { Ok(Content::F64(value)) } fn visit_char(self, value: char) -> Result where F: DeError, { Ok(Content::Char(value)) } fn visit_str(self, value: &str) -> Result where F: DeError, { Ok(Content::String(value.into())) } fn visit_borrowed_str(self, value: &'de str) -> Result where F: DeError, { Ok(Content::Str(value)) } fn visit_string(self, value: String) -> Result where F: DeError, { Ok(Content::String(value)) } fn visit_bytes(self, value: &[u8]) -> Result where F: DeError, { Ok(Content::ByteBuf(value.into())) } fn visit_borrowed_bytes(self, value: &'de [u8]) -> Result where F: DeError, { Ok(Content::Bytes(value)) } fn visit_byte_buf(self, value: Vec) -> Result where F: DeError, { Ok(Content::ByteBuf(value)) } fn visit_unit(self) -> Result where F: DeError, { Ok(Content::Unit) } fn visit_none(self) -> Result where F: DeError, { Ok(Content::None) } fn visit_some(self, deserializer: D) -> Result where D: Deserializer<'de>, { Deserialize::deserialize(deserializer).map(|v| Content::Some(Box::new(v))) } fn visit_newtype_struct(self, deserializer: D) -> Result where D: Deserializer<'de>, { Deserialize::deserialize(deserializer).map(|v| Content::Newtype(Box::new(v))) } fn visit_seq(self, mut visitor: V) -> Result where V: SeqAccess<'de>, { let mut vec = Vec::with_capacity(size_hint_cautious::>(visitor.size_hint())); while let Some(e) = visitor.next_element()? { vec.push(e); } Ok(Content::Seq(vec)) } fn visit_map(self, mut visitor: V) -> Result where V: MapAccess<'de>, { let mut vec = Vec::with_capacity(size_hint_cautious::<(Content<'_>, Content<'_>)>( visitor.size_hint(), )); while let Some(kv) = visitor.next_entry()? { vec.push(kv); } Ok(Content::Map(vec)) } fn visit_enum(self, _visitor: V) -> Result where V: EnumAccess<'de>, { Err(DeError::custom( "untagged and internally tagged enums do not support enum input", )) } } pub(crate) struct ContentDeserializer<'de, E> { is_human_readable: bool, content: Content<'de>, err: PhantomData, } impl<'de, E> ContentDeserializer<'de, E> where E: DeError, { #[cold] fn invalid_type(self, exp: &dyn Expected) -> E { let mut buf = [0; 58]; DeError::invalid_type(self.content.unexpected(&mut buf), exp) } fn deserialize_integer(self, visitor: V) -> Result where V: Visitor<'de>, { match self.content { Content::U8(v) => visitor.visit_u8(v), Content::U16(v) => visitor.visit_u16(v), Content::U32(v) => visitor.visit_u32(v), Content::U64(v) => visitor.visit_u64(v), Content::U128(v) => visitor.visit_u128(v), Content::I8(v) => visitor.visit_i8(v), Content::I16(v) => visitor.visit_i16(v), Content::I32(v) => visitor.visit_i32(v), Content::I64(v) => visitor.visit_i64(v), Content::I128(v) => visitor.visit_i128(v), _ => Err(self.invalid_type(&visitor)), } } fn deserialize_float(self, visitor: V) -> Result where V: Visitor<'de>, { match self.content { Content::F32(v) => visitor.visit_f32(v), Content::F64(v) => visitor.visit_f64(v), Content::U8(v) => visitor.visit_u8(v), Content::U16(v) => visitor.visit_u16(v), Content::U32(v) => visitor.visit_u32(v), Content::U64(v) => visitor.visit_u64(v), Content::U128(v) => visitor.visit_u128(v), Content::I8(v) => visitor.visit_i8(v), Content::I16(v) => visitor.visit_i16(v), Content::I32(v) => visitor.visit_i32(v), Content::I64(v) => visitor.visit_i64(v), Content::I128(v) => visitor.visit_i128(v), _ => Err(self.invalid_type(&visitor)), } } } fn visit_content_seq<'de, V, E>( content: Vec>, visitor: V, is_human_readable: bool, ) -> Result where V: Visitor<'de>, E: DeError, { let seq = content .into_iter() .map(|x| ContentDeserializer::new(x, is_human_readable)); let mut seq_visitor = serde::de::value::SeqDeserializer::new(seq); let value = visitor.visit_seq(&mut seq_visitor)?; seq_visitor.end()?; Ok(value) } fn visit_content_map<'de, V, E>( content: Vec<(Content<'de>, Content<'de>)>, visitor: V, is_human_readable: bool, ) -> Result where V: Visitor<'de>, E: DeError, { let map = content.into_iter().map(|(k, v)| { ( ContentDeserializer::new(k, is_human_readable), ContentDeserializer::new(v, is_human_readable), ) }); let mut map_visitor = serde::de::value::MapDeserializer::new(map); let value = visitor.visit_map(&mut map_visitor)?; map_visitor.end()?; Ok(value) } /// Used when deserializing an internally tagged enum because the content /// will be used exactly once. impl<'de, E> Deserializer<'de> for ContentDeserializer<'de, E> where E: DeError, { type Error = E; #[inline] fn is_human_readable(&self) -> bool { self.is_human_readable } fn deserialize_any(self, visitor: V) -> Result where V: Visitor<'de>, { match self.content { Content::Bool(v) => visitor.visit_bool(v), Content::U8(v) => visitor.visit_u8(v), Content::U16(v) => visitor.visit_u16(v), Content::U32(v) => visitor.visit_u32(v), Content::U64(v) => visitor.visit_u64(v), Content::U128(v) => visitor.visit_u128(v), Content::I8(v) => visitor.visit_i8(v), Content::I16(v) => visitor.visit_i16(v), Content::I32(v) => visitor.visit_i32(v), Content::I64(v) => visitor.visit_i64(v), Content::I128(v) => visitor.visit_i128(v), Content::F32(v) => visitor.visit_f32(v), Content::F64(v) => visitor.visit_f64(v), Content::Char(v) => visitor.visit_char(v), Content::String(v) => visitor.visit_string(v), Content::Str(v) => visitor.visit_borrowed_str(v), Content::ByteBuf(v) => visitor.visit_byte_buf(v), Content::Bytes(v) => visitor.visit_borrowed_bytes(v), Content::Unit => visitor.visit_unit(), Content::None => visitor.visit_none(), Content::Some(v) => { visitor.visit_some(ContentDeserializer::new(*v, self.is_human_readable)) } Content::Newtype(v) => { visitor.visit_newtype_struct(ContentDeserializer::new(*v, self.is_human_readable)) } Content::Seq(v) => visit_content_seq(v, visitor, self.is_human_readable), Content::Map(v) => visit_content_map(v, visitor, self.is_human_readable), } } fn deserialize_bool(self, visitor: V) -> Result where V: Visitor<'de>, { match self.content { Content::Bool(v) => visitor.visit_bool(v), _ => Err(self.invalid_type(&visitor)), } } fn deserialize_i8(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_integer(visitor) } fn deserialize_i16(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_integer(visitor) } fn deserialize_i32(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_integer(visitor) } fn deserialize_i64(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_integer(visitor) } fn deserialize_i128(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_integer(visitor) } fn deserialize_u8(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_integer(visitor) } fn deserialize_u16(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_integer(visitor) } fn deserialize_u32(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_integer(visitor) } fn deserialize_u64(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_integer(visitor) } fn deserialize_u128(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_integer(visitor) } fn deserialize_f32(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_float(visitor) } fn deserialize_f64(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_float(visitor) } fn deserialize_char(self, visitor: V) -> Result where V: Visitor<'de>, { match self.content { Content::Char(v) => visitor.visit_char(v), Content::String(v) => visitor.visit_string(v), Content::Str(v) => visitor.visit_borrowed_str(v), _ => Err(self.invalid_type(&visitor)), } } fn deserialize_str(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_string(visitor) } fn deserialize_string(self, visitor: V) -> Result where V: Visitor<'de>, { match self.content { Content::String(v) => visitor.visit_string(v), Content::Str(v) => visitor.visit_borrowed_str(v), Content::ByteBuf(v) => visitor.visit_byte_buf(v), Content::Bytes(v) => visitor.visit_borrowed_bytes(v), _ => Err(self.invalid_type(&visitor)), } } fn deserialize_bytes(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_byte_buf(visitor) } fn deserialize_byte_buf(self, visitor: V) -> Result where V: Visitor<'de>, { match self.content { Content::String(v) => visitor.visit_string(v), Content::Str(v) => visitor.visit_borrowed_str(v), Content::ByteBuf(v) => visitor.visit_byte_buf(v), Content::Bytes(v) => visitor.visit_borrowed_bytes(v), Content::Seq(v) => visit_content_seq(v, visitor, self.is_human_readable), _ => Err(self.invalid_type(&visitor)), } } fn deserialize_option(self, visitor: V) -> Result where V: Visitor<'de>, { match self.content { Content::None => visitor.visit_none(), Content::Some(v) => { visitor.visit_some(ContentDeserializer::new(*v, self.is_human_readable)) } Content::Unit => visitor.visit_unit(), _ => visitor.visit_some(self), } } fn deserialize_unit(self, visitor: V) -> Result where V: Visitor<'de>, { match self.content { Content::Unit => visitor.visit_unit(), // Allow deserializing newtype variant containing unit. // // #[derive(Deserialize)] // #[serde(tag = "result")] // enum Response { // Success(T), // } // // We want {"result":"Success"} to deserialize into Response<()>. Content::Map(ref v) if v.is_empty() => visitor.visit_unit(), _ => Err(self.invalid_type(&visitor)), } } fn deserialize_unit_struct( self, _name: &'static str, visitor: V, ) -> Result where V: Visitor<'de>, { match self.content { // As a special case, allow deserializing untagged newtype // variant containing unit struct. // // #[derive(Deserialize)] // struct Info; // // #[derive(Deserialize)] // #[serde(tag = "topic")] // enum Message { // Info(Info), // } // // We want {"topic":"Info"} to deserialize even though // ordinarily unit structs do not deserialize from empty map/seq. Content::Map(ref v) if v.is_empty() => visitor.visit_unit(), Content::Seq(ref v) if v.is_empty() => visitor.visit_unit(), _ => self.deserialize_any(visitor), } } fn deserialize_newtype_struct(self, _name: &str, visitor: V) -> Result where V: Visitor<'de>, { match self.content { Content::Newtype(v) => { visitor.visit_newtype_struct(ContentDeserializer::new(*v, self.is_human_readable)) } _ => visitor.visit_newtype_struct(self), } } fn deserialize_seq(self, visitor: V) -> Result where V: Visitor<'de>, { match self.content { Content::Seq(v) => visit_content_seq(v, visitor, self.is_human_readable), _ => Err(self.invalid_type(&visitor)), } } fn deserialize_tuple(self, _len: usize, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_seq(visitor) } fn deserialize_tuple_struct( self, _name: &'static str, _len: usize, visitor: V, ) -> Result where V: Visitor<'de>, { self.deserialize_seq(visitor) } fn deserialize_map(self, visitor: V) -> Result where V: Visitor<'de>, { match self.content { Content::Map(v) => visit_content_map(v, visitor, self.is_human_readable), _ => Err(self.invalid_type(&visitor)), } } fn deserialize_struct( self, _name: &'static str, _fields: &'static [&'static str], visitor: V, ) -> Result where V: Visitor<'de>, { match self.content { Content::Seq(v) => visit_content_seq(v, visitor, self.is_human_readable), Content::Map(v) => visit_content_map(v, visitor, self.is_human_readable), _ => Err(self.invalid_type(&visitor)), } } fn deserialize_enum( self, _name: &str, _variants: &'static [&'static str], visitor: V, ) -> Result where V: Visitor<'de>, { let (variant, value) = match self.content { Content::Map(value) => { let mut iter = value.into_iter(); let (variant, value) = match iter.next() { Some(v) => v, None => { return Err(DeError::invalid_value( Unexpected::Map, &"map with a single key", )); } }; // enums are encoded in json as maps with a single key:value pair if iter.next().is_some() { return Err(DeError::invalid_value( Unexpected::Map, &"map with a single key", )); } (variant, Some(value)) } s @ Content::String(_) | s @ Content::Str(_) => (s, None), other => { let mut buf = [0; 58]; return Err(DeError::invalid_type( other.unexpected(&mut buf), &"string or map", )); } }; visitor.visit_enum(EnumDeserializer::new( variant, value, self.is_human_readable, )) } fn deserialize_identifier(self, visitor: V) -> Result where V: Visitor<'de>, { match self.content { Content::String(v) => visitor.visit_string(v), Content::Str(v) => visitor.visit_borrowed_str(v), Content::ByteBuf(v) => visitor.visit_byte_buf(v), Content::Bytes(v) => visitor.visit_borrowed_bytes(v), Content::U8(v) => visitor.visit_u8(v), Content::U64(v) => visitor.visit_u64(v), _ => Err(self.invalid_type(&visitor)), } } fn deserialize_ignored_any(self, visitor: V) -> Result where V: Visitor<'de>, { drop(self); visitor.visit_unit() } } impl<'de, E> ContentDeserializer<'de, E> { /// private API, don't use pub(crate) fn new(content: Content<'de>, is_human_readable: bool) -> Self { ContentDeserializer { is_human_readable, content, err: PhantomData, } } } struct EnumDeserializer<'de, E> where E: DeError, { is_human_readable: bool, variant: Content<'de>, value: Option>, err: PhantomData, } impl<'de, E> EnumDeserializer<'de, E> where E: DeError, { pub fn new( variant: Content<'de>, value: Option>, is_human_readable: bool, ) -> EnumDeserializer<'de, E> { EnumDeserializer { is_human_readable, variant, value, err: PhantomData, } } } impl<'de, E> EnumAccess<'de> for EnumDeserializer<'de, E> where E: DeError, { type Error = E; type Variant = VariantDeserializer<'de, Self::Error>; fn variant_seed(self, seed: V) -> Result<(V::Value, Self::Variant), E> where V: DeserializeSeed<'de>, { let visitor = VariantDeserializer { is_human_readable: self.is_human_readable, value: self.value, err: PhantomData, }; seed.deserialize(ContentDeserializer::new( self.variant, self.is_human_readable, )) .map(|v| (v, visitor)) } } pub struct VariantDeserializer<'de, E> where E: DeError, { is_human_readable: bool, value: Option>, err: PhantomData, } impl<'de, E> VariantAccess<'de> for VariantDeserializer<'de, E> where E: DeError, { type Error = E; fn unit_variant(self) -> Result<(), E> { match self.value { Some(value) => { Deserialize::deserialize(ContentDeserializer::new(value, self.is_human_readable)) } None => Ok(()), } } fn newtype_variant_seed(self, seed: T) -> Result where T: DeserializeSeed<'de>, { match self.value { Some(value) => { seed.deserialize(ContentDeserializer::new(value, self.is_human_readable)) } None => Err(DeError::invalid_type( Unexpected::UnitVariant, &"newtype variant", )), } } fn tuple_variant(self, _len: usize, visitor: V) -> Result where V: Visitor<'de>, { match self.value { Some(Content::Seq(v)) => Deserializer::deserialize_any( SeqDeserializer::new(v, self.is_human_readable), visitor, ), Some(other) => { let mut buf = [0; 58]; Err(DeError::invalid_type( other.unexpected(&mut buf), &"tuple variant", )) } None => Err(DeError::invalid_type( Unexpected::UnitVariant, &"tuple variant", )), } } fn struct_variant( self, _fields: &'static [&'static str], visitor: V, ) -> Result where V: Visitor<'de>, { match self.value { Some(Content::Map(v)) => Deserializer::deserialize_any( MapDeserializer::new(v, self.is_human_readable), visitor, ), Some(Content::Seq(v)) => Deserializer::deserialize_any( SeqDeserializer::new(v, self.is_human_readable), visitor, ), Some(other) => { let mut buf = [0; 58]; Err(DeError::invalid_type( other.unexpected(&mut buf), &"struct variant", )) } None => Err(DeError::invalid_type( Unexpected::UnitVariant, &"struct variant", )), } } } struct SeqDeserializer<'de, E> where E: DeError, { is_human_readable: bool, iter: > as IntoIterator>::IntoIter, err: PhantomData, } impl<'de, E> SeqDeserializer<'de, E> where E: DeError, { fn new(vec: Vec>, is_human_readable: bool) -> Self { SeqDeserializer { is_human_readable, iter: vec.into_iter(), err: PhantomData, } } } impl<'de, E> Deserializer<'de> for SeqDeserializer<'de, E> where E: DeError, { type Error = E; #[inline] fn is_human_readable(&self) -> bool { self.is_human_readable } #[inline] fn deserialize_any(mut self, visitor: V) -> Result where V: Visitor<'de>, { let len = self.iter.len(); if len == 0 { visitor.visit_unit() } else { let ret = visitor.visit_seq(&mut self)?; let remaining = self.iter.len(); if remaining == 0 { Ok(ret) } else { Err(DeError::invalid_length(len, &"fewer elements in array")) } } } forward_to_deserialize_any! { bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes byte_buf option unit unit_struct newtype_struct seq tuple tuple_struct map struct enum identifier ignored_any } } impl<'de, E> SeqAccess<'de> for SeqDeserializer<'de, E> where E: DeError, { type Error = E; fn next_element_seed(&mut self, seed: T) -> Result, Self::Error> where T: DeserializeSeed<'de>, { match self.iter.next() { Some(value) => seed .deserialize(ContentDeserializer::new(value, self.is_human_readable)) .map(Some), None => Ok(None), } } fn size_hint(&self) -> Option { size_hint_from_bounds(&self.iter) } } struct MapDeserializer<'de, E> where E: DeError, { is_human_readable: bool, iter: , Content<'de>)> as IntoIterator>::IntoIter, value: Option>, err: PhantomData, } impl<'de, E> MapDeserializer<'de, E> where E: DeError, { fn new(map: Vec<(Content<'de>, Content<'de>)>, is_human_readable: bool) -> Self { MapDeserializer { is_human_readable, iter: map.into_iter(), value: None, err: PhantomData, } } } impl<'de, E> MapAccess<'de> for MapDeserializer<'de, E> where E: DeError, { type Error = E; fn next_key_seed(&mut self, seed: T) -> Result, Self::Error> where T: DeserializeSeed<'de>, { match self.iter.next() { Some((key, value)) => { self.value = Some(value); seed.deserialize(ContentDeserializer::new(key, self.is_human_readable)) .map(Some) } None => Ok(None), } } fn next_value_seed(&mut self, seed: T) -> Result where T: DeserializeSeed<'de>, { match self.value.take() { Some(value) => { seed.deserialize(ContentDeserializer::new(value, self.is_human_readable)) } None => Err(DeError::custom("value is missing")), } } fn size_hint(&self) -> Option { size_hint_from_bounds(&self.iter) } } impl<'de, E> Deserializer<'de> for MapDeserializer<'de, E> where E: DeError, { type Error = E; #[inline] fn is_human_readable(&self) -> bool { self.is_human_readable } #[inline] fn deserialize_any(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_map(self) } forward_to_deserialize_any! { bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes byte_buf option unit unit_struct newtype_struct seq tuple tuple_struct map struct enum identifier ignored_any } } /// Not public API. pub struct ContentRefDeserializer<'a, 'de, E> { is_human_readable: bool, content: &'a Content<'de>, err: PhantomData, } impl<'de, E> ContentRefDeserializer<'_, 'de, E> where E: DeError, { #[cold] fn invalid_type(self, exp: &dyn Expected) -> E { let mut buf = [0; 58]; DeError::invalid_type(self.content.unexpected(&mut buf), exp) } fn deserialize_integer(self, visitor: V) -> Result where V: Visitor<'de>, { match *self.content { Content::U8(v) => visitor.visit_u8(v), Content::U16(v) => visitor.visit_u16(v), Content::U32(v) => visitor.visit_u32(v), Content::U64(v) => visitor.visit_u64(v), Content::U128(v) => visitor.visit_u128(v), Content::I8(v) => visitor.visit_i8(v), Content::I16(v) => visitor.visit_i16(v), Content::I32(v) => visitor.visit_i32(v), Content::I64(v) => visitor.visit_i64(v), Content::I128(v) => visitor.visit_i128(v), _ => Err(self.invalid_type(&visitor)), } } fn deserialize_float(self, visitor: V) -> Result where V: Visitor<'de>, { match *self.content { Content::F32(v) => visitor.visit_f32(v), Content::F64(v) => visitor.visit_f64(v), Content::U8(v) => visitor.visit_u8(v), Content::U16(v) => visitor.visit_u16(v), Content::U32(v) => visitor.visit_u32(v), Content::U64(v) => visitor.visit_u64(v), Content::U128(v) => visitor.visit_u128(v), Content::I8(v) => visitor.visit_i8(v), Content::I16(v) => visitor.visit_i16(v), Content::I32(v) => visitor.visit_i32(v), Content::I64(v) => visitor.visit_i64(v), Content::I128(v) => visitor.visit_i128(v), _ => Err(self.invalid_type(&visitor)), } } } fn visit_content_seq_ref<'a, 'de, V, E>( content: &'a [Content<'de>], visitor: V, is_human_readable: bool, ) -> Result where V: Visitor<'de>, E: DeError, { let seq = content .iter() .map(|x| ContentRefDeserializer::new(x, is_human_readable)); let mut seq_visitor = serde::de::value::SeqDeserializer::new(seq); let value = visitor.visit_seq(&mut seq_visitor)?; seq_visitor.end()?; Ok(value) } fn visit_content_map_ref<'a, 'de, V, E>( content: &'a [(Content<'de>, Content<'de>)], visitor: V, is_human_readable: bool, ) -> Result where V: Visitor<'de>, E: DeError, { let map = content.iter().map(|(k, v)| { ( ContentRefDeserializer::new(k, is_human_readable), ContentRefDeserializer::new(v, is_human_readable), ) }); let mut map_visitor = serde::de::value::MapDeserializer::new(map); let value = visitor.visit_map(&mut map_visitor)?; map_visitor.end()?; Ok(value) } /// Used when deserializing an untagged enum because the content may need /// to be used more than once. impl<'de, E> Deserializer<'de> for ContentRefDeserializer<'_, 'de, E> where E: DeError, { type Error = E; #[inline] fn is_human_readable(&self) -> bool { self.is_human_readable } fn deserialize_any(self, visitor: V) -> Result where V: Visitor<'de>, { match *self.content { Content::Bool(v) => visitor.visit_bool(v), Content::U8(v) => visitor.visit_u8(v), Content::U16(v) => visitor.visit_u16(v), Content::U32(v) => visitor.visit_u32(v), Content::U64(v) => visitor.visit_u64(v), Content::U128(v) => visitor.visit_u128(v), Content::I8(v) => visitor.visit_i8(v), Content::I16(v) => visitor.visit_i16(v), Content::I32(v) => visitor.visit_i32(v), Content::I64(v) => visitor.visit_i64(v), Content::I128(v) => visitor.visit_i128(v), Content::F32(v) => visitor.visit_f32(v), Content::F64(v) => visitor.visit_f64(v), Content::Char(v) => visitor.visit_char(v), Content::String(ref v) => visitor.visit_str(v), Content::Str(v) => visitor.visit_borrowed_str(v), Content::ByteBuf(ref v) => visitor.visit_bytes(v), Content::Bytes(v) => visitor.visit_borrowed_bytes(v), Content::Unit => visitor.visit_unit(), Content::None => visitor.visit_none(), Content::Some(ref v) => { visitor.visit_some(ContentRefDeserializer::new(v, self.is_human_readable)) } Content::Newtype(ref v) => { visitor.visit_newtype_struct(ContentRefDeserializer::new(v, self.is_human_readable)) } Content::Seq(ref v) => visit_content_seq_ref(v, visitor, self.is_human_readable), Content::Map(ref v) => visit_content_map_ref(v, visitor, self.is_human_readable), } } fn deserialize_bool(self, visitor: V) -> Result where V: Visitor<'de>, { match *self.content { Content::Bool(v) => visitor.visit_bool(v), _ => Err(self.invalid_type(&visitor)), } } fn deserialize_i8(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_integer(visitor) } fn deserialize_i16(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_integer(visitor) } fn deserialize_i32(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_integer(visitor) } fn deserialize_i64(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_integer(visitor) } fn deserialize_i128(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_integer(visitor) } fn deserialize_u8(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_integer(visitor) } fn deserialize_u16(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_integer(visitor) } fn deserialize_u32(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_integer(visitor) } fn deserialize_u64(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_integer(visitor) } fn deserialize_u128(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_integer(visitor) } fn deserialize_f32(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_float(visitor) } fn deserialize_f64(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_float(visitor) } fn deserialize_char(self, visitor: V) -> Result where V: Visitor<'de>, { match *self.content { Content::Char(v) => visitor.visit_char(v), Content::String(ref v) => visitor.visit_str(v), Content::Str(v) => visitor.visit_borrowed_str(v), _ => Err(self.invalid_type(&visitor)), } } fn deserialize_str(self, visitor: V) -> Result where V: Visitor<'de>, { match *self.content { Content::String(ref v) => visitor.visit_str(v), Content::Str(v) => visitor.visit_borrowed_str(v), Content::ByteBuf(ref v) => visitor.visit_bytes(v), Content::Bytes(v) => visitor.visit_borrowed_bytes(v), _ => Err(self.invalid_type(&visitor)), } } fn deserialize_string(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_str(visitor) } fn deserialize_bytes(self, visitor: V) -> Result where V: Visitor<'de>, { match *self.content { Content::String(ref v) => visitor.visit_str(v), Content::Str(v) => visitor.visit_borrowed_str(v), Content::ByteBuf(ref v) => visitor.visit_bytes(v), Content::Bytes(v) => visitor.visit_borrowed_bytes(v), Content::Seq(ref v) => visit_content_seq_ref(v, visitor, self.is_human_readable), _ => Err(self.invalid_type(&visitor)), } } fn deserialize_byte_buf(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_bytes(visitor) } fn deserialize_option(self, visitor: V) -> Result where V: Visitor<'de>, { match *self.content { Content::None => visitor.visit_none(), Content::Some(ref v) => { visitor.visit_some(ContentRefDeserializer::new(v, self.is_human_readable)) } Content::Unit => visitor.visit_unit(), _ => visitor.visit_some(self), } } fn deserialize_unit(self, visitor: V) -> Result where V: Visitor<'de>, { match *self.content { Content::Unit => visitor.visit_unit(), _ => Err(self.invalid_type(&visitor)), } } fn deserialize_unit_struct( self, _name: &'static str, visitor: V, ) -> Result where V: Visitor<'de>, { self.deserialize_unit(visitor) } fn deserialize_newtype_struct(self, _name: &str, visitor: V) -> Result where V: Visitor<'de>, { match *self.content { Content::Newtype(ref v) => { visitor.visit_newtype_struct(ContentRefDeserializer::new(v, self.is_human_readable)) } _ => visitor.visit_newtype_struct(self), } } fn deserialize_seq(self, visitor: V) -> Result where V: Visitor<'de>, { match *self.content { Content::Seq(ref v) => visit_content_seq_ref(v, visitor, self.is_human_readable), _ => Err(self.invalid_type(&visitor)), } } fn deserialize_tuple(self, _len: usize, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_seq(visitor) } fn deserialize_tuple_struct( self, _name: &'static str, _len: usize, visitor: V, ) -> Result where V: Visitor<'de>, { self.deserialize_seq(visitor) } fn deserialize_map(self, visitor: V) -> Result where V: Visitor<'de>, { match *self.content { Content::Map(ref v) => visit_content_map_ref(v, visitor, self.is_human_readable), _ => Err(self.invalid_type(&visitor)), } } fn deserialize_struct( self, _name: &'static str, _fields: &'static [&'static str], visitor: V, ) -> Result where V: Visitor<'de>, { match *self.content { Content::Seq(ref v) => visit_content_seq_ref(v, visitor, self.is_human_readable), Content::Map(ref v) => visit_content_map_ref(v, visitor, self.is_human_readable), _ => Err(self.invalid_type(&visitor)), } } fn deserialize_enum( self, _name: &str, _variants: &'static [&'static str], visitor: V, ) -> Result where V: Visitor<'de>, { let (variant, value) = match *self.content { Content::Map(ref value) => { let mut iter = value.iter(); let (variant, value) = match iter.next() { Some(v) => v, None => { return Err(DeError::invalid_value( Unexpected::Map, &"map with a single key", )); } }; // enums are encoded in json as maps with a single key:value pair if iter.next().is_some() { return Err(DeError::invalid_value( Unexpected::Map, &"map with a single key", )); } (variant, Some(value)) } ref s @ Content::String(_) | ref s @ Content::Str(_) => (s, None), ref other => { let mut buf = [0; 58]; return Err(DeError::invalid_type( other.unexpected(&mut buf), &"string or map", )); } }; visitor.visit_enum(EnumRefDeserializer { is_human_readable: self.is_human_readable, variant, value, err: PhantomData, }) } fn deserialize_identifier(self, visitor: V) -> Result where V: Visitor<'de>, { match *self.content { Content::String(ref v) => visitor.visit_str(v), Content::Str(v) => visitor.visit_borrowed_str(v), Content::ByteBuf(ref v) => visitor.visit_bytes(v), Content::Bytes(v) => visitor.visit_borrowed_bytes(v), Content::U8(v) => visitor.visit_u8(v), Content::U64(v) => visitor.visit_u64(v), _ => Err(self.invalid_type(&visitor)), } } fn deserialize_ignored_any(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_unit() } } impl<'a, 'de, E> ContentRefDeserializer<'a, 'de, E> { /// private API, don't use pub(crate) fn new(content: &'a Content<'de>, is_human_readable: bool) -> Self { ContentRefDeserializer { is_human_readable, content, err: PhantomData, } } } struct EnumRefDeserializer<'a, 'de, E> where E: DeError, { is_human_readable: bool, variant: &'a Content<'de>, value: Option<&'a Content<'de>>, err: PhantomData, } impl<'de, 'a, E> EnumAccess<'de> for EnumRefDeserializer<'a, 'de, E> where E: DeError, { type Error = E; type Variant = VariantRefDeserializer<'a, 'de, Self::Error>; fn variant_seed(self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error> where V: DeserializeSeed<'de>, { let visitor = VariantRefDeserializer { is_human_readable: self.is_human_readable, value: self.value, err: PhantomData, }; seed.deserialize(ContentRefDeserializer::new( self.variant, self.is_human_readable, )) .map(|v| (v, visitor)) } } struct VariantRefDeserializer<'a, 'de, E> where E: DeError, { is_human_readable: bool, value: Option<&'a Content<'de>>, err: PhantomData, } impl<'de, E> VariantAccess<'de> for VariantRefDeserializer<'_, 'de, E> where E: DeError, { type Error = E; fn unit_variant(self) -> Result<(), E> { match self.value { Some(value) => { Deserialize::deserialize(ContentRefDeserializer::new(value, self.is_human_readable)) } None => Ok(()), } } fn newtype_variant_seed(self, seed: T) -> Result where T: DeserializeSeed<'de>, { match self.value { Some(value) => { seed.deserialize(ContentRefDeserializer::new(value, self.is_human_readable)) } None => Err(DeError::invalid_type( Unexpected::UnitVariant, &"newtype variant", )), } } fn tuple_variant(self, _len: usize, visitor: V) -> Result where V: Visitor<'de>, { match self.value { Some(Content::Seq(v)) => Deserializer::deserialize_any( SeqRefDeserializer::new(v, self.is_human_readable), visitor, ), Some(other) => { let mut buf = [0; 58]; Err(DeError::invalid_type( other.unexpected(&mut buf), &"tuple variant", )) } None => Err(DeError::invalid_type( Unexpected::UnitVariant, &"tuple variant", )), } } fn struct_variant( self, _fields: &'static [&'static str], visitor: V, ) -> Result where V: Visitor<'de>, { match self.value { Some(Content::Map(v)) => Deserializer::deserialize_any( MapRefDeserializer::new(v, self.is_human_readable), visitor, ), Some(Content::Seq(v)) => Deserializer::deserialize_any( SeqRefDeserializer::new(v, self.is_human_readable), visitor, ), Some(other) => { let mut buf = [0; 58]; Err(DeError::invalid_type( other.unexpected(&mut buf), &"struct variant", )) } None => Err(DeError::invalid_type( Unexpected::UnitVariant, &"struct variant", )), } } } struct SeqRefDeserializer<'a, 'de, E> where E: DeError, { is_human_readable: bool, iter: <&'a [Content<'de>] as IntoIterator>::IntoIter, err: PhantomData, } impl<'a, 'de, E> SeqRefDeserializer<'a, 'de, E> where E: DeError, { fn new(slice: &'a [Content<'de>], is_human_readable: bool) -> Self { SeqRefDeserializer { is_human_readable, iter: slice.iter(), err: PhantomData, } } } impl<'de, E> Deserializer<'de> for SeqRefDeserializer<'_, 'de, E> where E: DeError, { type Error = E; #[inline] fn is_human_readable(&self) -> bool { self.is_human_readable } #[inline] fn deserialize_any(mut self, visitor: V) -> Result where V: Visitor<'de>, { let len = self.iter.len(); if len == 0 { visitor.visit_unit() } else { let ret = visitor.visit_seq(&mut self)?; let remaining = self.iter.len(); if remaining == 0 { Ok(ret) } else { Err(DeError::invalid_length(len, &"fewer elements in array")) } } } forward_to_deserialize_any! { bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes byte_buf option unit unit_struct newtype_struct seq tuple tuple_struct map struct enum identifier ignored_any } } impl<'de, E> SeqAccess<'de> for SeqRefDeserializer<'_, 'de, E> where E: DeError, { type Error = E; fn next_element_seed(&mut self, seed: T) -> Result, Self::Error> where T: DeserializeSeed<'de>, { match self.iter.next() { Some(value) => seed .deserialize(ContentRefDeserializer::new(value, self.is_human_readable)) .map(Some), None => Ok(None), } } fn size_hint(&self) -> Option { size_hint_from_bounds(&self.iter) } } struct MapRefDeserializer<'a, 'de, E> where E: DeError, { is_human_readable: bool, iter: <&'a [(Content<'de>, Content<'de>)] as IntoIterator>::IntoIter, value: Option<&'a Content<'de>>, err: PhantomData, } impl<'a, 'de, E> MapRefDeserializer<'a, 'de, E> where E: DeError, { fn new(map: &'a [(Content<'de>, Content<'de>)], is_human_readable: bool) -> Self { MapRefDeserializer { is_human_readable, iter: map.iter(), value: None, err: PhantomData, } } } impl<'de, E> MapAccess<'de> for MapRefDeserializer<'_, 'de, E> where E: DeError, { type Error = E; fn next_key_seed(&mut self, seed: T) -> Result, Self::Error> where T: DeserializeSeed<'de>, { match self.iter.next() { Some((key, value)) => { self.value = Some(value); seed.deserialize(ContentRefDeserializer::new(key, self.is_human_readable)) .map(Some) } None => Ok(None), } } fn next_value_seed(&mut self, seed: T) -> Result where T: DeserializeSeed<'de>, { match self.value.take() { Some(value) => { seed.deserialize(ContentRefDeserializer::new(value, self.is_human_readable)) } None => Err(DeError::custom("value is missing")), } } fn size_hint(&self) -> Option { size_hint_from_bounds(&self.iter) } } impl<'de, E> Deserializer<'de> for MapRefDeserializer<'_, 'de, E> where E: DeError, { type Error = E; #[inline] fn is_human_readable(&self) -> bool { self.is_human_readable } #[inline] fn deserialize_any(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_map(self) } forward_to_deserialize_any! { bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes byte_buf option unit unit_struct newtype_struct seq tuple tuple_struct map struct enum identifier ignored_any } } impl<'de, E> IntoDeserializer<'de, E> for ContentDeserializer<'de, E> where E: DeError, { type Deserializer = Self; fn into_deserializer(self) -> Self { self } } impl<'de, E> IntoDeserializer<'de, E> for ContentRefDeserializer<'_, 'de, E> where E: DeError, { type Deserializer = Self; fn into_deserializer(self) -> Self { self } } serde_with-3.12.0/src/content/mod.rs000064400000000000000000000004061046102023000154320ustar 00000000000000//! Import of the unstable private `Content` type from `serde`. //! //! #![cfg(not(tarpaulin_include))] pub(crate) mod de; pub(crate) mod ser; serde_with-3.12.0/src/content/ser.rs000064400000000000000000000412351046102023000154510ustar 00000000000000//! Buffer for serializing data. //! //! This is a copy and improvement of the `serde` private type: //! //! The code is very stable in the `serde` crate, so no maintainability problem is expected. //! //! Since the type is private we copy the type here. //! `serde` is licensed as MIT+Apache2, the same as this crate. //! //! This version carries improvements compared to `serde`'s version. //! 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 crate::prelude::*; pub(crate) enum Content { Bool(bool), U8(u8), U16(u16), U32(u32), U64(u64), U128(u128), I8(i8), I16(i16), I32(i32), I64(i64), I128(i128), F32(f32), F64(f64), Char(char), String(String), Bytes(Vec), None, Some(Box), Unit, UnitStruct(&'static str), UnitVariant(&'static str, u32, &'static str), NewtypeStruct(&'static str, Box), NewtypeVariant(&'static str, u32, &'static str, Box), Seq(Vec), Tuple(Vec), TupleStruct(&'static str, Vec), TupleVariant(&'static str, u32, &'static str, Vec), Map(Vec<(Content, Content)>), Struct(&'static str, Vec<(&'static str, Content)>), StructVariant( &'static str, u32, &'static str, Vec<(&'static str, 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(&self, serializer: S) -> Result where S: Serializer, { match *self { Content::Bool(b) => serializer.serialize_bool(b), Content::U8(u) => serializer.serialize_u8(u), Content::U16(u) => serializer.serialize_u16(u), Content::U32(u) => serializer.serialize_u32(u), Content::U64(u) => serializer.serialize_u64(u), Content::U128(u) => serializer.serialize_u128(u), Content::I8(i) => serializer.serialize_i8(i), Content::I16(i) => serializer.serialize_i16(i), Content::I32(i) => serializer.serialize_i32(i), Content::I64(i) => serializer.serialize_i64(i), Content::I128(i) => serializer.serialize_i128(i), Content::F32(f) => serializer.serialize_f32(f), Content::F64(f) => serializer.serialize_f64(f), Content::Char(c) => serializer.serialize_char(c), Content::String(ref s) => serializer.serialize_str(s), Content::Bytes(ref b) => serializer.serialize_bytes(b), Content::None => serializer.serialize_none(), Content::Some(ref c) => serializer.serialize_some(&**c), Content::Unit => serializer.serialize_unit(), Content::UnitStruct(n) => serializer.serialize_unit_struct(n), Content::UnitVariant(n, i, v) => serializer.serialize_unit_variant(n, i, v), Content::NewtypeStruct(n, ref c) => serializer.serialize_newtype_struct(n, &**c), Content::NewtypeVariant(n, i, v, ref c) => { serializer.serialize_newtype_variant(n, i, v, &**c) } Content::Seq(ref elements) => elements.serialize(serializer), Content::Tuple(ref elements) => { let mut tuple = serializer.serialize_tuple(elements.len())?; for e in elements { tuple.serialize_element(e)?; } tuple.end() } Content::TupleStruct(n, ref fields) => { let mut ts = serializer.serialize_tuple_struct(n, fields.len())?; for f in fields { ts.serialize_field(f)?; } ts.end() } Content::TupleVariant(n, i, v, ref fields) => { let mut tv = serializer.serialize_tuple_variant(n, i, v, fields.len())?; for f in fields { tv.serialize_field(f)?; } tv.end() } Content::Map(ref entries) => { let mut map = serializer.serialize_map(Some(entries.len()))?; for (k, v) in entries { map.serialize_entry(k, v)?; } map.end() } Content::Struct(n, ref fields) => { let mut s = serializer.serialize_struct(n, fields.len())?; for (k, v) in fields { s.serialize_field(k, v)?; } s.end() } Content::StructVariant(n, i, v, ref fields) => { let mut sv = serializer.serialize_struct_variant(n, i, v, fields.len())?; for (k, v) in fields { sv.serialize_field(k, v)?; } sv.end() } } } } pub(crate) struct ContentSerializer { is_human_readable: bool, error: PhantomData, } impl ContentSerializer { pub(crate) fn new(is_human_readable: bool) -> Self { ContentSerializer { is_human_readable, error: PhantomData, } } } impl Default for ContentSerializer { fn default() -> Self { Self::new(true) } } impl Serializer for ContentSerializer where E: SerError, { type Ok = Content; type Error = E; type SerializeSeq = SeqSerialize; type SerializeTuple = TupleSerialize; type SerializeTupleStruct = TupleStructSerialize; type SerializeTupleVariant = TupleVariantSerialize; type SerializeMap = MapSerialize; type SerializeStruct = StructSerialize; type SerializeStructVariant = StructVariantSerialize; fn is_human_readable(&self) -> bool { self.is_human_readable } fn serialize_bool(self, v: bool) -> Result { Ok(Content::Bool(v)) } fn serialize_i8(self, v: i8) -> Result { Ok(Content::I8(v)) } fn serialize_i16(self, v: i16) -> Result { Ok(Content::I16(v)) } fn serialize_i32(self, v: i32) -> Result { Ok(Content::I32(v)) } fn serialize_i64(self, v: i64) -> Result { Ok(Content::I64(v)) } fn serialize_i128(self, v: i128) -> Result { Ok(Content::I128(v)) } fn serialize_u8(self, v: u8) -> Result { Ok(Content::U8(v)) } fn serialize_u16(self, v: u16) -> Result { Ok(Content::U16(v)) } fn serialize_u32(self, v: u32) -> Result { Ok(Content::U32(v)) } fn serialize_u64(self, v: u64) -> Result { Ok(Content::U64(v)) } fn serialize_u128(self, v: u128) -> Result { Ok(Content::U128(v)) } fn serialize_f32(self, v: f32) -> Result { Ok(Content::F32(v)) } fn serialize_f64(self, v: f64) -> Result { Ok(Content::F64(v)) } fn serialize_char(self, v: char) -> Result { Ok(Content::Char(v)) } fn serialize_str(self, value: &str) -> Result { Ok(Content::String(value.to_owned())) } fn serialize_bytes(self, value: &[u8]) -> Result { Ok(Content::Bytes(value.to_owned())) } fn serialize_none(self) -> Result { Ok(Content::None) } fn serialize_some(self, value: &T) -> Result where T: Serialize + ?Sized, { Ok(Content::Some(Box::new(value.serialize(self)?))) } fn serialize_unit(self) -> Result { Ok(Content::Unit) } fn serialize_unit_struct(self, name: &'static str) -> Result { Ok(Content::UnitStruct(name)) } fn serialize_unit_variant( self, name: &'static str, variant_index: u32, variant: &'static str, ) -> Result { Ok(Content::UnitVariant(name, variant_index, variant)) } fn serialize_newtype_struct(self, name: &'static str, value: &T) -> Result where T: Serialize + ?Sized, { Ok(Content::NewtypeStruct( name, Box::new(value.serialize(self)?), )) } fn serialize_newtype_variant( self, name: &'static str, variant_index: u32, variant: &'static str, value: &T, ) -> Result where T: Serialize + ?Sized, { Ok(Content::NewtypeVariant( name, variant_index, variant, Box::new(value.serialize(self)?), )) } fn serialize_seq(self, len: Option) -> Result { Ok(SeqSerialize { is_human_readable: self.is_human_readable, elements: Vec::with_capacity(len.unwrap_or(0)), error: PhantomData, }) } fn serialize_tuple(self, len: usize) -> Result { Ok(TupleSerialize { is_human_readable: self.is_human_readable, elements: Vec::with_capacity(len), error: PhantomData, }) } fn serialize_tuple_struct( self, name: &'static str, len: usize, ) -> Result { Ok(TupleStructSerialize { is_human_readable: self.is_human_readable, name, fields: Vec::with_capacity(len), error: PhantomData, }) } fn serialize_tuple_variant( self, name: &'static str, variant_index: u32, variant: &'static str, len: usize, ) -> Result { Ok(TupleVariantSerialize { is_human_readable: self.is_human_readable, name, variant_index, variant, fields: Vec::with_capacity(len), error: PhantomData, }) } fn serialize_map(self, len: Option) -> Result { Ok(MapSerialize { is_human_readable: self.is_human_readable, entries: Vec::with_capacity(len.unwrap_or(0)), key: None, error: PhantomData, }) } fn serialize_struct(self, name: &'static str, len: usize) -> Result { Ok(StructSerialize { is_human_readable: self.is_human_readable, name, fields: Vec::with_capacity(len), error: PhantomData, }) } fn serialize_struct_variant( self, name: &'static str, variant_index: u32, variant: &'static str, len: usize, ) -> Result { Ok(StructVariantSerialize { is_human_readable: self.is_human_readable, name, variant_index, variant, fields: Vec::with_capacity(len), error: PhantomData, }) } } pub(crate) struct SeqSerialize { is_human_readable: bool, elements: Vec, error: PhantomData, } impl SerializeSeq for SeqSerialize where E: SerError, { type Ok = Content; type Error = E; fn serialize_element(&mut self, value: &T) -> Result<(), E> where T: Serialize + ?Sized, { let value = value.serialize(ContentSerializer::::new(self.is_human_readable))?; self.elements.push(value); Ok(()) } fn end(self) -> Result { Ok(Content::Seq(self.elements)) } } pub(crate) struct TupleSerialize { is_human_readable: bool, elements: Vec, error: PhantomData, } impl SerializeTuple for TupleSerialize where E: SerError, { type Ok = Content; type Error = E; fn serialize_element(&mut self, value: &T) -> Result<(), E> where T: Serialize + ?Sized, { let value = value.serialize(ContentSerializer::::new(self.is_human_readable))?; self.elements.push(value); Ok(()) } fn end(self) -> Result { Ok(Content::Tuple(self.elements)) } } pub(crate) struct TupleStructSerialize { is_human_readable: bool, name: &'static str, fields: Vec, error: PhantomData, } impl SerializeTupleStruct for TupleStructSerialize where E: SerError, { type Ok = Content; type Error = E; fn serialize_field(&mut self, value: &T) -> Result<(), E> where T: Serialize + ?Sized, { let value = value.serialize(ContentSerializer::::new(self.is_human_readable))?; self.fields.push(value); Ok(()) } fn end(self) -> Result { Ok(Content::TupleStruct(self.name, self.fields)) } } pub(crate) struct TupleVariantSerialize { is_human_readable: bool, name: &'static str, variant_index: u32, variant: &'static str, fields: Vec, error: PhantomData, } impl SerializeTupleVariant for TupleVariantSerialize where E: SerError, { type Ok = Content; type Error = E; fn serialize_field(&mut self, value: &T) -> Result<(), E> where T: Serialize + ?Sized, { let value = value.serialize(ContentSerializer::::new(self.is_human_readable))?; self.fields.push(value); Ok(()) } fn end(self) -> Result { Ok(Content::TupleVariant( self.name, self.variant_index, self.variant, self.fields, )) } } pub(crate) struct MapSerialize { is_human_readable: bool, entries: Vec<(Content, Content)>, key: Option, error: PhantomData, } impl SerializeMap for MapSerialize where E: SerError, { type Ok = Content; type Error = E; fn serialize_key(&mut self, key: &T) -> Result<(), E> where T: Serialize + ?Sized, { let key = key.serialize(ContentSerializer::::new(self.is_human_readable))?; self.key = Some(key); Ok(()) } fn serialize_value(&mut self, value: &T) -> Result<(), E> where T: Serialize + ?Sized, { let key = self .key .take() .expect("serialize_value called before serialize_key"); let value = value.serialize(ContentSerializer::::new(self.is_human_readable))?; self.entries.push((key, value)); Ok(()) } fn end(self) -> Result { Ok(Content::Map(self.entries)) } fn serialize_entry(&mut self, key: &K, value: &V) -> Result<(), E> where K: Serialize + ?Sized, V: Serialize + ?Sized, { let key = key.serialize(ContentSerializer::::new(self.is_human_readable))?; let value = value.serialize(ContentSerializer::::new(self.is_human_readable))?; self.entries.push((key, value)); Ok(()) } } pub(crate) struct StructSerialize { is_human_readable: bool, name: &'static str, fields: Vec<(&'static str, Content)>, error: PhantomData, } impl SerializeStruct for StructSerialize where E: SerError, { type Ok = Content; type Error = E; fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), E> where T: Serialize + ?Sized, { let value = value.serialize(ContentSerializer::::new(self.is_human_readable))?; self.fields.push((key, value)); Ok(()) } fn end(self) -> Result { Ok(Content::Struct(self.name, self.fields)) } } pub(crate) struct StructVariantSerialize { is_human_readable: bool, name: &'static str, variant_index: u32, variant: &'static str, fields: Vec<(&'static str, Content)>, error: PhantomData, } impl SerializeStructVariant for StructVariantSerialize where E: SerError, { type Ok = Content; type Error = E; fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), E> where T: Serialize + ?Sized, { let value = value.serialize(ContentSerializer::::new(self.is_human_readable))?; self.fields.push((key, value)); Ok(()) } fn end(self) -> Result { Ok(Content::StructVariant( self.name, self.variant_index, self.variant, self.fields, )) } } serde_with-3.12.0/src/de/duplicates.rs000064400000000000000000000162351046102023000157350ustar 00000000000000use super::impls::macros::{foreach_map, foreach_set}; use crate::{ duplicate_key_impls::{ DuplicateInsertsFirstWinsMap, DuplicateInsertsLastWinsSet, PreventDuplicateInsertsMap, PreventDuplicateInsertsSet, }, prelude::*, }; #[cfg(feature = "hashbrown_0_14")] use hashbrown_0_14::{HashMap as HashbrownMap014, HashSet as HashbrownSet014}; #[cfg(feature = "hashbrown_0_15")] use hashbrown_0_15::{HashMap as HashbrownMap015, HashSet as HashbrownSet015}; #[cfg(feature = "indexmap_1")] use indexmap_1::{IndexMap, IndexSet}; #[cfg(feature = "indexmap_2")] use indexmap_2::{IndexMap as IndexMap2, IndexSet as IndexSet2}; struct SetPreventDuplicatesVisitor(PhantomData<(SET, T, TAs)>); impl<'de, SET, T, TAs> Visitor<'de> for SetPreventDuplicatesVisitor where SET: PreventDuplicateInsertsSet, 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(self, mut access: A) -> Result where A: SeqAccess<'de>, { let mut values = Self::Value::new(access.size_hint()); while let Some(value) = access.next_element::>()? { if !values.insert(value.into_inner()) { return Err(DeError::custom("invalid entry: found duplicate value")); }; } Ok(values) } } struct SetLastValueWinsVisitor(PhantomData<(SET, T, TAs)>); impl<'de, SET, T, TAs> Visitor<'de> for SetLastValueWinsVisitor where SET: DuplicateInsertsLastWinsSet, 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(self, mut access: A) -> Result where A: SeqAccess<'de>, { let mut values = Self::Value::new(access.size_hint()); while let Some(value) = access.next_element::>()? { 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> for SetPreventDuplicates where TAs: DeserializeAs<'de, T>, $(T: $tbound1 $(+ $tbound2)*,)? $($typaram: $bound1 $(+ $bound2)*),* { fn deserialize_as(deserializer: D) -> Result<$ty, D::Error> where D: Deserializer<'de>, { deserializer.deserialize_seq(SetPreventDuplicatesVisitor::<$ty, T, TAs>( PhantomData, )) } } impl<'de, T, TAs $(, $typaram)*> DeserializeAs<'de, $ty> for SetLastValueWins where TAs: DeserializeAs<'de, T>, $(T: $tbound1 $(+ $tbound2)*,)? $($typaram: $bound1 $(+ $bound2)*),* { fn deserialize_as(deserializer: D) -> Result<$ty, D::Error> where D: Deserializer<'de>, { deserializer .deserialize_seq(SetLastValueWinsVisitor::<$ty, T, TAs>(PhantomData)) } } } } foreach_set!(set_impl); struct MapPreventDuplicatesVisitor(PhantomData<(MAP, K, KAs, V, VAs)>); impl<'de, MAP, K, KAs, V, VAs> Visitor<'de> for MapPreventDuplicatesVisitor where MAP: PreventDuplicateInsertsMap, 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(self, mut access: A) -> Result where A: MapAccess<'de>, { let mut values = Self::Value::new(access.size_hint()); while let Some((key, value)) = access.next_entry::, DeserializeAsWrap>()? { if !values.insert(key.into_inner(), value.into_inner()) { return Err(DeError::custom("invalid entry: found duplicate key")); }; } Ok(values) } } struct MapFirstKeyWinsVisitor(PhantomData<(MAP, K, KAs, V, VAs)>); impl<'de, MAP, K, KAs, V, VAs> Visitor<'de> for MapFirstKeyWinsVisitor where MAP: DuplicateInsertsFirstWinsMap, 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(self, mut access: A) -> Result where A: MapAccess<'de>, { let mut values = Self::Value::new(access.size_hint()); while let Some((key, value)) = access.next_entry::, DeserializeAsWrap>()? { 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> for MapPreventDuplicates where KAs: DeserializeAs<'de, K>, VAs: DeserializeAs<'de, V>, $(K: $kbound1 $(+ $kbound2)*,)? $($typaram: $bound1 $(+ $bound2)*),* { fn deserialize_as(deserializer: D) -> Result<$ty, D::Error> where D: Deserializer<'de>, { deserializer.deserialize_map(MapPreventDuplicatesVisitor::< $ty, K, KAs, V, VAs, >(PhantomData)) } } impl<'de, K, V, KAs, VAs $(, $typaram)*> DeserializeAs<'de, $ty> for MapFirstKeyWins where KAs: DeserializeAs<'de, K>, VAs: DeserializeAs<'de, V>, $(K: $kbound1 $(+ $kbound2)*,)? $($typaram: $bound1 $(+ $bound2)*),* { fn deserialize_as(deserializer: D) -> Result<$ty, D::Error> where D: Deserializer<'de>, { deserializer.deserialize_map(MapFirstKeyWinsVisitor::<$ty, K, KAs, V, VAs>( PhantomData, )) } } }; } foreach_map!(map_impl); serde_with-3.12.0/src/de/impls.rs000064400000000000000000001760371046102023000147330ustar 00000000000000pub(crate) use self::macros::*; use crate::{formats::*, prelude::*}; #[cfg(feature = "hashbrown_0_14")] use hashbrown_0_14::{HashMap as HashbrownMap014, HashSet as HashbrownSet014}; #[cfg(feature = "hashbrown_0_15")] use hashbrown_0_15::{HashMap as HashbrownMap015, HashSet as HashbrownSet015}; #[cfg(feature = "indexmap_1")] use indexmap_1::{IndexMap, IndexSet}; #[cfg(feature = "indexmap_2")] use indexmap_2::{IndexMap as IndexMap2, IndexSet as IndexSet2}; /////////////////////////////////////////////////////////////////////////////// // Helper macro used internally #[cfg(feature = "alloc")] type BoxedSlice = Box<[T]>; pub(crate) mod macros { // The unused_imports lint has false-positives around macros // https://github.com/rust-lang/rust/issues/78894 #![allow(unused_imports)] macro_rules! foreach_map { ($m:ident) => { #[cfg(feature = "alloc")] $m!(BTreeMap, (|_size| BTreeMap::new())); #[cfg(feature = "std")] $m!( HashMap, (|size| HashMap::with_capacity_and_hasher(size, Default::default())) ); #[cfg(feature = "hashbrown_0_14")] $m!( HashbrownMap014, (|size| HashbrownMap014::with_capacity_and_hasher(size, Default::default())) ); #[cfg(feature = "hashbrown_0_15")] $m!( HashbrownMap015, (|size| HashbrownMap015::with_capacity_and_hasher(size, Default::default())) ); #[cfg(feature = "indexmap_1")] $m!( IndexMap, (|size| IndexMap::with_capacity_and_hasher(size, Default::default())) ); #[cfg(feature = "indexmap_2")] $m!( IndexMap2, (|size| IndexMap2::with_capacity_and_hasher(size, Default::default())) ); }; } macro_rules! foreach_set { ($m:ident) => { #[cfg(feature = "alloc")] $m!(BTreeSet, (|_| BTreeSet::new()), insert); #[cfg(feature = "std")] $m!( HashSet, (|size| HashSet::with_capacity_and_hasher(size, S::default())), insert ); #[cfg(feature = "hashbrown_0_14")] $m!( HashbrownSet014, (|size| HashbrownSet014::with_capacity_and_hasher(size, S::default())), insert ); #[cfg(feature = "hashbrown_0_15")] $m!( HashbrownSet015, (|size| HashbrownSet015::with_capacity_and_hasher(size, S::default())), insert ); #[cfg(feature = "indexmap_1")] $m!( IndexSet, (|size| IndexSet::with_capacity_and_hasher(size, S::default())), insert ); #[cfg(feature = "indexmap_2")] $m!( IndexSet2, (|size| IndexSet2::with_capacity_and_hasher(size, S::default())), insert ); }; } macro_rules! foreach_seq { ($m:ident) => { foreach_set!($m); #[cfg(feature = "alloc")] $m!( BinaryHeap, (|size| BinaryHeap::with_capacity(size)), push ); #[cfg(feature = "alloc")] $m!(BoxedSlice, (|size| Vec::with_capacity(size)), push); #[cfg(feature = "alloc")] $m!(LinkedList, (|_| LinkedList::new()), push_back); #[cfg(feature = "alloc")] $m!(Vec, (|size| Vec::with_capacity(size)), push); #[cfg(feature = "alloc")] $m!( VecDeque, (|size| VecDeque::with_capacity(size)), push_back ); }; } // Make the macros available to the rest of the crate pub(crate) use foreach_map; pub(crate) use foreach_seq; pub(crate) use foreach_set; } /////////////////////////////////////////////////////////////////////////////// // region: Simple Wrapper types (e.g., Box, Option) #[allow(unused_macros)] macro_rules! pinned_wrapper { ($wrapper:ident) => { impl<'de, T, U> DeserializeAs<'de, Pin<$wrapper>> for Pin<$wrapper> where U: DeserializeAs<'de, T>, { fn deserialize_as(deserializer: D) -> Result>, D::Error> where D: Deserializer<'de>, { Ok($wrapper::pin( DeserializeAsWrap::::deserialize(deserializer)?.into_inner(), )) } } }; } #[cfg(feature = "alloc")] impl<'de, T, U> DeserializeAs<'de, Box> for Box where U: DeserializeAs<'de, T>, { fn deserialize_as(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { Ok(Box::new( DeserializeAsWrap::::deserialize(deserializer)?.into_inner(), )) } } #[cfg(feature = "alloc")] pinned_wrapper!(Box); impl<'de, T, U> DeserializeAs<'de, Option> for Option where U: DeserializeAs<'de, T>, { fn deserialize_as(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { struct OptionVisitor(PhantomData<(T, U)>); impl<'de, T, U> Visitor<'de> for OptionVisitor where U: DeserializeAs<'de, T>, { type Value = Option; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("option") } #[inline] fn visit_unit(self) -> Result where E: DeError, { Ok(None) } #[inline] fn visit_none(self) -> Result where E: DeError, { Ok(None) } #[inline] fn visit_some(self, deserializer: D) -> Result where D: Deserializer<'de>, { U::deserialize_as(deserializer).map(Some) } } deserializer.deserialize_option(OptionVisitor::(PhantomData)) } } impl<'de, T, U> DeserializeAs<'de, Bound> for Bound where U: DeserializeAs<'de, T>, { fn deserialize_as(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { Ok( match Bound::>::deserialize(deserializer)? { Bound::Unbounded => Bound::Unbounded, Bound::Included(v) => Bound::Included(v.into_inner()), Bound::Excluded(v) => Bound::Excluded(v.into_inner()), }, ) } } #[cfg(feature = "alloc")] impl<'de, T, U> DeserializeAs<'de, Rc> for Rc where U: DeserializeAs<'de, T>, { fn deserialize_as(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { Ok(Rc::new( DeserializeAsWrap::::deserialize(deserializer)?.into_inner(), )) } } #[cfg(feature = "alloc")] pinned_wrapper!(Rc); #[cfg(feature = "alloc")] impl<'de, T, U> DeserializeAs<'de, RcWeak> for RcWeak where U: DeserializeAs<'de, T>, { fn deserialize_as(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { DeserializeAsWrap::>, Option>>::deserialize(deserializer)?; Ok(RcWeak::new()) } } #[cfg(all(feature = "alloc", target_has_atomic = "ptr"))] impl<'de, T, U> DeserializeAs<'de, Arc> for Arc where U: DeserializeAs<'de, T>, { fn deserialize_as(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { Ok(Arc::new( DeserializeAsWrap::::deserialize(deserializer)?.into_inner(), )) } } #[cfg(all(feature = "alloc", target_has_atomic = "ptr"))] pinned_wrapper!(Arc); #[cfg(all(feature = "alloc", target_has_atomic = "ptr"))] impl<'de, T, U> DeserializeAs<'de, ArcWeak> for ArcWeak where U: DeserializeAs<'de, T>, { fn deserialize_as(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { DeserializeAsWrap::>, Option>>::deserialize(deserializer)?; Ok(ArcWeak::new()) } } impl<'de, T, U> DeserializeAs<'de, Cell> for Cell where U: DeserializeAs<'de, T>, { fn deserialize_as(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { Ok(Cell::new( DeserializeAsWrap::::deserialize(deserializer)?.into_inner(), )) } } impl<'de, T, U> DeserializeAs<'de, RefCell> for RefCell where U: DeserializeAs<'de, T>, { fn deserialize_as(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { Ok(RefCell::new( DeserializeAsWrap::::deserialize(deserializer)?.into_inner(), )) } } #[cfg(feature = "std")] impl<'de, T, U> DeserializeAs<'de, Mutex> for Mutex where U: DeserializeAs<'de, T>, { fn deserialize_as(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { Ok(Mutex::new( DeserializeAsWrap::::deserialize(deserializer)?.into_inner(), )) } } #[cfg(feature = "std")] impl<'de, T, U> DeserializeAs<'de, RwLock> for RwLock where U: DeserializeAs<'de, T>, { fn deserialize_as(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { Ok(RwLock::new( DeserializeAsWrap::::deserialize(deserializer)?.into_inner(), )) } } impl<'de, T, TAs, E, EAs> DeserializeAs<'de, Result> for Result where TAs: DeserializeAs<'de, T>, EAs: DeserializeAs<'de, E>, { fn deserialize_as(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { Ok( match Result::, DeserializeAsWrap>::deserialize( deserializer, )? { Ok(value) => Ok(value.into_inner()), Err(err) => Err(err.into_inner()), }, ) } } impl<'de, T, As, const N: usize> DeserializeAs<'de, [T; N]> for [As; N] where As: DeserializeAs<'de, T>, { fn deserialize_as(deserializer: D) -> Result<[T; N], D::Error> where D: Deserializer<'de>, { struct ArrayVisitor(PhantomData); impl<'de, T, As, const M: usize> Visitor<'de> for ArrayVisitor, 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(self, seq: A) -> Result where A: SeqAccess<'de>, { utils::array_from_iterator( utils::SeqIter::new(seq).map( |res: Result, A::Error>| { res.map(DeserializeAsWrap::into_inner) }, ), &self, ) } } deserializer.deserialize_tuple(N, ArrayVisitor::, N>(PhantomData)) } } // endregion /////////////////////////////////////////////////////////////////////////////// // region: Collection Types (e.g., Maps, Sets, Vec) #[cfg(feature = "alloc")] macro_rules! seq_impl { ( $ty:ident < T $(: $tbound1:ident $(+ $tbound2:ident)*)? $(, $typaram:ident : $bound1:ident $(+ $bound2:ident)* )* >, $with_capacity:expr, $append:ident ) => { impl<'de, T, U $(, $typaram)*> DeserializeAs<'de, $ty> for $ty where U: DeserializeAs<'de, T>, $(T: $tbound1 $(+ $tbound2)*,)? $($typaram: $bound1 $(+ $bound2)*),* { fn deserialize_as(deserializer: D) -> Result<$ty, D::Error> where D: Deserializer<'de>, { struct SeqVisitor { marker: PhantomData<(T, U $(, $typaram)*)>, } impl<'de, T, U $(, $typaram)*> Visitor<'de> for SeqVisitor where U: DeserializeAs<'de, T>, $(T: $tbound1 $(+ $tbound2)*,)? $($typaram: $bound1 $(+ $bound2)*),* { type Value = $ty; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("a sequence") } fn visit_seq(self, mut seq: A) -> Result where A: SeqAccess<'de>, { #[allow(clippy::redundant_closure_call)] let mut values = ($with_capacity)(utils::size_hint_cautious::(seq.size_hint())); while let Some(value) = seq .next_element()? .map(|v: DeserializeAsWrap| v.into_inner()) { values.$append(value); } Ok(values.into()) } } let visitor = SeqVisitor:: { marker: PhantomData, }; deserializer.deserialize_seq(visitor) } } }; } foreach_seq!(seq_impl); #[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, KU, VU $(, $typaram)*> DeserializeAs<'de, $ty> for $ty where KU: DeserializeAs<'de, K>, VU: DeserializeAs<'de, V>, $(K: $kbound1 $(+ $kbound2)*,)* $($typaram: $bound1 $(+ $bound2)*),* { fn deserialize_as(deserializer: D) -> Result<$ty, D::Error> where D: Deserializer<'de>, { struct MapVisitor(PhantomData<(K, V, KU, VU $(, $typaram)*)>); impl<'de, K, V, KU, VU $(, $typaram)*> Visitor<'de> for MapVisitor where KU: DeserializeAs<'de, K>, VU: DeserializeAs<'de, V>, $(K: $kbound1 $(+ $kbound2)*,)* $($typaram: $bound1 $(+ $bound2)*),* { type Value = $ty; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("a map") } #[inline] fn visit_map(self, mut map: A) -> Result where A: MapAccess<'de>, { #[allow(clippy::redundant_closure_call)] let mut values = ($with_capacity)(utils::size_hint_cautious::<(K, V)>(map.size_hint())); while let Some((key, value)) = (map.next_entry())?.map(|(k, v): (DeserializeAsWrap::, DeserializeAsWrap::)| (k.into_inner(), v.into_inner())) { values.insert(key, value); } Ok(values) } } let visitor = MapVisitor:: (PhantomData); deserializer.deserialize_map(visitor) } } } } foreach_map!(map_impl); macro_rules! tuple_impl { ($len:literal $($n:tt $t:ident $tas:ident)+) => { impl<'de, $($t, $tas,)+> DeserializeAs<'de, ($($t,)+)> for ($($tas,)+) where $($tas: DeserializeAs<'de, $t>,)+ { fn deserialize_as(deserializer: D) -> Result<($($t,)+), D::Error> where D: Deserializer<'de>, { struct TupleVisitor<$($t,)+>(PhantomData<($($t,)+)>); impl<'de, $($t, $tas,)+> Visitor<'de> for TupleVisitor<$(DeserializeAsWrap<$t, $tas>,)+> where $($tas: DeserializeAs<'de, $t>,)+ { type Value = ($($t,)+); fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str(concat!("a tuple of size ", $len)) } #[allow(non_snake_case)] fn visit_seq(self, mut seq: A) -> Result where A: SeqAccess<'de>, { $( let $t: DeserializeAsWrap<$t, $tas> = match seq.next_element()? { Some(value) => value, None => return Err(DeError::invalid_length($n, &self)), }; )+ Ok(($($t.into_inner(),)+)) } } deserializer.deserialize_tuple( $len, TupleVisitor::<$(DeserializeAsWrap<$t, $tas>,)+>(PhantomData), ) } } }; } tuple_impl!(1 0 T0 As0); tuple_impl!(2 0 T0 As0 1 T1 As1); tuple_impl!(3 0 T0 As0 1 T1 As1 2 T2 As2); tuple_impl!(4 0 T0 As0 1 T1 As1 2 T2 As2 3 T3 As3); tuple_impl!(5 0 T0 As0 1 T1 As1 2 T2 As2 3 T3 As3 4 T4 As4); tuple_impl!(6 0 T0 As0 1 T1 As1 2 T2 As2 3 T3 As3 4 T4 As4 5 T5 As5); tuple_impl!(7 0 T0 As0 1 T1 As1 2 T2 As2 3 T3 As3 4 T4 As4 5 T5 As5 6 T6 As6); tuple_impl!(8 0 T0 As0 1 T1 As1 2 T2 As2 3 T3 As3 4 T4 As4 5 T5 As5 6 T6 As6 7 T7 As7); tuple_impl!(9 0 T0 As0 1 T1 As1 2 T2 As2 3 T3 As3 4 T4 As4 5 T5 As5 6 T6 As6 7 T7 As7 8 T8 As8); tuple_impl!(10 0 T0 As0 1 T1 As1 2 T2 As2 3 T3 As3 4 T4 As4 5 T5 As5 6 T6 As6 7 T7 As7 8 T8 As8 9 T9 As9); tuple_impl!(11 0 T0 As0 1 T1 As1 2 T2 As2 3 T3 As3 4 T4 As4 5 T5 As5 6 T6 As6 7 T7 As7 8 T8 As8 9 T9 As9 10 T10 As10); tuple_impl!(12 0 T0 As0 1 T1 As1 2 T2 As2 3 T3 As3 4 T4 As4 5 T5 As5 6 T6 As6 7 T7 As7 8 T8 As8 9 T9 As9 10 T10 As10 11 T11 As11); tuple_impl!(13 0 T0 As0 1 T1 As1 2 T2 As2 3 T3 As3 4 T4 As4 5 T5 As5 6 T6 As6 7 T7 As7 8 T8 As8 9 T9 As9 10 T10 As10 11 T11 As11 12 T12 As12); tuple_impl!(14 0 T0 As0 1 T1 As1 2 T2 As2 3 T3 As3 4 T4 As4 5 T5 As5 6 T6 As6 7 T7 As7 8 T8 As8 9 T9 As9 10 T10 As10 11 T11 As11 12 T12 As12 13 T13 As13); tuple_impl!(15 0 T0 As0 1 T1 As1 2 T2 As2 3 T3 As3 4 T4 As4 5 T5 As5 6 T6 As6 7 T7 As7 8 T8 As8 9 T9 As9 10 T10 As10 11 T11 As11 12 T12 As12 13 T13 As13 14 T14 As14); tuple_impl!(16 0 T0 As0 1 T1 As1 2 T2 As2 3 T3 As3 4 T4 As4 5 T5 As5 6 T6 As6 7 T7 As7 8 T8 As8 9 T9 As9 10 T10 As10 11 T11 As11 12 T12 As12 13 T13 As13 14 T14 As14 15 T15 As15); #[cfg(feature = "alloc")] macro_rules! map_as_tuple_seq_intern { ( $tyorig:ident < K $(: $kbound1:ident $(+ $kbound2:ident)*)?, V $(, $typaram:ident : $bound1:ident $(+ $bound2:ident)*)* >, $with_capacity:expr, $ty:ident <(KAs, VAs)> ) => { impl<'de, K, KAs, V, VAs $(, $typaram)*> DeserializeAs<'de, $tyorig> for $ty<(KAs, VAs)> where KAs: DeserializeAs<'de, K>, VAs: DeserializeAs<'de, V>, $(K: $kbound1 $(+ $kbound2)*,)? $($typaram: $bound1 $(+ $bound2)*,)* { fn deserialize_as(deserializer: D) -> Result<$tyorig, D::Error> where D: Deserializer<'de>, { struct SeqVisitor { marker: PhantomData<(K, KAs, V, VAs $(, $typaram)*)>, } impl<'de, K, KAs, V, VAs $(, $typaram)*> Visitor<'de> for SeqVisitor where KAs: DeserializeAs<'de, K>, VAs: DeserializeAs<'de, V>, $(K: $kbound1 $(+ $kbound2)*,)? $($typaram: $bound1 $(+ $bound2)*,)* { type Value = $tyorig; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("a sequence") } #[inline] fn visit_seq(self, access: A) -> Result where A: SeqAccess<'de>, { let iter = utils::SeqIter::new(access); iter.map(|res| { res.map( |(k, v): (DeserializeAsWrap, DeserializeAsWrap)| { (k.into_inner(), v.into_inner()) }, ) }) .collect() } } let visitor = SeqVisitor:: { marker: PhantomData, }; deserializer.deserialize_seq(visitor) } } }; } #[cfg(feature = "alloc")] macro_rules! map_as_tuple_seq { ( $tyorig:ident < K $(: $kbound1:ident $(+ $kbound2:ident)*)?, V $(, $typaram:ident : $bound1:ident $(+ $bound2:ident)*)* >, $with_capacity:expr ) => { map_as_tuple_seq_intern!($tyorig < K $(: $kbound1 $(+ $kbound2)*)? , V $(, $typaram : $bound1 $(+ $bound2)*)* >, $with_capacity, Seq<(KAs, VAs)>); #[cfg(feature = "alloc")] map_as_tuple_seq_intern!($tyorig < K $(: $kbound1 $(+ $kbound2)*)? , V $(, $typaram : $bound1 $(+ $bound2)*)* >, $with_capacity, Vec<(KAs, VAs)>); } } foreach_map!(map_as_tuple_seq); #[cfg(feature = "alloc")] macro_rules! tuple_seq_as_map_impl_intern { ( $tyorig:ident < (K, V) $(: $($bound:ident $(+)?)+)? $(, $typaram:ident : $bound1:ident $(+ $bound2:ident)* )* >, $with_capacity:expr, $append:ident, $ty:ident ) => { #[allow(clippy::implicit_hasher)] impl<'de, K, KAs, V, VAs $(, $typaram)*> DeserializeAs<'de, $tyorig < (K, V) $(, $typaram)* >> for $ty where KAs: DeserializeAs<'de, K>, VAs: DeserializeAs<'de, V>, (K, V): $($($bound +)*)?, $($typaram: $bound1 $(+ $bound2)*,)* { fn deserialize_as(deserializer: D) -> Result<$tyorig < (K, V) $(, $typaram)* >, D::Error> where D: Deserializer<'de>, { struct MapVisitor { marker: PhantomData<(K, KAs, V, VAs $(, $typaram)*)>, } impl<'de, K, KAs, V, VAs $(, $typaram)*> Visitor<'de> for MapVisitor where KAs: DeserializeAs<'de, K>, VAs: DeserializeAs<'de, V>, (K, V): $($($bound +)*)?, $($typaram: $bound1 $(+ $bound2)*,)* { type Value = $tyorig < (K, V) $(, $typaram)* >; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("a map") } #[inline] fn visit_map(self, access: A) -> Result where A: MapAccess<'de>, { let iter = utils::MapIter::new(access); iter.map(|res| { res.map( |(k, v): (DeserializeAsWrap, DeserializeAsWrap)| { (k.into_inner(), v.into_inner()) }, ) }) .collect() } } let visitor = MapVisitor:: { marker: PhantomData, }; deserializer.deserialize_map(visitor) } } } } #[cfg(feature = "alloc")] macro_rules! tuple_seq_as_map_impl { ( $tyorig:ident < T $(: $($bound:ident $(+)?)+)? $(, $typaram:ident : $bound1:ident $(+ $bound2:ident)* )* >, $with_capacity:expr, $append:ident ) => { tuple_seq_as_map_impl_intern!($tyorig < (K, V) $(: $($bound +)+)? $(, $typaram: $bound1 $(+ $bound2)*)*>, $with_capacity, $append, Map); #[cfg(feature = "alloc")] tuple_seq_as_map_impl_intern!($tyorig < (K, V) $(: $($bound +)+)? $(, $typaram: $bound1 $(+ $bound2)*)*>, $with_capacity, $append, BTreeMap); #[cfg(feature = "std")] tuple_seq_as_map_impl_intern!($tyorig < (K, V) $(: $($bound +)+)? $(, $typaram: $bound1 $(+ $bound2)*)*>, $with_capacity, $append, HashMap); } } foreach_seq!(tuple_seq_as_map_impl); // Option does not implement FromIterator directly, so we need a different implementation #[cfg(feature = "alloc")] macro_rules! tuple_seq_as_map_option_impl { ($ty:ident) => { #[allow(clippy::implicit_hasher)] impl<'de, K, KAs, V, VAs> DeserializeAs<'de, Option<(K, V)>> for $ty where KAs: DeserializeAs<'de, K>, VAs: DeserializeAs<'de, V>, { fn deserialize_as(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { struct MapVisitor { marker: PhantomData<(K, KAs, V, VAs)>, } impl<'de, K, KAs, V, VAs> Visitor<'de> for MapVisitor where KAs: DeserializeAs<'de, K>, VAs: DeserializeAs<'de, V>, { type Value = Option<(K, V)>; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("a map of size 1") } #[inline] fn visit_map(self, access: A) -> Result where A: MapAccess<'de>, { let iter = utils::MapIter::new(access); iter.map(|res| { res.map( |(k, v): (DeserializeAsWrap, DeserializeAsWrap)| { (k.into_inner(), v.into_inner()) }, ) }) .next() .transpose() } } let visitor = MapVisitor:: { marker: PhantomData, }; deserializer.deserialize_map(visitor) } } }; } #[cfg(feature = "alloc")] tuple_seq_as_map_option_impl!(BTreeMap); #[cfg(feature = "std")] tuple_seq_as_map_option_impl!(HashMap); macro_rules! tuple_seq_as_map_arr { ($ty:ident ) => { #[allow(clippy::implicit_hasher)] impl<'de, K, KAs, V, VAs, const N: usize> DeserializeAs<'de, [(K, V); N]> for $ty where KAs: DeserializeAs<'de, K>, VAs: DeserializeAs<'de, V>, { fn deserialize_as(deserializer: D) -> Result<[(K, V); N], D::Error> where D: Deserializer<'de>, { struct MapVisitor { marker: PhantomData<(K, KAs, V, VAs)>, } impl<'de, K, KAs, V, VAs, const M: usize> Visitor<'de> for MapVisitor 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(self, access: A) -> Result where A: MapAccess<'de>, { utils::array_from_iterator(utils::MapIter::new(access).map( |res: Result<(DeserializeAsWrap, DeserializeAsWrap), A::Error>| { res.map(|(k, v)| (k.into_inner(), v.into_inner())) } ), &self) } } let visitor = MapVisitor:: { marker: PhantomData, }; deserializer.deserialize_map(visitor) } } } } tuple_seq_as_map_arr!(Map); #[cfg(feature = "alloc")] tuple_seq_as_map_arr!(BTreeMap); #[cfg(feature = "std")] tuple_seq_as_map_arr!(HashMap); // endregion /////////////////////////////////////////////////////////////////////////////// // region: Conversion types which cause different serialization behavior impl<'de, T: Deserialize<'de>> DeserializeAs<'de, T> for Same { fn deserialize_as(deserializer: D) -> Result where D: Deserializer<'de>, { T::deserialize(deserializer) } } impl<'de, T> DeserializeAs<'de, T> for DisplayFromStr where T: FromStr, T::Err: Display, { fn deserialize_as(deserializer: D) -> Result where D: Deserializer<'de>, { struct Helper(PhantomData); impl Visitor<'_> for Helper where S: FromStr, ::Err: Display, { type Value = S; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("a string") } fn visit_str(self, value: &str) -> Result where E: DeError, { value.parse::().map_err(DeError::custom) } } deserializer.deserialize_str(Helper(PhantomData)) } } impl<'de, T, H, F> DeserializeAs<'de, T> for IfIsHumanReadable where H: DeserializeAs<'de, T>, F: DeserializeAs<'de, T>, { fn deserialize_as(deserializer: D) -> Result where D: Deserializer<'de>, { if deserializer.is_human_readable() { H::deserialize_as(deserializer) } else { F::deserialize_as(deserializer) } } } impl<'de, Str> DeserializeAs<'de, Option> for NoneAsEmptyString where Str: FromStr, Str::Err: Display, { fn deserialize_as(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { struct OptionStringEmptyNone(PhantomData); impl Visitor<'_> for OptionStringEmptyNone where S: FromStr, S::Err: Display, { type Value = Option; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("a string") } fn visit_str(self, value: &str) -> Result where E: DeError, { match value { "" => Ok(None), v => S::from_str(v).map(Some).map_err(DeError::custom), } } // handles the `null` case fn visit_unit(self) -> Result where E: DeError, { Ok(None) } } deserializer.deserialize_any(OptionStringEmptyNone(PhantomData)) } } #[cfg(feature = "alloc")] impl<'de, T, TAs> DeserializeAs<'de, T> for DefaultOnError where TAs: DeserializeAs<'de, T>, T: Default, { fn deserialize_as(deserializer: D) -> Result where D: Deserializer<'de>, { let is_hr = deserializer.is_human_readable(); let content: content::de::Content<'de> = match Deserialize::deserialize(deserializer) { Ok(content) => content, Err(_) => return Ok(Default::default()), }; Ok( match >::deserialize(content::de::ContentDeserializer::< D::Error, >::new(content, is_hr)) { Ok(elem) => elem.into_inner(), Err(_) => Default::default(), }, ) } } #[cfg(feature = "alloc")] impl<'de> DeserializeAs<'de, Vec> for BytesOrString { fn deserialize_as(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { struct BytesOrStringVisitor; impl<'de> Visitor<'de> for BytesOrStringVisitor { type Value = Vec; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("a list of bytes or a string") } fn visit_bytes(self, v: &[u8]) -> Result { Ok(v.to_vec()) } #[cfg(feature = "alloc")] fn visit_byte_buf(self, v: Vec) -> Result { Ok(v) } fn visit_str(self, v: &str) -> Result { Ok(v.as_bytes().to_vec()) } #[cfg(feature = "alloc")] fn visit_string(self, v: String) -> Result { Ok(v.into_bytes()) } fn visit_seq(self, seq: A) -> Result where A: SeqAccess<'de>, { utils::SeqIter::new(seq).collect() } } deserializer.deserialize_any(BytesOrStringVisitor) } } impl<'de, SEPARATOR, I, T> DeserializeAs<'de, I> for StringWithSeparator where SEPARATOR: Separator, I: FromIterator, T: FromStr, T::Err: Display, { fn deserialize_as(deserializer: D) -> Result where D: Deserializer<'de>, { struct Helper(PhantomData<(SEPARATOR, I, T)>); impl Visitor<'_> for Helper where SEPARATOR: Separator, I: FromIterator, T: FromStr, T::Err: Display, { type Value = I; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("a string") } fn visit_str(self, value: &str) -> Result where E: DeError, { if value.is_empty() { Ok(None.into_iter().collect()) } else { value .split(SEPARATOR::separator()) .map(FromStr::from_str) .collect::>() .map_err(DeError::custom) } } } deserializer.deserialize_str(Helper::(PhantomData)) } } #[cfg(feature = "std")] macro_rules! use_signed_duration { ( $main_trait:ident $internal_trait:ident => { $ty:ty; $converter:ident => $({ $format:ty, $strictness:ty => $($tbound:ident: $bound:ident $(,)?)* })* } ) => { $( impl<'de, $($tbound,)*> DeserializeAs<'de, $ty> for $main_trait<$format, $strictness> where $($tbound: $bound,)* { fn deserialize_as(deserializer: D) -> Result<$ty, D::Error> where D: Deserializer<'de>, { let dur: DurationSigned = $internal_trait::<$format, $strictness>::deserialize_as(deserializer)?; dur.$converter::() } } )* }; ( $( $main_trait:ident $internal_trait:ident, )+ => $rest:tt ) => { $( use_signed_duration!($main_trait $internal_trait => $rest); )+ }; } #[cfg(feature = "std")] use_signed_duration!( DurationSeconds DurationSeconds, DurationMilliSeconds DurationMilliSeconds, DurationMicroSeconds DurationMicroSeconds, DurationNanoSeconds DurationNanoSeconds, => { Duration; to_std_duration => {u64, Strict =>} {f64, Strict =>} {String, Strict =>} {FORMAT, Flexible => FORMAT: Format} } ); #[cfg(feature = "std")] use_signed_duration!( DurationSecondsWithFrac DurationSecondsWithFrac, DurationMilliSecondsWithFrac DurationMilliSecondsWithFrac, DurationMicroSecondsWithFrac DurationMicroSecondsWithFrac, DurationNanoSecondsWithFrac DurationNanoSecondsWithFrac, => { Duration; to_std_duration => {f64, Strict =>} {String, Strict =>} {FORMAT, Flexible => FORMAT: Format} } ); #[cfg(feature = "std")] use_signed_duration!( TimestampSeconds DurationSeconds, TimestampMilliSeconds DurationMilliSeconds, TimestampMicroSeconds DurationMicroSeconds, TimestampNanoSeconds DurationNanoSeconds, => { SystemTime; to_system_time => {i64, Strict =>} {f64, Strict =>} {String, Strict =>} {FORMAT, Flexible => FORMAT: Format} } ); #[cfg(feature = "std")] use_signed_duration!( TimestampSecondsWithFrac DurationSecondsWithFrac, TimestampMilliSecondsWithFrac DurationMilliSecondsWithFrac, TimestampMicroSecondsWithFrac DurationMicroSecondsWithFrac, TimestampNanoSecondsWithFrac DurationNanoSecondsWithFrac, => { SystemTime; to_system_time => {f64, Strict =>} {String, Strict =>} {FORMAT, Flexible => FORMAT: Format} } ); impl<'de, T, U> DeserializeAs<'de, T> for DefaultOnNull where U: DeserializeAs<'de, T>, T: Default, { fn deserialize_as(deserializer: D) -> Result where D: Deserializer<'de>, { Ok(Option::::deserialize_as(deserializer)?.unwrap_or_default()) } } impl<'de> DeserializeAs<'de, &'de [u8]> for Bytes { fn deserialize_as(deserializer: D) -> Result<&'de [u8], D::Error> where D: Deserializer<'de>, { <&'de [u8]>::deserialize(deserializer) } } // serde_bytes implementation for ByteBuf // https://github.com/serde-rs/bytes/blob/cbae606b9dc225fc094b031cc84eac9493da2058/src/bytebuf.rs#L196 // // Implements: // * visit_seq // * visit_bytes // * visit_byte_buf // * visit_str // * visit_string #[cfg(feature = "alloc")] impl<'de> DeserializeAs<'de, Vec> for Bytes { fn deserialize_as(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { struct VecVisitor; impl<'de> Visitor<'de> for VecVisitor { type Value = Vec; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("a byte array") } fn visit_seq(self, seq: A) -> Result where A: SeqAccess<'de>, { utils::SeqIter::new(seq).collect::>() } fn visit_bytes(self, v: &[u8]) -> Result where E: DeError, { Ok(v.to_vec()) } fn visit_byte_buf(self, v: Vec) -> Result where E: DeError, { Ok(v) } fn visit_str(self, v: &str) -> Result where E: DeError, { Ok(v.as_bytes().to_vec()) } fn visit_string(self, v: String) -> Result where E: DeError, { Ok(v.into_bytes()) } } deserializer.deserialize_byte_buf(VecVisitor) } } #[cfg(feature = "alloc")] impl<'de> DeserializeAs<'de, Box<[u8]>> for Bytes { fn deserialize_as(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { >>::deserialize_as(deserializer) .map(Vec::into_boxed_slice) } } // serde_bytes implementation for Cow<'a, [u8]> // https://github.com/serde-rs/bytes/blob/cbae606b9dc225fc094b031cc84eac9493da2058/src/de.rs#L77 // // Implements: // * visit_borrowed_bytes // * visit_borrowed_str // * visit_bytes // * visit_str // * visit_byte_buf // * visit_string // * visit_seq #[cfg(feature = "alloc")] impl<'de> DeserializeAs<'de, Cow<'de, [u8]>> for Bytes { fn deserialize_as(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { struct CowVisitor; impl<'de> Visitor<'de> for CowVisitor { type Value = Cow<'de, [u8]>; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("a byte array") } fn visit_borrowed_bytes(self, v: &'de [u8]) -> Result where E: DeError, { Ok(Cow::Borrowed(v)) } fn visit_borrowed_str(self, v: &'de str) -> Result where E: DeError, { Ok(Cow::Borrowed(v.as_bytes())) } fn visit_bytes(self, v: &[u8]) -> Result where E: DeError, { Ok(Cow::Owned(v.to_vec())) } fn visit_str(self, v: &str) -> Result where E: DeError, { Ok(Cow::Owned(v.as_bytes().to_vec())) } fn visit_byte_buf(self, v: Vec) -> Result where E: DeError, { Ok(Cow::Owned(v)) } fn visit_string(self, v: String) -> Result where E: DeError, { Ok(Cow::Owned(v.into_bytes())) } fn visit_seq(self, seq: V) -> Result where V: SeqAccess<'de>, { Ok(Cow::Owned( utils::SeqIter::new(seq).collect::>()?, )) } } deserializer.deserialize_bytes(CowVisitor) } } impl<'de, const N: usize> DeserializeAs<'de, [u8; N]> for Bytes { fn deserialize_as(deserializer: D) -> Result<[u8; N], D::Error> where D: Deserializer<'de>, { struct ArrayVisitor; impl<'de, const M: usize> Visitor<'de> for ArrayVisitor { 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(self, seq: A) -> Result where A: SeqAccess<'de>, { utils::array_from_iterator(utils::SeqIter::new(seq), &self) } fn visit_bytes(self, v: &[u8]) -> Result where E: DeError, { v.try_into() .map_err(|_| DeError::invalid_length(v.len(), &self)) } fn visit_str(self, v: &str) -> Result where E: DeError, { v.as_bytes() .try_into() .map_err(|_| DeError::invalid_length(v.len(), &self)) } } deserializer.deserialize_bytes(ArrayVisitor::) } } impl<'de, const N: usize> DeserializeAs<'de, &'de [u8; N]> for Bytes { fn deserialize_as(deserializer: D) -> Result<&'de [u8; N], D::Error> where D: Deserializer<'de>, { struct ArrayVisitor; impl<'de, const M: usize> Visitor<'de> for ArrayVisitor { 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(self, v: &'de [u8]) -> Result where E: DeError, { v.try_into() .map_err(|_| DeError::invalid_length(v.len(), &self)) } fn visit_borrowed_str(self, v: &'de str) -> Result where E: DeError, { v.as_bytes() .try_into() .map_err(|_| DeError::invalid_length(v.len(), &self)) } } deserializer.deserialize_bytes(ArrayVisitor::) } } #[cfg(feature = "alloc")] impl<'de, const N: usize> DeserializeAs<'de, Cow<'de, [u8; N]>> for Bytes { fn deserialize_as(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { struct CowVisitor; impl<'de, const M: usize> Visitor<'de> for CowVisitor { 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(self, v: &'de [u8]) -> Result where E: DeError, { Ok(Cow::Borrowed( v.try_into() .map_err(|_| DeError::invalid_length(v.len(), &self))?, )) } fn visit_borrowed_str(self, v: &'de str) -> Result where E: DeError, { Ok(Cow::Borrowed( v.as_bytes() .try_into() .map_err(|_| DeError::invalid_length(v.len(), &self))?, )) } fn visit_bytes(self, v: &[u8]) -> Result where E: DeError, { Ok(Cow::Owned( v.to_vec() .try_into() .map_err(|_| DeError::invalid_length(v.len(), &self))?, )) } fn visit_str(self, v: &str) -> Result where E: DeError, { Ok(Cow::Owned( v.as_bytes() .to_vec() .try_into() .map_err(|_| DeError::invalid_length(v.len(), &self))?, )) } fn visit_byte_buf(self, v: Vec) -> Result where E: DeError, { let len = v.len(); Ok(Cow::Owned( v.try_into() .map_err(|_| DeError::invalid_length(len, &self))?, )) } fn visit_string(self, v: String) -> Result where E: DeError, { let len = v.len(); Ok(Cow::Owned( v.into_bytes() .try_into() .map_err(|_| DeError::invalid_length(len, &self))?, )) } fn visit_seq(self, seq: V) -> Result where V: SeqAccess<'de>, { Ok(Cow::Owned(utils::array_from_iterator( utils::SeqIter::new(seq), &self, )?)) } } deserializer.deserialize_bytes(CowVisitor) } } #[cfg(feature = "alloc")] impl<'de, const N: usize> DeserializeAs<'de, Box<[u8; N]>> for Bytes { fn deserialize_as(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { Bytes::deserialize_as(deserializer).map(Box::new) } } #[cfg(feature = "alloc")] impl<'de, T, TAs, FORMAT> DeserializeAs<'de, Vec> for OneOrMany where TAs: DeserializeAs<'de, T>, FORMAT: Format, { fn deserialize_as(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { let is_hr = deserializer.is_human_readable(); let content: content::de::Content<'de> = Deserialize::deserialize(deserializer)?; let one_err: D::Error = match >::deserialize( content::de::ContentRefDeserializer::new(&content, is_hr), ) { Ok(one) => return Ok(alloc::vec![one.into_inner()]), Err(err) => err, }; let many_err: D::Error = match , Vec>>::deserialize( content::de::ContentDeserializer::new(content, is_hr), ) { Ok(many) => return Ok(many.into_inner()), Err(err) => err, }; Err(DeError::custom(format_args!( "OneOrMany could not deserialize any variant:\n One: {}\n Many: {}", one_err, many_err ))) } } #[cfg(feature = "alloc")] impl<'de, T, TAs1> DeserializeAs<'de, T> for PickFirst<(TAs1,)> where TAs1: DeserializeAs<'de, T>, { fn deserialize_as(deserializer: D) -> Result where D: Deserializer<'de>, { Ok(DeserializeAsWrap::::deserialize(deserializer)?.into_inner()) } } #[cfg(feature = "alloc")] impl<'de, T, TAs1, TAs2> DeserializeAs<'de, T> for PickFirst<(TAs1, TAs2)> where TAs1: DeserializeAs<'de, T>, TAs2: DeserializeAs<'de, T>, { fn deserialize_as(deserializer: D) -> Result where D: Deserializer<'de>, { let is_hr = deserializer.is_human_readable(); let content: content::de::Content<'de> = Deserialize::deserialize(deserializer)?; let first_err: D::Error = match >::deserialize( content::de::ContentRefDeserializer::new(&content, is_hr), ) { Ok(first) => return Ok(first.into_inner()), Err(err) => err, }; let second_err: D::Error = match >::deserialize( content::de::ContentDeserializer::new(content, is_hr), ) { Ok(second) => return Ok(second.into_inner()), Err(err) => err, }; Err(DeError::custom(format_args!( "PickFirst could not deserialize any variant:\n First: {}\n Second: {}", first_err, second_err ))) } } #[cfg(feature = "alloc")] impl<'de, T, TAs1, TAs2, TAs3> DeserializeAs<'de, T> for PickFirst<(TAs1, TAs2, TAs3)> where TAs1: DeserializeAs<'de, T>, TAs2: DeserializeAs<'de, T>, TAs3: DeserializeAs<'de, T>, { fn deserialize_as(deserializer: D) -> Result where D: Deserializer<'de>, { let is_hr = deserializer.is_human_readable(); let content: content::de::Content<'de> = Deserialize::deserialize(deserializer)?; let first_err: D::Error = match >::deserialize( content::de::ContentRefDeserializer::new(&content, is_hr), ) { Ok(first) => return Ok(first.into_inner()), Err(err) => err, }; let second_err: D::Error = match >::deserialize( content::de::ContentRefDeserializer::new(&content, is_hr), ) { Ok(second) => return Ok(second.into_inner()), Err(err) => err, }; let third_err: D::Error = match >::deserialize( content::de::ContentDeserializer::new(content, is_hr), ) { Ok(third) => return Ok(third.into_inner()), Err(err) => err, }; Err(DeError::custom(format_args!( "PickFirst could not deserialize any variant:\n First: {}\n Second: {}\n Third: {}", first_err, second_err, third_err, ))) } } #[cfg(feature = "alloc")] impl<'de, T, TAs1, TAs2, TAs3, TAs4> DeserializeAs<'de, T> for PickFirst<(TAs1, TAs2, TAs3, TAs4)> where TAs1: DeserializeAs<'de, T>, TAs2: DeserializeAs<'de, T>, TAs3: DeserializeAs<'de, T>, TAs4: DeserializeAs<'de, T>, { fn deserialize_as(deserializer: D) -> Result where D: Deserializer<'de>, { let is_hr = deserializer.is_human_readable(); let content: content::de::Content<'de> = Deserialize::deserialize(deserializer)?; let first_err: D::Error = match >::deserialize( content::de::ContentRefDeserializer::new(&content, is_hr), ) { Ok(first) => return Ok(first.into_inner()), Err(err) => err, }; let second_err: D::Error = match >::deserialize( content::de::ContentRefDeserializer::new(&content, is_hr), ) { Ok(second) => return Ok(second.into_inner()), Err(err) => err, }; let third_err: D::Error = match >::deserialize( content::de::ContentRefDeserializer::new(&content, is_hr), ) { Ok(third) => return Ok(third.into_inner()), Err(err) => err, }; let fourth_err: D::Error = match >::deserialize( content::de::ContentDeserializer::new(content, is_hr), ) { Ok(fourth) => return Ok(fourth.into_inner()), Err(err) => err, }; Err(DeError::custom(format_args!( "PickFirst could not deserialize any variant:\n First: {}\n Second: {}\n Third: {}\n Fourth: {}", first_err, second_err, third_err, fourth_err, ))) } } impl<'de, T, U> DeserializeAs<'de, T> for FromInto where U: Into, U: Deserialize<'de>, { fn deserialize_as(deserializer: D) -> Result where D: Deserializer<'de>, { Ok(U::deserialize(deserializer)?.into()) } } impl<'de, T, U> DeserializeAs<'de, T> for TryFromInto where U: TryInto, >::Error: Display, U: Deserialize<'de>, { fn deserialize_as(deserializer: D) -> Result where D: Deserializer<'de>, { U::deserialize(deserializer)? .try_into() .map_err(DeError::custom) } } impl<'de, T, U> DeserializeAs<'de, T> for FromIntoRef where U: Into, U: Deserialize<'de>, { fn deserialize_as(deserializer: D) -> Result where D: Deserializer<'de>, { Ok(U::deserialize(deserializer)?.into()) } } impl<'de, T, U> DeserializeAs<'de, T> for TryFromIntoRef where U: TryInto, >::Error: Display, U: Deserialize<'de>, { fn deserialize_as(deserializer: D) -> Result where D: Deserializer<'de>, { U::deserialize(deserializer)? .try_into() .map_err(DeError::custom) } } #[cfg(feature = "alloc")] impl<'de> DeserializeAs<'de, Cow<'de, str>> for BorrowCow { fn deserialize_as(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { struct CowVisitor; impl<'de> Visitor<'de> for CowVisitor { type Value = Cow<'de, str>; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("an optionally borrowed string") } fn visit_borrowed_str(self, v: &'de str) -> Result where E: DeError, { Ok(Cow::Borrowed(v)) } fn visit_str(self, v: &str) -> Result where E: DeError, { Ok(Cow::Owned(v.to_owned())) } fn visit_string(self, v: String) -> Result where E: DeError, { Ok(Cow::Owned(v)) } } deserializer.deserialize_string(CowVisitor) } } #[cfg(feature = "alloc")] impl<'de> DeserializeAs<'de, Cow<'de, [u8]>> for BorrowCow { fn deserialize_as(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { Bytes::deserialize_as(deserializer) } } #[cfg(feature = "alloc")] impl<'de, const N: usize> DeserializeAs<'de, Cow<'de, [u8; N]>> for BorrowCow { fn deserialize_as(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { Bytes::deserialize_as(deserializer) } } impl<'de> DeserializeAs<'de, bool> for BoolFromInt { fn deserialize_as(deserializer: D) -> Result where D: Deserializer<'de>, { struct U8Visitor; impl Visitor<'_> for U8Visitor { type Value = bool; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("an integer 0 or 1") } fn visit_u8(self, v: u8) -> Result where E: DeError, { match v { 0 => Ok(false), 1 => Ok(true), unexp => Err(DeError::invalid_value( Unexpected::Unsigned(u64::from(unexp)), &"0 or 1", )), } } fn visit_i8(self, v: i8) -> Result where E: DeError, { match v { 0 => Ok(false), 1 => Ok(true), unexp => Err(DeError::invalid_value( Unexpected::Signed(i64::from(unexp)), &"0 or 1", )), } } fn visit_u64(self, v: u64) -> Result where E: DeError, { match v { 0 => Ok(false), 1 => Ok(true), unexp => Err(DeError::invalid_value( Unexpected::Unsigned(unexp), &"0 or 1", )), } } fn visit_i64(self, v: i64) -> Result where E: DeError, { match v { 0 => Ok(false), 1 => Ok(true), unexp => Err(DeError::invalid_value(Unexpected::Signed(unexp), &"0 or 1")), } } fn visit_u128(self, v: u128) -> Result where E: DeError, { match v { 0 => Ok(false), 1 => Ok(true), unexp => { let mut buf: [u8; 58] = [0u8; 58]; Err(DeError::invalid_value( crate::utils::get_unexpected_u128(unexp, &mut buf), &self, )) } } } fn visit_i128(self, v: i128) -> Result where E: DeError, { match v { 0 => Ok(false), 1 => Ok(true), unexp => { let mut buf: [u8; 58] = [0u8; 58]; Err(DeError::invalid_value( crate::utils::get_unexpected_i128(unexp, &mut buf), &"0 or 1", )) } } } } deserializer.deserialize_u8(U8Visitor) } } impl<'de> DeserializeAs<'de, bool> for BoolFromInt { fn deserialize_as(deserializer: D) -> Result where D: Deserializer<'de>, { struct U8Visitor; impl Visitor<'_> for U8Visitor { type Value = bool; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("an integer") } fn visit_u8(self, v: u8) -> Result where E: DeError, { Ok(v != 0) } fn visit_i8(self, v: i8) -> Result where E: DeError, { Ok(v != 0) } fn visit_u64(self, v: u64) -> Result where E: DeError, { Ok(v != 0) } fn visit_i64(self, v: i64) -> Result where E: DeError, { Ok(v != 0) } fn visit_u128(self, v: u128) -> Result where E: DeError, { Ok(v != 0) } fn visit_i128(self, v: i128) -> Result where E: DeError, { Ok(v != 0) } } deserializer.deserialize_u8(U8Visitor) } } // endregion serde_with-3.12.0/src/de/mod.rs000064400000000000000000000132641046102023000143560ustar 00000000000000//! Module for [`DeserializeAs`][] implementations //! //! The module contains the [`DeserializeAs`][] trait and helper code. //! Additionally, it contains implementations of [`DeserializeAs`][] for types defined in the Rust Standard Library or this crate. //! //! You can find more details on how to implement this trait for your types in the documentation of the [`DeserializeAs`][] trait and details about the usage in the [user guide][]. //! //! [user guide]: crate::guide #[cfg(feature = "alloc")] mod duplicates; mod impls; #[cfg(feature = "alloc")] mod skip_error; 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 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`] /// /// The trait is only required for container-like types or types implementing specific conversion functions. /// Container-like types are [`Vec`], [`BTreeMap`], but also [`Option`] and [`Box`]. /// Conversion types deserialize into a different Rust type. /// For example, [`DisplayFromStr`] uses the [`FromStr`] trait after deserializing a string and [`DurationSeconds`] creates a [`Duration`] from either String or integer values. /// /// This code shows how to implement [`Deserialize`] for [`Box`]: /// /// ```rust,ignore /// impl<'de, T: Deserialize<'de>> Deserialize<'de> for Box { /// fn deserialize(deserializer: D) -> Result /// where /// D: Deserializer<'de>, /// { /// Ok(Box::new(Deserialize::deserialize(deserializer)?)) /// } /// } /// ``` /// /// and this code shows how to do the same using [`DeserializeAs`][]: /// /// ```rust,ignore /// impl<'de, T, U> DeserializeAs<'de, Box> for Box /// where /// U: DeserializeAs<'de, T>, /// { /// fn deserialize_as(deserializer: D) -> Result, D::Error> /// where /// D: Deserializer<'de>, /// { /// Ok(Box::new( /// DeserializeAsWrap::::deserialize(deserializer)?.into_inner(), /// )) /// } /// } /// ``` /// /// It uses two type parameters, `T` and `U` instead of only one and performs the deserialization step using the `DeserializeAsWrap` type. /// The `T` type is the type on the Rust side after deserialization, whereas the `U` type determines how the value will be deserialized. /// These two changes are usually enough to make a container type implement [`DeserializeAs`][]. /// /// /// [`DeserializeAsWrap`] is a piece of glue code which turns [`DeserializeAs`] into a serde compatible datatype, by converting all calls to `deserialize` into `deserialize_as`. /// This allows us to implement [`DeserializeAs`] such that it can be applied recursively throughout the whole data structure. /// This is mostly important for container types, such as `Vec` or `BTreeMap`. /// In a `BTreeMap` this allows us to specify two different serialization behaviors, one for key and one for value, using the [`DeserializeAs`] trait. /// /// ## Implementing a converter Type /// /// This shows a simplified implementation for [`DisplayFromStr`]. /// /// ```rust /// # #[cfg(feature = "macros")] { /// # use serde::Deserialize; /// # use serde::de::Error; /// # use serde_with::{serde_as, DeserializeAs}; /// # use std::str::FromStr; /// # use std::fmt::Display; /// struct DisplayFromStr; /// /// impl<'de, T> DeserializeAs<'de, T> for DisplayFromStr /// where /// T: FromStr, /// T::Err: Display, /// { /// fn deserialize_as(deserializer: D) -> Result /// where /// D: serde::Deserializer<'de>, /// { /// let s = String::deserialize(deserializer).map_err(Error::custom)?; /// s.parse().map_err(Error::custom) /// } /// } /// # /// # #[serde_as] /// # #[derive(serde::Deserialize)] /// # struct S (#[serde_as(as = "DisplayFromStr")] bool); /// # /// # assert!(!serde_json::from_str::(r#""false""#).unwrap().0); /// # } /// ``` /// [`Box`]: std::boxed::Box /// [`BTreeMap`]: std::collections::BTreeMap /// [`Duration`]: std::time::Duration /// [`FromStr`]: std::str::FromStr /// [`Vec`]: std::vec::Vec /// [impl-deserialize]: https://serde.rs/impl-deserialize.html pub trait DeserializeAs<'de, T>: Sized { /// Deserialize this value from the given Serde deserializer. fn deserialize_as(deserializer: D) -> Result where D: Deserializer<'de>; } /// Helper type to implement [`DeserializeAs`] for container-like types. pub struct DeserializeAsWrap { value: T, marker: PhantomData, } impl DeserializeAsWrap { /// Return the inner value of type `T`. pub fn into_inner(self) -> T { self.value } } impl<'de, T, U> Deserialize<'de> for DeserializeAsWrap where U: DeserializeAs<'de, T>, { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { U::deserialize_as(deserializer).map(|value| Self { value, marker: PhantomData, }) } } impl As { /// 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 where T: DeserializeAs<'de, I>, D: Deserializer<'de>, { T::deserialize_as(deserializer) } } serde_with-3.12.0/src/de/skip_error.rs000064400000000000000000000106411046102023000157520ustar 00000000000000use super::impls::macros::foreach_map; use crate::prelude::*; #[cfg(feature = "hashbrown_0_14")] use hashbrown_0_14::HashMap as HashbrownMap014; #[cfg(feature = "hashbrown_0_15")] use hashbrown_0_15::HashMap as HashbrownMap015; #[cfg(feature = "indexmap_1")] use indexmap_1::IndexMap; #[cfg(feature = "indexmap_2")] use indexmap_2::IndexMap as IndexMap2; enum GoodOrError { Good(T), // Only here to consume the TAs generic Error(PhantomData), } impl<'de, T, TAs> Deserialize<'de> for GoodOrError where TAs: DeserializeAs<'de, T>, { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { let is_hr = deserializer.is_human_readable(); let content: content::de::Content<'de> = Deserialize::deserialize(deserializer)?; Ok( match >::deserialize(content::de::ContentDeserializer::< D::Error, >::new(content, is_hr)) { Ok(elem) => GoodOrError::Good(elem.into_inner()), Err(_) => GoodOrError::Error(PhantomData), }, ) } } impl<'de, T, U> DeserializeAs<'de, Vec> for VecSkipError where U: DeserializeAs<'de, T>, { fn deserialize_as(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { struct SeqVisitor { marker: PhantomData, marker2: PhantomData, } impl<'de, T, TAs> Visitor<'de> for SeqVisitor where TAs: DeserializeAs<'de, T>, { type Value = Vec; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("a sequence") } fn visit_seq(self, seq: A) -> Result where A: SeqAccess<'de>, { utils::SeqIter::new(seq) .filter_map(|res: Result, A::Error>| match res { Ok(GoodOrError::Good(value)) => Some(Ok(value)), Ok(GoodOrError::Error(_)) => None, Err(err) => Some(Err(err)), }) .collect() } } let visitor = SeqVisitor:: { marker: PhantomData, marker2: PhantomData, }; deserializer.deserialize_seq(visitor) } } struct MapSkipErrorVisitor(PhantomData<(MAP, K, KAs, V, VAs)>); impl<'de, MAP, K, KAs, V, VAs> Visitor<'de> for MapSkipErrorVisitor where MAP: FromIterator<(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(self, access: A) -> Result where A: MapAccess<'de>, { type KVPair = (GoodOrError, GoodOrError); utils::MapIter::new(access) .filter_map(|res: Result, A::Error>| match res { Ok((GoodOrError::Good(key), GoodOrError::Good(value))) => Some(Ok((key, value))), Ok(_) => None, Err(err) => Some(Err(err)), }) .collect() } } #[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> for MapSkipError where KAs: DeserializeAs<'de, K>, VAs: DeserializeAs<'de, V>, $(K: $kbound1 $(+ $kbound2)*,)? $($typaram: $bound1 $(+ $bound2)*),* { fn deserialize_as(deserializer: D) -> Result<$ty, D::Error> where D: Deserializer<'de>, { deserializer.deserialize_map(MapSkipErrorVisitor::< $ty, K, KAs, V, VAs, >(PhantomData)) } } }; } foreach_map!(map_impl); serde_with-3.12.0/src/duplicate_key_impls/error_on_duplicate.rs000064400000000000000000000136621046102023000231160ustar 00000000000000use crate::prelude::*; pub trait PreventDuplicateInsertsSet { fn new(size_hint: Option) -> Self; /// Return true if the insert was successful and the value did not exist in the set fn insert(&mut self, value: T) -> bool; } pub trait PreventDuplicateInsertsMap { fn new(size_hint: Option) -> Self; /// Return true if the insert was successful and the key did not exist in the map fn insert(&mut self, key: K, value: V) -> bool; } #[cfg(feature = "std")] impl PreventDuplicateInsertsSet for HashSet where T: Eq + Hash, S: BuildHasher + Default, { #[inline] fn new(size_hint: Option) -> 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) -> bool { self.insert(value) } } #[cfg(feature = "hashbrown_0_14")] impl PreventDuplicateInsertsSet for hashbrown_0_14::HashSet where T: Eq + Hash, S: BuildHasher + Default, { #[inline] fn new(size_hint: Option) -> 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) -> bool { self.insert(value) } } #[cfg(feature = "hashbrown_0_15")] impl PreventDuplicateInsertsSet for hashbrown_0_15::HashSet where T: Eq + Hash, S: BuildHasher + Default, { #[inline] fn new(size_hint: Option) -> 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) -> bool { self.insert(value) } } #[cfg(feature = "indexmap_1")] impl PreventDuplicateInsertsSet for indexmap_1::IndexSet where T: Eq + Hash, S: BuildHasher + Default, { #[inline] fn new(size_hint: Option) -> 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) -> bool { self.insert(value) } } #[cfg(feature = "indexmap_2")] impl PreventDuplicateInsertsSet for indexmap_2::IndexSet where T: Eq + Hash, S: BuildHasher + Default, { #[inline] fn new(size_hint: Option) -> 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) -> bool { self.insert(value) } } impl PreventDuplicateInsertsSet for BTreeSet where T: Ord, { #[inline] fn new(_size_hint: Option) -> Self { Self::new() } #[inline] fn insert(&mut self, value: T) -> bool { self.insert(value) } } #[cfg(feature = "std")] impl PreventDuplicateInsertsMap for HashMap where K: Eq + Hash, S: BuildHasher + Default, { #[inline] fn new(size_hint: Option) -> 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, key: K, value: V) -> bool { self.insert(key, value).is_none() } } #[cfg(feature = "hashbrown_0_14")] impl PreventDuplicateInsertsMap for hashbrown_0_14::HashMap where K: Eq + Hash, S: BuildHasher + Default, { #[inline] fn new(size_hint: Option) -> 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, key: K, value: V) -> bool { self.insert(key, value).is_none() } } #[cfg(feature = "hashbrown_0_15")] impl PreventDuplicateInsertsMap for hashbrown_0_15::HashMap where K: Eq + Hash, S: BuildHasher + Default, { #[inline] fn new(size_hint: Option) -> 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, key: K, value: V) -> bool { self.insert(key, value).is_none() } } #[cfg(feature = "indexmap_1")] impl PreventDuplicateInsertsMap for indexmap_1::IndexMap where K: Eq + Hash, S: BuildHasher + Default, { #[inline] fn new(size_hint: Option) -> 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, key: K, value: V) -> bool { self.insert(key, value).is_none() } } #[cfg(feature = "indexmap_2")] impl PreventDuplicateInsertsMap for indexmap_2::IndexMap where K: Eq + Hash, S: BuildHasher + Default, { #[inline] fn new(size_hint: Option) -> 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, key: K, value: V) -> bool { self.insert(key, value).is_none() } } impl PreventDuplicateInsertsMap for BTreeMap where K: Ord, { #[inline] fn new(_size_hint: Option) -> Self { Self::new() } #[inline] fn insert(&mut self, key: K, value: V) -> bool { self.insert(key, value).is_none() } } serde_with-3.12.0/src/duplicate_key_impls/first_value_wins.rs000064400000000000000000000106271046102023000226200ustar 00000000000000use crate::prelude::*; pub trait DuplicateInsertsFirstWinsMap { fn new(size_hint: Option) -> Self; /// Insert the value into the map, if there is not already an existing value fn insert(&mut self, key: K, value: V); } #[cfg(feature = "std")] impl DuplicateInsertsFirstWinsMap for HashMap where K: Eq + Hash, S: BuildHasher + Default, { #[inline] fn new(size_hint: Option) -> 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, key: K, value: V) { use std::collections::hash_map::Entry; match self.entry(key) { // we want to keep the first value, so do nothing Entry::Occupied(_) => {} Entry::Vacant(vacant) => { vacant.insert(value); } } } } #[cfg(feature = "hashbrown_0_14")] impl DuplicateInsertsFirstWinsMap for hashbrown_0_14::HashMap where K: Eq + Hash, S: BuildHasher + Default, { #[inline] fn new(size_hint: Option) -> 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, key: K, value: V) { use hashbrown_0_14::hash_map::Entry; match self.entry(key) { // we want to keep the first value, so do nothing Entry::Occupied(_) => {} Entry::Vacant(vacant) => { vacant.insert(value); } } } } #[cfg(feature = "hashbrown_0_15")] impl DuplicateInsertsFirstWinsMap for hashbrown_0_15::HashMap where K: Eq + Hash, S: BuildHasher + Default, { #[inline] fn new(size_hint: Option) -> 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, key: K, value: V) { use hashbrown_0_15::hash_map::Entry; match self.entry(key) { // we want to keep the first value, so do nothing Entry::Occupied(_) => {} Entry::Vacant(vacant) => { vacant.insert(value); } } } } #[cfg(feature = "indexmap_1")] impl DuplicateInsertsFirstWinsMap for indexmap_1::IndexMap where K: Eq + Hash, S: BuildHasher + Default, { #[inline] fn new(size_hint: Option) -> 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, key: K, value: V) { use indexmap_1::map::Entry; match self.entry(key) { // we want to keep the first value, so do nothing Entry::Occupied(_) => {} Entry::Vacant(vacant) => { vacant.insert(value); } } } } #[cfg(feature = "indexmap_2")] impl DuplicateInsertsFirstWinsMap for indexmap_2::IndexMap where K: Eq + Hash, S: BuildHasher + Default, { #[inline] fn new(size_hint: Option) -> 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, key: K, value: V) { use indexmap_2::map::Entry; match self.entry(key) { // we want to keep the first value, so do nothing Entry::Occupied(_) => {} Entry::Vacant(vacant) => { vacant.insert(value); } } } } impl DuplicateInsertsFirstWinsMap for BTreeMap where K: Ord, { #[inline] fn new(_size_hint: Option) -> Self { Self::new() } #[inline] fn insert(&mut self, key: K, value: V) { use alloc::collections::btree_map::Entry; match self.entry(key) { // we want to keep the first value, so do nothing Entry::Occupied(_) => {} Entry::Vacant(vacant) => { vacant.insert(value); } } } } serde_with-3.12.0/src/duplicate_key_impls/last_value_wins.rs000064400000000000000000000061301046102023000224260ustar 00000000000000use crate::prelude::*; pub trait DuplicateInsertsLastWinsSet { fn new(size_hint: Option) -> Self; /// Insert or replace the existing value fn replace(&mut self, value: T); } #[cfg(feature = "std")] impl DuplicateInsertsLastWinsSet for HashSet where T: Eq + Hash, S: BuildHasher + Default, { #[inline] fn new(size_hint: Option) -> Self { match size_hint { Some(size) => Self::with_capacity_and_hasher(size, S::default()), None => Self::with_hasher(S::default()), } } #[inline] fn replace(&mut self, value: T) { // Hashset already fulfils the contract self.replace(value); } } #[cfg(feature = "hashbrown_0_14")] impl DuplicateInsertsLastWinsSet for hashbrown_0_14::HashSet where T: Eq + Hash, S: BuildHasher + Default, { #[inline] fn new(size_hint: Option) -> Self { match size_hint { Some(size) => Self::with_capacity_and_hasher(size, S::default()), None => Self::with_hasher(S::default()), } } #[inline] fn replace(&mut self, value: T) { // Hashset already fulfils the contract self.replace(value); } } #[cfg(feature = "hashbrown_0_15")] impl DuplicateInsertsLastWinsSet for hashbrown_0_15::HashSet where T: Eq + Hash, S: BuildHasher + Default, { #[inline] fn new(size_hint: Option) -> Self { match size_hint { Some(size) => Self::with_capacity_and_hasher(size, S::default()), None => Self::with_hasher(S::default()), } } #[inline] fn replace(&mut self, value: T) { // Hashset already fulfils the contract self.replace(value); } } #[cfg(feature = "indexmap_1")] impl DuplicateInsertsLastWinsSet for indexmap_1::IndexSet where T: Eq + Hash, S: BuildHasher + Default, { #[inline] fn new(size_hint: Option) -> Self { match size_hint { Some(size) => Self::with_capacity_and_hasher(size, S::default()), None => Self::with_hasher(S::default()), } } #[inline] fn replace(&mut self, value: T) { // Hashset already fulfils the contract self.replace(value); } } #[cfg(feature = "indexmap_2")] impl DuplicateInsertsLastWinsSet for indexmap_2::IndexSet where T: Eq + Hash, S: BuildHasher + Default, { #[inline] fn new(size_hint: Option) -> Self { match size_hint { Some(size) => Self::with_capacity_and_hasher(size, S::default()), None => Self::with_hasher(S::default()), } } #[inline] fn replace(&mut self, value: T) { // Hashset already fulfils the contract self.replace(value); } } impl DuplicateInsertsLastWinsSet for BTreeSet where T: Ord, { #[inline] fn new(_size_hint: Option) -> Self { Self::new() } #[inline] fn replace(&mut self, value: T) { // BTreeSet already fulfils the contract self.replace(value); } } serde_with-3.12.0/src/duplicate_key_impls/mod.rs000064400000000000000000000004171046102023000200100ustar 00000000000000mod error_on_duplicate; mod first_value_wins; mod last_value_wins; pub use self::{ error_on_duplicate::{PreventDuplicateInsertsMap, PreventDuplicateInsertsSet}, first_value_wins::DuplicateInsertsFirstWinsMap, last_value_wins::DuplicateInsertsLastWinsSet, }; serde_with-3.12.0/src/enum_map.rs000064400000000000000000000633141046102023000150110ustar 00000000000000use crate::{ content::ser::{Content, ContentSerializer}, prelude::*, }; /// Represent a list of enum values as a map. /// /// This **only works** if the enum uses the default *externally tagged* representation. /// Other enum representations are not supported. /// /// serde data formats often represent *externally tagged* enums as maps with a single key. /// The key is the enum variant name, and the value is the variant value. /// Sometimes a map with multiple keys should be treated like a list of enum values. /// /// # Examples /// /// ## JSON Map with multiple keys /// /// ```rust /// # #[cfg(feature = "macros")] { /// # use serde::{Deserialize, Serialize}; /// use serde_with::{serde_as, EnumMap}; /// /// # #[derive(Debug, Clone, PartialEq, Eq)] /// #[derive(Serialize, Deserialize)] /// enum EnumValue { /// Int(i32), /// String(String), /// Unit, /// Tuple(i32, String, bool), /// Struct { /// a: i32, /// b: String, /// c: bool, /// }, /// } /// /// #[serde_as] /// # #[derive(Debug, Clone, PartialEq, Eq)] /// #[derive(Serialize, Deserialize)] /// struct VecEnumValues ( /// #[serde_as(as = "EnumMap")] /// Vec, /// ); /// /// // --- /// /// // This will serialize this list of values /// let values = VecEnumValues(vec![ /// EnumValue::Int(123), /// EnumValue::String("FooBar".to_string()), /// EnumValue::Int(456), /// EnumValue::String("XXX".to_string()), /// EnumValue::Unit, /// EnumValue::Tuple(1, "Middle".to_string(), false), /// EnumValue::Struct { /// a: 666, /// b: "BBB".to_string(), /// c: true, /// }, /// ]); /// /// // into this JSON map /// // Duplicate keys are emitted for identical enum variants. /// let expected = /// r#"{ /// "Int": 123, /// "String": "FooBar", /// "Int": 456, /// "String": "XXX", /// "Unit": null, /// "Tuple": [ /// 1, /// "Middle", /// false /// ], /// "Struct": { /// "a": 666, /// "b": "BBB", /// "c": true /// } /// }"#; /// /// // Both serialization and deserialization work flawlessly. /// let serialized = serde_json::to_string_pretty(&values).unwrap(); /// assert_eq!(expected, serialized); /// let deserialized: VecEnumValues = serde_json::from_str(&serialized).unwrap(); /// assert_eq!(values, deserialized); /// # } /// ``` /// /// ## XML structure with varying keys /// /// With `serde_xml_rs` tuple and struct variants are not supported since they fail to roundtrip. /// The enum may have such variants as long as they are not serialized or deserialized. /// /// ``` /// # #[cfg(feature = "macros")] { /// # use serde::{Deserialize, Serialize}; /// use serde_with::{serde_as, EnumMap}; /// /// # #[derive(Debug, Clone, PartialEq, Eq)] /// #[derive(Serialize, Deserialize)] /// enum EnumValue { /// Int(i32), /// String(String), /// Unit, /// } /// /// #[serde_as] /// # #[derive(Debug, Clone, PartialEq, Eq)] /// #[derive(Serialize, Deserialize)] /// struct VecEnumValues { /// #[serde_as(as = "EnumMap")] /// vec: Vec, /// } /// /// // --- /// /// // This will serialize this list of values /// let values = VecEnumValues { /// vec: vec![ /// EnumValue::Int(123), /// EnumValue::String("FooBar".to_string()), /// EnumValue::Int(456), /// EnumValue::String("XXX".to_string()), /// EnumValue::Unit, /// ], /// }; /// /// // into this XML document /// // Duplicate keys are emitted for identical enum variants. /// let expected = r#" /// /// /// /// 123 /// FooBar /// 456 /// XXX /// /// /// "# /// // Remove whitespace /// .replace(" ", "") /// .replace('\n', ""); /// /// // Both serialization and deserialization work flawlessly. /// let serialized = serde_xml_rs::to_string(&values).unwrap(); /// assert_eq!(expected, serialized); /// let deserialized: VecEnumValues = serde_xml_rs::from_str(&serialized).unwrap(); /// assert_eq!(values, deserialized); /// # } /// ``` pub struct EnumMap; impl SerializeAs> for EnumMap where T: Serialize, { fn serialize_as(source: &Vec, serializer: S) -> Result where S: Serializer, { source.serialize(SeqAsMapSerializer(serializer)) } } impl<'de, T> DeserializeAs<'de, Vec> for EnumMap where T: Deserialize<'de>, { fn deserialize_as(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { struct EnumMapVisitor { is_human_readable: bool, phantom: PhantomData, } impl<'de, T> Visitor<'de> for EnumMapVisitor where T: Deserialize<'de>, { type Value = Vec; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("a map of enum values") } fn visit_map>(self, map: A) -> Result { Vec::deserialize(SeqDeserializer { delegate: map, is_human_readable: self.is_human_readable, }) } } let is_human_readable = deserializer.is_human_readable(); deserializer.deserialize_map(EnumMapVisitor { is_human_readable, phantom: PhantomData, }) } } static END_OF_MAP_IDENTIFIER: &str = "__PRIVATE_END_OF_MAP_MARKER__"; // Serialization code below here /// Convert a sequence to a map during serialization. /// /// Only `serialize_seq` is implemented and forwarded to `serialize_map` on the inner `Serializer`. /// The elements are serialized with [`SerializeSeqElement`]. struct SeqAsMapSerializer(S); impl Serializer for SeqAsMapSerializer where S: Serializer, { type Ok = S::Ok; type Error = S::Error; type SerializeSeq = SerializeSeqElement; type SerializeTuple = Impossible; type SerializeTupleStruct = Impossible; type SerializeTupleVariant = Impossible; type SerializeMap = Impossible; type SerializeStruct = Impossible; type SerializeStructVariant = Impossible; fn is_human_readable(&self) -> bool { self.0.is_human_readable() } fn serialize_bool(self, _v: bool) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_i8(self, _v: i8) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_i16(self, _v: i16) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_i32(self, _v: i32) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_i64(self, _v: i64) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_i128(self, _v: i128) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_u8(self, _v: u8) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_u16(self, _v: u16) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_u32(self, _v: u32) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_u64(self, _v: u64) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_u128(self, _v: u128) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_f32(self, _v: f32) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_f64(self, _v: f64) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_char(self, _v: char) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_str(self, _v: &str) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_bytes(self, _v: &[u8]) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_none(self) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_some(self, _value: &T) -> Result where T: Serialize + ?Sized, { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_unit(self) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_unit_struct(self, _name: &'static str) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_unit_variant( self, _name: &'static str, _variant_index: u32, _variant: &'static str, ) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_newtype_struct( self, _name: &'static str, _value: &T, ) -> Result where T: Serialize + ?Sized, { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_newtype_variant( self, _name: &'static str, _variant_index: u32, _variant: &'static str, _value: &T, ) -> Result where T: Serialize + ?Sized, { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_seq(self, len: Option) -> Result { let is_human_readable = self.0.is_human_readable(); self.0 .serialize_map(len) .map(|delegate| SerializeSeqElement { delegate, is_human_readable, }) } fn serialize_tuple(self, _len: usize) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_tuple_struct( self, _name: &'static str, _len: usize, ) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_tuple_variant( self, _name: &'static str, _variant_index: u32, _variant: &'static str, _len: usize, ) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_map(self, _len: Option) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_struct( self, _name: &'static str, _len: usize, ) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_struct_variant( self, _name: &'static str, _variant_index: u32, _variant: &'static str, _len: usize, ) -> Result { Err(SerError::custom("wrong type for EnumMap")) } } /// Serialize a single element but turn the sequence into a map logic. /// /// 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 { delegate: M, is_human_readable: bool, } impl SerializeSeq for SerializeSeqElement where M: SerializeMap, { type Ok = M::Ok; type Error = M::Error; fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> where T: Serialize + ?Sized, { value.serialize(EnumAsMapElementSerializer { delegate: &mut self.delegate, is_human_readable: self.is_human_readable, })?; Ok(()) } fn end(self) -> Result { self.delegate.end() } } struct EnumAsMapElementSerializer<'a, M> { delegate: &'a mut M, is_human_readable: bool, } impl<'a, M> Serializer for EnumAsMapElementSerializer<'a, M> where M: SerializeMap, { type Ok = (); type Error = M::Error; type SerializeSeq = Impossible; type SerializeTuple = Impossible; type SerializeTupleStruct = Impossible; type SerializeTupleVariant = SerializeVariant<'a, M>; type SerializeMap = Impossible; type SerializeStruct = Impossible; type SerializeStructVariant = SerializeVariant<'a, M>; fn is_human_readable(&self) -> bool { self.is_human_readable } fn serialize_bool(self, _v: bool) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_i8(self, _v: i8) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_i16(self, _v: i16) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_i32(self, _v: i32) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_i64(self, _v: i64) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_i128(self, _v: i128) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_u8(self, _v: u8) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_u16(self, _v: u16) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_u32(self, _v: u32) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_u64(self, _v: u64) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_u128(self, _v: u128) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_f32(self, _v: f32) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_f64(self, _v: f64) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_char(self, _v: char) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_str(self, _v: &str) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_bytes(self, _v: &[u8]) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_none(self) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_some(self, _value: &T) -> Result where T: Serialize + ?Sized, { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_unit(self) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_unit_struct(self, _name: &'static str) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_unit_variant( self, _name: &'static str, _variant_index: u32, variant: &'static str, ) -> Result { self.delegate.serialize_entry(variant, &())?; Ok(()) } fn serialize_newtype_struct( self, _name: &'static str, _value: &T, ) -> Result where T: Serialize + ?Sized, { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_newtype_variant( self, _name: &'static str, _variant_index: u32, variant: &'static str, value: &T, ) -> Result where T: Serialize + ?Sized, { self.delegate.serialize_entry(variant, value)?; Ok(()) } fn serialize_seq(self, _len: Option) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_tuple(self, _len: usize) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_tuple_struct( self, _name: &'static str, _len: usize, ) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_tuple_variant( self, name: &'static str, _variant_index: u32, variant: &'static str, len: usize, ) -> Result { Ok(SerializeVariant { delegate: self.delegate, is_human_readable: self.is_human_readable, variant, content: Content::TupleStruct(name, Vec::with_capacity(len)), }) } fn serialize_map(self, _len: Option) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_struct( self, _name: &'static str, _len: usize, ) -> Result { Err(SerError::custom("wrong type for EnumMap")) } fn serialize_struct_variant( self, name: &'static str, _variant_index: u32, variant: &'static str, len: usize, ) -> Result { Ok(SerializeVariant { delegate: self.delegate, is_human_readable: self.is_human_readable, variant, content: Content::Struct(name, Vec::with_capacity(len)), }) } } /// Serialize a struct or tuple variant enum as a map element /// /// [`SerializeStructVariant`] serializes a struct variant, and [`SerializeTupleVariant`] a tuple variant. struct SerializeVariant<'a, M> { delegate: &'a mut M, is_human_readable: bool, variant: &'static str, content: Content, } impl SerializeStructVariant for SerializeVariant<'_, M> where M: SerializeMap, { type Ok = (); type Error = M::Error; fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> where T: Serialize + ?Sized, { // Serialize to a Content type first let value: Content = value.serialize(ContentSerializer::new(self.is_human_readable))?; if let Content::Struct(_name, fields) = &mut self.content { fields.push((key, value)); } Ok(()) } fn end(self) -> Result { self.delegate.serialize_entry(&self.variant, &self.content) } } impl SerializeTupleVariant for SerializeVariant<'_, M> where M: SerializeMap, { type Ok = (); type Error = M::Error; fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> where T: Serialize + ?Sized, { // Serialize to a Content type first let value: Content = value.serialize(ContentSerializer::new(self.is_human_readable))?; if let Content::TupleStruct(_name, fields) = &mut self.content { fields.push(value); } Ok(()) } fn end(self) -> Result { self.delegate.serialize_entry(&self.variant, &self.content) } } // Below is deserialization code /// 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 { delegate: M, is_human_readable: bool, } impl<'de, M> Deserializer<'de> for SeqDeserializer where M: MapAccess<'de>, { type Error = M::Error; fn is_human_readable(&self) -> bool { self.is_human_readable } fn deserialize_seq(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_seq(self) } fn deserialize_any(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_seq(visitor) } serde::forward_to_deserialize_any! { bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes byte_buf option unit unit_struct newtype_struct tuple tuple_struct map struct enum identifier ignored_any } } impl<'de, M> SeqAccess<'de> for SeqDeserializer where M: MapAccess<'de>, { type Error = M::Error; fn next_element_seed(&mut self, seed: T) -> Result, Self::Error> where T: DeserializeSeed<'de>, { // 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. if err.to_string().contains(END_OF_MAP_IDENTIFIER) { Ok(None) } else { Err(err) } } } } fn size_hint(&self) -> Option { self.delegate.size_hint() } } /// Deserialize an enum from a map element /// /// 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 { delegate: M, is_human_readable: bool, } impl<'de, M> Deserializer<'de> for EnumDeserializer where M: MapAccess<'de>, { type Error = M::Error; fn is_human_readable(&self) -> bool { self.is_human_readable } fn deserialize_any(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_enum("", &[], visitor) } fn deserialize_enum( self, _name: &'static str, _variants: &'static [&'static str], visitor: V, ) -> Result where V: Visitor<'de>, { visitor.visit_enum(self) } serde::forward_to_deserialize_any! { bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes byte_buf option unit unit_struct newtype_struct seq tuple tuple_struct map struct identifier ignored_any } } impl<'de, M> EnumAccess<'de> for EnumDeserializer where M: MapAccess<'de>, { type Error = M::Error; type Variant = Self; fn variant_seed(mut self, seed: T) -> Result<(T::Value, Self::Variant), Self::Error> where T: DeserializeSeed<'de>, { 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(DeError::custom(END_OF_MAP_IDENTIFIER)), } } } impl<'de, M> VariantAccess<'de> for EnumDeserializer where M: MapAccess<'de>, { type Error = M::Error; fn unit_variant(mut self) -> Result<(), Self::Error> { self.delegate.next_value() } fn newtype_variant_seed(mut self, seed: T) -> Result where T: DeserializeSeed<'de>, { self.delegate.next_value_seed(seed) } fn tuple_variant(mut self, len: usize, visitor: V) -> Result where V: Visitor<'de>, { self.delegate .next_value_seed(SeedTupleVariant { len, visitor }) } fn struct_variant( mut self, _fields: &'static [&'static str], visitor: V, ) -> Result where V: Visitor<'de>, { self.delegate.next_value_seed(SeedStructVariant { visitor }) } } struct SeedTupleVariant { len: usize, visitor: V, } impl<'de, V> DeserializeSeed<'de> for SeedTupleVariant where V: Visitor<'de>, { type Value = V::Value; fn deserialize(self, deserializer: D) -> Result where D: Deserializer<'de>, { deserializer.deserialize_tuple(self.len, self.visitor) } } struct SeedStructVariant { visitor: V, } impl<'de, V> DeserializeSeed<'de> for SeedStructVariant where V: Visitor<'de>, { type Value = V::Value; fn deserialize(self, deserializer: D) -> Result where D: Deserializer<'de>, { deserializer.deserialize_map(self.visitor) } } serde_with-3.12.0/src/flatten_maybe.rs000064400000000000000000000057411046102023000160220ustar 00000000000000/// Support deserializing from flattened and non-flattened representation /// /// When working with different serialization formats, sometimes it is more idiomatic to flatten /// fields, while other formats prefer nesting. Using `#[serde(flatten)]` only the flattened form /// is supported. /// /// This helper creates a function, which support deserializing from either the flattened or the /// nested form. It gives an error, when both forms are provided. The `flatten` attribute is /// required on the field such that the helper works. The serialization format will always be /// flattened. /// /// # Examples /// /// ```rust /// # use serde::Deserialize; /// # /// // Setup the types /// #[derive(Deserialize, Debug)] /// struct S { /// #[serde(flatten, deserialize_with = "deserialize_t")] /// t: T, /// } /// /// #[derive(Deserialize, Debug)] /// struct T { /// i: i32, /// } /// /// // The macro creates custom deserialization code. /// // You need to specify a function name and the field name of the flattened field. /// serde_with::flattened_maybe!(deserialize_t, "t"); /// /// # fn main() { /// // Supports both flattened /// let j = r#" {"i":1} "#; /// assert!(serde_json::from_str::(j).is_ok()); /// # // Ensure the t field is not dead code /// # assert_eq!(serde_json::from_str::(j).unwrap().t.i, 1); /// /// // and non-flattened versions. /// let j = r#" {"t":{"i":1}} "#; /// assert!(serde_json::from_str::(j).is_ok()); /// /// // Ensure that the value is given /// let j = r#" {} "#; /// assert!(serde_json::from_str::(j).is_err()); /// /// // and only occurs once, not multiple times. /// let j = r#" {"i":1,"t":{"i":1}} "#; /// assert!(serde_json::from_str::(j).is_err()); /// # } /// ``` #[macro_export] macro_rules! flattened_maybe { ($fn:ident, $field:tt) => { fn $fn<'de, T, D>(deserializer: D) -> $crate::__private__::Result where T: $crate::serde::Deserialize<'de>, D: $crate::serde::Deserializer<'de>, { use $crate::{ __private__::{ Option::{self, None, Some}, Result::{self, Err, Ok}, }, serde, }; #[derive($crate::serde_derive::Deserialize)] #[serde(crate = "serde")] pub struct Both { #[serde(flatten)] flat: Option, #[serde(rename = $field)] not_flat: Option, } let both: Both = $crate::serde::Deserialize::deserialize(deserializer)?; match (both.flat, both.not_flat) { (Some(t), None) | (None, Some(t)) => Ok(t), (None, None) => Err($crate::serde::de::Error::missing_field($field)), (Some(_), Some(_)) => Err($crate::serde::de::Error::custom(concat!( "`", $field, "` is both flattened and not" ))), } } }; } serde_with-3.12.0/src/formats.rs000064400000000000000000000071741046102023000146650ustar 00000000000000//! Specify the format and how lenient the deserialization is #[allow(unused_imports)] use crate::prelude::*; /// Specify how to serialize/deserialize a type /// /// The format specifier allows to configure how a value is serialized/deserialized. /// For example, you can serialize a timestamp as an integer using the UNIX epoch, as a string containing an integer, or as a string using ISO 8601. /// This [`Format`] traits allows more flexibility in configuring the format without the need to create a new type for each case. pub trait Format {} macro_rules! impl_format { ($(#[$attr:meta] $t:ty)*) => { $( #[$attr] impl Format for $t {} )* }; } macro_rules! create_format { ($(#[$attr:meta] $t:ident)*) => { $( #[$attr] pub struct $t; impl_format!(#[$attr] $t); )* }; } impl_format!( /// Serialize into an i8 i8 /// Serialize into a u8 u8 /// Serialize into an i16 i16 /// Serialize into a u16 u16 /// Serialize into an i32 i32 /// Serialize into a u32 u32 /// Serialize into an i64 i64 /// Serialize into a u64 u64 /// Serialize into an i128 i128 /// Serialize into a u128 u128 /// Serialize into a f32 f32 /// Serialize into a f64 f64 /// Serialize into a bool bool ); #[cfg(feature = "alloc")] impl_format!( /// Serialize into a String String ); create_format!( /// Use uppercase characters Uppercase /// Use lowercase characters Lowercase /// Use in combination with [`OneOrMany`](crate::OneOrMany). Emit single element for lists of size 1. PreferOne /// Use in combination with [`OneOrMany`](crate::OneOrMany). Always emit the list form. PreferMany /// Emit padding during serialization. Padded /// Do not emit padding during serialization. Unpadded ); /// Specify how lenient the deserialization process should be /// /// Formats which make use of this trait should specify how it affects the deserialization behavior. pub trait Strictness {} /// Use strict deserialization behavior, see [`Strictness`]. pub struct Strict; impl Strictness for Strict {} /// Use a flexible deserialization behavior, see [`Strictness`]. pub struct Flexible; impl Strictness for Flexible {} /// Separator for string-based collection de/serialization pub trait Separator { /// Return the string delimiting two elements in the string-based collection fn separator() -> &'static str; } /// Predefined separator using a single space pub struct SpaceSeparator; impl Separator for SpaceSeparator { #[inline] fn separator() -> &'static str { " " } } /// Predefined separator using a single comma pub struct CommaSeparator; impl Separator for CommaSeparator { #[inline] fn separator() -> &'static str { "," } } /// Predefined separator using a single semicolon pub struct SemicolonSeparator; impl Separator for SemicolonSeparator { #[inline] fn separator() -> &'static str { ";" } } /// Predefined separator using a single semicolon pub struct ColonSeparator; impl Separator for ColonSeparator { #[inline] fn separator() -> &'static str { ":" } } /// Predefined separator using a single linefeed. pub struct UnixLineSeparator; impl Separator for UnixLineSeparator { #[inline] fn separator() -> &'static str { "\n" } } /// Predefined separator using a DOS/Windows line ending. pub struct DosLineSeparator; impl Separator for DosLineSeparator { #[inline] fn separator() -> &'static str { "\r\n" } } serde_with-3.12.0/src/guide/feature_flags.rs000064400000000000000000000001241046102023000171020ustar 00000000000000//! # Available Feature Flags //! #![doc = document_features::document_features!()] serde_with-3.12.0/src/guide/serde_as.md000064400000000000000000000300361046102023000160410ustar 00000000000000# `serde_as` Annotation This is an alternative to serde's `with` annotation. It is more flexible and composable, but works with fewer types. The scheme is based on two new traits, [`SerializeAs`] and [`DeserializeAs`], which need to be implemented by all types which want to be compatible with `serde_as`. The proc-macro attribute [`#[serde_as]`][crate::serde_as] exists as a usability boost for users. The basic design of `serde_as` was developed by [@markazmierczak](https://github.com/markazmierczak). This page contains some general advice on the usage of `serde_as` and on implementing the necessary traits. [**A list of all supported transformations enabled by `serde_as` is available on this page.**](crate::guide::serde_as_transformations) 1. [Switching from serde's with to `serde_as`](#switching-from-serdes-with-to-serde_as) 1. [Deserializing Optional Fields](#deserializing-optional-fields) 2. [Gating `serde_as` on Features](#gating-serde_as-on-features) 2. [Implementing `SerializeAs` / `DeserializeAs`](#implementing-serializeas--deserializeas) 1. [Using `#[serde_as]` on types without `SerializeAs` and `Serialize` implementations](#using-serde_as-on-types-without-serializeas-and-serialize-implementations) 2. [Using `#[serde_as]` with serde's remote derives](#using-serde_as-with-serdes-remote-derives) 3. [Re-exporting `serde_as`](#re-exporting-serde_as) ## Switching from serde's with to `serde_as` For the user, the main difference is that instead of ```rust,ignore #[serde(with = "...")] ``` you now have to write ```rust,ignore #[serde_as(as = "...")] ``` and place the `#[serde_as]` attribute *before* the `#[derive]` attribute. You still need the `#[derive(Serialize, Deserialize)]` on the struct/enum. 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`. The default de/serialization behavior can be restored by using `_` as a placeholder, e.g., `BTreeMap<_, DisplayFromStr>`. Combined, this looks like: ```rust use serde::{Deserialize, Serialize}; use serde_with::{serde_as, DisplayFromStr}; # #[allow(dead_code)] #[serde_as] #[derive(Serialize, Deserialize)] struct A { #[serde_as(as = "DisplayFromStr")] mime: mime::Mime, } ``` The main advantage is that you can compose `serde_as` stuff, which is impossible with the `with` annotation. For example, the `mime` field from above could be nested in one or more data structures: ```rust # use std::collections::BTreeMap; # use serde::{Deserialize, Serialize}; # use serde_with::{serde_as, DisplayFromStr}; # # #[allow(dead_code)] #[serde_as] #[derive(Serialize, Deserialize)] struct A { #[serde_as(as = "Option>>")] mime: Option>>, } ``` ### Deserializing Optional Fields In many cases, using `serde_as` on a field of type `Option` should behave as expected. This means the field can still be missing during deserialization and will be filled with the value `None`. This "magic" can break in some cases. Then it becomes necessary to apply `#[serde(default)]` on the field in question. If the field is of type `Option` and the conversion type is of `Option`, the default attribute is automatically applied. These variants are detected as `Option`. * `Option` * `std::option::Option`, with or without leading `::` * `core::option::Option`, with or without leading `::` Any renaming will interfere with the detection, such as `use std::option::Option as StdOption;`. For more information, you can inspect the documentation of the `serde_as` macro. ```rust # use serde::{Deserialize, Serialize}; # use serde_with::{serde_as, DisplayFromStr}; # # #[allow(dead_code)] #[serde_as] #[derive(Serialize, Deserialize)] struct A { #[serde_as(as = "Option")] // In this situation both `Option`s will be correctly identified and // `#[serde(default)]` will be applied on this field. val: Option, } ``` In the future, this behavior might change and `default` would be applied on `Option` fields. You can add your feedback at [serde_with#185]. ### Gating `serde_as` on Features Gating `serde_as` behind optional features is possible using the `cfg_eval` attribute. The attribute is available via the [`cfg_eval`-crate](https://docs.rs/cfg_eval) on stable or using the [Rust attribute](https://doc.rust-lang.org/1.70.0/core/prelude/v1/attr.cfg_eval.html) on unstable nightly. The `cfg_eval` attribute must be placed **before** the struct-level `serde_as` attribute. You can combine them in a single `cfg_attr`, as long as the order is preserved. ```rust,ignore #[cfg_attr(feature="serde", cfg_eval::cfg_eval, serde_as)] #[cfg_attr(feature="serde", derive(Serialize, Deserialize))] struct Struct { #[cfg_attr(feature="serde", serde_as(as = "Vec<(_, _)>"))] map: HashMap<(i32,i32), i32>, } ``` ## Implementing `SerializeAs` / `DeserializeAs` You can support [`SerializeAs`] / [`DeserializeAs`] on your own types too. Most "leaf" types do not need to implement these traits, since they are supported implicitly. "Leaf" types refer to types which directly serialize, like plain data types. [`SerializeAs`] / [`DeserializeAs`] is essential for collection types, like `Vec` or `BTreeMap`, since they need special handling for the key/value de/serialization such that the conversions can be done on the key/values. You also find them implemented on the conversion types, such as the [`DisplayFromStr`] type. These comprise the bulk of this crate and allow you to perform all the nice conversions to [hex strings], the [bytes to string converter], or [duration to UNIX epoch]. In many cases, conversion is only required from one serializable type to another one, without requiring the full power of the `Serialize` or `Deserialize` traits. In these cases, the [`serde_conv!`] macro conveniently allows defining conversion types without the boilerplate. The documentation of [`serde_conv!`] contains more details how to use it. The trait documentations for [`SerializeAs`] and [`DeserializeAs`] describe in details how to implement them for container types like `Box` or `Vec` and other types. ### Using `#[serde_as]` on types without `SerializeAs` and `Serialize` implementations The `SerializeAs` and `DeserializeAs` traits can easily be used together with types from other crates without running into orphan rule problems. This is a distinct advantage of the `serde_as` system. For this example, we assume we have a type `RemoteType` from a dependency which does not implement `Serialize` nor `SerializeAs`. We assume we have a module containing a `serialize` and a `deserialize` function, which can be used in the `#[serde(with = "MODULE")]` annotation. You find an example in the [official serde documentation](https://serde.rs/custom-date-format.html). Our goal is to serialize this `Data` struct. Currently, we do not have anything we can use to replace `???` with, since `_` only works if `RemoteType` would implement `Serialize`, which it does not. ```rust # #[cfg(any())] { #[serde_as] #[derive(serde::Serialize)] struct Data { #[serde_as(as = "Vec")] vec: Vec, } # } ``` We need to create a new type for which we can implement `SerializeAs`, to replace the `???`. The `SerializeAs` implementation is **always** written for a local type. This allows it to seamlessly work with types from dependencies without running into orphan rule problems. ```rust # #[cfg(any())] { struct LocalType; impl SerializeAs for LocalType { fn serialize_as(value: &RemoteType, serializer: S) -> Result where S: Serializer, { MODULE::serialize(value, serializer) } } impl<'de> DeserializeAs<'de, RemoteType> for LocalType { fn deserialize_as(deserializer: D) -> Result where D: Deserializer<'de>, { MODULE::deserialize(deserializer) } } # } ``` This is what the final implementation looks like. We assumed we already have a module `MODULE` with a `serialize` function, which we use here to provide the implementation. As can be seen, this is mostly boilerplate, since the most part is encapsulated in `$module::serialize`. The final `Data` struct will now look like: ```rust # #[cfg(any())] { #[serde_as] #[derive(serde::Serialize)] struct Data { #[serde_as(as = "Vec")] vec: Vec, } # } ``` ### Using `#[serde_as]` with serde's remote derives A special case of the above section is using it on remote derives. This is a special functionality of serde, where it derives the de/serialization code for a type from another crate if all fields are `pub`. You can find all the details in the [official serde documentation](https://serde.rs/remote-derive.html). ```rust # #[cfg(any())] { // Pretend that this is somebody else's crate, not a module. mod other_crate { // Neither Serde nor the other crate provides Serialize and Deserialize // impls for this struct. pub struct Duration { pub secs: i64, pub nanos: i32, } } //////////////////////////////////////////////////////////////////////////////// use other_crate::Duration; // Serde calls this the definition of the remote type. It is just a copy of the // remote data structure. The `remote` attribute gives the path to the actual // type we intend to derive code for. #[derive(serde::Serialize, serde::Deserialize)] #[serde(remote = "Duration")] struct DurationDef { secs: i64, nanos: i32, } # } ``` Our goal is now to use `Duration` within `serde_as`. We use the existing `DurationDef` type and its `serialize` and `deserialize` functions. We can write this implementation. The implementation for `DeserializeAs` works analogue. ```rust # #[cfg(any())] { impl SerializeAs for DurationDef { fn serialize_as(value: &Duration, serializer: S) -> Result where S: serde::Serializer, { DurationDef::serialize(value, serializer) } } # } ``` This now allows us to use `Duration` for serialization. ```rust # #[cfg(any())] { use other_crate::Duration; #[serde_as] #[derive(serde::Serialize)] struct Data { #[serde_as(as = "Vec")] vec: Vec, } # } ``` ## Re-exporting `serde_as` If `serde_as` is being used in a context where the `serde_with` crate is not available from the root path, but is re-exported at some other path, the `crate = "..."` attribute argument should be used to specify its path. This may be the case if `serde_as` is being used in a procedural macro - otherwise, users of that macro would need to add `serde_with` to their own Cargo manifest. The `crate` argument will generally be used in conjunction with [`serde`'s own `crate` argument]. For example, a type definition may be defined in a procedural macro: ```rust,ignore // some_other_lib_derive/src/lib.rs use proc_macro::TokenStream; use quote::quote; #[proc_macro] pub fn define_some_type(_item: TokenStream) -> TokenStream { let def = quote! { #[serde(crate = "::some_other_lib::serde")] #[::some_other_lib::serde_with::serde_as(crate = "::some_other_lib::serde_with")] #[derive(::some_other_lib::serde::Deserialize)] struct Data { #[serde_as(as = "_")] a: u32, } }; TokenStream::from(def) } ``` This can be re-exported through a library which also re-exports `serde` and `serde_with`: ```rust,ignore // some_other_lib/src/lib.rs pub use serde; pub use serde_with; pub use some_other_lib_derive::define_some_type; ``` The procedural macro can be used by other crates without any additional imports: ```rust,ignore // consuming_crate/src/main.rs some_other_lib::define_some_type!(); ``` [`DeserializeAs`]: crate::DeserializeAs [`DisplayFromStr`]: crate::DisplayFromStr [`serde_as`]: crate::serde_as [`serde_conv!`]: crate::serde_conv! [`serde`'s own `crate` argument]: https://serde.rs/container-attrs.html#crate [`SerializeAs`]: crate::SerializeAs [bytes to string converter]: crate::BytesOrString [duration to UNIX epoch]: crate::DurationSeconds [hex strings]: crate::hex::Hex [serde_with#185]: https://github.com/jonasbb/serde_with/issues/185 serde_with-3.12.0/src/guide/serde_as_transformations.md000064400000000000000000000357661046102023000213710ustar 00000000000000# De/Serialize Transformations Available This page lists the transformations implemented in this crate and supported by `serde_as`. 1. [Base64 encode bytes](#base64-encode-bytes) 2. [Big Array support](#big-array-support) 3. [`bool` from integer](#bool-from-integer) 4. [Borrow from the input for `Cow` type](#borrow-from-the-input-for-cow-type) 5. [`Bytes` with more efficiency](#bytes-with-more-efficiency) 6. [Convert to an intermediate type using `Into`](#convert-to-an-intermediate-type-using-into) 7. [Convert to an intermediate type using `TryInto`](#convert-to-an-intermediate-type-using-tryinto) 8. [`Default` from `null`](#default-from-null) 9. [De/Serialize into `Vec`, ignoring errors](#deserialize-into-vec-ignoring-errors) 10. [De/Serialize into a map, ignoring errors](#deserialize-into-a-map-ignoring-errors) 11. [De/Serialize with `FromStr` and `Display`](#deserialize-with-fromstr-and-display) 12. [`Duration` as seconds](#duration-as-seconds) 13. [Hex encode bytes](#hex-encode-bytes) 14. [Ignore deserialization errors](#ignore-deserialization-errors) 15. [`Maps` to `Vec` of enums](#maps-to-vec-of-enums) 16. [`Maps` to `Vec` of tuples](#maps-to-vec-of-tuples) 17. [`NaiveDateTime` like UTC timestamp](#naivedatetime-like-utc-timestamp) 18. [`None` as empty `String`](#none-as-empty-string) 19. [One or many elements into `Vec`](#one-or-many-elements-into-vec) 20. [Overwrite existing set values](#overwrite-existing-set-values) 21. [Pick first successful deserialization](#pick-first-successful-deserialization) 22. [Prefer the first map key when duplicates exist](#prefer-the-first-map-key-when-duplicates-exist) 23. [Prevent duplicate map keys](#prevent-duplicate-map-keys) 24. [Prevent duplicate set values](#prevent-duplicate-set-values) 25. [Struct fields as map keys](#struct-fields-as-map-keys) 26. [Timestamps as seconds since UNIX epoch](#timestamps-as-seconds-since-unix-epoch) 27. [Value into JSON String](#value-into-json-string) 28. [`Vec` of tuples to `Maps`](#vec-of-tuples-to-maps) 29. [Well-known time formats for `OffsetDateTime`](#well-known-time-formats-for-offsetdatetime) 30. [De/Serialize depending on `De/Serializer::is_human_readable`](#deserialize-depending-on-deserializeris_human_readable) ## Base64 encode bytes [`Base64`] Requires the `base64` feature. The character set and padding behavior can be configured. ```ignore // Rust #[serde_as(as = "serde_with::base64::Base64")] value: Vec, #[serde_as(as = "Base64")] bcrypt_unpadded: Vec, // JSON "value": "SGVsbG8gV29ybGQ=", "bcrypt_unpadded": "QETqZE6eT07wZEO", ``` ## Big Array support Support for arrays of arbitrary size. ```ignore // Rust #[serde_as(as = "[[_; 64]; 33]")] value: [[u8; 64]; 33], // JSON "value": [[0,0,0,0,0,...], [0,0,0,...], ...], ``` ## `bool` from integer Deserialize an integer and convert it into a `bool`. [`BoolFromInt`] (default) deserializes 0 to `false` and `1` to `true`, other numbers are errors. [`BoolFromInt`] deserializes any non-zero as `true`. Serialization only emits 0/1. ```ignore // Rust #[serde_as(as = "BoolFromInt")] // BoolFromInt b: bool, // JSON "b": 1, ``` ## Borrow from the input for `Cow` type The types `Cow<'_, str>`, `Cow<'_, [u8]>`, or `Cow<'_, [u8; N]>` can borrow from the input, avoiding extra copies. ```ignore // Rust #[serde_as(as = "BorrowCow")] value: Cow<'a, str>, // JSON "value": "foobar", ``` ## `Bytes` with more efficiency [`Bytes`] More efficient serialization for byte slices and similar. ```ignore // Rust #[serde_as(as = "Bytes")] value: Vec, // JSON "value": [0, 1, 2, 3, ...], ``` ## Convert to an intermediate type using `Into` [`FromInto`] ```ignore // Rust #[serde_as(as = "FromInto<(u8, u8, u8)>")] value: Rgb, impl From<(u8, u8, u8)> for Rgb { ... } impl From for (u8, u8, u8) { ... } // JSON "value": [128, 64, 32], ``` ## Convert to an intermediate type using `TryInto` [`TryFromInto`] ```ignore // Rust #[serde_as(as = "TryFromInto")] value: u8, // JSON "value": 127, ``` ## `Default` from `null` [`DefaultOnNull`] ```ignore // Rust #[serde_as(as = "DefaultOnNull")] value: u32, #[serde_as(as = "DefaultOnNull")] value2: u32, // JSON "value": 123, "value2": "999", // Deserializes null into the Default value, i.e., null => 0 ``` ## De/Serialize into `Vec`, ignoring errors [`VecSkipError`] For formats with heterogeneously typed sequences, we can collect only the deserializable elements. This is also useful for unknown enum variants. ```ignore #[derive(serde::Deserialize)] enum Color { Red, Green, Blue, } // JSON "colors": ["Blue", "Yellow", "Green"], // Rust #[serde_as(as = "VecSkipError<_>")] colors: Vec, // => vec![Blue, Green] ``` ## De/Serialize into a map, ignoring errors [`MapSkipError`] For formats with heterogeneously typed maps, we can collect only the elements where both key and value are deserializable. This is also useful in conjunction to `#[serde(flatten)]` to ignore some entries when capturing additional fields. ```ignore // JSON "value": {"0": "v0", "5": "v5", "str": "str", "10": 2}, // Rust #[serde_as(as = "MapSkipError")] value: BTreeMap, // Only deserializes entries with a numerical key and a string value, i.e., {0 => "v0", 5 => "v5"} ``` ## De/Serialize with `FromStr` and `Display` Useful if a type implements `FromStr` / `Display` but not `Deserialize` / `Serialize`. [`DisplayFromStr`] ```ignore // Rust #[serde_as(as = "serde_with::DisplayFromStr")] value: u128, #[serde_as(as = "serde_with::DisplayFromStr")] mime: mime::Mime, // JSON "value": "340282366920938463463374607431768211455", "mime": "text/*", ``` ## `Duration` as seconds [`DurationSeconds`] ```ignore // Rust #[serde_as(as = "serde_with::DurationSeconds")] value: Duration, // JSON "value": 86400, ``` [`DurationSecondsWithFrac`] supports sub-second precision: ```ignore // Rust #[serde_as(as = "serde_with::DurationSecondsWithFrac")] value: Duration, // JSON "value": 1.234, ``` Different serialization formats are possible: ```ignore // Rust #[serde_as(as = "serde_with::DurationSecondsWithFrac")] value: Duration, // JSON "value": "1.234", ``` The same conversions are also implemented for [`chrono::Duration`] with the `chrono` feature. The same conversions are also implemented for [`time::Duration`] with the `time_0_3` feature. ## Hex encode bytes [`Hex`] Requires the `hex` feature. The hex string can use upper- and lowercase characters. ```ignore // Rust #[serde_as(as = "serde_with::hex::Hex")] lowercase: Vec, #[serde_as(as = "serde_with::hex::Hex")] uppercase: Vec, // JSON "lowercase": "deadbeef", "uppercase": "DEADBEEF", ``` ## Ignore deserialization errors Check the documentation for [`DefaultOnError`]. ## `Maps` to `Vec` of enums [`EnumMap`] Combine multiple enum values into a single map. The key is the enum variant name, and the value is the variant value. This only works with [*externally tagged*] enums, the default enum representation. Other forms cannot be supported. ```ignore enum EnumValue { Int(i32), String(String), Unit, Tuple(i32, String), Struct { a: i32, b: String, }, } // Rust struct VecEnumValues ( #[serde_as(as = "EnumMap")] Vec, ); VecEnumValues(vec![ EnumValue::Int(123), EnumValue::String("Foo".to_string()), EnumValue::Unit, EnumValue::Tuple(1, "Bar".to_string()), EnumValue::Struct { a: 666, b: "Baz".to_string(), }, ]) // JSON { "Int": 123, "String": "Foo", "Unit": null, "Tuple": [ 1, "Bar", ], "Struct": { "a": 666, "b": "Baz", } } ``` [*externally tagged*]: https://serde.rs/enum-representations.html#externally-tagged ## `Maps` to `Vec` of tuples ```ignore // Rust #[serde_as(as = "Seq<(_, _)>")] // also works with Vec value: HashMap, // also works with other maps like BTreeMap or IndexMap // JSON "value": [ ["hello", 1], ["world", 2] ], ``` The [inverse operation](#vec-of-tuples-to-maps) is also available. ## `NaiveDateTime` like UTC timestamp Requires the `chrono` feature. ```ignore // Rust #[serde_as(as = "chrono::DateTime")] value: chrono::NaiveDateTime, // JSON "value": "1994-11-05T08:15:30Z", ^ Pretend DateTime is UTC ``` ## `None` as empty `String` [`NoneAsEmptyString`] ```ignore // Rust #[serde_as(as = "serde_with::NoneAsEmptyString")] value: Option, // JSON "value": "", // converts to None "value": "Hello World!", // converts to Some ``` ## One or many elements into `Vec` [`OneOrMany`] ```ignore // Rust #[serde_as(as = "serde_with::OneOrMany<_>")] value: Vec, // JSON "value": "", // Deserializes single elements "value": ["Hello", "World!"], // or lists of many ``` ## Overwrite existing set values [`SetLastValueWins`] serdes default behavior for sets is to take the first value, when multiple "equal" values are inserted into a set. This changes the logic to prefer the last value. ## Pick first successful deserialization [`PickFirst`] ```ignore // Rust #[serde_as(as = "serde_with::PickFirst<(_, serde_with::DisplayFromStr)>")] value: u32, // JSON // serialize into "value": 666, // deserialize from either "value": 666, "value": "666", ``` ## Prefer the first map key when duplicates exist [`MapFirstKeyWins`] Serde's default behavior is to take the last key-value combination, if multiple "equal" keys exist. This changes the logic to instead prefer the first found key-value combination. ## Prevent duplicate map keys [`MapPreventDuplicates`] Error during deserialization, when duplicate map keys are detected. ## Prevent duplicate set values [`SetPreventDuplicates`] Error during deserialization, when duplicate set values are detected. ## Struct fields as map keys [`KeyValueMap`] 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 ```ignore // Somewhere there is a collection: // #[serde_as(as = "KeyValueMap<_>")] // Vec, #[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 }, ``` ## Timestamps as seconds since UNIX epoch [`TimestampSeconds`] ```ignore // Rust #[serde_as(as = "serde_with::TimestampSeconds")] value: SystemTime, // JSON "value": 86400, ``` [`TimestampSecondsWithFrac`] supports sub-second precision: ```ignore // Rust #[serde_as(as = "serde_with::TimestampSecondsWithFrac")] value: SystemTime, // JSON "value": 1.234, ``` Different serialization formats are possible: ```ignore // Rust #[serde_as(as = "serde_with::TimestampSecondsWithFrac")] value: SystemTime, // JSON "value": "1.234", ``` The same conversions are also implemented for [`chrono::DateTime`], [`chrono::DateTime`], and [`chrono::NaiveDateTime`] with the `chrono` feature. The conversions are available for [`time::OffsetDateTime`] and [`time::PrimitiveDateTime`] with the `time_0_3` feature enabled. ## Value into JSON String Some JSON APIs are weird and return a JSON encoded string in a JSON response [`JsonString`] Requires the `json` feature. ```ignore // Rust #[derive(Deserialize, Serialize)] struct OtherStruct { value: usize, } #[serde_as(as = "serde_with::json::JsonString")] value: OtherStruct, // JSON "value": "{\"value\":5}", ``` ```ignore #[serde_as(as = "JsonString>")] value: BTreeMap<[u8; 2], u32>, // JSON {"value":"[[\"[1,2]\",3],[\"[4,5]\",6]]"} ``` ## `Vec` of tuples to `Maps` ```ignore // Rust #[serde_as(as = "Map<_, _>")] // also works with BTreeMap and HashMap value: Vec<(String, u32)>, // JSON "value": { "hello": 1, "world": 2 }, ``` This operation is also available for other sequence types. This includes `BinaryHeap<(K, V)>`, `BTreeSet<(K, V)>`, `HashSet<(K, V)>`, `LinkedList<(K, V)>`, `VecDeque<(K, V)>`, `Option<(K, V)>` and `[(K, V); N]` for all sizes of N. The [inverse operation](#maps-to-vec-of-tuples) is also available. ## Well-known time formats for `OffsetDateTime` [`time::OffsetDateTime`] can be serialized in string format in different well-known formats. Three formats are supported, [`time::format_description::well_known::Rfc2822`], [`time::format_description::well_known::Rfc3339`], and [`time::format_description::well_known::Iso8601`]. ```ignore // Rust #[serde_as(as = "time::format_description::well_known::Rfc2822")] rfc_2822: OffsetDateTime, #[serde_as(as = "time::format_description::well_known::Rfc3339")] rfc_3339: OffsetDateTime, #[serde_as(as = "time::format_description::well_known::Iso8601")] iso_8601: OffsetDateTime, // JSON "rfc_2822": "Fri, 21 Nov 1997 09:55:06 -0600", "rfc_3339": "1997-11-21T09:55:06-06:00", "iso_8061": "1997-11-21T09:55:06-06:00", ``` These conversions are available with the `time_0_3` feature flag. ## De/Serialize depending on `De/Serializer::is_human_readable` Used to specify different transformations for text-based and binary formats. [`IfIsHumanReadable`] ```ignore // Rust #[serde_as(as = "serde_with::IfIsHumanReadable")] value: u128, // JSON "value": "340282366920938463463374607431768211455", ``` [`Base64`]: crate::base64::Base64 [`BoolFromInt`]: crate::BoolFromInt [`BoolFromInt`]: crate::BoolFromInt [`Bytes`]: crate::Bytes [`chrono::DateTime`]: chrono::DateTime [`chrono::DateTime`]: chrono::DateTime [`chrono::Duration`]: chrono::Duration [`chrono::NaiveDateTime`]: chrono::NaiveDateTime [`DefaultOnError`]: crate::DefaultOnError [`DefaultOnNull`]: crate::DefaultOnNull [`DisplayFromStr`]: crate::DisplayFromStr [`DurationSeconds`]: crate::DurationSeconds [`DurationSecondsWithFrac`]: crate::DurationSecondsWithFrac [`EnumMap`]: crate::EnumMap [`FromInto`]: crate::FromInto [`Hex`]: crate::hex::Hex [`IfIsHumanReadable`]: crate::IfIsHumanReadable [`JsonString`]: crate::json::JsonString [`KeyValueMap`]: crate::KeyValueMap [`MapFirstKeyWins`]: crate::MapFirstKeyWins [`MapPreventDuplicates`]: crate::MapPreventDuplicates [`NoneAsEmptyString`]: crate::NoneAsEmptyString [`OneOrMany`]: crate::OneOrMany [`PickFirst`]: crate::PickFirst [`SetLastValueWins`]: crate::SetLastValueWins [`SetPreventDuplicates`]: crate::SetPreventDuplicates [`time::Duration`]: time_0_3::Duration [`time::format_description::well_known::Iso8601`]: time_0_3::format_description::well_known::Iso8601 [`time::format_description::well_known::Rfc2822`]: time_0_3::format_description::well_known::Rfc2822 [`time::format_description::well_known::Rfc3339`]: time_0_3::format_description::well_known::Rfc3339 [`time::OffsetDateTime`]: time_0_3::OffsetDateTime [`time::PrimitiveDateTime`]: time_0_3::PrimitiveDateTime [`TimestampSeconds`]: crate::TimestampSeconds [`TimestampSecondsWithFrac`]: crate::TimestampSecondsWithFrac [`TryFromInto`]: crate::TryFromInto [`VecSkipError`]: crate::VecSkipError [`MapSkipError`]: crate::MapSkipError serde_with-3.12.0/src/guide.md000064400000000000000000000072021046102023000142530ustar 00000000000000# `serde_with` User Guide This crate provides helper functions to extend and change how [`serde`] serializes different data types. For example, you can serialize [a map as a sequence of tuples][crate::guide::serde_as#maps-to-vec-of-tuples], serialize [using the `Display` and `FromStr` traits][`DisplayFromStr`], or serialize [an empty `String` like `None`][NoneAsEmptyString]. `serde_with` covers types from the Rust Standard Library and some common crates like [`chrono`][serde_with_chrono]. [**A list of all supported transformations is available on this page.**](crate::guide::serde_as_transformations) The crate offers four types of functionality. ## 1. A more flexible and composable replacement for the `with` annotation, called `serde_as` This is an alternative to [serde's `with` annotation][with-annotation], which adds flexibility and composability to the scheme. The main downside is that it works with fewer types than [`with` annotations][with-annotation]. However, all types from the Rust Standard Library should be supported in all combinations and any missing entry is a bug. 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`. The default de/serialization behavior can be restored by using `_` as a placeholder, e.g., `BTreeMap<_, DisplayFromStr>`. The `serde_as` scheme is based on two new traits: [`SerializeAs`] and [`DeserializeAs`]. [Check out the detailed page about `serde_as` and the available features.](crate::guide::serde_as) ### Example ```rust # use serde::{Deserialize, Serialize}; # use serde_with::{serde_as, DisplayFromStr, Map}; # use std::net::Ipv4Addr; # #[serde_as] # #[derive(Debug, PartialEq, Eq)] #[derive(Deserialize, Serialize)] struct Data { // Type does not implement Serialize or Deserialize #[serde_as(as = "DisplayFromStr")] address: Ipv4Addr, // Treat the Vec like a map with duplicates // Convert u32 into a String and keep the String the same type #[serde_as(as = "Map")] vec_as_map: Vec<(u32, String)>, } let data = Data { address: Ipv4Addr::new(192, 168, 0, 1), vec_as_map: vec![ (123, "Hello".into()), (456, "World".into()), (123, "Hello".into()), ], }; let json = r#"{ "address": "192.168.0.1", "vec_as_map": { "123": "Hello", "456": "World", "123": "Hello" } }"#; // Test Serialization assert_eq!(json, serde_json::to_string_pretty(&data).unwrap()); // Test Deserialization assert_eq!(data, serde_json::from_str(json).unwrap()); ``` ## 2. proc-macros to make it easier to use both above parts The proc-macros are an optional addition and improve the user experience for common tasks. We have already seen how the `serde_as` attribute is used to define the serialization instructions. The proc-macro attributes are defined in the [`serde_with_macros`] crate and re-exported from the root of this crate. The proc-macros are optional, but enabled by default. For further details, please refer to the documentation of each proc-macro. ## 3. Derive macros to implement `Deserialize` and `Serialize` The derive macros work similar to the serde provided ones, but they do implement other de/serialization schemes. For example, the derives [`DeserializeFromStr`] and [`SerializeDisplay`] require that the type also implement [`FromStr`] and [`Display`] and de/serializes from/to a string instead of the usual way of iterating over all fields. [`Display`]: std::fmt::Display [`FromStr`]: std::str::FromStr [`serde_with_macros`]: serde_with_macros [serde_with_chrono]: crate::chrono [with-annotation]: https://serde.rs/field-attrs.html#with serde_with-3.12.0/src/hex.rs000064400000000000000000000110011046102023000137560ustar 00000000000000//! De/Serialization of hexadecimal encoded bytes //! //! This modules is only available when using the `hex` feature of the crate. //! //! Please check the documentation on the [`Hex`] type for details. use crate::prelude::*; /// Serialize bytes as a hex string /// /// The type serializes a sequence of bytes as a hexadecimal string. /// It works on any type implementing `AsRef<[u8]>` for serialization and `TryFrom>` for deserialization. /// /// The format type parameter specifies if the hex string should use lower- or uppercase characters. /// Valid options are the types [`formats::Lowercase`] and [`formats::Uppercase`]. /// Deserialization always supports lower- and uppercase characters, even mixed in one string. /// /// # Example /// /// ```rust /// # #[cfg(feature = "macros")] { /// # use serde::{Deserialize, Serialize}; /// # use serde_json::json; /// # use serde_with::serde_as; /// # /// #[serde_as] /// # #[derive(Debug, PartialEq, Eq)] /// #[derive(Deserialize, Serialize)] /// struct BytesLowercase( /// // Equivalent to serde_with::hex::Hex /// #[serde_as(as = "serde_with::hex::Hex")] /// Vec /// ); /// /// #[serde_as] /// # #[derive(Debug, PartialEq, Eq)] /// #[derive(Deserialize, Serialize)] /// struct BytesUppercase( /// #[serde_as(as = "serde_with::hex::Hex")] /// Vec /// ); /// /// let b = b"Hello World!"; /// /// // Hex with lowercase letters /// assert_eq!( /// json!("48656c6c6f20576f726c6421"), /// serde_json::to_value(BytesLowercase(b.to_vec())).unwrap() /// ); /// // Hex with uppercase letters /// assert_eq!( /// json!("48656C6C6F20576F726C6421"), /// serde_json::to_value(BytesUppercase(b.to_vec())).unwrap() /// ); /// /// // Serialization always work from lower- and uppercase characters, even mixed case. /// assert_eq!( /// BytesLowercase(vec![0x00, 0xaa, 0xbc, 0x99, 0xff]), /// serde_json::from_value(json!("00aAbc99FF")).unwrap() /// ); /// assert_eq!( /// BytesUppercase(vec![0x00, 0xaa, 0xbc, 0x99, 0xff]), /// serde_json::from_value(json!("00aAbc99FF")).unwrap() /// ); /// /// #[serde_as] /// # #[derive(Debug, PartialEq, Eq)] /// #[derive(Deserialize, Serialize)] /// struct ByteArray( /// // Equivalent to serde_with::hex::Hex /// #[serde_as(as = "serde_with::hex::Hex")] /// [u8; 12] /// ); /// /// let b = *b"Hello World!"; /// /// assert_eq!( /// json!("48656c6c6f20576f726c6421"), /// serde_json::to_value(ByteArray(b)).unwrap() /// ); /// /// // Serialization always work from lower- and uppercase characters, even mixed case. /// assert_eq!( /// ByteArray([0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0xaa, 0xbc, 0x99, 0xff]), /// serde_json::from_value(json!("0011223344556677aAbc99FF")).unwrap() /// ); /// /// // Remember that the conversion may fail. (The following errors are specific to fixed-size arrays) /// let error_result: Result = serde_json::from_value(json!("42")); // Too short /// error_result.unwrap_err(); /// /// let error_result: Result = /// serde_json::from_value(json!("000000000000000000000000000000")); // Too long /// error_result.unwrap_err(); /// # } /// ``` pub struct Hex(PhantomData); impl SerializeAs for Hex where T: AsRef<[u8]>, { fn serialize_as(source: &T, serializer: S) -> Result where S: Serializer, { serializer.serialize_str(&::hex::encode(source)) } } impl SerializeAs for Hex where T: AsRef<[u8]>, { fn serialize_as(source: &T, serializer: S) -> Result where S: Serializer, { serializer.serialize_str(&::hex::encode_upper(source)) } } impl<'de, T, FORMAT> DeserializeAs<'de, T> for Hex where T: TryFrom>, FORMAT: formats::Format, { fn deserialize_as(deserializer: D) -> Result where D: Deserializer<'de>, { as Deserialize<'de>>::deserialize(deserializer) .and_then(|s| ::hex::decode(&*s).map_err(DeError::custom)) .and_then(|vec: Vec| { let length = vec.len(); vec.try_into().map_err(|_e: T::Error| { DeError::custom(format_args!( "Can't convert a Byte Vector of length {length} to the output type." )) }) }) } } serde_with-3.12.0/src/json.rs000064400000000000000000000065101046102023000141540ustar 00000000000000//! De/Serialization of JSON //! //! This modules is only available when using the `json` feature of the crate. use crate::prelude::*; /// Serialize value as string containing JSON /// /// *Note*: This type is not necessary for normal usage of serde with JSON. /// It is only required if the serialized format contains a string, which itself contains JSON. /// /// # Errors /// /// Serialization can fail if `T`'s implementation of `Serialize` decides to /// fail, or if `T` contains a map with non-string keys. /// /// # Examples /// /// ``` /// # #[cfg(feature = "macros")] { /// # use serde::{Deserialize, Serialize}; /// # use serde_with::{serde_as, json::JsonString}; /// # /// #[serde_as] /// #[derive(Deserialize, Serialize)] /// struct A { /// #[serde_as(as = "JsonString")] /// other_struct: B, /// } /// #[derive(Deserialize, Serialize)] /// struct B { /// value: usize, /// } /// /// let v: A = serde_json::from_str(r#"{"other_struct":"{\"value\":5}"}"#).unwrap(); /// assert_eq!(5, v.other_struct.value); /// /// let x = A { /// other_struct: B { value: 10 }, /// }; /// assert_eq!( /// r#"{"other_struct":"{\"value\":10}"}"#, /// serde_json::to_string(&x).unwrap() /// ); /// # } /// ``` /// /// The `JsonString` converter takes a type argument, which allows altering the serialization behavior of the inner value, before it gets turned into a JSON string. /// /// ``` /// # #[cfg(feature = "macros")] { /// # use serde::{Deserialize, Serialize}; /// # use serde_with::{serde_as, json::JsonString}; /// # use std::collections::BTreeMap; /// # /// #[serde_as] /// #[derive(Debug, Serialize, Deserialize, PartialEq)] /// struct Struct { /// #[serde_as(as = "JsonString>")] /// value: BTreeMap<[u8; 2], u32>, /// } /// /// let value = Struct { /// value: BTreeMap::from([([1, 2], 3), ([4, 5], 6)]), /// }; /// assert_eq!( /// r#"{"value":"[[\"[1,2]\",3],[\"[4,5]\",6]]"}"#, /// serde_json::to_string(&value).unwrap() /// ); /// # } /// ``` pub struct JsonString(PhantomData); impl SerializeAs for JsonString where TAs: SerializeAs, { fn serialize_as(source: &T, serializer: S) -> Result where S: Serializer, { serializer.serialize_str( &serde_json::to_string(&SerializeAsWrap::::new(source)) .map_err(SerError::custom)?, ) } } impl<'de, T, TAs> DeserializeAs<'de, T> for JsonString where TAs: for<'a> DeserializeAs<'a, T>, { fn deserialize_as(deserializer: D) -> Result where D: Deserializer<'de>, { struct Helper(PhantomData<(S, SAs)>); impl Visitor<'_> for Helper where SAs: for<'a> DeserializeAs<'a, S>, { type Value = S; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("valid json object") } fn visit_str(self, value: &str) -> Result where E: DeError, { serde_json::from_str(value) .map(DeserializeAsWrap::::into_inner) .map_err(DeError::custom) } } deserializer.deserialize_str(Helper::(PhantomData)) } } serde_with-3.12.0/src/key_value_map.rs000064400000000000000000001065771046102023000160420ustar 00000000000000use crate::{ content::{ de::{Content as DeContent, ContentDeserializer}, ser::{Content as SerContent, ContentSerializer}, }, prelude::*, }; /// Convert `Vec` elements into key-value map entries /// /// This maps a single struct/tuple/etc. to a map entry. /// The map key is converted to a struct field. /// The other values will be mapped to the map value. /// /// The conversion supports structs, tuple structs, tuples, maps, and sequences. /// Structs need a field that is named `$key$` to be used as the map key. /// This can be done with the `#[serde(rename = "$key$")]` attribute. /// Maps similarly need a map-key that is named `$key$`. /// For tuples, tuple structs, and sequences the first element is used as the map key. /// /// # Examples /// /// ## Struct with String key in JSON /// /// ```rust /// # #[cfg(feature = "macros")] { /// # use serde::{Deserialize, Serialize}; /// use serde_with::{serde_as, KeyValueMap}; /// /// # #[derive(Debug, Clone, PartialEq, Eq)] /// #[derive(Serialize, Deserialize)] /// struct SimpleStruct { /// b: bool, /// // The field named `$key$` will become the map key /// #[serde(rename = "$key$")] /// id: String, /// i: i32, /// } /// /// #[serde_as] /// # #[derive(Debug, Clone, PartialEq, Eq)] /// #[derive(Serialize, Deserialize)] /// struct KVMap( /// #[serde_as(as = "KeyValueMap<_>")] /// Vec, /// ); /// /// // --- /// /// // This will serialize this list of values /// let values = KVMap(vec![ /// SimpleStruct { /// b: false, /// id: "id-0000".to_string(), /// i: 123, /// }, /// SimpleStruct { /// b: true, /// id: "id-0001".to_string(), /// i: 555, /// }, /// SimpleStruct { /// b: false, /// id: "id-0002".to_string(), /// i: 987, /// }, /// ]); /// /// // into this JSON map /// let expected = /// r#"{ /// "id-0000": { /// "b": false, /// "i": 123 /// }, /// "id-0001": { /// "b": true, /// "i": 555 /// }, /// "id-0002": { /// "b": false, /// "i": 987 /// } /// }"#; /// /// // Both serialization and deserialization work flawlessly. /// let serialized = serde_json::to_string_pretty(&values).unwrap(); /// assert_eq!(expected, serialized); /// let deserialized: KVMap = serde_json::from_str(&serialized).unwrap(); /// assert_eq!(values, deserialized); /// # } /// ``` /// /// ## Tuple struct with complex key in YAML /// /// ```rust /// # #[cfg(feature = "macros")] { /// # use serde::{Deserialize, Serialize}; /// use serde_with::{serde_as, KeyValueMap}; /// use std::net::IpAddr; /// # use std::str::FromStr; /// /// # #[derive(Debug, Clone, PartialEq, Eq)] /// #[derive(Serialize, Deserialize)] /// struct TupleStruct ( /// // The first element in a tuple struct, tuple, or sequence becomes the map key /// (IpAddr, u8), /// bool, /// ); /// /// #[serde_as] /// # #[derive(Debug, Clone, PartialEq, Eq)] /// #[derive(Serialize, Deserialize)] /// struct KVMap( /// #[serde_as(as = "KeyValueMap<_>")] /// Vec, /// ); /// /// // --- /// /// // This will serialize this list of values /// let values = KVMap(vec![ /// TupleStruct( /// (IpAddr::from_str("127.0.0.1").unwrap(), 8), /// true /// ), /// TupleStruct( /// (IpAddr::from_str("::1").unwrap(), 128), /// true /// ), /// TupleStruct( /// (IpAddr::from_str("198.51.100.0").unwrap(), 24), /// true /// ), /// ]); /// /// // into this YAML /// let expected = /// r#"? - 127.0.0.1 /// - 8 /// : - true /// ? - ::1 /// - 128 /// : - true /// ? - 198.51.100.0 /// - 24 /// : - true /// "#; /// /// // Both serialization and deserialization work flawlessly. /// let serialized = serde_yaml::to_string(&values).unwrap(); /// assert_eq!(expected, serialized); /// let deserialized: KVMap = serde_yaml::from_str(&serialized).unwrap(); /// assert_eq!(values, deserialized); /// # } /// ``` pub struct KeyValueMap(PhantomData); impl SerializeAs> for KeyValueMap where TAs: SerializeAs, { fn serialize_as(source: &Vec, serializer: S) -> Result where S: Serializer, { >::serialize_as(source, SeqAsMapSerializer(serializer)) } } impl<'de, T, TAs> DeserializeAs<'de, Vec> for KeyValueMap where TAs: DeserializeAs<'de, T>, { fn deserialize_as(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { struct KeyValueMapVisitor { is_human_readable: bool, phantom: PhantomData<(T, TAs)>, } impl<'de, T, TAs> Visitor<'de> for KeyValueMapVisitor where TAs: DeserializeAs<'de, T>, { type Value = Vec; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("a map") } fn visit_map>(self, map: A) -> Result { >::deserialize_as(SeqDeserializer { delegate: map, is_human_readable: self.is_human_readable, }) } } let is_human_readable = deserializer.is_human_readable(); deserializer.deserialize_map(KeyValueMapVisitor:: { is_human_readable, phantom: PhantomData, }) } } // TODO Replace this with a const generic string once adt_const_params is stable. // This will allow something like this. // The `"id"` part is the field name, which gets converted to/from the map key. // #[serde_as(as = r#"KeyValueMap<"id", _>"#)] // Vec, static MAP_KEY_IDENTIFIER: &str = "$key$"; /// Convert a sequence to a map during serialization. /// /// Only `serialize_seq` is implemented and forwarded to `serialize_map` on the inner `Serializer`. /// The elements are serialized with [`SerializeSeqElement`]. struct SeqAsMapSerializer(S); impl Serializer for SeqAsMapSerializer where S: Serializer, { type Ok = S::Ok; type Error = S::Error; type SerializeSeq = SerializeSeqElement; type SerializeTuple = Impossible; type SerializeTupleStruct = Impossible; type SerializeTupleVariant = Impossible; type SerializeMap = Impossible; type SerializeStruct = Impossible; type SerializeStructVariant = Impossible; fn is_human_readable(&self) -> bool { self.0.is_human_readable() } fn serialize_bool(self, _v: bool) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_i8(self, _v: i8) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_i16(self, _v: i16) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_i32(self, _v: i32) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_i64(self, _v: i64) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_i128(self, _v: i128) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_u8(self, _v: u8) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_u16(self, _v: u16) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_u32(self, _v: u32) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_u64(self, _v: u64) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_u128(self, _v: u128) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_f32(self, _v: f32) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_f64(self, _v: f64) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_char(self, _v: char) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_str(self, _v: &str) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_bytes(self, _v: &[u8]) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_none(self) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_some(self, _value: &T) -> Result where T: Serialize + ?Sized, { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_unit(self) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_unit_struct(self, _name: &'static str) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_unit_variant( self, _name: &'static str, _variant_index: u32, _variant: &'static str, ) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_newtype_struct( self, _name: &'static str, _value: &T, ) -> Result where T: Serialize + ?Sized, { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_newtype_variant( self, _name: &'static str, _variant_index: u32, _variant: &'static str, _value: &T, ) -> Result where T: Serialize + ?Sized, { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_seq(self, len: Option) -> Result { let is_human_readable = self.0.is_human_readable(); self.0 .serialize_map(len) .map(|delegate| SerializeSeqElement { delegate, is_human_readable, }) } fn serialize_tuple(self, _len: usize) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_tuple_struct( self, _name: &'static str, _len: usize, ) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_tuple_variant( self, _name: &'static str, _variant_index: u32, _variant: &'static str, _len: usize, ) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_map(self, _len: Option) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_struct( self, _name: &'static str, _len: usize, ) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_struct_variant( self, _name: &'static str, _variant_index: u32, _variant: &'static str, _len: usize, ) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } } /// Serialize a single element but turn the sequence into a map logic. /// /// It uses [`ElementAsKeyValueSerializer`] for the map element serialization. /// /// The [`Serializer`] implementation handles `serialize_struct`, `serialize_map` and `serialize_seq` functions by deferring the work to [`SerializeStruct`], [`SerializeMap`] and [`SerializeSeq`] respectively. struct SerializeSeqElement { delegate: M, is_human_readable: bool, } impl SerializeSeq for SerializeSeqElement where M: SerializeMap, { type Ok = M::Ok; type Error = M::Error; fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> where T: Serialize + ?Sized, { value.serialize(ElementAsKeyValueSerializer { delegate: &mut self.delegate, is_human_readable: self.is_human_readable, })?; Ok(()) } fn end(self) -> Result { self.delegate.end() } } struct ElementAsKeyValueSerializer<'a, M> { delegate: &'a mut M, is_human_readable: bool, } impl<'a, M> Serializer for ElementAsKeyValueSerializer<'a, M> where M: SerializeMap, { type Ok = (); type Error = M::Error; type SerializeSeq = KeyValueSeqSerializer<'a, M>; type SerializeTuple = KeyValueTupleSerializer<'a, M>; type SerializeTupleStruct = KeyValueTupleStructSerializer<'a, M>; type SerializeTupleVariant = Impossible; type SerializeMap = KeyValueMapSerializer<'a, M>; type SerializeStruct = KeyValueStructSerializer<'a, M>; type SerializeStructVariant = Impossible; fn is_human_readable(&self) -> bool { self.is_human_readable } fn serialize_bool(self, _v: bool) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_i8(self, _v: i8) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_i16(self, _v: i16) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_i32(self, _v: i32) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_i64(self, _v: i64) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_i128(self, _v: i128) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_u8(self, _v: u8) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_u16(self, _v: u16) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_u32(self, _v: u32) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_u64(self, _v: u64) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_u128(self, _v: u128) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_f32(self, _v: f32) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_f64(self, _v: f64) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_char(self, _v: char) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_str(self, _v: &str) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_bytes(self, _v: &[u8]) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_none(self) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_some(self, _value: &T) -> Result where T: Serialize + ?Sized, { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_unit(self) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_unit_struct(self, _name: &'static str) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_unit_variant( self, _name: &'static str, _variant_index: u32, _variant: &'static str, ) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_newtype_struct( self, _name: &'static str, _value: &T, ) -> Result where T: Serialize + ?Sized, { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_newtype_variant( self, _name: &'static str, _variant_index: u32, _variant: &'static str, _value: &T, ) -> Result where T: Serialize + ?Sized, { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_seq(self, len: Option) -> Result { Ok(KeyValueSeqSerializer { delegate: self.delegate, is_human_readable: self.is_human_readable, content: Vec::with_capacity(len.unwrap_or(17) - 1), key: None, }) } fn serialize_tuple(self, len: usize) -> Result { Ok(KeyValueTupleSerializer { delegate: self.delegate, is_human_readable: self.is_human_readable, content: Vec::with_capacity(len - 1), key: None, }) } fn serialize_tuple_struct( self, name: &'static str, len: usize, ) -> Result { Ok(KeyValueTupleStructSerializer { delegate: self.delegate, is_human_readable: self.is_human_readable, name, content: Vec::with_capacity(len - 1), key: None, }) } fn serialize_tuple_variant( self, _name: &'static str, _variant_index: u32, _variant: &'static str, _len: usize, ) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } fn serialize_map(self, len: Option) -> Result { Ok(KeyValueMapSerializer { delegate: self.delegate, is_human_readable: self.is_human_readable, content: Vec::with_capacity(len.unwrap_or(17) - 1), next_is_magic_key: false, key: None, tmp: None, }) } fn serialize_struct( self, name: &'static str, len: usize, ) -> Result { Ok(KeyValueStructSerializer { delegate: self.delegate, is_human_readable: self.is_human_readable, name, content: Vec::with_capacity(len - 1), key: None, }) } fn serialize_struct_variant( self, _name: &'static str, _variant_index: u32, _variant: &'static str, _len: usize, ) -> Result { Err(SerError::custom("wrong type for KeyValueMap")) } } /// Serialize a sequence to a key and value pair of a map. /// /// This requires that the sequence has at least one element. struct KeyValueSeqSerializer<'a, M> { delegate: &'a mut M, is_human_readable: bool, content: Vec, key: Option, } impl SerializeSeq for KeyValueSeqSerializer<'_, M> where M: SerializeMap, { type Ok = (); type Error = M::Error; fn serialize_element(&mut self, element: &T) -> Result<(), Self::Error> where T: Serialize + ?Sized, { let element: SerContent = element.serialize(ContentSerializer::new(self.is_human_readable))?; if self.key.is_none() { self.key = Some(element); return Ok(()); } self.content.push(element); Ok(()) } fn end(self) -> Result { if let Some(key) = self.key { self.delegate .serialize_entry(&key, &SerContent::Seq(self.content)) } else { Err(SerError::custom("missing value for `$key$` field")) } } } /// Serialize a tuple to a key and value pair of a map. /// /// This requires that the tuple has at least one element. struct KeyValueTupleSerializer<'a, M> { delegate: &'a mut M, is_human_readable: bool, content: Vec, key: Option, } impl SerializeTuple for KeyValueTupleSerializer<'_, M> where M: SerializeMap, { type Ok = (); type Error = M::Error; fn serialize_element(&mut self, element: &T) -> Result<(), Self::Error> where T: Serialize + ?Sized, { let element: SerContent = element.serialize(ContentSerializer::new(self.is_human_readable))?; if self.key.is_none() { self.key = Some(element); return Ok(()); } self.content.push(element); Ok(()) } fn end(self) -> Result { if let Some(key) = self.key { self.delegate .serialize_entry(&key, &SerContent::Tuple(self.content)) } else { Err(SerError::custom("missing value for `$key$` field")) } } } /// Serialize a tuple struct to a key and value pair of a map. /// /// This requires that the tuple struct has at least one element. struct KeyValueTupleStructSerializer<'a, M> { delegate: &'a mut M, is_human_readable: bool, name: &'static str, content: Vec, key: Option, } impl SerializeTupleStruct for KeyValueTupleStructSerializer<'_, M> where M: SerializeMap, { type Ok = (); type Error = M::Error; fn serialize_field(&mut self, field: &T) -> Result<(), Self::Error> where T: Serialize + ?Sized, { let field: SerContent = field.serialize(ContentSerializer::new(self.is_human_readable))?; if self.key.is_none() { self.key = Some(field); return Ok(()); } self.content.push(field); Ok(()) } fn end(self) -> Result { if let Some(key) = self.key { self.delegate .serialize_entry(&key, &SerContent::TupleStruct(self.name, self.content)) } else { Err(SerError::custom("missing value for `$key$` field")) } } } /// Serialize a map to a key and value pair of a map. /// /// This requires that the map has one element which serializes using the magic `$key$` key. struct KeyValueMapSerializer<'a, M> { delegate: &'a mut M, is_human_readable: bool, content: Vec<(SerContent, SerContent)>, next_is_magic_key: bool, key: Option, tmp: Option, } impl SerializeMap for KeyValueMapSerializer<'_, M> where M: SerializeMap, { type Ok = (); type Error = M::Error; fn serialize_key(&mut self, key: &T) -> Result<(), Self::Error> where T: Serialize + ?Sized, { let key: SerContent = key.serialize(ContentSerializer::new(self.is_human_readable))?; if key.as_str() == Some(MAP_KEY_IDENTIFIER) { self.next_is_magic_key = true; return Ok(()); } self.tmp = Some(key); Ok(()) } fn serialize_value(&mut self, value: &T) -> Result<(), Self::Error> where T: Serialize + ?Sized, { let value: SerContent = value.serialize(ContentSerializer::new(self.is_human_readable))?; if self.next_is_magic_key { self.next_is_magic_key = false; self.key = Some(value); return Ok(()); } self.content.push(( self.tmp .take() .expect("serialize_value called before serialize_key"), value, )); Ok(()) } fn end(self) -> Result { if let Some(key) = self.key { self.delegate .serialize_entry(&key, &SerContent::Map(self.content)) } else { Err(SerError::custom("missing value for `$key$` field")) } } } /// Serialize a struct to a key and value pair of a map. /// /// This requires that the struct has one field named `$key$`. struct KeyValueStructSerializer<'a, M> { delegate: &'a mut M, is_human_readable: bool, name: &'static str, content: Vec<(&'static str, SerContent)>, key: Option, } impl SerializeStruct for KeyValueStructSerializer<'_, M> where M: SerializeMap, { type Ok = (); type Error = M::Error; fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> where T: Serialize + ?Sized, { // Serialize to a Content type first let value: SerContent = value.serialize(ContentSerializer::new(self.is_human_readable))?; if key == MAP_KEY_IDENTIFIER { self.key = Some(value); return Ok(()); } self.content.push((key, value)); Ok(()) } fn end(self) -> Result { if let Some(key) = self.key { self.delegate .serialize_entry(&key, &SerContent::Struct(self.name, self.content)) } else { Err(SerError::custom("missing value for `$key$` field")) } } } // Below is deserialization code /// 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 { delegate: M, is_human_readable: bool, } impl<'de, M> Deserializer<'de> for SeqDeserializer where M: MapAccess<'de>, { type Error = M::Error; fn is_human_readable(&self) -> bool { self.is_human_readable } fn deserialize_seq(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_seq(self) } fn deserialize_any(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_seq(visitor) } serde::forward_to_deserialize_any! { bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes byte_buf option unit unit_struct newtype_struct tuple tuple_struct map struct enum identifier ignored_any } } impl<'de, M> SeqAccess<'de> for SeqDeserializer where M: MapAccess<'de>, { type Error = M::Error; fn next_element_seed(&mut self, seed: T) -> Result, Self::Error> where T: DeserializeSeed<'de>, { let key_value: Option> = self.delegate.next_key()?; if let Some(key_value) = key_value { seed.deserialize(MapKeyDeserializer { delegate: &mut self.delegate, is_human_readable: self.is_human_readable, key_value, }) .map(Some) } else { Ok(None) } } fn size_hint(&self) -> Option { self.delegate.size_hint() } } struct MapKeyDeserializer<'de, M> { delegate: M, is_human_readable: bool, key_value: DeContent<'de>, } impl<'de, M> Deserializer<'de> for MapKeyDeserializer<'de, M> where M: MapAccess<'de>, { type Error = M::Error; fn is_human_readable(&self) -> bool { self.is_human_readable } fn deserialize_any(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_map(visitor) } fn deserialize_seq(mut self, visitor: V) -> Result where V: Visitor<'de>, { self.delegate.next_value_seed(KeyValueSeqDeserialize { delegate: visitor, first: Some(self.key_value), }) } fn deserialize_tuple(mut self, len: usize, visitor: V) -> Result where V: Visitor<'de>, { self.delegate.next_value_seed(KeyValueTupleDeserialize { delegate: visitor, len, first: Some(self.key_value), }) } fn deserialize_tuple_struct( mut self, name: &'static str, len: usize, visitor: V, ) -> Result where V: Visitor<'de>, { self.delegate .next_value_seed(KeyValueTupleStructDeserialize { delegate: visitor, name, len, first: Some(self.key_value), }) } fn deserialize_map(mut self, visitor: V) -> Result where V: Visitor<'de>, { self.delegate.next_value_seed(KeyValueMapDeserialize { delegate: visitor, first: Some(self.key_value), }) } fn deserialize_struct( mut self, name: &'static str, fields: &'static [&'static str], visitor: V, ) -> Result where V: Visitor<'de>, { self.delegate.next_value_seed(KeyValueStructDeserialize { delegate: visitor, name, fields, first: Some(self.key_value), }) } serde::forward_to_deserialize_any! { bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes byte_buf option unit unit_struct newtype_struct enum identifier ignored_any } } struct KeyValueSeqDeserialize<'de, V> { delegate: V, first: Option>, } impl<'de, V> DeserializeSeed<'de> for KeyValueSeqDeserialize<'de, V> where V: Visitor<'de>, { type Value = V::Value; fn deserialize(mut self, deserializer: D) -> Result where D: Deserializer<'de>, { let is_human_readable = deserializer.is_human_readable(); deserializer.deserialize_seq(VisitorWrapper { delegate: self.delegate, is_human_readable, first: self.first.take(), }) } } struct KeyValueTupleDeserialize<'de, V> { delegate: V, len: usize, first: Option>, } impl<'de, V> DeserializeSeed<'de> for KeyValueTupleDeserialize<'de, V> where V: Visitor<'de>, { type Value = V::Value; fn deserialize(mut self, deserializer: D) -> Result where D: Deserializer<'de>, { let is_human_readable = deserializer.is_human_readable(); deserializer.deserialize_tuple( self.len, VisitorWrapper { delegate: self.delegate, is_human_readable, first: self.first.take(), }, ) } } struct KeyValueTupleStructDeserialize<'de, V> { delegate: V, name: &'static str, len: usize, first: Option>, } impl<'de, V> DeserializeSeed<'de> for KeyValueTupleStructDeserialize<'de, V> where V: Visitor<'de>, { type Value = V::Value; fn deserialize(mut self, deserializer: D) -> Result where D: Deserializer<'de>, { let is_human_readable = deserializer.is_human_readable(); deserializer.deserialize_tuple_struct( self.name, self.len, VisitorWrapper { delegate: self.delegate, is_human_readable, first: self.first.take(), }, ) } } struct KeyValueMapDeserialize<'de, V> { delegate: V, first: Option>, } impl<'de, V> DeserializeSeed<'de> for KeyValueMapDeserialize<'de, V> where V: Visitor<'de>, { type Value = V::Value; fn deserialize(mut self, deserializer: D) -> Result where D: Deserializer<'de>, { let is_human_readable = deserializer.is_human_readable(); deserializer.deserialize_map(VisitorWrapper { delegate: self.delegate, is_human_readable, first: self.first.take(), }) } } struct KeyValueStructDeserialize<'de, V> { delegate: V, name: &'static str, fields: &'static [&'static str], first: Option>, } impl<'de, V> DeserializeSeed<'de> for KeyValueStructDeserialize<'de, V> where V: Visitor<'de>, { type Value = V::Value; fn deserialize(mut self, deserializer: D) -> Result where D: Deserializer<'de>, { let is_human_readable = deserializer.is_human_readable(); deserializer.deserialize_struct( self.name, self.fields, VisitorWrapper { delegate: self.delegate, is_human_readable, first: self.first.take(), }, ) } } struct VisitorWrapper<'de, V> { delegate: V, is_human_readable: bool, first: Option>, } impl<'de, V> Visitor<'de> for VisitorWrapper<'de, V> where V: Visitor<'de>, { type Value = V::Value; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { self.delegate.expecting(formatter) } fn visit_map(self, map: A) -> Result where A: MapAccess<'de>, { self.delegate.visit_map(MapAccessWrapper { delegate: map, is_human_readable: self.is_human_readable, first: self.first, }) } fn visit_seq(self, seq: A) -> Result where A: SeqAccess<'de>, { self.delegate.visit_seq(SeqAccessWrapper { delegate: seq, is_human_readable: self.is_human_readable, first: self.first, }) } } struct MapAccessWrapper<'de, M> { delegate: M, is_human_readable: bool, first: Option>, } impl<'de, M> MapAccess<'de> for MapAccessWrapper<'de, M> where M: MapAccess<'de>, { type Error = M::Error; fn next_key_seed(&mut self, seed: K) -> Result, Self::Error> where K: DeserializeSeed<'de>, { if self.first.is_some() { seed.deserialize(serde::de::value::StringDeserializer::new( MAP_KEY_IDENTIFIER.to_string(), )) .map(Some) } else { self.delegate.next_key_seed(seed) } } fn next_value_seed(&mut self, seed: V) -> Result where V: DeserializeSeed<'de>, { if let Some(first) = self.first.take() { seed.deserialize(ContentDeserializer::new(first, self.is_human_readable)) } else { self.delegate.next_value_seed(seed) } } } struct SeqAccessWrapper<'de, M> { delegate: M, is_human_readable: bool, first: Option>, } impl<'de, S> SeqAccess<'de> for SeqAccessWrapper<'de, S> where S: SeqAccess<'de>, { type Error = S::Error; fn next_element_seed(&mut self, seed: K) -> Result, Self::Error> where K: DeserializeSeed<'de>, { if let Some(first) = self.first.take() { seed.deserialize(ContentDeserializer::new(first, self.is_human_readable)) .map(Some) } else { self.delegate.next_element_seed(seed) } } } serde_with-3.12.0/src/lib.rs000064400000000000000000002673761046102023000137740ustar 00000000000000#![doc(test(attr( allow( unknown_lints, // Problematic handling for foreign From impls in tests // https://github.com/rust-lang/rust/issues/121621 non_local_definitions, // Some tests use foo as name clippy::disallowed_names, ), deny( missing_debug_implementations, rust_2018_idioms, trivial_casts, trivial_numeric_casts, unused_extern_crates, unused_import_braces, unused_qualifications, warnings, ), forbid(unsafe_code), )))] // Not needed for 2018 edition and conflicts with `rust_2018_idioms` #![doc(test(no_crate_inject))] #![doc(html_root_url = "https://docs.rs/serde_with/3.12.0/")] #![cfg_attr(docsrs, feature(doc_cfg))] #![no_std] //! [![crates.io badge](https://img.shields.io/crates/v/serde_with.svg)](https://crates.io/crates/serde_with/) //! [![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) //! [![Rustexplorer](https://img.shields.io/badge/Try%20on-rustexplorer-lightgrey?logo=rust&logoColor=orange)](https://www.rustexplorer.com/b/py7ida) //! //! --- //! //! This crate provides custom de/serialization helpers to use in combination with [serde's `with` annotation][with-annotation] and with the improved [`serde_as`][as-annotation]-annotation. //! 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`] 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 / suffix to each field name of a struct, without changing the de/serialize implementations of the struct using [`with_prefix!`][] / [`with_suffix!`][]. //! * Deserialize a comma separated list like `#hash,#tags,#are,#great` into a `Vec`. //! Check the documentation for [`serde_with::StringWithSeparator::`][StringWithSeparator]. //! //! ## Getting Help //! //! **Check out the [user guide][user guide] to find out more tips and tricks about this crate.** //! //! For further help using this crate you can [open a new discussion](https://github.com/jonasbb/serde_with/discussions/new) or ask on [users.rust-lang.org](https://users.rust-lang.org/). //! For bugs, please open a [new issue](https://github.com/jonasbb/serde_with/issues/new) on GitHub. //! //! # Use `serde_with` in your Project //! //! ```bash //! # Add the current version to your Cargo.toml //! cargo add serde_with //! ``` //! //! The crate contains different features for integration with other common crates. //! 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 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`. //! 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 //! # #[cfg(all(feature = "macros", feature = "json"))] { //! # use serde::{Deserialize, Serialize}; //! # use serde_with::{serde_as, DisplayFromStr}; //! #[serde_as] //! # #[derive(Debug, Eq, PartialEq)] //! #[derive(Deserialize, Serialize)] //! struct Foo { //! // Serialize with Display, deserialize with FromStr //! #[serde_as(as = "DisplayFromStr")] //! bar: u8, //! } //! //! // This will serialize //! # let foo = //! Foo {bar: 12} //! # ; //! //! // into this JSON //! # let json = r#" //! {"bar": "12"} //! # "#; //! # assert_eq!(json.replace(" ", "").replace("\n", ""), serde_json::to_string(&foo).unwrap()); //! # assert_eq!(foo, serde_json::from_str(json).unwrap()); //! # } //! ``` //! //! ## Large and const-generic arrays //! //! serde does not support arrays with more than 32 elements or using const-generics. //! 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 //! # #[cfg(all(feature = "macros", feature = "json"))] { //! # use serde::{Deserialize, Serialize}; //! # use serde_with::{serde_as, Bytes}; //! #[serde_as] //! # #[derive(Debug, Eq, PartialEq)] //! #[derive(Deserialize, Serialize)] //! struct Arrays { //! #[serde_as(as = "[_; N]")] //! constgeneric: [bool; N], //! //! #[serde_as(as = "Box<[[_; 64]; N]>")] //! nested: Box<[[u8; 64]; N]>, //! //! #[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]), //! bytes: [0x42; 128], //! }; //! assert!(serde_json::to_string(&arrays).is_ok()); //! # } //! ``` //! //! ## `skip_serializing_none` //! //! 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 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 //! # #[cfg(all(feature = "macros", feature = "json"))] { //! # use serde::{Deserialize, Serialize}; //! # use serde_with::skip_serializing_none; //! #[skip_serializing_none] //! # #[derive(Debug, Eq, PartialEq)] //! #[derive(Deserialize, Serialize)] //! struct Foo { //! a: Option, //! b: Option, //! c: Option, //! d: Option, //! e: Option, //! f: Option, //! g: Option, //! } //! //! // This will serialize //! # let foo = //! Foo {a: None, b: None, c: None, d: Some(4), e: None, f: None, g: Some(7)} //! # ; //! //! // into this JSON //! # let json = r#" //! {"d": 4, "g": 7} //! # "#; //! # assert_eq!(json.replace(" ", "").replace("\n", ""), serde_json::to_string(&foo).unwrap()); //! # assert_eq!(foo, serde_json::from_str(json).unwrap()); //! # } //! ``` //! //! ## 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]. //! //! ```rust //! # #[cfg(all(feature = "macros", feature = "hex"))] //! # use { //! # serde::{Deserialize, Serialize}, //! # serde_with::{serde_as, DisplayFromStr, DurationSeconds, hex::Hex, Map}, //! # }; //! # #[cfg(all(feature = "macros", feature = "hex"))] //! use std::time::Duration; //! //! # #[cfg(all(feature = "macros", feature = "hex"))] //! #[serde_as] //! # #[derive(Debug, Eq, PartialEq)] //! #[derive(Deserialize, Serialize)] //! enum Foo { //! Durations( //! // Serialize them into a list of number as seconds //! #[serde_as(as = "Vec")] //! Vec, //! ), //! 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")] //! bytes: Vec<(i32, Vec)>, //! } //! } //! //! # #[cfg(all(feature = "macros", feature = "json", feature = "hex"))] { //! // This will serialize //! # let foo = //! Foo::Durations( //! vec![Duration::new(5, 0), Duration::new(3600, 0), Duration::new(0, 0)] //! ) //! # ; //! // into this JSON //! # let json = r#" //! { //! "Durations": [5, 3600, 0] //! } //! # "#; //! # assert_eq!(json.replace(" ", "").replace("\n", ""), serde_json::to_string(&foo).unwrap()); //! # assert_eq!(foo, serde_json::from_str(json).unwrap()); //! //! // and serializes //! # let foo = //! Foo::Bytes { //! bytes: vec![ //! (1, vec![0, 1, 2]), //! (-100, vec![100, 200, 255]), //! (1, vec![0, 111, 222]), //! ], //! } //! # ; //! // into this JSON //! # let json = r#" //! { //! "Bytes": { //! "bytes": { //! "1": "000102", //! "-100": "64c8ff", //! "1": "006fde" //! } //! } //! } //! # "#; //! # assert_eq!(json.replace(" ", "").replace("\n", ""), serde_json::to_string(&foo).unwrap()); //! # assert_eq!(foo, serde_json::from_str(json).unwrap()); //! # } //! ``` //! //! [`DisplayFromStr`]: https://docs.rs/serde_with/3.12.0/serde_with/struct.DisplayFromStr.html //! [`with_prefix!`]: https://docs.rs/serde_with/3.12.0/serde_with/macro.with_prefix.html //! [`with_suffix!`]: https://docs.rs/serde_with/3.12.0/serde_with/macro.with_suffix.html //! [feature flags]: https://docs.rs/serde_with/3.12.0/serde_with/guide/feature_flags/index.html //! [skip_serializing_none]: https://docs.rs/serde_with/3.12.0/serde_with/attr.skip_serializing_none.html //! [StringWithSeparator]: https://docs.rs/serde_with/3.12.0/serde_with/struct.StringWithSeparator.html //! [user guide]: https://docs.rs/serde_with/3.12.0/serde_with/guide/index.html //! [with-annotation]: https://serde.rs/field-attrs.html#with //! [as-annotation]: https://docs.rs/serde_with/3.12.0/serde_with/guide/serde_as/index.html #[cfg(feature = "alloc")] extern crate alloc; #[doc(hidden)] pub extern crate core; #[doc(hidden)] pub extern crate serde; #[doc(hidden)] pub extern crate serde_derive; #[cfg(feature = "std")] extern crate std; #[cfg(feature = "base64")] #[cfg_attr(docsrs, doc(cfg(feature = "base64")))] pub mod base64; #[cfg(feature = "chrono_0_4")] #[cfg_attr(docsrs, doc(cfg(feature = "chrono_0_4")))] pub mod chrono_0_4; /// Legacy export of the [`chrono_0_4`] module. #[cfg(feature = "chrono")] #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] pub mod chrono { pub use chrono_0_4::*; } #[cfg(feature = "alloc")] mod content; pub mod de; #[cfg(feature = "alloc")] mod duplicate_key_impls; #[cfg(feature = "alloc")] mod enum_map; #[cfg(feature = "std")] mod flatten_maybe; pub mod formats; #[cfg(feature = "hex")] #[cfg_attr(docsrs, doc(cfg(feature = "hex")))] pub mod hex; #[cfg(feature = "json")] #[cfg_attr(docsrs, doc(cfg(feature = "json")))] pub mod json; #[cfg(feature = "alloc")] mod key_value_map; pub mod rust; #[cfg(feature = "schemars_0_8")] #[cfg_attr(docsrs, doc(cfg(feature = "schemars_0_8")))] pub mod schemars_0_8; pub mod ser; #[cfg(feature = "std")] mod serde_conv; #[cfg(feature = "time_0_3")] #[cfg_attr(docsrs, doc(cfg(feature = "time_0_3")))] pub mod time_0_3; mod utils; #[cfg(feature = "std")] #[doc(hidden)] pub mod with_prefix; #[cfg(feature = "std")] #[doc(hidden)] pub mod with_suffix; // Taken from shepmaster/snafu // Originally licensed as MIT+Apache 2 // https://github.com/shepmaster/snafu/blob/fd37d79d4531ed1d3eebffad0d658928eb860cfe/src/lib.rs#L121-L165 #[cfg(feature = "guide")] #[allow(unused_macro_rules)] macro_rules! generate_guide { (pub mod $name:ident; $($rest:tt)*) => { generate_guide!(@gen ".", pub mod $name { } $($rest)*); }; (pub mod $name:ident { $($children:tt)* } $($rest:tt)*) => { generate_guide!(@gen ".", pub mod $name { $($children)* } $($rest)*); }; (@gen $prefix:expr, ) => {}; (@gen $prefix:expr, pub mod $name:ident; $($rest:tt)*) => { generate_guide!(@gen $prefix, pub mod $name { } $($rest)*); }; (@gen $prefix:expr, @code pub mod $name:ident; $($rest:tt)*) => { pub mod $name; generate_guide!(@gen $prefix, $($rest)*); }; (@gen $prefix:expr, pub mod $name:ident { $($children:tt)* } $($rest:tt)*) => { doc_comment::doc_comment! { include_str!(concat!($prefix, "/", stringify!($name), ".md")), pub mod $name { generate_guide!(@gen concat!($prefix, "/", stringify!($name)), $($children)*); } } generate_guide!(@gen $prefix, $($rest)*); }; } #[cfg(feature = "guide")] generate_guide! { pub mod guide { @code pub mod feature_flags; pub mod serde_as; pub mod serde_as_transformations; } } pub(crate) mod prelude { #![allow(unused_imports)] pub(crate) use crate::utils::duration::{DurationSigned, Sign}; pub use crate::{de::*, ser::*, *}; #[cfg(all(feature = "alloc", target_has_atomic = "ptr"))] pub use alloc::sync::{Arc, Weak as ArcWeak}; #[cfg(feature = "alloc")] pub use alloc::{ borrow::{Cow, ToOwned}, boxed::Box, collections::{BTreeMap, BTreeSet, BinaryHeap, LinkedList, VecDeque}, rc::{Rc, Weak as RcWeak}, string::{String, ToString}, vec::Vec, }; pub use core::{ cell::{Cell, RefCell}, convert::{TryFrom, TryInto}, fmt::{self, Display}, hash::{BuildHasher, Hash}, marker::PhantomData, ops::Bound, option::Option, pin::Pin, result::Result, str::FromStr, time::Duration, }; pub use serde::{ de::{ Deserialize, DeserializeOwned, DeserializeSeed, Deserializer, EnumAccess, Error as DeError, Expected, IgnoredAny, IntoDeserializer, MapAccess, SeqAccess, Unexpected, VariantAccess, Visitor, }, forward_to_deserialize_any, ser::{ Error as SerError, Impossible, Serialize, SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant, SerializeTuple, SerializeTupleStruct, SerializeTupleVariant, Serializer, }, }; #[cfg(feature = "std")] pub use std::{ collections::{HashMap, HashSet}, sync::{Mutex, RwLock}, time::SystemTime, }; } /// This module is not part of the public API /// /// Do not rely on any exports. #[doc(hidden)] pub mod __private__ { pub use crate::prelude::*; } #[cfg(feature = "alloc")] #[doc(inline)] pub use crate::enum_map::EnumMap; #[cfg(feature = "alloc")] #[doc(inline)] pub use crate::key_value_map::KeyValueMap; #[doc(inline)] pub use crate::{de::DeserializeAs, ser::SerializeAs}; use core::marker::PhantomData; // Re-Export all proc_macros, as these should be seen as part of the serde_with crate #[cfg(feature = "macros")] #[cfg_attr(docsrs, doc(cfg(feature = "macros")))] #[doc(inline)] pub use serde_with_macros::*; /// Adapter to convert from `serde_as` to the serde traits. /// /// The `As` type adapter allows using types which implement [`DeserializeAs`] or [`SerializeAs`] in place of serde's `with` annotation. /// The `with` annotation allows running custom code when de/serializing, however it is quite inflexible. /// The traits [`DeserializeAs`]/[`SerializeAs`] are more flexible, as they allow composition and nesting of types to create more complex de/serialization behavior. /// However, they are not directly compatible with serde, as they are not provided by serde. /// The `As` type adapter makes them compatible, by forwarding the function calls to `serialize`/`deserialize` to the corresponding functions `serialize_as` and `deserialize_as`. /// /// It is not required to use this type directly. /// Instead, it is highly encouraged to use the [`#[serde_as]`][serde_as] attribute since it includes further usability improvements. /// If the use of the use of the proc-macro is not acceptable, then `As` can be used directly with serde. /// /// ```rust /// # #[cfg(feature = "alloc")] { /// # use serde::{Deserialize, Serialize}; /// # use serde_with::{As, DisplayFromStr}; /// # /// # #[allow(dead_code)] /// #[derive(Deserialize, Serialize)] /// # struct S { /// // Serialize numbers as sequence of strings, using Display and FromStr /// #[serde(with = "As::>")] /// field: Vec, /// # } /// # } /// ``` /// If the normal `Deserialize`/`Serialize` traits should be used, the placeholder type [`Same`] can be used. /// It implements [`DeserializeAs`][]/[`SerializeAs`][], when the underlying type implements `Deserialize`/`Serialize`. /// /// ```rust /// # #[cfg(feature = "alloc")] { /// # use serde::{Deserialize, Serialize}; /// # use serde_with::{As, DisplayFromStr, Same}; /// # use std::collections::BTreeMap; /// # /// # #[allow(dead_code)] /// #[derive(Deserialize, Serialize)] /// # struct S { /// // Serialize map, turn keys into strings but keep type of value /// #[serde(with = "As::>")] /// field: BTreeMap, /// # } /// # } /// ``` /// /// [serde_as]: https://docs.rs/serde_with/3.12.0/serde_with/attr.serde_as.html pub struct As(PhantomData); /// Adapter to convert from `serde_as` to the serde traits. /// /// This is the counter-type to [`As`][]. /// It can be used whenever a type implementing [`DeserializeAs`]/[`SerializeAs`] is required but the normal [`Deserialize`](::serde::Deserialize)/[`Serialize`](::serde::Serialize) traits should be used. /// Check [`As`] for an example. pub struct Same; /// De/Serialize using [`Display`] and [`FromStr`] implementation /// /// This allows deserializing a string as a number. /// It can be very useful for serialization formats like JSON, which do not support integer /// numbers and have to resort to strings to represent them. /// /// Another use case is types with [`Display`] and [`FromStr`] implementations, but without serde /// support, which can be found in some crates. /// /// If you control the type you want to de/serialize, you can instead use the two derive macros, [`SerializeDisplay`] and [`DeserializeFromStr`]. /// They properly implement the traits [`serde::Serialize`] and [`serde::Deserialize`] such that user of the type no longer have to use the `serde_as` system. /// /// # Examples /// /// ```rust /// # #[cfg(feature = "macros")] { /// # use serde::{Deserialize, Serialize}; /// # use serde_json::json; /// # use serde_with::{serde_as, DisplayFromStr}; /// # /// #[serde_as] /// #[derive(Deserialize, Serialize)] /// struct A { /// #[serde_as(as = "DisplayFromStr")] /// mime: mime::Mime, /// #[serde_as(as = "DisplayFromStr")] /// number: u32, /// } /// /// let v: A = serde_json::from_value(json!({ /// "mime": "text/plain", /// "number": "159", /// })).unwrap(); /// assert_eq!(mime::TEXT_PLAIN, v.mime); /// assert_eq!(159, v.number); /// /// let x = A { /// mime: mime::STAR_STAR, /// number: 777, /// }; /// assert_eq!(json!({ "mime": "*/*", "number": "777" }), serde_json::to_value(x).unwrap()); /// # } /// ``` /// /// [`Display`]: std::fmt::Display /// [`FromStr`]: std::str::FromStr pub struct DisplayFromStr; /// Use the first format if [`De/Serializer::is_human_readable`], otherwise use the second /// /// If the second format is not specified, the normal /// [`Deserialize`](::serde::Deserialize)/[`Serialize`](::serde::Serialize) traits are used. /// /// # Examples /// /// ```rust /// # #[cfg(feature = "macros")] { /// # use serde::{Deserialize, Serialize}; /// # use serde_json::json; /// # use serde_with::{serde_as, DisplayFromStr, IfIsHumanReadable, DurationMilliSeconds, DurationSeconds}; /// use std::time::Duration; /// /// #[serde_as] /// #[derive(Deserialize, Serialize)] /// struct A { /// #[serde_as(as = "IfIsHumanReadable")] /// number: u32, /// } /// let x = A { /// number: 777, /// }; /// assert_eq!(json!({ "number": "777" }), serde_json::to_value(&x).unwrap()); /// assert_eq!(vec![145, 205, 3, 9], rmp_serde::to_vec(&x).unwrap()); /// /// #[serde_as] /// #[derive(Deserialize, Serialize)] /// struct B { /// #[serde_as(as = "IfIsHumanReadable")] /// duration: Duration, /// } /// let x = B { /// duration: Duration::from_millis(1500), /// }; /// assert_eq!(json!({ "duration": 1500 }), serde_json::to_value(&x).unwrap()); /// assert_eq!(vec![145, 2], rmp_serde::to_vec(&x).unwrap()); /// # } /// ``` /// [`De/Serializer::is_human_readable`]: serde::Serializer::is_human_readable /// [`is_human_readable`]: serde::Serializer::is_human_readable pub struct IfIsHumanReadable(PhantomData, PhantomData); /// De/Serialize a [`Option`] type while transforming the empty string to [`None`] /// /// Convert an [`Option`] from/to string using [`FromStr`] and [`Display`](::core::fmt::Display) implementations. /// An empty string is deserialized as [`None`] and a [`None`] vice versa. /// /// # Examples /// /// ``` /// # #[cfg(feature = "macros")] { /// # use serde::{Deserialize, Serialize}; /// # use serde_json::json; /// # use serde_with::{serde_as, NoneAsEmptyString}; /// # /// #[serde_as] /// #[derive(Deserialize, Serialize)] /// struct A { /// #[serde_as(as = "NoneAsEmptyString")] /// tags: Option, /// } /// /// let v: A = serde_json::from_value(json!({ "tags": "" })).unwrap(); /// assert_eq!(None, v.tags); /// /// let v: A = serde_json::from_value(json!({ "tags": "Hi" })).unwrap(); /// assert_eq!(Some("Hi".to_string()), v.tags); /// /// let x = A { /// tags: Some("This is text".to_string()), /// }; /// assert_eq!(json!({ "tags": "This is text" }), serde_json::to_value(x).unwrap()); /// /// let x = A { /// tags: None, /// }; /// assert_eq!(json!({ "tags": "" }), serde_json::to_value(x).unwrap()); /// # } /// ``` /// /// [`FromStr`]: std::str::FromStr pub struct NoneAsEmptyString; /// Deserialize value and return [`Default`] on error /// /// The main use case is ignoring error while deserializing. /// Instead of erroring, it simply deserializes the [`Default`] variant of the type. /// It is not possible to find the error location, i.e., which field had a deserialization error, with this method. /// During serialization this wrapper does nothing. /// The serialization behavior of the underlying type is preserved. /// The type must implement [`Default`] for this conversion to work. /// /// # Examples /// /// ``` /// # #[cfg(feature = "macros")] { /// # use serde::Deserialize; /// # use serde_with::{serde_as, DefaultOnError}; /// # /// #[serde_as] /// #[derive(Deserialize, Debug)] /// struct A { /// #[serde_as(deserialize_as = "DefaultOnError")] /// value: u32, /// } /// /// let a: A = serde_json::from_str(r#"{"value": 123}"#).unwrap(); /// assert_eq!(123, a.value); /// /// // null is of invalid type /// let a: A = serde_json::from_str(r#"{"value": null}"#).unwrap(); /// assert_eq!(0, a.value); /// /// // String is of invalid type /// let a: A = serde_json::from_str(r#"{"value": "123"}"#).unwrap(); /// assert_eq!(0, a.value); /// /// // Map is of invalid type /// let a: A = dbg!(serde_json::from_str(r#"{"value": {}}"#)).unwrap(); /// assert_eq!(0, a.value); /// /// // Missing entries still cause errors /// assert!(serde_json::from_str::(r#"{ }"#).is_err()); /// # } /// ``` /// /// Deserializing missing values can be supported by adding the `default` field attribute: /// /// ``` /// # #[cfg(feature = "macros")] { /// # use serde::Deserialize; /// # use serde_with::{serde_as, DefaultOnError}; /// # /// #[serde_as] /// #[derive(Deserialize)] /// struct B { /// #[serde_as(deserialize_as = "DefaultOnError")] /// #[serde(default)] /// value: u32, /// } /// /// let b: B = serde_json::from_str(r#"{ }"#).unwrap(); /// assert_eq!(0, b.value); /// # } /// ``` /// /// `DefaultOnError` can be combined with other conversion methods. /// In this example, we deserialize a `Vec`, each element is deserialized from a string. /// If the string does not parse as a number, then we get the default value of 0. /// /// ```rust /// # #[cfg(feature = "macros")] { /// # use serde::{Deserialize, Serialize}; /// # use serde_json::json; /// # use serde_with::{serde_as, DefaultOnError, DisplayFromStr}; /// # /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct C { /// #[serde_as(as = "Vec>")] /// value: Vec, /// } /// /// let c: C = serde_json::from_value(json!({ /// "value": ["1", "2", "a3", "", {}, "6"] /// })).unwrap(); /// assert_eq!(vec![1, 2, 0, 0, 0, 6], c.value); /// # } /// ``` #[cfg(feature = "alloc")] pub struct DefaultOnError(PhantomData); /// Deserialize [`Default`] from `null` values /// /// Instead of erroring on `null` values, it simply deserializes the [`Default`] variant of the type. /// During serialization this wrapper does nothing. /// The serialization behavior of the underlying type is preserved. /// The type must implement [`Default`] for this conversion to work. /// /// # Examples /// /// ``` /// # #[cfg(feature = "macros")] { /// # use serde::Deserialize; /// # use serde_with::{serde_as, DefaultOnNull}; /// # /// #[serde_as] /// #[derive(Deserialize, Debug)] /// struct A { /// #[serde_as(deserialize_as = "DefaultOnNull")] /// value: u32, /// } /// /// let a: A = serde_json::from_str(r#"{"value": 123}"#).unwrap(); /// assert_eq!(123, a.value); /// /// // null values are deserialized into the default, here 0 /// let a: A = serde_json::from_str(r#"{"value": null}"#).unwrap(); /// assert_eq!(0, a.value); /// # } /// ``` /// /// `DefaultOnNull` can be combined with other conversion methods. /// In this example, we deserialize a `Vec`, each element is deserialized from a string. /// If we encounter null, then we get the default value of 0. /// /// ```rust /// # #[cfg(feature = "macros")] { /// # use serde::{Deserialize, Serialize}; /// # use serde_json::json; /// # use serde_with::{serde_as, DefaultOnNull, DisplayFromStr}; /// # /// #[serde_as] /// #[derive(Serialize, Deserialize)] /// struct C { /// #[serde_as(as = "Vec>")] /// value: Vec, /// } /// /// let c: C = serde_json::from_value(json!({ /// "value": ["1", "2", null, null, "5"] /// })).unwrap(); /// assert_eq!(vec![1, 2, 0, 0, 5], c.value); /// # } /// ``` pub struct DefaultOnNull(PhantomData); /// Deserialize from bytes or string /// /// Any Rust [`String`] can be converted into bytes, i.e., `Vec`. /// Accepting both as formats while deserializing can be helpful while interacting with language /// which have a looser definition of string than Rust. /// /// # Example /// ```rust /// # #[cfg(feature = "macros")] { /// # use serde::{Deserialize, Serialize}; /// # use serde_json::json; /// # use serde_with::{serde_as, BytesOrString}; /// # /// #[serde_as] /// #[derive(Deserialize, Serialize)] /// struct A { /// #[serde_as(as = "BytesOrString")] /// bytes_or_string: Vec, /// } /// /// // Here we deserialize from a byte array ... /// let j = json!({ /// "bytes_or_string": [ /// 0, /// 1, /// 2, /// 3 /// ] /// }); /// /// let a: A = serde_json::from_value(j.clone()).unwrap(); /// assert_eq!(vec![0, 1, 2, 3], a.bytes_or_string); /// /// // and serialization works too. /// assert_eq!(j, serde_json::to_value(&a).unwrap()); /// /// // But we also support deserializing from a String /// let j = json!({ /// "bytes_or_string": "✨Works!" /// }); /// /// let a: A = serde_json::from_value(j).unwrap(); /// assert_eq!("✨Works!".as_bytes(), &*a.bytes_or_string); /// # } /// ``` /// [`String`]: std::string::String #[cfg(feature = "alloc")] pub struct BytesOrString; /// De/Serialize Durations as number of seconds. /// /// De/serialize durations as number of seconds with sub-second precision. /// Sub-second precision is *only* supported for [`DurationSecondsWithFrac`], but not for [`DurationSeconds`]. /// You can configure the serialization format between integers, floats, and stringified numbers with the `FORMAT` specifier and configure the deserialization with the `STRICTNESS` specifier. /// /// The `STRICTNESS` specifier can either be [`formats::Strict`] or [`formats::Flexible`] and defaults to [`formats::Strict`]. /// [`formats::Strict`] means that deserialization only supports the type given in `FORMAT`, e.g., if `FORMAT` is `u64` deserialization from a `f64` will error. /// [`formats::Flexible`] means that deserialization will perform a best effort to extract the correct duration and allows deserialization from any type. /// For example, deserializing `DurationSeconds` will discard any subsecond precision during deserialization from `f64` and will parse a `String` as an integer number. /// Serialization of integers will round the duration to the nearest value. /// /// This type also supports [`chrono::Duration`] with the `chrono_0_4`-[feature flag]. /// This type also supports [`time::Duration`][::time_0_3::Duration] with the `time_0_3`-[feature flag]. /// /// This table lists the available `FORMAT`s for the different duration types. /// The `FORMAT` specifier defaults to `u64`/`f64`. /// /// | Duration Type | Converter | Available `FORMAT`s | /// | --------------------- | ------------------------- | ------------------------ | /// | `std::time::Duration` | `DurationSeconds` | *`u64`*, `f64`, `String` | /// | `std::time::Duration` | `DurationSecondsWithFrac` | *`f64`*, `String` | /// | `chrono::Duration` | `DurationSeconds` | `i64`, `f64`, `String` | /// | `chrono::Duration` | `DurationSecondsWithFrac` | *`f64`*, `String` | /// | `time::Duration` | `DurationSeconds` | `i64`, `f64`, `String` | /// | `time::Duration` | `DurationSecondsWithFrac` | *`f64`*, `String` | /// /// # Examples /// /// ```rust /// # #[cfg(feature = "macros")] { /// # use serde::{Deserialize, Serialize}; /// # use serde_json::json; /// # use serde_with::{serde_as, DurationSeconds}; /// use std::time::Duration; /// /// #[serde_as] /// # #[derive(Debug, PartialEq)] /// #[derive(Deserialize, Serialize)] /// struct Durations { /// #[serde_as(as = "DurationSeconds")] /// d_u64: Duration, /// #[serde_as(as = "DurationSeconds")] /// d_f64: Duration, /// #[serde_as(as = "DurationSeconds")] /// d_string: Duration, /// } /// /// // Serialization /// // See how the values get rounded, since subsecond precision is not allowed. /// /// let d = Durations { /// d_u64: Duration::new(12345, 0), // Create from seconds and nanoseconds /// d_f64: Duration::new(12345, 500_000_000), /// d_string: Duration::new(12345, 999_999_999), /// }; /// // Observe the different data types /// let expected = json!({ /// "d_u64": 12345, /// "d_f64": 12346.0, /// "d_string": "12346", /// }); /// assert_eq!(expected, serde_json::to_value(d).unwrap()); /// /// // Deserialization works too /// // Subsecond precision in numbers will be rounded away /// /// let json = json!({ /// "d_u64": 12345, /// "d_f64": 12345.5, /// "d_string": "12346", /// }); /// let expected = Durations { /// d_u64: Duration::new(12345, 0), // Create from seconds and nanoseconds /// d_f64: Duration::new(12346, 0), /// d_string: Duration::new(12346, 0), /// }; /// assert_eq!(expected, serde_json::from_value(json).unwrap()); /// # } /// ``` /// /// [`chrono::Duration`] is also supported when using the `chrono_0_4` feature. /// It is a signed duration, thus can be de/serialized as an `i64` instead of a `u64`. /// /// ```rust /// # #[cfg(all(feature = "macros", feature = "chrono_0_4"))] { /// # use serde::{Deserialize, Serialize}; /// # use serde_json::json; /// # use serde_with::{serde_as, DurationSeconds}; /// # use chrono_0_4::Duration; /// # /* Ugliness to make the docs look nicer since I want to hide the rename of the chrono crate /// use chrono::Duration; /// # */ /// /// #[serde_as] /// # #[derive(Debug, PartialEq)] /// #[derive(Deserialize, Serialize)] /// struct Durations { /// #[serde_as(as = "DurationSeconds")] /// d_i64: Duration, /// #[serde_as(as = "DurationSeconds")] /// d_f64: Duration, /// #[serde_as(as = "DurationSeconds")] /// d_string: Duration, /// } /// /// // Serialization /// // See how the values get rounded, since subsecond precision is not allowed. /// /// let d = Durations { /// d_i64: Duration::seconds(-12345), /// d_f64: Duration::seconds(-12345) + Duration::milliseconds(500), /// d_string: Duration::seconds(12345) + Duration::nanoseconds(999_999_999), /// }; /// // Observe the different data types /// let expected = json!({ /// "d_i64": -12345, /// "d_f64": -12345.0, /// "d_string": "12346", /// }); /// assert_eq!(expected, serde_json::to_value(d).unwrap()); /// /// // Deserialization works too /// // Subsecond precision in numbers will be rounded away /// /// let json = json!({ /// "d_i64": -12345, /// "d_f64": -12345.5, /// "d_string": "12346", /// }); /// let expected = Durations { /// d_i64: Duration::seconds(-12345), /// d_f64: Duration::seconds(-12346), /// d_string: Duration::seconds(12346), /// }; /// assert_eq!(expected, serde_json::from_value(json).unwrap()); /// # } /// ``` /// /// [`chrono::Duration`]: ::chrono_0_4::Duration /// [feature flag]: https://docs.rs/serde_with/3.12.0/serde_with/guide/feature_flags/index.html pub struct DurationSeconds< FORMAT: formats::Format = u64, STRICTNESS: formats::Strictness = formats::Strict, >(PhantomData<(FORMAT, STRICTNESS)>); /// De/Serialize Durations as number of seconds. /// /// De/serialize durations as number of seconds with subsecond precision. /// Subsecond precision is *only* supported for [`DurationSecondsWithFrac`], but not for [`DurationSeconds`]. /// You can configure the serialization format between integers, floats, and stringified numbers with the `FORMAT` specifier and configure the deserialization with the `STRICTNESS` specifier. /// Serialization of integers will round the duration to the nearest value. /// /// The `STRICTNESS` specifier can either be [`formats::Strict`] or [`formats::Flexible`] and defaults to [`formats::Strict`]. /// [`formats::Strict`] means that deserialization only supports the type given in `FORMAT`, e.g., if `FORMAT` is `u64` deserialization from a `f64` will error. /// [`formats::Flexible`] means that deserialization will perform a best effort to extract the correct duration and allows deserialization from any type. /// For example, deserializing `DurationSeconds` will discard any subsecond precision during deserialization from `f64` and will parse a `String` as an integer number. /// /// This type also supports [`chrono::Duration`] with the `chrono`-[feature flag]. /// This type also supports [`time::Duration`][::time_0_3::Duration] with the `time_0_3`-[feature flag]. /// /// This table lists the available `FORMAT`s for the different duration types. /// The `FORMAT` specifier defaults to `u64`/`f64`. /// /// | Duration Type | Converter | Available `FORMAT`s | /// | --------------------- | ------------------------- | ------------------------ | /// | `std::time::Duration` | `DurationSeconds` | *`u64`*, `f64`, `String` | /// | `std::time::Duration` | `DurationSecondsWithFrac` | *`f64`*, `String` | /// | `chrono::Duration` | `DurationSeconds` | `i64`, `f64`, `String` | /// | `chrono::Duration` | `DurationSecondsWithFrac` | *`f64`*, `String` | /// | `time::Duration` | `DurationSeconds` | `i64`, `f64`, `String` | /// | `time::Duration` | `DurationSecondsWithFrac` | *`f64`*, `String` | /// /// # Examples /// /// ```rust /// # #[cfg(feature = "macros")] { /// # use serde::{Deserialize, Serialize}; /// # use serde_json::json; /// # use serde_with::{serde_as, DurationSecondsWithFrac}; /// use std::time::Duration; /// /// #[serde_as] /// # #[derive(Debug, PartialEq)] /// #[derive(Deserialize, Serialize)] /// struct Durations { /// #[serde_as(as = "DurationSecondsWithFrac")] /// d_f64: Duration, /// #[serde_as(as = "DurationSecondsWithFrac")] /// d_string: Duration, /// } /// /// // Serialization /// // See how the values get rounded, since subsecond precision is not allowed. /// /// let d = Durations { /// d_f64: Duration::new(12345, 500_000_000), // Create from seconds and nanoseconds /// d_string: Duration::new(12345, 999_999_000), /// }; /// // Observe the different data types /// let expected = json!({ /// "d_f64": 12345.5, /// "d_string": "12345.999999", /// }); /// assert_eq!(expected, serde_json::to_value(d).unwrap()); /// /// // Deserialization works too /// // Subsecond precision in numbers will be rounded away /// /// let json = json!({ /// "d_f64": 12345.5, /// "d_string": "12345.987654", /// }); /// let expected = Durations { /// d_f64: Duration::new(12345, 500_000_000), // Create from seconds and nanoseconds /// d_string: Duration::new(12345, 987_654_000), /// }; /// assert_eq!(expected, serde_json::from_value(json).unwrap()); /// # } /// ``` /// /// [`chrono::Duration`] is also supported when using the `chrono_0_4` feature. /// It is a signed duration, thus can be de/serialized as an `i64` instead of a `u64`. /// /// ```rust /// # #[cfg(all(feature = "macros", feature = "chrono_0_4"))] { /// # use serde::{Deserialize, Serialize}; /// # use serde_json::json; /// # use serde_with::{serde_as, DurationSecondsWithFrac}; /// # use chrono_0_4::Duration; /// # /* Ugliness to make the docs look nicer since I want to hide the rename of the chrono crate /// use chrono::Duration; /// # */ /// /// #[serde_as] /// # #[derive(Debug, PartialEq)] /// #[derive(Deserialize, Serialize)] /// struct Durations { /// #[serde_as(as = "DurationSecondsWithFrac")] /// d_f64: Duration, /// #[serde_as(as = "DurationSecondsWithFrac")] /// d_string: Duration, /// } /// /// // Serialization /// /// let d = Durations { /// d_f64: Duration::seconds(-12345) + Duration::milliseconds(500), /// d_string: Duration::seconds(12345) + Duration::nanoseconds(999_999_000), /// }; /// // Observe the different data types /// let expected = json!({ /// "d_f64": -12344.5, /// "d_string": "12345.999999", /// }); /// assert_eq!(expected, serde_json::to_value(d).unwrap()); /// /// // Deserialization works too /// /// let json = json!({ /// "d_f64": -12344.5, /// "d_string": "12345.987", /// }); /// let expected = Durations { /// d_f64: Duration::seconds(-12345) + Duration::milliseconds(500), /// d_string: Duration::seconds(12345) + Duration::milliseconds(987), /// }; /// assert_eq!(expected, serde_json::from_value(json).unwrap()); /// # } /// ``` /// /// [`chrono::Duration`]: ::chrono_0_4::Duration /// [feature flag]: https://docs.rs/serde_with/3.12.0/serde_with/guide/feature_flags/index.html pub struct DurationSecondsWithFrac< FORMAT: formats::Format = f64, STRICTNESS: formats::Strictness = formats::Strict, >(PhantomData<(FORMAT, STRICTNESS)>); /// Equivalent to [`DurationSeconds`] with milli-seconds as base unit. /// /// This type is equivalent to [`DurationSeconds`] except that each unit represents 1 milli-second instead of 1 second for [`DurationSeconds`]. pub struct DurationMilliSeconds< FORMAT: formats::Format = u64, STRICTNESS: formats::Strictness = formats::Strict, >(PhantomData<(FORMAT, STRICTNESS)>); /// Equivalent to [`DurationSecondsWithFrac`] with milli-seconds as base unit. /// /// This type is equivalent to [`DurationSecondsWithFrac`] except that each unit represents 1 milli-second instead of 1 second for [`DurationSecondsWithFrac`]. pub struct DurationMilliSecondsWithFrac< FORMAT: formats::Format = f64, STRICTNESS: formats::Strictness = formats::Strict, >(PhantomData<(FORMAT, STRICTNESS)>); /// Equivalent to [`DurationSeconds`] with micro-seconds as base unit. /// /// This type is equivalent to [`DurationSeconds`] except that each unit represents 1 micro-second instead of 1 second for [`DurationSeconds`]. pub struct DurationMicroSeconds< FORMAT: formats::Format = u64, STRICTNESS: formats::Strictness = formats::Strict, >(PhantomData<(FORMAT, STRICTNESS)>); /// Equivalent to [`DurationSecondsWithFrac`] with micro-seconds as base unit. /// /// This type is equivalent to [`DurationSecondsWithFrac`] except that each unit represents 1 micro-second instead of 1 second for [`DurationSecondsWithFrac`]. pub struct DurationMicroSecondsWithFrac< FORMAT: formats::Format = f64, STRICTNESS: formats::Strictness = formats::Strict, >(PhantomData<(FORMAT, STRICTNESS)>); /// Equivalent to [`DurationSeconds`] with nano-seconds as base unit. /// /// This type is equivalent to [`DurationSeconds`] except that each unit represents 1 nano-second instead of 1 second for [`DurationSeconds`]. pub struct DurationNanoSeconds< FORMAT: formats::Format = u64, STRICTNESS: formats::Strictness = formats::Strict, >(PhantomData<(FORMAT, STRICTNESS)>); /// Equivalent to [`DurationSecondsWithFrac`] with nano-seconds as base unit. /// /// This type is equivalent to [`DurationSecondsWithFrac`] except that each unit represents 1 nano-second instead of 1 second for [`DurationSecondsWithFrac`]. pub struct DurationNanoSecondsWithFrac< FORMAT: formats::Format = f64, STRICTNESS: formats::Strictness = formats::Strict, >(PhantomData<(FORMAT, STRICTNESS)>); /// De/Serialize timestamps as seconds since the UNIX epoch /// /// De/serialize timestamps as seconds since the UNIX epoch. /// Subsecond precision is *only* supported for [`TimestampSecondsWithFrac`], but not for [`TimestampSeconds`]. /// You can configure the serialization format between integers, floats, and stringified numbers with the `FORMAT` specifier and configure the deserialization with the `STRICTNESS` specifier. /// Serialization of integers will round the timestamp to the nearest value. /// /// The `STRICTNESS` specifier can either be [`formats::Strict`] or [`formats::Flexible`] and defaults to [`formats::Strict`]. /// [`formats::Strict`] means that deserialization only supports the type given in `FORMAT`, e.g., if `FORMAT` is `i64` deserialization from a `f64` will error. /// [`formats::Flexible`] means that deserialization will perform a best effort to extract the correct timestamp and allows deserialization from any type. /// For example, deserializing `TimestampSeconds` will discard any subsecond precision during deserialization from `f64` and will parse a `String` as an integer number. /// /// This type also supports [`chrono::DateTime`] with the `chrono_0_4`-[feature flag]. /// This type also supports [`time::OffsetDateTime`][::time_0_3::OffsetDateTime] and [`time::PrimitiveDateTime`][::time_0_3::PrimitiveDateTime] with the `time_0_3`-[feature flag]. /// /// This table lists the available `FORMAT`s for the different timestamp types. /// The `FORMAT` specifier defaults to `i64` or `f64`. /// /// | Timestamp Type | Converter | Available `FORMAT`s | /// | ------------------------- | -------------------------- | ------------------------ | /// | `std::time::SystemTime` | `TimestampSeconds` | *`i64`*, `f64`, `String` | /// | `std::time::SystemTime` | `TimestampSecondsWithFrac` | *`f64`*, `String` | /// | `chrono::DateTime` | `TimestampSeconds` | *`i64`*, `f64`, `String` | /// | `chrono::DateTime` | `TimestampSecondsWithFrac` | *`f64`*, `String` | /// | `chrono::DateTime` | `TimestampSeconds` | *`i64`*, `f64`, `String` | /// | `chrono::DateTime` | `TimestampSecondsWithFrac` | *`f64`*, `String` | /// | `chrono::NaiveDateTime` | `TimestampSeconds` | *`i64`*, `f64`, `String` | /// | `chrono::NaiveDateTime` | `TimestampSecondsWithFrac` | *`f64`*, `String` | /// | `time::OffsetDateTime` | `TimestampSeconds` | *`i64`*, `f64`, `String` | /// | `time::OffsetDateTime` | `TimestampSecondsWithFrac` | *`f64`*, `String` | /// | `time::PrimitiveDateTime` | `TimestampSeconds` | *`i64`*, `f64`, `String` | /// | `time::PrimitiveDateTime` | `TimestampSecondsWithFrac` | *`f64`*, `String` | /// /// # Examples /// /// ```rust /// # #[cfg(feature = "macros")] { /// # use serde::{Deserialize, Serialize}; /// # use serde_json::json; /// # use serde_with::{serde_as, TimestampSeconds}; /// use std::time::{Duration, SystemTime}; /// /// #[serde_as] /// # #[derive(Debug, PartialEq)] /// #[derive(Deserialize, Serialize)] /// struct Timestamps { /// #[serde_as(as = "TimestampSeconds")] /// st_i64: SystemTime, /// #[serde_as(as = "TimestampSeconds")] /// st_f64: SystemTime, /// #[serde_as(as = "TimestampSeconds")] /// st_string: SystemTime, /// } /// /// // Serialization /// // See how the values get rounded, since subsecond precision is not allowed. /// /// let ts = Timestamps { /// st_i64: SystemTime::UNIX_EPOCH.checked_add(Duration::new(12345, 0)).unwrap(), /// st_f64: SystemTime::UNIX_EPOCH.checked_add(Duration::new(12345, 500_000_000)).unwrap(), /// st_string: SystemTime::UNIX_EPOCH.checked_add(Duration::new(12345, 999_999_999)).unwrap(), /// }; /// // Observe the different data types /// let expected = json!({ /// "st_i64": 12345, /// "st_f64": 12346.0, /// "st_string": "12346", /// }); /// assert_eq!(expected, serde_json::to_value(ts).unwrap()); /// /// // Deserialization works too /// // Subsecond precision in numbers will be rounded away /// /// let json = json!({ /// "st_i64": 12345, /// "st_f64": 12345.5, /// "st_string": "12346", /// }); /// let expected = Timestamps { /// st_i64: SystemTime::UNIX_EPOCH.checked_add(Duration::new(12345, 0)).unwrap(), /// st_f64: SystemTime::UNIX_EPOCH.checked_add(Duration::new(12346, 0)).unwrap(), /// st_string: SystemTime::UNIX_EPOCH.checked_add(Duration::new(12346, 0)).unwrap(), /// }; /// assert_eq!(expected, serde_json::from_value(json).unwrap()); /// # } /// ``` /// /// [`chrono::DateTime`] and [`chrono::DateTime`] are also supported when using the `chrono` feature. /// Like [`SystemTime`], it is a signed timestamp, thus can be de/serialized as an `i64`. /// /// ```rust /// # #[cfg(all(feature = "macros", feature = "chrono_0_4"))] { /// # use serde::{Deserialize, Serialize}; /// # use serde_json::json; /// # use serde_with::{serde_as, TimestampSeconds}; /// # use chrono_0_4::{DateTime, Local, TimeZone, Utc}; /// # /* Ugliness to make the docs look nicer since I want to hide the rename of the chrono crate /// use chrono::{DateTime, Local, TimeZone, Utc}; /// # */ /// /// #[serde_as] /// # #[derive(Debug, PartialEq)] /// #[derive(Deserialize, Serialize)] /// struct Timestamps { /// #[serde_as(as = "TimestampSeconds")] /// dt_i64: DateTime, /// #[serde_as(as = "TimestampSeconds")] /// dt_f64: DateTime, /// #[serde_as(as = "TimestampSeconds")] /// dt_string: DateTime, /// } /// /// // Serialization /// // See how the values get rounded, since subsecond precision is not allowed. /// /// let ts = Timestamps { /// dt_i64: Utc.timestamp_opt(-12345, 0).unwrap(), /// dt_f64: Local.timestamp_opt(-12345, 500_000_000).unwrap(), /// dt_string: Utc.timestamp_opt(12345, 999_999_999).unwrap(), /// }; /// // Observe the different data types /// let expected = json!({ /// "dt_i64": -12345, /// "dt_f64": -12345.0, /// "dt_string": "12346", /// }); /// assert_eq!(expected, serde_json::to_value(ts).unwrap()); /// /// // Deserialization works too /// // Subsecond precision in numbers will be rounded away /// /// let json = json!({ /// "dt_i64": -12345, /// "dt_f64": -12345.5, /// "dt_string": "12346", /// }); /// let expected = Timestamps { /// dt_i64: Utc.timestamp_opt(-12345, 0).unwrap(), /// dt_f64: Local.timestamp_opt(-12346, 0).unwrap(), /// dt_string: Utc.timestamp_opt(12346, 0).unwrap(), /// }; /// assert_eq!(expected, serde_json::from_value(json).unwrap()); /// # } /// ``` /// /// [`SystemTime`]: std::time::SystemTime /// [`chrono::DateTime`]: ::chrono_0_4::DateTime /// [`chrono::DateTime`]: ::chrono_0_4::DateTime /// [feature flag]: https://docs.rs/serde_with/3.12.0/serde_with/guide/feature_flags/index.html pub struct TimestampSeconds< FORMAT: formats::Format = i64, STRICTNESS: formats::Strictness = formats::Strict, >(PhantomData<(FORMAT, STRICTNESS)>); /// De/Serialize timestamps as seconds since the UNIX epoch /// /// De/serialize timestamps as seconds since the UNIX epoch. /// Subsecond precision is *only* supported for [`TimestampSecondsWithFrac`], but not for [`TimestampSeconds`]. /// You can configure the serialization format between integers, floats, and stringified numbers with the `FORMAT` specifier and configure the deserialization with the `STRICTNESS` specifier. /// Serialization of integers will round the timestamp to the nearest value. /// /// The `STRICTNESS` specifier can either be [`formats::Strict`] or [`formats::Flexible`] and defaults to [`formats::Strict`]. /// [`formats::Strict`] means that deserialization only supports the type given in `FORMAT`, e.g., if `FORMAT` is `i64` deserialization from a `f64` will error. /// [`formats::Flexible`] means that deserialization will perform a best effort to extract the correct timestamp and allows deserialization from any type. /// For example, deserializing `TimestampSeconds` will discard any subsecond precision during deserialization from `f64` and will parse a `String` as an integer number. /// /// This type also supports [`chrono::DateTime`] and [`chrono::NaiveDateTime`][NaiveDateTime] with the `chrono`-[feature flag]. /// This type also supports [`time::OffsetDateTime`][::time_0_3::OffsetDateTime] and [`time::PrimitiveDateTime`][::time_0_3::PrimitiveDateTime] with the `time_0_3`-[feature flag]. /// /// This table lists the available `FORMAT`s for the different timestamp types. /// The `FORMAT` specifier defaults to `i64` or `f64`. /// /// | Timestamp Type | Converter | Available `FORMAT`s | /// | ------------------------- | -------------------------- | ------------------------ | /// | `std::time::SystemTime` | `TimestampSeconds` | *`i64`*, `f64`, `String` | /// | `std::time::SystemTime` | `TimestampSecondsWithFrac` | *`f64`*, `String` | /// | `chrono::DateTime` | `TimestampSeconds` | *`i64`*, `f64`, `String` | /// | `chrono::DateTime` | `TimestampSecondsWithFrac` | *`f64`*, `String` | /// | `chrono::DateTime` | `TimestampSeconds` | *`i64`*, `f64`, `String` | /// | `chrono::DateTime` | `TimestampSecondsWithFrac` | *`f64`*, `String` | /// | `chrono::NaiveDateTime` | `TimestampSeconds` | *`i64`*, `f64`, `String` | /// | `chrono::NaiveDateTime` | `TimestampSecondsWithFrac` | *`f64`*, `String` | /// | `time::OffsetDateTime` | `TimestampSeconds` | *`i64`*, `f64`, `String` | /// | `time::OffsetDateTime` | `TimestampSecondsWithFrac` | *`f64`*, `String` | /// | `time::PrimitiveDateTime` | `TimestampSeconds` | *`i64`*, `f64`, `String` | /// | `time::PrimitiveDateTime` | `TimestampSecondsWithFrac` | *`f64`*, `String` | /// /// # Examples /// /// ```rust /// # #[cfg(feature = "macros")] { /// # use serde::{Deserialize, Serialize}; /// # use serde_json::json; /// # use serde_with::{serde_as, TimestampSecondsWithFrac}; /// use std::time::{Duration, SystemTime}; /// /// #[serde_as] /// # #[derive(Debug, PartialEq)] /// #[derive(Deserialize, Serialize)] /// struct Timestamps { /// #[serde_as(as = "TimestampSecondsWithFrac")] /// st_f64: SystemTime, /// #[serde_as(as = "TimestampSecondsWithFrac")] /// st_string: SystemTime, /// } /// /// // Serialization /// // See how the values get rounded, since subsecond precision is not allowed. /// /// let ts = Timestamps { /// st_f64: SystemTime::UNIX_EPOCH.checked_add(Duration::new(12345, 500_000_000)).unwrap(), /// st_string: SystemTime::UNIX_EPOCH.checked_add(Duration::new(12345, 999_999_000)).unwrap(), /// }; /// // Observe the different data types /// let expected = json!({ /// "st_f64": 12345.5, /// "st_string": "12345.999999", /// }); /// assert_eq!(expected, serde_json::to_value(ts).unwrap()); /// /// // Deserialization works too /// // Subsecond precision in numbers will be rounded away /// /// let json = json!({ /// "st_f64": 12345.5, /// "st_string": "12345.987654", /// }); /// let expected = Timestamps { /// st_f64: SystemTime::UNIX_EPOCH.checked_add(Duration::new(12345, 500_000_000)).unwrap(), /// st_string: SystemTime::UNIX_EPOCH.checked_add(Duration::new(12345, 987_654_000)).unwrap(), /// }; /// assert_eq!(expected, serde_json::from_value(json).unwrap()); /// # } /// ``` /// /// [`chrono::DateTime`] and [`chrono::DateTime`] are also supported when using the `chrono_0_4` feature. /// Like [`SystemTime`], it is a signed timestamp, thus can be de/serialized as an `i64`. /// /// ```rust /// # #[cfg(all(feature = "macros", feature = "chrono_0_4"))] { /// # use serde::{Deserialize, Serialize}; /// # use serde_json::json; /// # use serde_with::{serde_as, TimestampSecondsWithFrac}; /// # use chrono_0_4::{DateTime, Local, TimeZone, Utc}; /// # /* Ugliness to make the docs look nicer since I want to hide the rename of the chrono crate /// use chrono::{DateTime, Local, TimeZone, Utc}; /// # */ /// /// #[serde_as] /// # #[derive(Debug, PartialEq)] /// #[derive(Deserialize, Serialize)] /// struct Timestamps { /// #[serde_as(as = "TimestampSecondsWithFrac")] /// dt_f64: DateTime, /// #[serde_as(as = "TimestampSecondsWithFrac")] /// dt_string: DateTime, /// } /// /// // Serialization /// /// let ts = Timestamps { /// dt_f64: Utc.timestamp_opt(-12345, 500_000_000).unwrap(), /// dt_string: Local.timestamp_opt(12345, 999_999_000).unwrap(), /// }; /// // Observe the different data types /// let expected = json!({ /// "dt_f64": -12344.5, /// "dt_string": "12345.999999", /// }); /// assert_eq!(expected, serde_json::to_value(ts).unwrap()); /// /// // Deserialization works too /// /// let json = json!({ /// "dt_f64": -12344.5, /// "dt_string": "12345.987", /// }); /// let expected = Timestamps { /// dt_f64: Utc.timestamp_opt(-12345, 500_000_000).unwrap(), /// dt_string: Local.timestamp_opt(12345, 987_000_000).unwrap(), /// }; /// assert_eq!(expected, serde_json::from_value(json).unwrap()); /// # } /// ``` /// /// [`SystemTime`]: std::time::SystemTime /// [`chrono::DateTime`]: ::chrono_0_4::DateTime /// [`chrono::DateTime`]: ::chrono_0_4::DateTime /// [`chrono::DateTime`]: ::chrono_0_4::DateTime /// [NaiveDateTime]: ::chrono_0_4::NaiveDateTime /// [feature flag]: https://docs.rs/serde_with/3.12.0/serde_with/guide/feature_flags/index.html pub struct TimestampSecondsWithFrac< FORMAT: formats::Format = f64, STRICTNESS: formats::Strictness = formats::Strict, >(PhantomData<(FORMAT, STRICTNESS)>); /// Equivalent to [`TimestampSeconds`] with milli-seconds as base unit. /// /// This type is equivalent to [`TimestampSeconds`] except that each unit represents 1 milli-second instead of 1 second for [`TimestampSeconds`]. pub struct TimestampMilliSeconds< FORMAT: formats::Format = i64, STRICTNESS: formats::Strictness = formats::Strict, >(PhantomData<(FORMAT, STRICTNESS)>); /// Equivalent to [`TimestampSecondsWithFrac`] with milli-seconds as base unit. /// /// This type is equivalent to [`TimestampSecondsWithFrac`] except that each unit represents 1 milli-second instead of 1 second for [`TimestampSecondsWithFrac`]. pub struct TimestampMilliSecondsWithFrac< FORMAT: formats::Format = f64, STRICTNESS: formats::Strictness = formats::Strict, >(PhantomData<(FORMAT, STRICTNESS)>); /// Equivalent to [`TimestampSeconds`] with micro-seconds as base unit. /// /// This type is equivalent to [`TimestampSeconds`] except that each unit represents 1 micro-second instead of 1 second for [`TimestampSeconds`]. pub struct TimestampMicroSeconds< FORMAT: formats::Format = i64, STRICTNESS: formats::Strictness = formats::Strict, >(PhantomData<(FORMAT, STRICTNESS)>); /// Equivalent to [`TimestampSecondsWithFrac`] with micro-seconds as base unit. /// /// This type is equivalent to [`TimestampSecondsWithFrac`] except that each unit represents 1 micro-second instead of 1 second for [`TimestampSecondsWithFrac`]. pub struct TimestampMicroSecondsWithFrac< FORMAT: formats::Format = f64, STRICTNESS: formats::Strictness = formats::Strict, >(PhantomData<(FORMAT, STRICTNESS)>); /// Equivalent to [`TimestampSeconds`] with nano-seconds as base unit. /// /// This type is equivalent to [`TimestampSeconds`] except that each unit represents 1 nano-second instead of 1 second for [`TimestampSeconds`]. pub struct TimestampNanoSeconds< FORMAT: formats::Format = i64, STRICTNESS: formats::Strictness = formats::Strict, >(PhantomData<(FORMAT, STRICTNESS)>); /// Equivalent to [`TimestampSecondsWithFrac`] with nano-seconds as base unit. /// /// This type is equivalent to [`TimestampSecondsWithFrac`] except that each unit represents 1 nano-second instead of 1 second for [`TimestampSecondsWithFrac`]. pub struct TimestampNanoSecondsWithFrac< FORMAT: formats::Format = f64, STRICTNESS: formats::Strictness = formats::Strict, >(PhantomData<(FORMAT, STRICTNESS)>); /// Optimized handling of owned and borrowed byte representations. /// /// Serialization of byte sequences like `&[u8]` or `Vec` is quite inefficient since each value will be serialized individually. /// This converter type optimizes the serialization and deserialization. /// /// This is a port of the [`serde_bytes`] crate making it compatible with the `serde_as` annotation, which allows it to be used in more cases than provided by [`serde_bytes`]. /// /// The type provides de/serialization for these types: /// /// * `[u8; N]`, not possible using `serde_bytes` /// * `&[u8; N]`, not possible using `serde_bytes` /// * `&[u8]` /// * `Box<[u8; N]>`, not possible using `serde_bytes` /// * `Box<[u8]>` /// * `Vec` /// * `Cow<'_, [u8]>` /// * `Cow<'_, [u8; N]>`, not possible using `serde_bytes` /// /// [`serde_bytes`]: https://crates.io/crates/serde_bytes /// /// # Examples /// /// ``` /// # #[cfg(feature = "macros")] { /// # use serde::{Deserialize, Serialize}; /// # use serde_with::{serde_as, Bytes}; /// # use std::borrow::Cow; /// # /// #[serde_as] /// # #[derive(Debug, PartialEq)] /// #[derive(Deserialize, Serialize)] /// struct Test<'a> { /// #[serde_as(as = "Bytes")] /// array: [u8; 15], /// #[serde_as(as = "Bytes")] /// boxed: Box<[u8]>, /// #[serde_as(as = "Bytes")] /// #[serde(borrow)] /// cow: Cow<'a, [u8]>, /// #[serde_as(as = "Bytes")] /// #[serde(borrow)] /// cow_array: Cow<'a, [u8; 15]>, /// #[serde_as(as = "Bytes")] /// vec: Vec, /// } /// /// let value = Test { /// array: *b"0123456789ABCDE", /// boxed: b"...".to_vec().into_boxed_slice(), /// cow: Cow::Borrowed(b"FooBar"), /// cow_array: Cow::Borrowed(&[42u8; 15]), /// vec: vec![0x41, 0x61, 0x21], /// }; /// let expected = r#"( /// array: "MDEyMzQ1Njc4OUFCQ0RF", /// boxed: "Li4u", /// cow: "Rm9vQmFy", /// cow_array: "KioqKioqKioqKioqKioq", /// vec: "QWEh", /// )"#; /// /// # let pretty_config = ron::ser::PrettyConfig::new() /// # .new_line("\n".into()); /// assert_eq!(expected, ron::ser::to_string_pretty(&value, pretty_config).unwrap()); /// assert_eq!(value, ron::from_str(expected).unwrap()); /// # } /// ``` /// /// Fully borrowed types can also be used but you'll need a Deserializer that /// supports Serde's 0-copy deserialization: /// /// ``` /// # #[cfg(feature = "macros")] { /// # use serde::{Deserialize, Serialize}; /// # use serde_with::{serde_as, Bytes}; /// # /// #[serde_as] /// # #[derive(Debug, PartialEq)] /// #[derive(Deserialize, Serialize)] /// struct TestBorrows<'a> { /// #[serde_as(as = "Bytes")] /// #[serde(borrow)] /// array_buf: &'a [u8; 15], /// #[serde_as(as = "Bytes")] /// #[serde(borrow)] /// buf: &'a [u8], /// } /// /// let value = TestBorrows { /// array_buf: &[10u8; 15], /// buf: &[20u8, 21u8, 22u8], /// }; /// let expected = r#"( /// array_buf: "CgoKCgoKCgoKCgoKCgoK", /// buf: "FBUW", /// )"#; /// /// # let pretty_config = ron::ser::PrettyConfig::new() /// # .new_line("\n".into()); /// assert_eq!(expected, ron::ser::to_string_pretty(&value, pretty_config).unwrap()); /// // RON doesn't support borrowed deserialization of byte arrays /// # } /// ``` /// /// ## Alternative to [`BytesOrString`] /// /// The [`Bytes`] can replace [`BytesOrString`]. /// [`Bytes`] is implemented for more types, which makes it better. /// The serialization behavior of [`Bytes`] differs from [`BytesOrString`], therefore only `deserialize_as` should be used. /// /// ```rust /// # #[cfg(feature = "macros")] { /// # use serde::Deserialize; /// # use serde_json::json; /// # use serde_with::{serde_as, Bytes}; /// # /// #[serde_as] /// # #[derive(Debug, PartialEq)] /// #[derive(Deserialize, serde::Serialize)] /// struct Test { /// #[serde_as(deserialize_as = "Bytes")] /// from_bytes: Vec, /// #[serde_as(deserialize_as = "Bytes")] /// from_str: Vec, /// } /// /// // Different serialized values ... /// let j = json!({ /// "from_bytes": [70,111,111,45,66,97,114], /// "from_str": "Foo-Bar", /// }); /// /// // can be deserialized ... /// let test = Test { /// from_bytes: b"Foo-Bar".to_vec(), /// from_str: b"Foo-Bar".to_vec(), /// }; /// assert_eq!(test, serde_json::from_value(j).unwrap()); /// /// // and serialization will always be a byte sequence /// # assert_eq!(json!( /// { /// "from_bytes": [70,111,111,45,66,97,114], /// "from_str": [70,111,111,45,66,97,114], /// } /// # ), serde_json::to_value(&test).unwrap()); /// # } /// ``` pub struct Bytes; /// Deserialize one or many elements /// /// Sometimes it is desirable to have a shortcut in writing 1-element lists in a config file. /// Usually, this is done by either writing a list or the list element itself. /// This distinction is not semantically important on the Rust side, thus both forms should deserialize into the same `Vec`. /// /// The `OneOrMany` adapter achieves exactly this use case. /// The serialization behavior can be tweaked to either always serialize as a list using [`PreferMany`] or to serialize as the inner element if possible using [`PreferOne`]. /// By default, [`PreferOne`] is assumed, which can also be omitted like `OneOrMany<_>`. /// /// [`PreferMany`]: crate::formats::PreferMany /// [`PreferOne`]: crate::formats::PreferOne /// /// # Examples /// /// ```rust /// # #[cfg(feature = "macros")] { /// # use serde::Deserialize; /// # use serde_json::json; /// # use serde_with::{serde_as, OneOrMany}; /// # use serde_with::formats::{PreferOne, PreferMany}; /// # /// #[serde_as] /// # #[derive(Debug, PartialEq)] /// #[derive(Deserialize, serde::Serialize)] /// struct Data { /// #[serde_as(as = "OneOrMany<_, PreferOne>")] /// countries: Vec, /// #[serde_as(as = "OneOrMany<_, PreferMany>")] /// cities: Vec, /// } /// /// // The adapter allows deserializing a `Vec` from either /// // a single element /// let j = json!({ /// "countries": "Spain", /// "cities": "Berlin", /// }); /// assert!(serde_json::from_value::(j).is_ok()); /// /// // or from a list. /// let j = json!({ /// "countries": ["Germany", "France"], /// "cities": ["Amsterdam"], /// }); /// assert!(serde_json::from_value::(j).is_ok()); /// /// // For serialization you can choose how a single element should be encoded. /// // Either directly, with `PreferOne` (default), or as a list with `PreferMany`. /// let data = Data { /// countries: vec!["Spain".to_string()], /// cities: vec!["Berlin".to_string()], /// }; /// let j = json!({ /// "countries": "Spain", /// "cities": ["Berlin"], /// }); /// assert_eq!(serde_json::to_value(data).unwrap(), j); /// # } /// ``` #[cfg(feature = "alloc")] pub struct OneOrMany(PhantomData<(T, FORMAT)>); /// Try multiple deserialization options until one succeeds. /// /// This adapter allows you to specify a list of deserialization options. /// They are tried in order and the first one working is applied. /// Serialization always picks the first option. /// /// `PickFirst` has one type parameter which must be instantiated with a tuple of two, three, or four elements. /// For example, `PickFirst<(_, DisplayFromStr)>` on a field of type `u32` allows deserializing from a number or from a string via the `FromStr` trait. /// The value will be serialized as a number, since that is what the first type `_` indicates. /// /// # Examples /// /// Deserialize a number from either a number or a string. /// /// ```rust /// # #[cfg(feature = "macros")] { /// # use serde::{Deserialize, Serialize}; /// # use serde_json::json; /// # use serde_with::{serde_as, DisplayFromStr, PickFirst}; /// # /// #[serde_as] /// # #[derive(Debug, PartialEq)] /// #[derive(Deserialize, Serialize)] /// struct Data { /// #[serde_as(as = "PickFirst<(_, DisplayFromStr)>")] /// as_number: u32, /// #[serde_as(as = "PickFirst<(DisplayFromStr, _)>")] /// as_string: u32, /// } /// let data = Data { /// as_number: 123, /// as_string: 456 /// }; /// /// // Both fields can be deserialized from numbers: /// let j = json!({ /// "as_number": 123, /// "as_string": 456, /// }); /// assert_eq!(data, serde_json::from_value(j).unwrap()); /// /// // or from a string: /// let j = json!({ /// "as_number": "123", /// "as_string": "456", /// }); /// assert_eq!(data, serde_json::from_value(j).unwrap()); /// /// // For serialization the first type in the tuple determines the behavior. /// // The `as_number` field will use the normal `Serialize` behavior and produce a number, /// // while `as_string` used `Display` to produce a string. /// let expected = json!({ /// "as_number": 123, /// "as_string": "456", /// }); /// assert_eq!(expected, serde_json::to_value(&data).unwrap()); /// # } /// ``` #[cfg(feature = "alloc")] pub struct PickFirst(PhantomData); /// Serialize value by converting to/from a proxy type with serde support. /// /// This adapter serializes a type `O` by converting it into a second type `T` and serializing `T`. /// Deserializing works analogue, by deserializing a `T` and then converting into `O`. /// /// ```rust /// # #[cfg(any())] { /// struct S { /// #[serde_as(as = "FromInto")] /// value: O, /// } /// # } /// ``` /// /// For serialization `O` needs to be `O: Into + Clone`. /// For deserialization the opposite `T: Into` is required. /// The `Clone` bound is required since `serialize` operates on a reference but `Into` implementations on references are uncommon. /// /// **Note**: [`TryFromInto`] is the more generalized version of this adapter which uses the [`TryInto`] trait instead. /// /// # Example /// /// ```rust /// # #[cfg(feature = "macros")] { /// # use serde::{Deserialize, Serialize}; /// # use serde_json::json; /// # use serde_with::{serde_as, FromInto}; /// # /// #[derive(Clone, Debug, PartialEq)] /// struct Rgb { /// red: u8, /// green: u8, /// blue: u8, /// } /// /// # /* /// impl From<(u8, u8, u8)> for Rgb { ... } /// impl From for (u8, u8, u8) { ... } /// # */ /// # /// # impl From<(u8, u8, u8)> for Rgb { /// # fn from(v: (u8, u8, u8)) -> Self { /// # Rgb { /// # red: v.0, /// # green: v.1, /// # blue: v.2, /// # } /// # } /// # } /// # /// # impl From for (u8, u8, u8) { /// # fn from(v: Rgb) -> Self { /// # (v.red, v.green, v.blue) /// # } /// # } /// /// #[serde_as] /// # #[derive(Debug, PartialEq)] /// #[derive(Deserialize, Serialize)] /// struct Color { /// #[serde_as(as = "FromInto<(u8, u8, u8)>")] /// rgb: Rgb, /// } /// let color = Color { /// rgb: Rgb { /// red: 128, /// green: 64, /// blue: 32, /// }, /// }; /// /// // Define our expected JSON form /// let j = json!({ /// "rgb": [128, 64, 32], /// }); /// // Ensure serialization and deserialization produce the expected results /// assert_eq!(j, serde_json::to_value(&color).unwrap()); /// assert_eq!(color, serde_json::from_value(j).unwrap()); /// # } /// ``` pub struct FromInto(PhantomData); /// Serialize a reference value by converting to/from a proxy type with serde support. /// /// This adapter serializes a type `O` by converting it into a second type `T` and serializing `T`. /// Deserializing works analogue, by deserializing a `T` and then converting into `O`. /// /// ```rust /// # #[cfg(any())] { /// struct S { /// #[serde_as(as = "FromIntoRef")] /// value: O, /// } /// # } /// ``` /// /// For serialization `O` needs to be `for<'a> &'a O: Into`. /// For deserialization the opposite `T: Into` is required. /// /// **Note**: [`TryFromIntoRef`] is the more generalized version of this adapter which uses the [`TryInto`] trait instead. /// /// # Example /// /// ```rust /// # #[cfg(feature = "macros")] { /// # use serde::{Deserialize, Serialize}; /// # use serde_json::json; /// # use serde_with::{serde_as, FromIntoRef}; /// # /// #[derive(Debug, PartialEq)] /// struct Rgb { /// red: u8, /// green: u8, /// blue: u8, /// } /// /// # /* /// impl From<(u8, u8, u8)> for Rgb { ... } /// impl From for (u8, u8, u8) { ... } /// # */ /// # /// # impl From<(u8, u8, u8)> for Rgb { /// # fn from(v: (u8, u8, u8)) -> Self { /// # Rgb { /// # red: v.0, /// # green: v.1, /// # blue: v.2, /// # } /// # } /// # } /// # /// # impl<'a> From<&'a Rgb> for (u8, u8, u8) { /// # fn from(v: &'a Rgb) -> Self { /// # (v.red, v.green, v.blue) /// # } /// # } /// /// #[serde_as] /// # #[derive(Debug, PartialEq)] /// #[derive(Deserialize, Serialize)] /// struct Color { /// #[serde_as(as = "FromIntoRef<(u8, u8, u8)>")] /// rgb: Rgb, /// } /// let color = Color { /// rgb: Rgb { /// red: 128, /// green: 64, /// blue: 32, /// }, /// }; /// /// // Define our expected JSON form /// let j = json!({ /// "rgb": [128, 64, 32], /// }); /// // Ensure serialization and deserialization produce the expected results /// assert_eq!(j, serde_json::to_value(&color).unwrap()); /// assert_eq!(color, serde_json::from_value(j).unwrap()); /// # } /// ``` pub struct FromIntoRef(PhantomData); /// Serialize value by converting to/from a proxy type with serde support. /// /// This adapter serializes a type `O` by converting it into a second type `T` and serializing `T`. /// Deserializing works analogue, by deserializing a `T` and then converting into `O`. /// /// ```rust /// # #[cfg(any())] { /// struct S { /// #[serde_as(as = "TryFromInto")] /// value: O, /// } /// # } /// ``` /// /// For serialization `O` needs to be `O: TryInto + Clone`. /// For deserialization the opposite `T: TryInto` is required. /// The `Clone` bound is required since `serialize` operates on a reference but `TryInto` implementations on references are uncommon. /// In both cases the `TryInto::Error` type must implement [`Display`](std::fmt::Display). /// /// **Note**: [`FromInto`] is the more specialized version of this adapter which uses the infallible [`Into`] trait instead. /// [`TryFromInto`] is strictly more general and can also be used where [`FromInto`] is applicable. /// The example shows a use case, when only the deserialization behavior is fallible, but not serializing. /// /// # Example /// /// ```rust /// # #[cfg(feature = "macros")] { /// # use serde::{Deserialize, Serialize}; /// # use serde_json::json; /// # use serde_with::{serde_as, TryFromInto}; /// # /// #[derive(Clone, Debug, PartialEq)] /// enum Boollike { /// True, /// False, /// } /// /// # /* /// impl From for u8 { ... } /// # */ /// # /// impl TryFrom for Boollike { /// type Error = String; /// fn try_from(v: u8) -> Result { /// match v { /// 0 => Ok(Boollike::False), /// 1 => Ok(Boollike::True), /// _ => Err(format!("Boolikes can only be constructed from 0 or 1 but found {}", v)) /// } /// } /// } /// # /// # impl From for u8 { /// # fn from(v: Boollike) -> Self { /// # match v { /// # Boollike::True => 1, /// # Boollike::False => 0, /// # } /// # } /// # } /// /// #[serde_as] /// # #[derive(Debug, PartialEq)] /// #[derive(Deserialize, Serialize)] /// struct Data { /// #[serde_as(as = "TryFromInto")] /// b: Boollike, /// } /// let data = Data { /// b: Boollike::True, /// }; /// /// // Define our expected JSON form /// let j = json!({ /// "b": 1, /// }); /// // Ensure serialization and deserialization produce the expected results /// assert_eq!(j, serde_json::to_value(&data).unwrap()); /// assert_eq!(data, serde_json::from_value(j).unwrap()); /// /// // Numbers besides 0 or 1 should be an error /// let j = json!({ /// "b": 2, /// }); /// assert_eq!("Boolikes can only be constructed from 0 or 1 but found 2", serde_json::from_value::(j).unwrap_err().to_string()); /// # } /// ``` pub struct TryFromInto(PhantomData); /// Serialize a reference value by converting to/from a proxy type with serde support. /// /// This adapter serializes a type `O` by converting it into a second type `T` and serializing `T`. /// Deserializing works analogue, by deserializing a `T` and then converting into `O`. /// /// ```rust /// # #[cfg(any())] { /// struct S { /// #[serde_as(as = "TryFromIntoRef")] /// value: O, /// } /// # } /// ``` /// /// For serialization `O` needs to be `for<'a> &'a O: TryInto`. /// For deserialization the opposite `T: TryInto` is required. /// In both cases the `TryInto::Error` type must implement [`Display`](std::fmt::Display). /// /// **Note**: [`FromIntoRef`] is the more specialized version of this adapter which uses the infallible [`Into`] trait instead. /// [`TryFromIntoRef`] is strictly more general and can also be used where [`FromIntoRef`] is applicable. /// The example shows a use case, when only the deserialization behavior is fallible, but not serializing. /// /// # Example /// /// ```rust /// # #[cfg(feature = "macros")] { /// # use serde::{Deserialize, Serialize}; /// # use serde_json::json; /// # use serde_with::{serde_as, TryFromIntoRef}; /// # /// #[derive(Debug, PartialEq)] /// enum Boollike { /// True, /// False, /// } /// /// # /* /// impl From for u8 { ... } /// # */ /// # /// impl TryFrom for Boollike { /// type Error = String; /// fn try_from(v: u8) -> Result { /// match v { /// 0 => Ok(Boollike::False), /// 1 => Ok(Boollike::True), /// _ => Err(format!("Boolikes can only be constructed from 0 or 1 but found {}", v)) /// } /// } /// } /// # /// # impl<'a> From<&'a Boollike> for u8 { /// # fn from(v: &'a Boollike) -> Self { /// # match v { /// # Boollike::True => 1, /// # Boollike::False => 0, /// # } /// # } /// # } /// /// #[serde_as] /// # #[derive(Debug, PartialEq)] /// #[derive(Deserialize, Serialize)] /// struct Data { /// #[serde_as(as = "TryFromIntoRef")] /// b: Boollike, /// } /// let data = Data { /// b: Boollike::True, /// }; /// /// // Define our expected JSON form /// let j = json!({ /// "b": 1, /// }); /// // Ensure serialization and deserialization produce the expected results /// assert_eq!(j, serde_json::to_value(&data).unwrap()); /// assert_eq!(data, serde_json::from_value(j).unwrap()); /// /// // Numbers besides 0 or 1 should be an error /// let j = json!({ /// "b": 2, /// }); /// assert_eq!("Boolikes can only be constructed from 0 or 1 but found 2", serde_json::from_value::(j).unwrap_err().to_string()); /// # } /// ``` pub struct TryFromIntoRef(PhantomData); /// Borrow `Cow` data during deserialization when possible. /// /// The types `Cow<'a, [u8]>`, `Cow<'a, [u8; N]>`, and `Cow<'a, str>` can borrow from the input data during deserialization. /// serde supports this, by annotating the fields with `#[serde(borrow)]`. but does not support borrowing on nested types. /// This gap is filled by this `BorrowCow` adapter. /// /// Using this adapter with `Cow<'a, [u8]>`/`Cow<'a, [u8; N]>` will serialize the value as a sequence of `u8` values. /// This *might* not allow to borrow the data during deserialization. /// For a different format, which is also more efficient, use the [`Bytes`] adapter, which is also implemented for `Cow`. /// /// When combined with the [`serde_as`] attribute, the `#[serde(borrow)]` annotation will be added automatically. /// If the annotation is wrong or too broad, for example because of multiple lifetime parameters, a manual annotation is required. /// /// # Examples /// /// ```rust /// # #[cfg(feature = "macros")] { /// # use serde::{Deserialize, Serialize}; /// # use serde_with::{serde_as, BorrowCow}; /// # use std::borrow::Cow; /// # /// #[serde_as] /// # #[derive(Debug, PartialEq)] /// #[derive(Deserialize, Serialize)] /// struct Data<'a, 'b, 'c> { /// #[serde_as(as = "BorrowCow")] /// str: Cow<'a, str>, /// #[serde_as(as = "BorrowCow")] /// slice: Cow<'b, [u8]>, /// /// #[serde_as(as = "Option<[BorrowCow; 1]>")] /// nested: Option<[Cow<'c, str>; 1]>, /// } /// let data = Data { /// str: "foobar".into(), /// slice: b"foobar"[..].into(), /// nested: Some(["HelloWorld".into()]), /// }; /// /// // Define our expected JSON form /// let j = r#"{ /// "str": "foobar", /// "slice": [ /// 102, /// 111, /// 111, /// 98, /// 97, /// 114 /// ], /// "nested": [ /// "HelloWorld" /// ] /// }"#; /// // Ensure serialization and deserialization produce the expected results /// assert_eq!(j, serde_json::to_string_pretty(&data).unwrap()); /// assert_eq!(data, serde_json::from_str(j).unwrap()); /// /// // Cow borrows from the input data /// let deserialized: Data<'_, '_, '_> = serde_json::from_str(j).unwrap(); /// assert!(matches!(deserialized.str, Cow::Borrowed(_))); /// assert!(matches!(deserialized.nested, Some([Cow::Borrowed(_)]))); /// // JSON does not allow borrowing bytes, so `slice` does not borrow /// assert!(matches!(deserialized.slice, Cow::Owned(_))); /// # } /// ``` #[cfg(feature = "alloc")] pub struct BorrowCow; /// Deserialize a sequence into `Vec`, skipping elements which fail to deserialize. /// /// The serialization behavior is identical to `Vec`. This is an alternative to `Vec` /// which is resilient against unexpected data. /// /// # Examples /// /// ```rust /// # #[cfg(feature = "macros")] { /// # use serde::{Deserialize, Serialize}; /// # use serde_with::{serde_as, VecSkipError}; /// # /// # #[derive(Debug, PartialEq)] /// #[derive(Deserialize, Serialize)] /// # #[non_exhaustive] /// enum Color { /// Red, /// Green, /// Blue, /// } /// # use Color::*; /// #[serde_as] /// # #[derive(Debug, PartialEq)] /// #[derive(Deserialize, Serialize)] /// struct Palette(#[serde_as(as = "VecSkipError<_>")] Vec); /// /// let data = Palette(vec![Blue, Green,]); /// let source_json = r#"["Blue", "Yellow", "Green"]"#; /// let data_json = r#"["Blue","Green"]"#; /// // Ensure serialization and deserialization produce the expected results /// assert_eq!(data_json, serde_json::to_string(&data).unwrap()); /// assert_eq!(data, serde_json::from_str(source_json).unwrap()); /// # } /// ``` #[cfg(feature = "alloc")] pub struct VecSkipError(PhantomData); /// Deserialize a map, skipping keys and values which fail to deserialize. /// /// By default serde terminates if it fails to deserialize a key or a value when deserializing /// a map. Sometimes a map has heterogeneous keys or values but we only care about some specific /// types, and it is desirable to skip entries on errors. /// /// It is especially useful in conjunction to `#[serde(flatten)]` to capture a map mixed in with /// other entries which we don't want to exhaust in the type definition. /// /// The serialization behavior is identical to the underlying map. /// /// The implementation supports both the [`HashMap`] and the [`BTreeMap`] from the standard library. /// /// [`BTreeMap`]: std::collections::BTreeMap /// [`HashMap`]: std::collections::HashMap /// /// # Examples /// /// ```rust /// # #[cfg(feature = "macros")] { /// # use serde::{Deserialize, Serialize}; /// # use std::collections::BTreeMap; /// # use serde_with::{serde_as, DisplayFromStr, MapSkipError}; /// # /// #[serde_as] /// # #[derive(Debug, PartialEq)] /// #[derive(Deserialize, Serialize)] /// struct VersionNames { /// yanked: Vec, /// #[serde_as(as = "MapSkipError")] /// #[serde(flatten)] /// names: BTreeMap, /// } /// /// let data = VersionNames { /// yanked: vec![2, 5], /// names: BTreeMap::from_iter([ /// (0u16, "v0".to_string()), /// (1, "v1".to_string()), /// (4, "v4".to_string()) /// ]), /// }; /// let source_json = r#"{ /// "0": "v0", /// "1": "v1", /// "4": "v4", /// "yanked": [2, 5], /// "last_updated": 1704085200 /// }"#; /// let data_json = r#"{"yanked":[2,5],"0":"v0","1":"v1","4":"v4"}"#; /// // Ensure serialization and deserialization produce the expected results /// assert_eq!(data_json, serde_json::to_string(&data).unwrap()); /// assert_eq!(data, serde_json::from_str(source_json).unwrap()); /// # } /// ``` #[cfg(feature = "alloc")] pub struct MapSkipError(PhantomData<(K, V)>); /// Deserialize a boolean from a number /// /// Deserialize a number (of `u8`) and turn it into a boolean. /// The adapter supports a [`Strict`](crate::formats::Strict) and [`Flexible`](crate::formats::Flexible) format. /// In `Strict` mode, the number must be `0` or `1`. /// All other values produce an error. /// In `Flexible` mode, the number any non-zero value is converted to `true`. /// /// During serialization only `0` or `1` are ever emitted. /// /// # Examples /// /// ```rust /// # #[cfg(feature = "macros")] { /// # use serde::{Deserialize, Serialize}; /// # use serde_json::json; /// # use serde_with::{serde_as, BoolFromInt}; /// # /// #[serde_as] /// # #[derive(Debug, PartialEq)] /// #[derive(Deserialize, Serialize)] /// struct Data(#[serde_as(as = "BoolFromInt")] bool); /// /// let data = Data(true); /// let j = json!(1); /// // Ensure serialization and deserialization produce the expected results /// assert_eq!(j, serde_json::to_value(&data).unwrap()); /// assert_eq!(data, serde_json::from_value(j).unwrap()); /// /// // false maps to 0 /// let data = Data(false); /// let j = json!(0); /// assert_eq!(j, serde_json::to_value(&data).unwrap()); /// assert_eq!(data, serde_json::from_value(j).unwrap()); // /// #[serde_as] /// # #[derive(Debug, PartialEq)] /// #[derive(Deserialize, Serialize)] /// struct Flexible(#[serde_as(as = "BoolFromInt")] bool); /// /// // Flexible turns any non-zero number into true /// let data = Flexible(true); /// let j = json!(100); /// assert_eq!(data, serde_json::from_value(j).unwrap()); /// # } /// ``` pub struct BoolFromInt(PhantomData); /// De/Serialize a delimited collection using [`Display`] and [`FromStr`] implementation /// /// `StringWithSeparator` takes a second type, which needs to implement [`Display`]+[`FromStr`] and constitutes the inner type of the collection. /// You can define an arbitrary separator, by specifying a type which implements [`Separator`]. /// Some common ones, like space and comma are already predefined and you can find them [here][`Separator`]. /// /// An empty string deserializes as an empty collection. /// /// # Examples /// /// ``` /// # #[cfg(feature = "macros")] { /// # use serde::{Deserialize, Serialize}; /// # /// # use serde_with::{serde_as, StringWithSeparator}; /// use serde_with::formats::{CommaSeparator, SpaceSeparator}; /// use std::collections::BTreeSet; /// /// #[serde_as] /// #[derive(Deserialize, Serialize)] /// struct A { /// #[serde_as(as = "StringWithSeparator::")] /// tags: Vec, /// #[serde_as(as = "StringWithSeparator::")] /// more_tags: BTreeSet, /// } /// /// let v: A = serde_json::from_str(r##"{ /// "tags": "#hello #world", /// "more_tags": "foo,bar,bar" /// }"##).unwrap(); /// assert_eq!(vec!["#hello", "#world"], v.tags); /// assert_eq!(2, v.more_tags.len()); /// /// let x = A { /// tags: vec!["1".to_string(), "2".to_string(), "3".to_string()], /// more_tags: BTreeSet::new(), /// }; /// assert_eq!( /// r#"{"tags":"1 2 3","more_tags":""}"#, /// serde_json::to_string(&x).unwrap() /// ); /// # } /// ``` /// /// [`Display`]: core::fmt::Display /// [`FromStr`]: core::str::FromStr /// [`Separator`]: crate::formats::Separator /// [`serde_as`]: crate::guide::serde_as pub struct StringWithSeparator(PhantomData<(Sep, T)>); /// This serializes a list of tuples into a map /// /// Normally, you want to use a [`HashMap`] or a [`BTreeMap`] when deserializing a map. /// However, sometimes this is not possible due to type constraints, e.g., if the type implements neither [`Hash`] nor [`Ord`]. /// Another use case is deserializing a map with duplicate keys. /// /// # Examples /// /// `Wrapper` does not implement [`Hash`] nor [`Ord`], thus prohibiting the use [`HashMap`] or [`BTreeMap`]. /// The JSON also contains a duplicate key. /// /// [`BTreeMap`]: std::collections::BTreeMap /// [`HashMap`]: std::collections::HashMap /// [`Vec`]: std::vec::Vec /// /// ```rust /// # #[cfg(feature = "macros")] { /// # use serde::{Deserialize, Serialize}; /// # use serde_with::{serde_as, Map}; /// # /// #[serde_as] /// #[derive(Debug, Deserialize, Serialize, Default)] /// struct S { /// #[serde_as(as = "Map<_, _>")] /// s: Vec<(Wrapper, String)>, /// } /// /// #[derive(Clone, Debug, Serialize, Deserialize)] /// #[serde(transparent)] /// struct Wrapper(T); /// /// let data = S { /// s: vec![ /// (Wrapper(1), "a".to_string()), /// (Wrapper(2), "b".to_string()), /// (Wrapper(3), "c".to_string()), /// (Wrapper(2), "d".to_string()), /// ], /// }; /// /// let json = r#"{ /// "s": { /// "1": "a", /// "2": "b", /// "3": "c", /// "2": "d" /// } /// }"#; /// assert_eq!(json, serde_json::to_string_pretty(&data).unwrap()); /// # } /// ``` pub struct Map(PhantomData<(K, V)>); /// De/Serialize a Map into a list of tuples /// /// Some formats, like JSON, have limitations on the types of keys for maps. /// In case of JSON, keys are restricted to strings. /// Rust features more powerful keys, for example tuples, which can not be serialized to JSON. /// /// This helper serializes the Map into a list of tuples, which do not have the same type restrictions. /// /// # Examples /// /// ```rust /// # #[cfg(feature = "macros")] { /// # use serde::{Deserialize, Serialize}; /// # use serde_json::json; /// # use serde_with::{serde_as, Seq}; /// # use std::collections::BTreeMap; /// # /// #[serde_as] /// # #[derive(Debug, PartialEq)] /// #[derive(Deserialize, Serialize)] /// struct A { /// #[serde_as(as = "Seq<(_, _)>")] /// s: BTreeMap<(String, u32), u32>, /// } /// /// // This converts the Rust type /// let data = A { /// s: BTreeMap::from([ /// (("Hello".to_string(), 123), 0), /// (("World".to_string(), 456), 1), /// ]), /// }; /// /// // into this JSON /// let value = json!({ /// "s": [ /// [["Hello", 123], 0], /// [["World", 456], 1] /// ] /// }); /// /// assert_eq!(value, serde_json::to_value(&data).unwrap()); /// assert_eq!(data, serde_json::from_value(value).unwrap()); /// # } /// ``` pub struct Seq(PhantomData); /// Ensure no duplicate keys exist in a map. /// /// By default serde has a last-value-wins implementation, if duplicate keys for a map exist. /// Sometimes it is desirable to know when such an event happens, as the first value is overwritten /// and it can indicate an error in the serialized data. /// /// This helper returns an error if two identical keys exist in a map. /// /// The implementation supports both the [`HashMap`] and the [`BTreeMap`] from the standard library. /// /// [`BTreeMap`]: std::collections::BTreeMap /// [`HashMap`]: std::collections::HashMap /// /// # Example /// /// ```rust /// # #[cfg(feature = "macros")] { /// # use serde::Deserialize; /// # use std::collections::HashMap; /// # use serde_with::{serde_as, MapPreventDuplicates}; /// # /// #[serde_as] /// # #[derive(Debug, Eq, PartialEq)] /// #[derive(Deserialize)] /// struct Doc { /// #[serde_as(as = "MapPreventDuplicates<_, _>")] /// map: HashMap, /// } /// /// // Maps are serialized normally, /// let s = r#"{"map": {"1": 1, "2": 2, "3": 3}}"#; /// let mut v = Doc { /// map: HashMap::new(), /// }; /// v.map.insert(1, 1); /// v.map.insert(2, 2); /// v.map.insert(3, 3); /// assert_eq!(v, serde_json::from_str(s).unwrap()); /// /// // but create an error if duplicate keys, like the `1`, exist. /// let s = r#"{"map": {"1": 1, "2": 2, "1": 3}}"#; /// let res: Result = serde_json::from_str(s); /// assert!(res.is_err()); /// # } /// ``` #[cfg(feature = "alloc")] pub struct MapPreventDuplicates(PhantomData<(K, V)>); /// Ensure that the first key is taken, if duplicate keys exist /// /// By default serde has a last-key-wins implementation, if duplicate keys for a map exist. /// Sometimes the opposite strategy is desired. This helper implements a first-key-wins strategy. /// /// The implementation supports both the [`HashMap`] and the [`BTreeMap`] from the standard library. /// /// [`BTreeMap`]: std::collections::BTreeMap /// [`HashMap`]: std::collections::HashMap #[cfg(feature = "alloc")] pub struct MapFirstKeyWins(PhantomData<(K, V)>); /// Ensure no duplicate values exist in a set. /// /// By default serde has a last-value-wins implementation, if duplicate values for a set exist. /// Sometimes it is desirable to know when such an event happens, as the first value is overwritten /// and it can indicate an error in the serialized data. /// /// This helper returns an error if two identical values exist in a set. /// /// The implementation supports both the [`HashSet`] and the [`BTreeSet`] from the standard library. /// /// [`BTreeSet`]: std::collections::BTreeSet /// [`HashSet`]: std::collections::HashSet /// /// # Example /// /// ```rust /// # #[cfg(feature = "macros")] { /// # use std::collections::HashSet; /// # use serde::Deserialize; /// # use serde_with::{serde_as, SetPreventDuplicates}; /// # /// #[serde_as] /// # #[derive(Debug, Eq, PartialEq)] /// #[derive(Deserialize)] /// struct Doc { /// #[serde_as(as = "SetPreventDuplicates<_>")] /// set: HashSet, /// } /// /// // Sets are serialized normally, /// let s = r#"{"set": [1, 2, 3, 4]}"#; /// let v = Doc { /// set: HashSet::from_iter(vec![1, 2, 3, 4]), /// }; /// assert_eq!(v, serde_json::from_str(s).unwrap()); /// /// // but create an error if duplicate values, like the `1`, exist. /// let s = r#"{"set": [1, 2, 3, 4, 1]}"#; /// let res: Result = serde_json::from_str(s); /// assert!(res.is_err()); /// # } /// ``` #[cfg(feature = "alloc")] pub struct SetPreventDuplicates(PhantomData); /// Ensure that the last value is taken, if duplicate values exist /// /// By default serde has a first-value-wins implementation, if duplicate keys for a set exist. /// Sometimes the opposite strategy is desired. This helper implements a first-value-wins strategy. /// /// The implementation supports both the [`HashSet`] and the [`BTreeSet`] from the standard library. /// /// [`BTreeSet`]: std::collections::BTreeSet /// [`HashSet`]: std::collections::HashSet #[cfg(feature = "alloc")] pub struct SetLastValueWins(PhantomData); /// Helper for implementing [`JsonSchema`] on serializers whose output depends /// on the type of the concrete field. /// /// It is added implicitly by the [`#[serde_as]`](crate::serde_as) macro when any `schemars` /// feature is enabled. /// /// [`JsonSchema`]: ::schemars_0_8::JsonSchema #[cfg(feature = "schemars_0_8")] pub struct Schema(PhantomData, PhantomData); serde_with-3.12.0/src/rust.rs000064400000000000000000000537361046102023000142140ustar 00000000000000//! De/Serialization for Rust's builtin and std types use crate::prelude::*; /// Makes a distinction between a missing, unset, or existing value /// /// Some serialization formats make a distinction between missing fields, fields with a `null` /// value, and existing values. One such format is JSON. By default it is not easily possible to /// differentiate between a missing value and a field which is `null`, as they deserialize to the /// same value. This helper changes it, by using an `Option>` to deserialize into. /// /// * `None`: Represents a missing value. /// * `Some(None)`: Represents a `null` value. /// * `Some(Some(value))`: Represents an existing value. /// /// Note: This cannot be made compatible to `serde_as`, since skipping of values is only available on the field level. /// A hypothetical `DoubleOption` with a `SerializeAs` implementation would allow writing something like this. /// This cannot work, since there is no way to tell the `Vec` to skip the inner `DoubleOption` if it is `None`. /// /// ```rust /// # #[cfg(any())] { /// # struct Foobar { /// #[serde_as(as = "Vec>")] /// data: Vec>>, /// # } /// # } /// ``` /// /// # Examples /// /// ```rust /// # use serde::{Deserialize, Serialize}; /// # /// # #[derive(Debug, PartialEq, Eq)] /// #[derive(Deserialize, Serialize)] /// struct Doc { /// #[serde( /// default, // <- important for deserialization /// skip_serializing_if = "Option::is_none", // <- important for serialization /// with = "::serde_with::rust::double_option", /// )] /// a: Option>, /// } /// // Missing Value /// let s = r#"{}"#; /// assert_eq!(Doc { a: None }, serde_json::from_str(s).unwrap()); /// assert_eq!(s, serde_json::to_string(&Doc { a: None }).unwrap()); /// /// // Unset Value /// let s = r#"{"a":null}"#; /// assert_eq!(Doc { a: Some(None) }, serde_json::from_str(s).unwrap()); /// assert_eq!(s, serde_json::to_string(&Doc { a: Some(None) }).unwrap()); /// /// // Existing Value /// let s = r#"{"a":5}"#; /// assert_eq!(Doc { a: Some(Some(5)) }, serde_json::from_str(s).unwrap()); /// assert_eq!(s, serde_json::to_string(&Doc { a: Some(Some(5)) }).unwrap()); /// ``` #[allow(clippy::option_option)] pub mod double_option { use super::*; /// Deserialize potentially non-existing optional value pub fn deserialize<'de, T, D>(deserializer: D) -> Result>, D::Error> where T: Deserialize<'de>, D: Deserializer<'de>, { Deserialize::deserialize(deserializer).map(Some) } /// Serialize optional value pub fn serialize(values: &Option>, serializer: S) -> Result where S: Serializer, T: Serialize, { match values { None => serializer.serialize_unit(), Some(None) => serializer.serialize_none(), Some(Some(v)) => serializer.serialize_some(&v), } } } /// Serialize inner value if [`Some`]`(T)`. If [`None`], serialize the unit struct `()`. /// /// When used in conjunction with `skip_serializing_if = "Option::is_none"` and /// `default`, you can build an optional value by skipping if it is [`None`], or serializing its /// inner value if [`Some`]`(T)`. /// /// Not all serialization formats easily support optional values. /// While JSON uses the [`Option`] type to represent optional values and only serializes the inner /// part of the [`Some`]`()`, other serialization formats, such as [RON][], choose to serialize the /// [`Some`] around a value. /// This helper helps building a truly optional value for such serializers. /// /// [RON]: https://github.com/ron-rs/ron /// /// # Example /// /// ```rust /// # use serde::{Deserialize, Serialize}; /// # /// # #[derive(Debug, Eq, PartialEq)] /// #[derive(Deserialize, Serialize)] /// struct Doc { /// mandatory: usize, /// #[serde( /// default, // <- important for deserialization /// skip_serializing_if = "Option::is_none", // <- important for serialization /// with = "::serde_with::rust::unwrap_or_skip", /// )] /// optional: Option, /// } /// /// // Transparently add/remove Some() wrapper /// # let pretty_config = ron::ser::PrettyConfig::new() /// # .new_line("\n".into()); /// let s = r#"( /// mandatory: 1, /// optional: 2, /// )"#; /// let v = Doc { /// mandatory: 1, /// optional: Some(2), /// }; /// assert_eq!(v, ron::de::from_str(s).unwrap()); /// assert_eq!(s, ron::ser::to_string_pretty(&v, pretty_config).unwrap()); /// /// // Missing values are deserialized as `None` /// // while `None` values are skipped during serialization. /// # let pretty_config = ron::ser::PrettyConfig::new() /// # .new_line("\n".into()); /// let s = r#"( /// mandatory: 1, /// )"#; /// let v = Doc { /// mandatory: 1, /// optional: None, /// }; /// assert_eq!(v, ron::de::from_str(s).unwrap()); /// assert_eq!(s, ron::ser::to_string_pretty(&v, pretty_config).unwrap()); /// ``` pub mod unwrap_or_skip { use super::*; /// Deserialize value wrapped in Some(T) pub fn deserialize<'de, D, T>(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, T: DeserializeOwned, { T::deserialize(deserializer).map(Some) } /// Serialize value if Some(T), unit struct if None pub fn serialize(option: &Option, serializer: S) -> Result where T: Serialize, S: Serializer, { if let Some(value) = option { value.serialize(serializer) } else { ().serialize(serializer) } } } /// Ensure no duplicate values exist in a set. /// /// By default serde has a last-value-wins implementation, if duplicate values for a set exist. /// Sometimes it is desirable to know when such an event happens, as the first value is overwritten /// and it can indicate an error in the serialized data. /// /// This helper returns an error if two identical values exist in a set. /// /// The implementation supports both the [`HashSet`] and the [`BTreeSet`] from the standard library. /// /// # Converting to `serde_as` /// /// The same functionality can be more clearly expressed using the `serde_as` macro and [`SetPreventDuplicates`]. /// The `_` is a placeholder which works for any type which implements [`Serialize`]/[`Deserialize`]. /// /// ```rust /// # #[cfg(any())] { /// #[serde_as] /// #[derive(Deserialize, Serialize)] /// struct A { /// #[serde_as(as = "SetPreventDuplicates<_, _>")] /// s: HashSet, /// } /// # } /// ``` /// /// [`HashSet`]: std::collections::HashSet /// [`BTreeSet`]: std::collections::HashSet /// /// # Example /// /// ```rust /// # use std::collections::HashSet; /// # use serde::Deserialize; /// # /// # #[derive(Debug, Eq, PartialEq)] /// #[derive(Deserialize)] /// struct Doc { /// #[serde(with = "::serde_with::rust::sets_duplicate_value_is_error")] /// set: HashSet, /// } /// /// // Sets are serialized normally, /// let s = r#"{"set": [1, 2, 3, 4]}"#; /// let v = Doc { /// set: HashSet::from_iter(vec![1, 2, 3, 4]), /// }; /// assert_eq!(v, serde_json::from_str(s).unwrap()); /// /// // but create an error if duplicate values, like the `1`, exist. /// let s = r#"{"set": [1, 2, 3, 4, 1]}"#; /// let res: Result = serde_json::from_str(s); /// assert!(res.is_err()); /// ``` #[cfg(feature = "alloc")] pub mod sets_duplicate_value_is_error { use super::*; use crate::duplicate_key_impls::PreventDuplicateInsertsSet; /// Deserialize a set and return an error on duplicate values pub fn deserialize<'de, D, T, V>(deserializer: D) -> Result where T: PreventDuplicateInsertsSet, V: Deserialize<'de>, D: Deserializer<'de>, { struct SeqVisitor { marker: PhantomData, set_item_type: PhantomData, } impl<'de, T, V> Visitor<'de> for SeqVisitor where T: PreventDuplicateInsertsSet, V: Deserialize<'de>, { type Value = T; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("a sequence") } #[inline] fn visit_seq(self, mut access: A) -> Result where A: SeqAccess<'de>, { let mut values = Self::Value::new(access.size_hint()); while let Some(value) = access.next_element()? { if !values.insert(value) { return Err(DeError::custom("invalid entry: found duplicate value")); }; } Ok(values) } } let visitor = SeqVisitor { marker: PhantomData, set_item_type: PhantomData, }; deserializer.deserialize_seq(visitor) } /// Serialize the set with the default serializer pub fn serialize(value: &T, serializer: S) -> Result where T: Serialize, S: Serializer, { value.serialize(serializer) } } /// Ensure no duplicate keys exist in a map. /// /// By default serde has a last-value-wins implementation, if duplicate keys for a map exist. /// Sometimes it is desirable to know when such an event happens, as the first value is overwritten /// and it can indicate an error in the serialized data. /// /// This helper returns an error if two identical keys exist in a map. /// /// The implementation supports both the [`HashMap`] and the [`BTreeMap`] from the standard library. /// /// # Converting to `serde_as` /// /// The same functionality can be more clearly expressed using the `serde_as` macro and [`MapPreventDuplicates`]. /// The `_` is a placeholder which works for any type which implements [`Serialize`]/[`Deserialize`]. /// /// ```rust /// # #[cfg(any())] { /// #[serde_as] /// #[derive(Deserialize, Serialize)] /// struct A { /// #[serde_as(as = "MapPreventDuplicates<_, _>")] /// s: HashMap, /// } /// # } /// ``` /// /// [`HashMap`]: std::collections::HashMap /// [`BTreeMap`]: std::collections::HashMap /// /// # Example /// /// ```rust /// # use serde::Deserialize; /// # use std::collections::HashMap; /// # /// # #[derive(Debug, Eq, PartialEq)] /// #[derive(Deserialize)] /// struct Doc { /// #[serde(with = "::serde_with::rust::maps_duplicate_key_is_error")] /// map: HashMap, /// } /// /// // Maps are serialized normally, /// let s = r#"{"map": {"1": 1, "2": 2, "3": 3}}"#; /// let mut v = Doc { /// map: HashMap::new(), /// }; /// v.map.insert(1, 1); /// v.map.insert(2, 2); /// v.map.insert(3, 3); /// assert_eq!(v, serde_json::from_str(s).unwrap()); /// /// // but create an error if duplicate keys, like the `1`, exist. /// let s = r#"{"map": {"1": 1, "2": 2, "1": 3}}"#; /// let res: Result = serde_json::from_str(s); /// assert!(res.is_err()); /// ``` #[cfg(feature = "alloc")] pub mod maps_duplicate_key_is_error { use super::*; use crate::duplicate_key_impls::PreventDuplicateInsertsMap; /// Deserialize a map and return an error on duplicate keys pub fn deserialize<'de, D, T, K, V>(deserializer: D) -> Result where T: PreventDuplicateInsertsMap, K: Deserialize<'de>, V: Deserialize<'de>, D: Deserializer<'de>, { struct MapVisitor { marker: PhantomData, map_key_type: PhantomData, map_value_type: PhantomData, } impl<'de, T, K, V> Visitor<'de> for MapVisitor where T: PreventDuplicateInsertsMap, K: Deserialize<'de>, V: Deserialize<'de>, { type Value = T; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("a map") } #[inline] fn visit_map(self, mut access: A) -> Result where A: MapAccess<'de>, { let mut values = Self::Value::new(access.size_hint()); while let Some((key, value)) = access.next_entry()? { if !values.insert(key, value) { return Err(DeError::custom("invalid entry: found duplicate key")); }; } Ok(values) } } let visitor = MapVisitor { marker: PhantomData, map_key_type: PhantomData, map_value_type: PhantomData, }; deserializer.deserialize_map(visitor) } /// Serialize the map with the default serializer pub fn serialize(value: &T, serializer: S) -> Result where T: Serialize, S: Serializer, { value.serialize(serializer) } } /// Ensure that the last value is taken, if duplicate values exist /// /// By default serde has a first-value-wins implementation, if duplicate keys for a set exist. /// Sometimes the opposite strategy is desired. This helper implements a first-value-wins strategy. /// /// The implementation supports both the [`HashSet`] and the [`BTreeSet`] from the standard library. /// /// # Converting to `serde_as` /// /// The same functionality can be more clearly expressed using the `serde_as` macro and [`SetLastValueWins`]. /// The `_` is a placeholder which works for any type which implements [`Serialize`]/[`Deserialize`]. /// /// ```rust /// # #[cfg(any())] { /// #[serde_as] /// #[derive(Deserialize, Serialize)] /// struct A { /// #[serde_as(as = "SetLastValueWins<_, _>")] /// s: HashSet, /// } /// # } /// ``` /// /// [`HashSet`]: std::collections::HashSet /// [`BTreeSet`]: std::collections::HashSet #[cfg(feature = "alloc")] pub mod sets_last_value_wins { use super::*; use crate::duplicate_key_impls::DuplicateInsertsLastWinsSet; /// Deserialize a set and keep the last of equal values pub fn deserialize<'de, D, T, V>(deserializer: D) -> Result where T: DuplicateInsertsLastWinsSet, V: Deserialize<'de>, D: Deserializer<'de>, { struct SeqVisitor { marker: PhantomData, set_item_type: PhantomData, } impl<'de, T, V> Visitor<'de> for SeqVisitor where T: DuplicateInsertsLastWinsSet, V: Deserialize<'de>, { type Value = T; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("a sequence") } #[inline] fn visit_seq(self, mut access: A) -> Result where A: SeqAccess<'de>, { let mut values = Self::Value::new(access.size_hint()); while let Some(value) = access.next_element()? { values.replace(value); } Ok(values) } } let visitor = SeqVisitor { marker: PhantomData, set_item_type: PhantomData, }; deserializer.deserialize_seq(visitor) } /// Serialize the set with the default serializer pub fn serialize(value: &T, serializer: S) -> Result where T: Serialize, S: Serializer, { value.serialize(serializer) } } /// Ensure that the first key is taken, if duplicate keys exist /// /// By default serde has a last-key-wins implementation, if duplicate keys for a map exist. /// Sometimes the opposite strategy is desired. This helper implements a first-key-wins strategy. /// /// The implementation supports both the [`HashMap`] and the [`BTreeMap`] from the standard library. /// /// [`HashMap`]: std::collections::HashMap /// [`BTreeMap`]: std::collections::HashMap /// /// # Converting to `serde_as` /// /// The same functionality can be more clearly expressed using the `serde_as` macro and [`MapFirstKeyWins`]. /// The `_` is a placeholder which works for any type which implements [`Serialize`]/[`Deserialize`]. /// /// ```rust /// # #[cfg(any())] { /// #[serde_as] /// #[derive(Deserialize, Serialize)] /// struct A { /// #[serde_as(as = "MapFirstKeyWins<_, _>")] /// s: HashMap, /// } /// # } /// ``` /// /// # Example /// /// ```rust /// # use serde::Deserialize; /// # use std::collections::HashMap; /// # /// # #[derive(Debug, Eq, PartialEq)] /// #[derive(Deserialize)] /// struct Doc { /// #[serde(with = "::serde_with::rust::maps_first_key_wins")] /// map: HashMap, /// } /// /// // Maps are serialized normally, /// let s = r#"{"map": {"1": 1, "2": 2, "3": 3}}"#; /// let mut v = Doc { /// map: HashMap::new(), /// }; /// v.map.insert(1, 1); /// v.map.insert(2, 2); /// v.map.insert(3, 3); /// assert_eq!(v, serde_json::from_str(s).unwrap()); /// /// // but create an error if duplicate keys, like the `1`, exist. /// let s = r#"{"map": {"1": 1, "2": 2, "1": 3}}"#; /// let mut v = Doc { /// map: HashMap::new(), /// }; /// v.map.insert(1, 1); /// v.map.insert(2, 2); /// assert_eq!(v, serde_json::from_str(s).unwrap()); /// ``` #[cfg(feature = "alloc")] pub mod maps_first_key_wins { use super::*; use crate::duplicate_key_impls::DuplicateInsertsFirstWinsMap; /// Deserialize a map and return an error on duplicate keys pub fn deserialize<'de, D, T, K, V>(deserializer: D) -> Result where T: DuplicateInsertsFirstWinsMap, K: Deserialize<'de>, V: Deserialize<'de>, D: Deserializer<'de>, { struct MapVisitor { marker: PhantomData, map_key_type: PhantomData, map_value_type: PhantomData, } impl<'de, T, K, V> Visitor<'de> for MapVisitor where T: DuplicateInsertsFirstWinsMap, K: Deserialize<'de>, V: Deserialize<'de>, { type Value = T; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("a map") } #[inline] fn visit_map(self, mut access: A) -> Result where A: MapAccess<'de>, { let mut values = Self::Value::new(access.size_hint()); while let Some((key, value)) = access.next_entry()? { values.insert(key, value); } Ok(values) } } let visitor = MapVisitor { marker: PhantomData, map_key_type: PhantomData, map_value_type: PhantomData, }; deserializer.deserialize_map(visitor) } /// Serialize the map with the default serializer pub fn serialize(value: &T, serializer: S) -> Result where T: Serialize, S: Serializer, { value.serialize(serializer) } } /// Deserialize any value, ignore it, and return the default value for the type being deserialized. /// /// This function can be used in two different ways: /// /// 1. It is useful for instance to create an enum with a catch-all variant that will accept any incoming data. /// 2. [`untagged`] enum representations do not allow the `other` annotation as the fallback enum variant. /// With this function you can emulate an `other` variant, which can deserialize any data carrying enum. /// /// **Note:** Using this function will prevent deserializing data-less enum variants. /// If this is a problem depends on the data format. /// For example, deserializing `"Bar"` as an enum in JSON would fail, since it carries no data. /// /// # Examples /// /// ## Deserializing a heterogeneous collection of XML nodes /// /// When [`serde-xml-rs`] deserializes an XML tag to an enum, it always maps the tag /// name to the enum variant name, and the tag attributes and children to the enum contents. /// This means that in order for an enum variant to accept any XML tag, it both has to use /// `#[serde(other)]` to accept any tag name, and `#[serde(deserialize_with = "deserialize_ignore_any")]` /// to accept any attributes and children. /// /// ```rust /// # use serde::Deserialize; /// use serde_with::rust::deserialize_ignore_any; /// /// # #[derive(Debug, PartialEq)] /// #[derive(Deserialize)] /// #[serde(rename_all = "lowercase")] /// enum Item { /// Foo(String), /// Bar(String), /// #[serde(other, deserialize_with = "deserialize_ignore_any")] /// Other, /// } /// /// // Deserialize this XML /// # let items: Vec = serde_xml_rs::from_str( /// r" /// a /// b /// c /// d /// " /// # ).unwrap(); /// /// // into these Items /// # let expected = /// vec![ /// Item::Foo(String::from("a")), /// Item::Bar(String::from("b")), /// Item::Foo(String::from("c")), /// Item::Other, /// ] /// # ; /// # assert_eq!(expected, items); /// ``` /// /// ## Simulating an `other` enum variant in an `untagged` enum /// /// ```rust /// # use serde::Deserialize; /// # use serde_json::json; /// use serde_with::rust::deserialize_ignore_any; /// /// # #[derive(Debug, PartialEq)] /// #[derive(Deserialize)] /// #[serde(untagged)] /// enum Item { /// Foo{x: u8}, /// #[serde(deserialize_with = "deserialize_ignore_any")] /// Other, /// } /// /// // Deserialize this JSON /// # let items: Vec = serde_json::from_value( /// json!([ /// {"y": 1}, /// {"x": 1}, /// ]) /// # ).unwrap(); /// /// // into these Items /// # let expected = /// vec![Item::Other, Item::Foo{x: 1}] /// # ; /// # assert_eq!(expected, items); /// ``` /// /// [`serde-xml-rs`]: https://docs.rs/serde-xml-rs /// [`untagged`]: https://serde.rs/enum-representations.html#untagged pub fn deserialize_ignore_any<'de, D: Deserializer<'de>, T: Default>( deserializer: D, ) -> Result { IgnoredAny::deserialize(deserializer).map(|_| T::default()) } serde_with-3.12.0/src/schemars_0_8.rs000064400000000000000000001142571046102023000154660ustar 00000000000000//! Integration with [schemars v0.8](schemars_0_8). //! //! This module is only available if using the `schemars_0_8` feature of the crate. //! //! If you would like to add support for schemars to your own `serde_with` helpers //! see [`JsonSchemaAs`]. use crate::{ formats::{Flexible, Format, PreferMany, PreferOne, Separator, Strict}, prelude::{Schema as WrapSchema, *}, }; use ::schemars_0_8::{ gen::SchemaGenerator, schema::{ ArrayValidation, InstanceType, Metadata, NumberValidation, ObjectValidation, Schema, SchemaObject, SingleOrVec, SubschemaValidation, }, JsonSchema, }; use core::{ mem::ManuallyDrop, ops::{Deref, DerefMut}, }; //=================================================================== // Trait Definition /// A type which can be described as a JSON schema document. /// /// This trait is as [`SerializeAs`] is to [`Serialize`] but for [`JsonSchema`]. /// You can use it to make your custom [`SerializeAs`] and [`DeserializeAs`] /// types also support being described via JSON schemas. /// /// It is used by the [`Schema`][1] type in order to implement [`JsonSchema`] /// for the relevant types. [`Schema`][1] is used implicitly by the [`serde_as`] /// macro to instruct `schemars` on how to generate JSON schemas for fields /// annotated with `#[serde_as(as = "...")]` attributes. /// /// # Examples /// Suppose we have our very own `PositiveInt` type. Then we could add support /// for generating a schema from it like this /// /// ``` /// # extern crate schemars_0_8 as schemars; /// # use serde::{Serialize, Serializer, Deserialize, Deserializer}; /// # use serde_with::{SerializeAs, DeserializeAs}; /// use serde_with::schemars_0_8::JsonSchemaAs; /// use schemars::gen::SchemaGenerator; /// use schemars::schema::Schema; /// use schemars::JsonSchema; /// /// # #[allow(dead_code)] /// struct PositiveInt; /// /// impl SerializeAs for PositiveInt { /// // ... /// # fn serialize_as(&value: &i32, ser: S) -> Result /// # where /// # S: Serializer /// # { /// # if value < 0 { /// # return Err(serde::ser::Error::custom( /// # "expected a positive integer value, got a negative one" /// # )); /// # } /// # /// # value.serialize(ser) /// # } /// } /// /// impl<'de> DeserializeAs<'de, i32> for PositiveInt { /// // ... /// # fn deserialize_as(de: D) -> Result /// # where /// # D: Deserializer<'de>, /// # { /// # match i32::deserialize(de) { /// # Ok(value) if value < 0 => Err(serde::de::Error::custom( /// # "expected a positive integer value, got a negative one" /// # )), /// # value => value /// # } /// # } /// } /// /// impl JsonSchemaAs for PositiveInt { /// fn schema_name() -> String { /// "PositiveInt".into() /// } /// /// fn json_schema(gen: &mut SchemaGenerator) -> Schema { /// let mut schema = ::json_schema(gen).into_object(); /// schema.number().minimum = Some(0.0); /// schema.into() /// } /// } /// ``` /// /// [0]: crate::serde_as /// [1]: crate::Schema pub trait JsonSchemaAs { /// Whether JSON Schemas generated for this type should be re-used where possible using the `$ref` keyword. /// /// For trivial types (such as primitives), this should return `false`. For more complex types, it should return `true`. /// For recursive types, this **must** return `true` to prevent infinite cycles when generating schemas. /// /// By default, this returns `true`. fn is_referenceable() -> bool { true } /// The name of the generated JSON Schema. /// /// This is used as the title for root schemas, and the key within the root's `definitions` property for sub-schemas. /// /// As the schema name is used as as part of `$ref` it has to be a valid URI path segment according to /// [RFC 3986 Section-3](https://datatracker.ietf.org/doc/html/rfc3986#section-3). fn schema_name() -> String; /// Returns a string that uniquely identifies the schema produced by this type. /// /// This does not have to be a human-readable string, and the value will not itself be included in generated schemas. /// If two types produce different schemas, then they **must** have different `schema_id()`s, /// but two types that produce identical schemas should *ideally* have the same `schema_id()`. /// /// The default implementation returns the same value as `schema_name()`. fn schema_id() -> Cow<'static, str> { Cow::Owned(Self::schema_name()) } /// Generates a JSON Schema for this type. /// /// If the returned schema depends on any [referenceable](JsonSchema::is_referenceable) schemas, then this method will /// add them to the [`SchemaGenerator`]'s schema definitions. /// /// This should not return a `$ref` schema. fn json_schema(gen: &mut SchemaGenerator) -> Schema; } impl JsonSchema for WrapSchema where T: ?Sized, TA: JsonSchemaAs, { fn schema_name() -> String { TA::schema_name() } fn schema_id() -> Cow<'static, str> { TA::schema_id() } fn json_schema(gen: &mut SchemaGenerator) -> Schema { TA::json_schema(gen) } fn is_referenceable() -> bool { TA::is_referenceable() } } //=================================================================== // Macro helpers macro_rules! forward_schema { ($fwd:ty) => { fn schema_name() -> String { <$fwd as JsonSchema>::schema_name() } fn schema_id() -> Cow<'static, str> { <$fwd as JsonSchema>::schema_id() } fn json_schema(gen: &mut SchemaGenerator) -> Schema { <$fwd as JsonSchema>::json_schema(gen) } fn is_referenceable() -> bool { <$fwd as JsonSchema>::is_referenceable() } }; } //=================================================================== // Common definitions for various std types impl<'a, T: 'a, TA: 'a> JsonSchemaAs<&'a T> for &'a TA where T: ?Sized, TA: JsonSchemaAs, { forward_schema!(&'a WrapSchema); } impl<'a, T: 'a, TA: 'a> JsonSchemaAs<&'a mut T> for &'a mut TA where T: ?Sized, TA: JsonSchemaAs, { forward_schema!(&'a mut WrapSchema); } impl JsonSchemaAs> for Option where TA: JsonSchemaAs, { forward_schema!(Option>); } impl JsonSchemaAs> for Box where T: ?Sized, TA: JsonSchemaAs, { forward_schema!(Box>); } impl JsonSchemaAs> for Rc where T: ?Sized, TA: JsonSchemaAs, { forward_schema!(Rc>); } impl JsonSchemaAs> for Arc where T: ?Sized, TA: JsonSchemaAs, { forward_schema!(Arc>); } impl JsonSchemaAs> for Vec where TA: JsonSchemaAs, { forward_schema!(Vec>); } impl JsonSchemaAs> for VecDeque where TA: JsonSchemaAs, { forward_schema!(VecDeque>); } // schemars only requires that V implement JsonSchema for BTreeMap impl JsonSchemaAs> for BTreeMap where VA: JsonSchemaAs, { forward_schema!(BTreeMap, WrapSchema>); } // schemars only requires that V implement JsonSchema for HashMap impl JsonSchemaAs> for HashMap where VA: JsonSchemaAs, { forward_schema!(HashMap, WrapSchema, S>); } impl JsonSchemaAs> for BTreeSet where TA: JsonSchemaAs, { forward_schema!(BTreeSet>); } impl JsonSchemaAs for HashSet where TA: JsonSchemaAs, { forward_schema!(HashSet, S>); } impl JsonSchemaAs<[T; N]> for [TA; N] where TA: JsonSchemaAs, { fn schema_name() -> String { std::format!("[{}; {}]", >::schema_name(), N) } fn schema_id() -> Cow<'static, str> { std::format!("[{}; {}]", >::schema_id(), N).into() } fn json_schema(gen: &mut SchemaGenerator) -> Schema { let (max, min) = match N.try_into() { Ok(len) => (Some(len), Some(len)), Err(_) => (None, Some(u32::MAX)), }; SchemaObject { instance_type: Some(InstanceType::Array.into()), array: Some(Box::new(ArrayValidation { items: Some(gen.subschema_for::>().into()), max_items: max, min_items: min, ..Default::default() })), ..Default::default() } .into() } fn is_referenceable() -> bool { false } } macro_rules! schema_for_tuple { ( ( $( $ts:ident )+ ) ( $( $as:ident )+ ) ) => { impl<$($ts,)+ $($as,)+> JsonSchemaAs<($($ts,)+)> for ($($as,)+) where $( $as: JsonSchemaAs<$ts>, )+ { forward_schema!(( $( WrapSchema<$ts, $as>, )+ )); } } } impl JsonSchemaAs<()> for () { forward_schema!(()); } // schemars only implements JsonSchema for tuples up to 15 elements so we do // the same here. schema_for_tuple!((T0)(A0)); schema_for_tuple!((T0 T1) (A0 A1)); schema_for_tuple!((T0 T1 T2) (A0 A1 A2)); schema_for_tuple!((T0 T1 T2 T3) (A0 A1 A2 A3)); schema_for_tuple!((T0 T1 T2 T3 T4) (A0 A1 A2 A3 A4)); schema_for_tuple!((T0 T1 T2 T3 T4 T5) (A0 A1 A2 A3 A4 A5)); schema_for_tuple!((T0 T1 T2 T3 T4 T5 T6) (A0 A1 A2 A3 A4 A5 A6)); schema_for_tuple!((T0 T1 T2 T3 T4 T5 T6 T7) (A0 A1 A2 A3 A4 A5 A6 A7)); schema_for_tuple!((T0 T1 T2 T3 T4 T5 T6 T7 T8) (A0 A1 A2 A3 A4 A5 A6 A7 A8)); schema_for_tuple!((T0 T1 T2 T3 T4 T5 T6 T7 T8 T9) (A0 A1 A2 A3 A4 A5 A6 A7 A8 A9)); schema_for_tuple!((T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10) (A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10)); schema_for_tuple!((T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11) (A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11)); schema_for_tuple!( (T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12) (A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12) ); schema_for_tuple!( (T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13) (A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13) ); schema_for_tuple!( (T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14) (A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13 A14) ); schema_for_tuple!( (T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15) (A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13 A14 A15) ); //=================================================================== // Impls for serde_with types. impl JsonSchemaAs for Same { forward_schema!(T); } impl JsonSchemaAs for DisplayFromStr { forward_schema!(String); } impl JsonSchemaAs for BoolFromInt { fn schema_name() -> String { "BoolFromInt".into() } fn schema_id() -> Cow<'static, str> { "serde_with::BoolFromInt".into() } fn json_schema(_: &mut SchemaGenerator) -> Schema { SchemaObject { instance_type: Some(InstanceType::Integer.into()), number: Some(Box::new(NumberValidation { minimum: Some(0.0), maximum: Some(1.0), ..Default::default() })), ..Default::default() } .into() } fn is_referenceable() -> bool { false } } impl JsonSchemaAs for BoolFromInt { fn schema_name() -> String { "BoolFromInt".into() } fn schema_id() -> Cow<'static, str> { "serde_with::BoolFromInt".into() } fn json_schema(_: &mut SchemaGenerator) -> Schema { SchemaObject { instance_type: Some(InstanceType::Integer.into()), ..Default::default() } .into() } fn is_referenceable() -> bool { false } } impl<'a, T: 'a> JsonSchemaAs> for BorrowCow where T: ?Sized + ToOwned, Cow<'a, T>: JsonSchema, { forward_schema!(Cow<'a, T>); } impl JsonSchemaAs for Bytes { forward_schema!(Vec); } impl JsonSchemaAs> for BytesOrString { fn schema_name() -> String { "BytesOrString".into() } fn schema_id() -> Cow<'static, str> { "serde_with::BytesOrString".into() } fn json_schema(gen: &mut SchemaGenerator) -> Schema { SchemaObject { subschemas: Some(Box::new(SubschemaValidation { any_of: Some(std::vec![ gen.subschema_for::>(), SchemaObject { instance_type: Some(InstanceType::String.into()), metadata: Some(Box::new(Metadata { write_only: true, ..Default::default() })), ..Default::default() } .into() ]), ..Default::default() })), ..Default::default() } .into() } fn is_referenceable() -> bool { false } } impl JsonSchemaAs for DefaultOnError where TA: JsonSchemaAs, { forward_schema!(WrapSchema); } impl JsonSchemaAs for DefaultOnNull where TA: JsonSchemaAs, { forward_schema!(Option>); } impl JsonSchemaAs for FromInto { forward_schema!(T); } impl JsonSchemaAs for FromIntoRef { forward_schema!(T); } impl JsonSchemaAs for TryFromInto { forward_schema!(U); } impl JsonSchemaAs for TryFromIntoRef { forward_schema!(U); } impl JsonSchemaAs for IfIsHumanReadable where TA: JsonSchemaAs, { // serde_json always has `is_human_readable` set to true so we just use the // schema for the human readable variant. forward_schema!(WrapSchema); } macro_rules! schema_for_map { ($type:ty) => { impl JsonSchemaAs<$type> for Map where VA: JsonSchemaAs, { forward_schema!(WrapSchema, BTreeMap>); } }; } schema_for_map!([(K, V)]); schema_for_map!(BTreeSet<(K, V)>); schema_for_map!(BinaryHeap<(K, V)>); schema_for_map!(Box<[(K, V)]>); schema_for_map!(LinkedList<(K, V)>); schema_for_map!(Vec<(K, V)>); schema_for_map!(VecDeque<(K, V)>); impl JsonSchemaAs> for Map where VA: JsonSchemaAs, { forward_schema!(WrapSchema, BTreeMap>); } impl JsonSchemaAs> for EnumMap where T: JsonSchema, { fn schema_name() -> String { std::format!("EnumMap({})", T::schema_name()) } fn schema_id() -> Cow<'static, str> { std::format!("serde_with::EnumMap({})", T::schema_id()).into() } // We generate the schema here by going through all the variants of the // enum (the oneOf property) and sticking all their properties onto an // object. // // This will be wrong if the object is not an externally tagged enum but in // that case serialization and deserialization will fail so it is probably // OK. fn json_schema(gen: &mut SchemaGenerator) -> Schema { let mut object = SchemaObject { instance_type: Some(InstanceType::Object.into()), ..Default::default() }; let inner = T::json_schema(gen).into_object(); let one_of = match inner.subschemas { Some(subschemas) => match subschemas.one_of { Some(one_of) => one_of, None => return object.into(), }, None => return object.into(), }; let properties = &mut object.object().properties; for schema in one_of { if let Some(object) = schema.into_object().object { properties.extend(object.properties.into_iter()); } } object.object().additional_properties = Some(Box::new(Schema::Bool(false))); object.into() } fn is_referenceable() -> bool { true } } impl WrapSchema, KeyValueMap> where TA: JsonSchemaAs, { /// Transform a schema from the entry type of a `KeyValueMap` to the /// resulting field type. /// /// This usually means doing one of two things: /// 1. removing the `$key$` property from an object, or, /// 2. removing the first item from an array. /// /// We also need to adjust any fields that control the number of items or /// properties allowed such as `(max|min)_properties` or `(max|min)_items`. /// /// This is mostly straightforward. Where things get hairy is when dealing /// with subschemas. JSON schemas allow you to build the schema for an /// object by combining multiple subschemas: /// - You can match exactly one of a set of subschemas (`one_of`). /// - You can match any of a set of subschemas (`any_of`). /// - You can match all of a set of subschemas (`all_of`). /// /// Unfortunately for us, we need to handle all of these options by recursing /// into the subschemas and applying the same transformations as above. fn kvmap_transform_schema(gen: &mut SchemaGenerator, schema: &mut Schema) { let mut parents = Vec::new(); Self::kvmap_transform_schema_impl(gen, schema, &mut parents, 0); } fn kvmap_transform_schema_impl( gen: &mut SchemaGenerator, schema: &mut Schema, parents: &mut Vec, depth: u32, ) { if depth > 8 { return; } let mut done = false; let schema = match schema { Schema::Object(schema) => schema, _ => return, }; // The schema is a reference to a schema defined elsewhere. // // If possible we replace it with its definition but if that is not // available then we give up and leave it as-is. let mut parents = if let Some(reference) = &schema.reference { let name = match reference.strip_prefix(&gen.settings().definitions_path) { Some(name) => name, // Reference is defined elsewhere, nothing we can do. None => return, }; // We are in a recursive reference loop. No point in continuing. if parents.iter().any(|parent| parent == name) { return; } let name = name.to_owned(); *schema = match gen.definitions().get(&name) { Some(Schema::Object(schema)) => schema.clone(), _ => return, }; parents.push(name); DropGuard::new(parents, |parents| drop(parents.pop())) } else { DropGuard::unguarded(parents) }; if let Some(object) = &mut schema.object { // For objects KeyValueMap uses the $key$ property so we need to remove it from // the inner schema. done |= object.properties.remove("$key$").is_some(); done |= object.required.remove("$key$"); if let Some(max) = &mut object.max_properties { *max = max.saturating_sub(1); } if let Some(min) = &mut object.max_properties { *min = min.saturating_sub(1); } } if let Some(array) = &mut schema.array { // For arrays KeyValueMap uses the first array element so we need to remove it // from the inner schema. if let Some(SingleOrVec::Vec(items)) = &mut array.items { // If the array is empty then the leading element may be following the // additionalItem schema. In that case we do nothing. if !items.is_empty() { items.remove(0); done = true; } } if let Some(max) = &mut array.max_items { *max = max.saturating_sub(1); } if let Some(min) = &mut array.min_items { *min = min.saturating_sub(1); } } // We've already modified the schema so there's no need to do more work. if done { return; } let subschemas = match &mut schema.subschemas { Some(subschemas) => subschemas, None => return, }; if let Some(one_of) = &mut subschemas.one_of { for subschema in one_of { Self::kvmap_transform_schema_impl(gen, subschema, &mut parents, depth + 1); } } if let Some(any_of) = &mut subschemas.any_of { for subschema in any_of { Self::kvmap_transform_schema_impl(gen, subschema, &mut parents, depth + 1); } } if let Some(all_of) = &mut subschemas.all_of { for subschema in all_of { Self::kvmap_transform_schema_impl(gen, subschema, &mut parents, depth + 1); } } } } impl JsonSchemaAs> for KeyValueMap where TA: JsonSchemaAs, { fn schema_name() -> String { std::format!("KeyValueMap({})", >::schema_name()) } fn schema_id() -> Cow<'static, str> { std::format!( "serde_with::KeyValueMap({})", >::schema_id() ) .into() } fn json_schema(gen: &mut SchemaGenerator) -> Schema { let mut value = >::json_schema(gen); , KeyValueMap>>::kvmap_transform_schema(gen, &mut value); SchemaObject { instance_type: Some(InstanceType::Object.into()), object: Some(Box::new(ObjectValidation { additional_properties: Some(Box::new(value)), ..Default::default() })), ..Default::default() } .into() } fn is_referenceable() -> bool { true } } impl JsonSchemaAs<[(K, V); N]> for Map where VA: JsonSchemaAs, { forward_schema!(WrapSchema, BTreeMap>); } macro_rules! map_first_last_wins_schema { ($(=> $extra:ident)? $type:ty) => { impl JsonSchemaAs<$type> for MapFirstKeyWins where VA: JsonSchemaAs, { forward_schema!(BTreeMap, WrapSchema>); } impl JsonSchemaAs<$type> for MapPreventDuplicates where VA: JsonSchemaAs, { forward_schema!(BTreeMap, WrapSchema>); } } } map_first_last_wins_schema!(BTreeMap); map_first_last_wins_schema!(=> S HashMap); #[cfg(feature = "hashbrown_0_14")] map_first_last_wins_schema!(=> S hashbrown_0_14::HashMap); #[cfg(feature = "hashbrown_0_15")] map_first_last_wins_schema!(=> S hashbrown_0_15::HashMap); #[cfg(feature = "indexmap_1")] map_first_last_wins_schema!(=> S indexmap_1::IndexMap); #[cfg(feature = "indexmap_2")] map_first_last_wins_schema!(=> S indexmap_2::IndexMap); impl JsonSchemaAs> for OneOrMany where TA: JsonSchemaAs, { fn schema_name() -> String { std::format!( "OneOrMany({},PreferOne)", >::schema_name() ) } fn schema_id() -> Cow<'static, str> { std::format!( "serde_with::OneOrMany({},PreferOne)", >::schema_id() ) .into() } fn json_schema(gen: &mut SchemaGenerator) -> Schema { let single = gen.subschema_for::>(); let array = SchemaObject { instance_type: Some(InstanceType::Array.into()), array: Some(Box::new(ArrayValidation { items: Some(single.clone().into()), ..Default::default() })), ..Default::default() }; SchemaObject { subschemas: Some(Box::new(SubschemaValidation { any_of: Some(std::vec![single, array.into()]), ..Default::default() })), ..Default::default() } .into() } } impl JsonSchemaAs> for OneOrMany where TA: JsonSchemaAs, { fn schema_name() -> String { std::format!( "OneOrMany<{}, PreferMany>", >::schema_name() ) } fn schema_id() -> Cow<'static, str> { std::format!( "serde_with::OneOrMany<{}, PreferMany>", >::schema_id() ) .into() } fn json_schema(gen: &mut SchemaGenerator) -> Schema { let inner = gen.subschema_for::>(); let single = SchemaObject { metadata: Some(Box::new(Metadata { write_only: true, ..Default::default() })), subschemas: Some(Box::new(SubschemaValidation { all_of: Some(std::vec![inner.clone()]), ..Default::default() })), ..Default::default() }; let array = SchemaObject { instance_type: Some(InstanceType::Array.into()), array: Some(Box::new(ArrayValidation { items: Some(Schema::from(single.clone()).into()), ..Default::default() })), ..Default::default() }; SchemaObject { subschemas: Some(Box::new(SubschemaValidation { any_of: Some(std::vec![single.into(), array.into()]), ..Default::default() })), ..Default::default() } .into() } } macro_rules! schema_for_pickfirst { ($( $param:ident )+) => { impl JsonSchemaAs for PickFirst<($( $param, )+)> where $( $param: JsonSchemaAs, )+ { fn schema_name() -> String { std::format!( concat!( "PickFirst(", $( "{", stringify!($param), "}", )+ ")" ), $( $param = >::schema_name(), )+ ) } fn schema_id() -> Cow<'static, str> { std::format!( concat!( "serde_with::PickFirst(", $( "{", stringify!($param), "}", )+ ")" ), $( $param = >::schema_id(), )+ ) .into() } fn json_schema(gen: &mut SchemaGenerator) -> Schema { let mut first = true; let subschemas = std::vec![$( { let is_first = std::mem::replace(&mut first, false); let schema = gen.subschema_for::>(); if !is_first { SchemaObject { metadata: Some(Box::new(Metadata { write_only: true, ..Default::default() })), subschemas: Some(Box::new(SubschemaValidation { all_of: Some(std::vec![schema]), ..Default::default() })), ..Default::default() } .into() } else { schema } } ),+]; SchemaObject { subschemas: Some(Box::new(SubschemaValidation { any_of: Some(subschemas), ..Default::default() })), ..Default::default() } .into() } } } } schema_for_pickfirst!(A); schema_for_pickfirst!(A B); schema_for_pickfirst!(A B C); schema_for_pickfirst!(A B C D); impl JsonSchemaAs for SetLastValueWins where TA: JsonSchemaAs, { fn schema_id() -> Cow<'static, str> { std::format!( "serde_with::SetLastValueWins<{}>", as JsonSchema>::schema_id() ) .into() } fn schema_name() -> String { std::format!( "SetLastValueWins<{}>", as JsonSchema>::schema_name() ) } fn json_schema(gen: &mut SchemaGenerator) -> Schema { let schema = as JsonSchema>::json_schema(gen); let mut schema = schema.into_object(); // We explicitly allow duplicate items since the whole point of // SetLastValueWins is to take the duplicate value. if let Some(array) = &mut schema.array { array.unique_items = None; } schema.into() } fn is_referenceable() -> bool { false } } impl JsonSchemaAs for SetPreventDuplicates where TA: JsonSchemaAs, { forward_schema!(WrapSchema); } impl JsonSchemaAs for StringWithSeparator where SEP: Separator, { forward_schema!(String); } impl JsonSchemaAs> for VecSkipError where TA: JsonSchemaAs, { forward_schema!(Vec>); } mod timespan { use super::*; // #[non_exhaustive] is not actually necessary here but it should // help avoid warnings about semver breakage if this ever changes. #[non_exhaustive] #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum TimespanTargetType { String, F64, U64, I64, } impl TimespanTargetType { pub const fn is_signed(self) -> bool { !matches!(self, Self::U64) } } /// Internal helper trait used to constrain which types we implement /// `JsonSchemaAs` for. pub trait TimespanSchemaTarget { /// The underlying type. /// /// This is mainly used to decide which variant of the resulting schema /// should be marked as `write_only: true`. const TYPE: TimespanTargetType; /// Whether the target type is signed. /// /// This is only true for `std::time::Duration`. const SIGNED: bool = true; } macro_rules! timespan_type_of { (String) => { TimespanTargetType::String }; (f64) => { TimespanTargetType::F64 }; (i64) => { TimespanTargetType::I64 }; (u64) => { TimespanTargetType::U64 }; } macro_rules! declare_timespan_target { ( $target:ty { $($format:ident),* $(,)? } ) => { $( impl TimespanSchemaTarget<$format> for $target { const TYPE: TimespanTargetType = timespan_type_of!($format); } )* } } impl TimespanSchemaTarget for Duration { const TYPE: TimespanTargetType = TimespanTargetType::U64; const SIGNED: bool = false; } impl TimespanSchemaTarget for Duration { const TYPE: TimespanTargetType = TimespanTargetType::F64; const SIGNED: bool = false; } impl TimespanSchemaTarget for Duration { const TYPE: TimespanTargetType = TimespanTargetType::String; const SIGNED: bool = false; } declare_timespan_target!(SystemTime { i64, f64, String }); #[cfg(feature = "chrono_0_4")] declare_timespan_target!(::chrono_0_4::Duration { i64, f64, String }); #[cfg(feature = "chrono_0_4")] declare_timespan_target!(::chrono_0_4::DateTime<::chrono_0_4::Utc> { i64, f64, String }); #[cfg(feature = "chrono_0_4")] declare_timespan_target!(::chrono_0_4::DateTime<::chrono_0_4::Local> { i64, f64, String }); #[cfg(feature = "chrono_0_4")] declare_timespan_target!(::chrono_0_4::NaiveDateTime { i64, f64, String }); #[cfg(feature = "time_0_3")] declare_timespan_target!(::time_0_3::Duration { i64, f64, String }); #[cfg(feature = "time_0_3")] declare_timespan_target!(::time_0_3::OffsetDateTime { i64, f64, String }); #[cfg(feature = "time_0_3")] declare_timespan_target!(::time_0_3::PrimitiveDateTime { i64, f64, String }); } use self::timespan::{TimespanSchemaTarget, TimespanTargetType}; /// Internal type used for the base impls on `DurationXXX` and `TimestampYYY` types. /// /// This allows the `JsonSchema` impls that are Strict to be generic without /// committing to it as part of the public API. struct Timespan(PhantomData<(Format, Strictness)>); impl JsonSchemaAs for Timespan where T: TimespanSchemaTarget, F: Format + JsonSchema, { forward_schema!(F); } impl TimespanTargetType { pub(crate) fn to_flexible_schema(self, signed: bool) -> Schema { use ::schemars_0_8::schema::StringValidation; let mut number = SchemaObject { instance_type: Some(InstanceType::Number.into()), number: (!signed).then(|| { Box::new(NumberValidation { minimum: Some(0.0), ..Default::default() }) }), ..Default::default() }; // This is a more lenient version of the regex used to determine // whether JSON numbers are valid. Specifically, it allows multiple // leading zeroes whereas that is illegal in JSON. let regex = r#"[0-9]+(\.[0-9]+)?([eE][+-]?[0-9]+)?"#; let mut string = SchemaObject { instance_type: Some(InstanceType::String.into()), string: Some(Box::new(StringValidation { pattern: Some(match signed { true => std::format!("^-?{regex}$"), false => std::format!("^{regex}$"), }), ..Default::default() })), ..Default::default() }; if self == Self::String { number.metadata().write_only = true; } else { string.metadata().write_only = true; } SchemaObject { subschemas: Some(Box::new(SubschemaValidation { one_of: Some(std::vec![number.into(), string.into()]), ..Default::default() })), ..Default::default() } .into() } pub(crate) fn schema_id(self) -> &'static str { match self { Self::String => "serde_with::FlexibleStringTimespan", Self::F64 => "serde_with::FlexibleF64Timespan", Self::U64 => "serde_with::FlexibleU64Timespan", Self::I64 => "serde_with::FlexibleI64Timespan", } } } impl JsonSchemaAs for Timespan where T: TimespanSchemaTarget, F: Format + JsonSchema, { fn schema_name() -> String { >::TYPE .schema_id() .strip_prefix("serde_with::") .expect("schema id did not start with `serde_with::` - this is a bug") .into() } fn schema_id() -> Cow<'static, str> { >::TYPE.schema_id().into() } fn json_schema(_: &mut SchemaGenerator) -> Schema { >::TYPE .to_flexible_schema(>::SIGNED) } fn is_referenceable() -> bool { false } } macro_rules! forward_duration_schema { ($ty:ident) => { impl JsonSchemaAs for $ty where T: TimespanSchemaTarget, F: Format + JsonSchema { forward_schema!(WrapSchema>); } impl JsonSchemaAs for $ty where T: TimespanSchemaTarget, F: Format + JsonSchema { forward_schema!(WrapSchema>); } }; } forward_duration_schema!(DurationSeconds); forward_duration_schema!(DurationMilliSeconds); forward_duration_schema!(DurationMicroSeconds); forward_duration_schema!(DurationNanoSeconds); forward_duration_schema!(DurationSecondsWithFrac); forward_duration_schema!(DurationMilliSecondsWithFrac); forward_duration_schema!(DurationMicroSecondsWithFrac); forward_duration_schema!(DurationNanoSecondsWithFrac); forward_duration_schema!(TimestampSeconds); forward_duration_schema!(TimestampMilliSeconds); forward_duration_schema!(TimestampMicroSeconds); forward_duration_schema!(TimestampNanoSeconds); forward_duration_schema!(TimestampSecondsWithFrac); forward_duration_schema!(TimestampMilliSecondsWithFrac); forward_duration_schema!(TimestampMicroSecondsWithFrac); forward_duration_schema!(TimestampNanoSecondsWithFrac); //=================================================================== // Extra internal helper structs struct DropGuard { value: ManuallyDrop, guard: Option, } impl DropGuard { pub fn new(value: T, guard: F) -> Self { Self { value: ManuallyDrop::new(value), guard: Some(guard), } } pub fn unguarded(value: T) -> Self { Self { value: ManuallyDrop::new(value), guard: None, } } } impl Deref for DropGuard { type Target = T; fn deref(&self) -> &Self::Target { &self.value } } impl DerefMut for DropGuard { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.value } } impl Drop for DropGuard { fn drop(&mut self) { // SAFETY: value is known to be initialized since we only ever remove it here. let value = unsafe { ManuallyDrop::take(&mut self.value) }; if let Some(guard) = self.guard.take() { guard(value); } } } serde_with-3.12.0/src/ser/duplicates.rs000064400000000000000000000053551046102023000161370ustar 00000000000000use super::impls::macros::{foreach_map, foreach_set}; use crate::prelude::*; #[cfg(feature = "hashbrown_0_14")] use hashbrown_0_14::{HashMap as HashbrownMap014, HashSet as HashbrownSet014}; #[cfg(feature = "hashbrown_0_15")] use hashbrown_0_15::{HashMap as HashbrownMap015, HashSet as HashbrownSet015}; #[cfg(feature = "indexmap_1")] use indexmap_1::{IndexMap, IndexSet}; #[cfg(feature = "indexmap_2")] use indexmap_2::{IndexMap as IndexMap2, IndexSet as IndexSet2}; macro_rules! set_duplicate_handling { ($tyorig:ident < T $(, $typaram:ident : $bound:ident)* >) => { impl SerializeAs<$tyorig> for SetPreventDuplicates where TAs: SerializeAs, $($typaram: ?Sized + $bound,)* { fn serialize_as(value: &$tyorig, serializer: S) -> Result where S: Serializer, { <$tyorig>::serialize_as(value, serializer) } } impl SerializeAs<$tyorig> for SetLastValueWins where TAs: SerializeAs, $($typaram: ?Sized + $bound,)* { fn serialize_as(value: &$tyorig, serializer: S) -> Result where S: Serializer, { <$tyorig>::serialize_as(value, serializer) } } } } foreach_set!(set_duplicate_handling); macro_rules! map_duplicate_handling { ($tyorig:ident < K, V $(, $typaram:ident : $bound:ident)* >) => { impl SerializeAs<$tyorig> for MapPreventDuplicates where KAs: SerializeAs, VAs: SerializeAs, $($typaram: ?Sized + $bound,)* { fn serialize_as(value: &$tyorig, serializer: S) -> Result where S: Serializer, { <$tyorig>::serialize_as(value, serializer) } } impl SerializeAs<$tyorig> for MapFirstKeyWins where KAs: SerializeAs, VAs: SerializeAs, $($typaram: ?Sized + $bound,)* { fn serialize_as(value: &$tyorig, serializer: S) -> Result where S: Serializer, { <$tyorig>::serialize_as(value, serializer) } } } } foreach_map!(map_duplicate_handling); serde_with-3.12.0/src/ser/impls.rs000064400000000000000000000730221046102023000151220ustar 00000000000000pub(crate) use self::macros::*; use crate::{formats::Strictness, prelude::*}; #[cfg(feature = "hashbrown_0_14")] use hashbrown_0_14::{HashMap as HashbrownMap014, HashSet as HashbrownSet014}; #[cfg(feature = "hashbrown_0_15")] use hashbrown_0_15::{HashMap as HashbrownMap015, HashSet as HashbrownSet015}; #[cfg(feature = "indexmap_1")] use indexmap_1::{IndexMap, IndexSet}; #[cfg(feature = "indexmap_2")] use indexmap_2::{IndexMap as IndexMap2, IndexSet as IndexSet2}; /////////////////////////////////////////////////////////////////////////////// // Helper macro used internally #[cfg(feature = "alloc")] type BoxedSlice = Box<[T]>; type Slice = [T]; type Ref<'a, T> = &'a T; type RefMut<'a, T> = &'a mut T; pub(crate) mod macros { // The unused_imports lint has false-positives around macros // https://github.com/rust-lang/rust/issues/78894 #![allow(unused_imports)] macro_rules! foreach_map { ($m:ident) => { #[cfg(feature = "alloc")] $m!(BTreeMap); #[cfg(feature = "std")] $m!(HashMap); #[cfg(feature = "hashbrown_0_14")] $m!(HashbrownMap014); #[cfg(feature = "hashbrown_0_15")] $m!(HashbrownMap015); #[cfg(feature = "indexmap_1")] $m!(IndexMap); #[cfg(feature = "indexmap_2")] $m!(IndexMap2); }; } macro_rules! foreach_set { ($m:ident, $T:tt) => { #[cfg(feature = "alloc")] $m!(BTreeSet<$T>); #[cfg(feature = "std")] $m!(HashSet<$T, H: Sized>); #[cfg(feature = "hashbrown_0_14")] $m!(HashbrownSet014<$T, H: Sized>); #[cfg(feature = "hashbrown_0_15")] $m!(HashbrownSet015<$T, H: Sized>); #[cfg(feature = "indexmap_1")] $m!(IndexSet<$T, H: Sized>); #[cfg(feature = "indexmap_2")] $m!(IndexSet2<$T, H: Sized>); }; ($m:ident) => { foreach_set!($m, T); }; } macro_rules! foreach_seq { ($m:ident, $T:tt) => { foreach_set!($m, $T); $m!(Slice<$T>); #[cfg(feature = "alloc")] $m!(BinaryHeap<$T>); #[cfg(feature = "alloc")] $m!(BoxedSlice<$T>); #[cfg(feature = "alloc")] $m!(LinkedList<$T>); #[cfg(feature = "alloc")] $m!(Vec<$T>); #[cfg(feature = "alloc")] $m!(VecDeque<$T>); }; ($m: ident) => { foreach_seq!($m, T); }; } // Make the macros available to the rest of the crate pub(crate) use foreach_map; pub(crate) use foreach_seq; pub(crate) use foreach_set; } /////////////////////////////////////////////////////////////////////////////// // region: Simple Wrapper types (e.g., Box, Option) #[allow(unused_macros)] macro_rules! pinned_wrapper { ($wrapper:ident $($lifetime:lifetime)?) => { impl<$($lifetime,)? T, U> SerializeAs>> for Pin<$wrapper<$($lifetime,)? U>> where U: SerializeAs, { fn serialize_as(source: &Pin<$wrapper<$($lifetime,)? T>>, serializer: S) -> Result where S: Serializer, { SerializeAsWrap::::new(source).serialize(serializer) } } }; } impl<'a, T, U> SerializeAs<&'a T> for &'a U where U: SerializeAs, T: ?Sized, U: ?Sized, { fn serialize_as(source: &&'a T, serializer: S) -> Result where S: Serializer, { SerializeAsWrap::::new(source).serialize(serializer) } } impl<'a, T, U> SerializeAs<&'a mut T> for &'a mut U where U: SerializeAs, T: ?Sized, U: ?Sized, { fn serialize_as(source: &&'a mut T, serializer: S) -> Result where S: Serializer, { SerializeAsWrap::::new(source).serialize(serializer) } } pinned_wrapper!(Ref 'a); pinned_wrapper!(RefMut 'a); #[cfg(feature = "alloc")] impl SerializeAs> for Box where U: SerializeAs, { fn serialize_as(source: &Box, serializer: S) -> Result where S: Serializer, { SerializeAsWrap::::new(source).serialize(serializer) } } #[cfg(feature = "alloc")] pinned_wrapper!(Box); impl SerializeAs> for Option where U: SerializeAs, { fn serialize_as(source: &Option, serializer: S) -> Result where S: Serializer, { match *source { Some(ref value) => serializer.serialize_some(&SerializeAsWrap::::new(value)), None => serializer.serialize_none(), } } } impl SerializeAs> for Bound where U: SerializeAs, T: Sized, { fn serialize_as(source: &Bound, serializer: S) -> Result where S: Serializer, { match source { Bound::Unbounded => Bound::Unbounded, Bound::Included(ref v) => Bound::Included(SerializeAsWrap::::new(v)), Bound::Excluded(ref v) => Bound::Excluded(SerializeAsWrap::::new(v)), } .serialize(serializer) } } #[cfg(feature = "alloc")] impl SerializeAs> for Rc where U: SerializeAs, { fn serialize_as(source: &Rc, serializer: S) -> Result where S: Serializer, { SerializeAsWrap::::new(source).serialize(serializer) } } #[cfg(feature = "alloc")] pinned_wrapper!(Rc); #[cfg(feature = "alloc")] impl SerializeAs> for RcWeak where U: SerializeAs, { fn serialize_as(source: &RcWeak, serializer: S) -> Result where S: Serializer, { SerializeAsWrap::>, Option>>::new(&source.upgrade()) .serialize(serializer) } } #[cfg(all(feature = "alloc", target_has_atomic = "ptr"))] impl SerializeAs> for Arc where U: SerializeAs, { fn serialize_as(source: &Arc, serializer: S) -> Result where S: Serializer, { SerializeAsWrap::::new(source).serialize(serializer) } } #[cfg(all(feature = "alloc", target_has_atomic = "ptr"))] pinned_wrapper!(Arc); #[cfg(all(feature = "alloc", target_has_atomic = "ptr"))] impl SerializeAs> for ArcWeak where U: SerializeAs, { fn serialize_as(source: &ArcWeak, serializer: S) -> Result where S: Serializer, { SerializeAsWrap::>, Option>>::new(&source.upgrade()) .serialize(serializer) } } impl SerializeAs> for Cell where U: SerializeAs, T: Copy, { fn serialize_as(source: &Cell, serializer: S) -> Result where S: Serializer, { SerializeAsWrap::::new(&source.get()).serialize(serializer) } } impl SerializeAs> for RefCell where U: SerializeAs, { fn serialize_as(source: &RefCell, serializer: S) -> Result where S: Serializer, { match source.try_borrow() { Ok(source) => SerializeAsWrap::::new(&*source).serialize(serializer), Err(_) => Err(S::Error::custom("already mutably borrowed")), } } } #[cfg(feature = "std")] impl SerializeAs> for Mutex where U: SerializeAs, { fn serialize_as(source: &Mutex, serializer: S) -> Result where S: Serializer, { match source.lock() { Ok(source) => SerializeAsWrap::::new(&*source).serialize(serializer), Err(_) => Err(S::Error::custom("lock poison error while serializing")), } } } #[cfg(feature = "std")] impl SerializeAs> for RwLock where U: SerializeAs, { fn serialize_as(source: &RwLock, serializer: S) -> Result where S: Serializer, { match source.read() { Ok(source) => SerializeAsWrap::::new(&*source).serialize(serializer), Err(_) => Err(S::Error::custom("lock poison error while serializing")), } } } impl SerializeAs> for Result where TAs: SerializeAs, EAs: SerializeAs, { fn serialize_as(source: &Result, serializer: S) -> Result where S: Serializer, { source .as_ref() .map(SerializeAsWrap::::new) .map_err(SerializeAsWrap::::new) .serialize(serializer) } } impl SerializeAs<[T; N]> for [As; N] where As: SerializeAs, { fn serialize_as(array: &[T; N], serializer: S) -> Result where S: Serializer, { let mut arr = serializer.serialize_tuple(N)?; for elem in array { arr.serialize_element(&SerializeAsWrap::::new(elem))?; } arr.end() } } // endregion /////////////////////////////////////////////////////////////////////////////// // region: Collection Types (e.g., Maps, Sets, Vec) macro_rules! seq_impl { ($ty:ident < T $(: $tbound1:ident $(+ $tbound2:ident)*)* $(, $typaram:ident : $bound:ident )* >) => { impl SerializeAs<$ty> for $ty where U: SerializeAs, $(T: ?Sized + $tbound1 $(+ $tbound2)*,)* $($typaram: ?Sized + $bound,)* { fn serialize_as(source: &$ty, serializer: S) -> Result where S: Serializer, { serializer.collect_seq(source.iter().map(|item| SerializeAsWrap::::new(item))) } } } } foreach_seq!(seq_impl); #[cfg(feature = "alloc")] macro_rules! map_impl { ($ty:ident < K, V $(, $typaram:ident : $bound:ident)* >) => { impl SerializeAs<$ty> for $ty where KU: SerializeAs, VU: SerializeAs, $($typaram: ?Sized + $bound,)* { fn serialize_as(source: &$ty, serializer: S) -> Result where S: Serializer, { serializer.collect_map(source.iter().map(|(k, v)| (SerializeAsWrap::::new(k), SerializeAsWrap::::new(v)))) } } } } foreach_map!(map_impl); macro_rules! tuple_impl { ($len:literal $($n:tt $t:ident $tas:ident)+) => { impl<$($t, $tas,)+> SerializeAs<($($t,)+)> for ($($tas,)+) where $($tas: SerializeAs<$t>,)+ { fn serialize_as(tuple: &($($t,)+), serializer: S) -> Result where S: Serializer, { use serde::ser::SerializeTuple; let mut tup = serializer.serialize_tuple($len)?; $( tup.serialize_element(&SerializeAsWrap::<$t, $tas>::new(&tuple.$n))?; )+ tup.end() } } }; } tuple_impl!(1 0 T0 As0); tuple_impl!(2 0 T0 As0 1 T1 As1); tuple_impl!(3 0 T0 As0 1 T1 As1 2 T2 As2); tuple_impl!(4 0 T0 As0 1 T1 As1 2 T2 As2 3 T3 As3); tuple_impl!(5 0 T0 As0 1 T1 As1 2 T2 As2 3 T3 As3 4 T4 As4); tuple_impl!(6 0 T0 As0 1 T1 As1 2 T2 As2 3 T3 As3 4 T4 As4 5 T5 As5); tuple_impl!(7 0 T0 As0 1 T1 As1 2 T2 As2 3 T3 As3 4 T4 As4 5 T5 As5 6 T6 As6); tuple_impl!(8 0 T0 As0 1 T1 As1 2 T2 As2 3 T3 As3 4 T4 As4 5 T5 As5 6 T6 As6 7 T7 As7); tuple_impl!(9 0 T0 As0 1 T1 As1 2 T2 As2 3 T3 As3 4 T4 As4 5 T5 As5 6 T6 As6 7 T7 As7 8 T8 As8); tuple_impl!(10 0 T0 As0 1 T1 As1 2 T2 As2 3 T3 As3 4 T4 As4 5 T5 As5 6 T6 As6 7 T7 As7 8 T8 As8 9 T9 As9); tuple_impl!(11 0 T0 As0 1 T1 As1 2 T2 As2 3 T3 As3 4 T4 As4 5 T5 As5 6 T6 As6 7 T7 As7 8 T8 As8 9 T9 As9 10 T10 As10); tuple_impl!(12 0 T0 As0 1 T1 As1 2 T2 As2 3 T3 As3 4 T4 As4 5 T5 As5 6 T6 As6 7 T7 As7 8 T8 As8 9 T9 As9 10 T10 As10 11 T11 As11); tuple_impl!(13 0 T0 As0 1 T1 As1 2 T2 As2 3 T3 As3 4 T4 As4 5 T5 As5 6 T6 As6 7 T7 As7 8 T8 As8 9 T9 As9 10 T10 As10 11 T11 As11 12 T12 As12); tuple_impl!(14 0 T0 As0 1 T1 As1 2 T2 As2 3 T3 As3 4 T4 As4 5 T5 As5 6 T6 As6 7 T7 As7 8 T8 As8 9 T9 As9 10 T10 As10 11 T11 As11 12 T12 As12 13 T13 As13); tuple_impl!(15 0 T0 As0 1 T1 As1 2 T2 As2 3 T3 As3 4 T4 As4 5 T5 As5 6 T6 As6 7 T7 As7 8 T8 As8 9 T9 As9 10 T10 As10 11 T11 As11 12 T12 As12 13 T13 As13 14 T14 As14); tuple_impl!(16 0 T0 As0 1 T1 As1 2 T2 As2 3 T3 As3 4 T4 As4 5 T5 As5 6 T6 As6 7 T7 As7 8 T8 As8 9 T9 As9 10 T10 As10 11 T11 As11 12 T12 As12 13 T13 As13 14 T14 As14 15 T15 As15); #[cfg(feature = "alloc")] macro_rules! map_as_tuple_seq_intern { ($tyorig:ident < K, V $(, $typaram:ident : $bound:ident)* >, $ty:ident <(K, V)>) => { impl SerializeAs<$tyorig> for $ty<(KAs, VAs)> where KAs: SerializeAs, VAs: SerializeAs, $($typaram: ?Sized + $bound,)* { fn serialize_as(source: &$tyorig, serializer: S) -> Result where S: Serializer, { serializer.collect_seq(source.iter().map(|(k, v)| { ( SerializeAsWrap::::new(k), SerializeAsWrap::::new(v), ) })) } } }; } #[cfg(feature = "alloc")] macro_rules! map_as_tuple_seq { ($tyorig:ident < K, V $(, $typaram:ident : $bound:ident)* >) => { map_as_tuple_seq_intern!($tyorig, Seq<(K, V)>); #[cfg(feature = "alloc")] map_as_tuple_seq_intern!($tyorig, Vec<(K, V)>); } } foreach_map!(map_as_tuple_seq); macro_rules! tuple_seq_as_map_impl_intern { ($tyorig:ident < (K, V) $(, $typaram:ident : $bound:ident)* >, $ty:ident ) => { #[allow(clippy::implicit_hasher)] impl SerializeAs<$tyorig<(K, V) $(, $typaram)*>> for $ty where KAs: SerializeAs, VAs: SerializeAs, $($typaram: ?Sized + $bound,)* { fn serialize_as(source: &$tyorig<(K, V) $(, $typaram)*>, serializer: S) -> Result where S: Serializer, { serializer.collect_map(source.iter().map(|(k, v)| { ( SerializeAsWrap::::new(k), SerializeAsWrap::::new(v), ) })) } } }; } macro_rules! tuple_seq_as_map_impl { ($tyorig:ident < (K, V) $(, $typaram:ident : $bound:ident)* >) => { tuple_seq_as_map_impl_intern!($tyorig<(K, V) $(, $typaram: $bound)* >, Map); #[cfg(feature = "alloc")] tuple_seq_as_map_impl_intern!($tyorig<(K, V) $(, $typaram: $bound)* >, BTreeMap); #[cfg(feature = "std")] tuple_seq_as_map_impl_intern!($tyorig<(K, V) $(, $typaram: $bound)* >, HashMap); } } foreach_seq!(tuple_seq_as_map_impl, (K, V)); tuple_seq_as_map_impl!(Option<(K, V)>); macro_rules! tuple_seq_as_map_arr { ($tyorig:ty, $ty:ident ) => { #[allow(clippy::implicit_hasher)] impl SerializeAs<$tyorig> for $ty where KAs: SerializeAs, VAs: SerializeAs, { fn serialize_as(source: &$tyorig, serializer: S) -> Result where S: Serializer, { serializer.collect_map(source.iter().map(|(k, v)| { ( SerializeAsWrap::::new(k), SerializeAsWrap::::new(v), ) })) } } }; } tuple_seq_as_map_arr!([(K, V); N], Map); #[cfg(feature = "alloc")] tuple_seq_as_map_arr!([(K, V); N], BTreeMap); #[cfg(feature = "std")] tuple_seq_as_map_arr!([(K, V); N], HashMap); // endregion /////////////////////////////////////////////////////////////////////////////// // region: Conversion types which cause different serialization behavior impl SerializeAs for Same where T: Serialize + ?Sized, { fn serialize_as(source: &T, serializer: S) -> Result where S: Serializer, { source.serialize(serializer) } } impl SerializeAs for DisplayFromStr where T: Display, { fn serialize_as(source: &T, serializer: S) -> Result where S: Serializer, { serializer.collect_str(source) } } impl SerializeAs for IfIsHumanReadable where T: ?Sized, H: SerializeAs, F: SerializeAs, { fn serialize_as(source: &T, serializer: S) -> Result where S: Serializer, { if serializer.is_human_readable() { H::serialize_as(source, serializer) } else { F::serialize_as(source, serializer) } } } impl SerializeAs> for NoneAsEmptyString where T: Display, { fn serialize_as(source: &Option, serializer: S) -> Result where S: Serializer, { if let Some(value) = source { serializer.collect_str(value) } else { serializer.serialize_str("") } } } #[cfg(feature = "alloc")] impl SerializeAs for DefaultOnError where TAs: SerializeAs, { fn serialize_as(source: &T, serializer: S) -> Result where S: Serializer, { TAs::serialize_as(source, serializer) } } #[cfg(feature = "alloc")] impl SerializeAs> for BytesOrString { fn serialize_as(source: &Vec, serializer: S) -> Result where S: Serializer, { source.serialize(serializer) } } impl SerializeAs for StringWithSeparator where SEPARATOR: formats::Separator, for<'x> &'x I: IntoIterator, T: Display, // This set of bounds is enough to make the function compile but has inference issues // making it unusable at the moment. // https://github.com/rust-lang/rust/issues/89196#issuecomment-932024770 // for<'x> &'x I: IntoIterator, // for<'x> <&'x I as IntoIterator>::Item: Display, { fn serialize_as(source: &I, serializer: S) -> Result where S: Serializer, { pub(crate) struct DisplayWithSeparator<'a, I, SEPARATOR>(&'a I, PhantomData); impl<'a, I, SEPARATOR> DisplayWithSeparator<'a, I, SEPARATOR> { pub(crate) fn new(iter: &'a I) -> Self { Self(iter, PhantomData) } } impl<'a, I, SEPARATOR> Display for DisplayWithSeparator<'a, I, SEPARATOR> where SEPARATOR: formats::Separator, &'a I: IntoIterator, <&'a I as IntoIterator>::Item: Display, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut iter = self.0.into_iter(); if let Some(first) = iter.next() { first.fmt(f)?; } for elem in iter { f.write_str(SEPARATOR::separator())?; elem.fmt(f)?; } Ok(()) } } serializer.collect_str(&DisplayWithSeparator::::new(source)) } } macro_rules! use_signed_duration { ( $main_trait:ident $internal_trait:ident => { $ty:ty => $({ $format:ty, $strictness:ty => $($tbound:ident: $bound:ident $(,)?)* })* } ) => { $( impl<$($tbound,)*> SerializeAs<$ty> for $main_trait<$format, $strictness> where $($tbound: $bound,)* { fn serialize_as(source: &$ty, serializer: S) -> Result where S: Serializer, { $internal_trait::<$format, $strictness>::serialize_as( &DurationSigned::from(source), serializer, ) } } )* }; ( $( $main_trait:ident $internal_trait:ident, )+ => $rest:tt ) => { $( use_signed_duration!($main_trait $internal_trait => $rest); )+ }; } use_signed_duration!( DurationSeconds DurationSeconds, DurationMilliSeconds DurationMilliSeconds, DurationMicroSeconds DurationMicroSeconds, DurationNanoSeconds DurationNanoSeconds, => { Duration => {u64, STRICTNESS => STRICTNESS: Strictness} {f64, STRICTNESS => STRICTNESS: Strictness} } ); #[cfg(feature = "alloc")] use_signed_duration!( DurationSeconds DurationSeconds, DurationMilliSeconds DurationMilliSeconds, DurationMicroSeconds DurationMicroSeconds, DurationNanoSeconds DurationNanoSeconds, => { Duration => {String, STRICTNESS => STRICTNESS: Strictness} } ); use_signed_duration!( DurationSecondsWithFrac DurationSecondsWithFrac, DurationMilliSecondsWithFrac DurationMilliSecondsWithFrac, DurationMicroSecondsWithFrac DurationMicroSecondsWithFrac, DurationNanoSecondsWithFrac DurationNanoSecondsWithFrac, => { Duration => {f64, STRICTNESS => STRICTNESS: Strictness} } ); #[cfg(feature = "alloc")] use_signed_duration!( DurationSecondsWithFrac DurationSecondsWithFrac, DurationMilliSecondsWithFrac DurationMilliSecondsWithFrac, DurationMicroSecondsWithFrac DurationMicroSecondsWithFrac, DurationNanoSecondsWithFrac DurationNanoSecondsWithFrac, => { Duration => {String, STRICTNESS => STRICTNESS: Strictness} } ); #[cfg(feature = "std")] use_signed_duration!( TimestampSeconds DurationSeconds, TimestampMilliSeconds DurationMilliSeconds, TimestampMicroSeconds DurationMicroSeconds, TimestampNanoSeconds DurationNanoSeconds, => { SystemTime => {i64, STRICTNESS => STRICTNESS: Strictness} {f64, STRICTNESS => STRICTNESS: Strictness} {String, STRICTNESS => STRICTNESS: Strictness} } ); #[cfg(feature = "std")] use_signed_duration!( TimestampSecondsWithFrac DurationSecondsWithFrac, TimestampMilliSecondsWithFrac DurationMilliSecondsWithFrac, TimestampMicroSecondsWithFrac DurationMicroSecondsWithFrac, TimestampNanoSecondsWithFrac DurationNanoSecondsWithFrac, => { SystemTime => {f64, STRICTNESS => STRICTNESS: Strictness} {String, STRICTNESS => STRICTNESS: Strictness} } ); impl SerializeAs for DefaultOnNull where U: SerializeAs, { fn serialize_as(source: &T, serializer: S) -> Result where S: Serializer, { serializer.serialize_some(&SerializeAsWrap::::new(source)) } } impl SerializeAs<&[u8]> for Bytes { fn serialize_as(bytes: &&[u8], serializer: S) -> Result where S: Serializer, { serializer.serialize_bytes(bytes) } } #[cfg(feature = "alloc")] impl SerializeAs> for Bytes { fn serialize_as(bytes: &Vec, serializer: S) -> Result where S: Serializer, { serializer.serialize_bytes(bytes) } } #[cfg(feature = "alloc")] impl SerializeAs> for Bytes { fn serialize_as(bytes: &Box<[u8]>, serializer: S) -> Result where S: Serializer, { serializer.serialize_bytes(bytes) } } #[cfg(feature = "alloc")] impl<'a> SerializeAs> for Bytes { fn serialize_as(bytes: &Cow<'a, [u8]>, serializer: S) -> Result where S: Serializer, { serializer.serialize_bytes(bytes) } } impl SerializeAs<[u8; N]> for Bytes { fn serialize_as(bytes: &[u8; N], serializer: S) -> Result where S: Serializer, { serializer.serialize_bytes(bytes) } } impl SerializeAs<&[u8; N]> for Bytes { fn serialize_as(bytes: &&[u8; N], serializer: S) -> Result where S: Serializer, { serializer.serialize_bytes(*bytes) } } #[cfg(feature = "alloc")] impl SerializeAs> for Bytes { fn serialize_as(bytes: &Box<[u8; N]>, serializer: S) -> Result where S: Serializer, { serializer.serialize_bytes(&**bytes) } } #[cfg(feature = "alloc")] impl<'a, const N: usize> SerializeAs> for Bytes { fn serialize_as(bytes: &Cow<'a, [u8; N]>, serializer: S) -> Result where S: Serializer, { serializer.serialize_bytes(bytes.as_ref()) } } #[cfg(feature = "alloc")] impl SerializeAs> for OneOrMany where U: SerializeAs, { fn serialize_as(source: &Vec, serializer: S) -> Result where S: Serializer, { match source.len() { 1 => SerializeAsWrap::::new(source.iter().next().expect("Cannot be empty")) .serialize(serializer), _ => SerializeAsWrap::, Vec>::new(source).serialize(serializer), } } } #[cfg(feature = "alloc")] impl SerializeAs> for OneOrMany where U: SerializeAs, { fn serialize_as(source: &Vec, serializer: S) -> Result where S: Serializer, { SerializeAsWrap::, Vec>::new(source).serialize(serializer) } } #[cfg(feature = "alloc")] impl SerializeAs for PickFirst<(TAs1,)> where TAs1: SerializeAs, { fn serialize_as(source: &T, serializer: S) -> Result where S: Serializer, { SerializeAsWrap::::new(source).serialize(serializer) } } #[cfg(feature = "alloc")] impl SerializeAs for PickFirst<(TAs1, TAs2)> where TAs1: SerializeAs, { fn serialize_as(source: &T, serializer: S) -> Result where S: Serializer, { SerializeAsWrap::::new(source).serialize(serializer) } } #[cfg(feature = "alloc")] impl SerializeAs for PickFirst<(TAs1, TAs2, TAs3)> where TAs1: SerializeAs, { fn serialize_as(source: &T, serializer: S) -> Result where S: Serializer, { SerializeAsWrap::::new(source).serialize(serializer) } } #[cfg(feature = "alloc")] impl SerializeAs for PickFirst<(TAs1, TAs2, TAs3, TAs4)> where TAs1: SerializeAs, { fn serialize_as(source: &T, serializer: S) -> Result where S: Serializer, { SerializeAsWrap::::new(source).serialize(serializer) } } impl SerializeAs for FromInto where T: Into + Clone, U: Serialize, { fn serialize_as(source: &T, serializer: S) -> Result where S: Serializer, { source.clone().into().serialize(serializer) } } impl SerializeAs for TryFromInto where T: TryInto + Clone, >::Error: Display, U: Serialize, { fn serialize_as(source: &T, serializer: S) -> Result where S: Serializer, { source .clone() .try_into() .map_err(S::Error::custom)? .serialize(serializer) } } impl SerializeAs for FromIntoRef where for<'a> &'a T: Into, U: Serialize, { fn serialize_as(source: &T, serializer: S) -> Result where S: Serializer, { source.into().serialize(serializer) } } impl SerializeAs for TryFromIntoRef where for<'a> &'a T: TryInto, for<'a> <&'a T as TryInto>::Error: Display, U: Serialize, { fn serialize_as(source: &T, serializer: S) -> Result where S: Serializer, { source .try_into() .map_err(S::Error::custom)? .serialize(serializer) } } #[cfg(feature = "alloc")] impl<'a> SerializeAs> for BorrowCow { fn serialize_as(source: &Cow<'a, str>, serializer: S) -> Result where S: Serializer, { serializer.serialize_str(source) } } #[cfg(feature = "alloc")] impl<'a> SerializeAs> for BorrowCow { fn serialize_as(value: &Cow<'a, [u8]>, serializer: S) -> Result where S: Serializer, { serializer.collect_seq(value.iter()) } } #[cfg(feature = "alloc")] impl<'a, const N: usize> SerializeAs> for BorrowCow { fn serialize_as(value: &Cow<'a, [u8; N]>, serializer: S) -> Result where S: Serializer, { serializer.collect_seq(value.iter()) } } impl SerializeAs for BoolFromInt { fn serialize_as(source: &bool, serializer: S) -> Result where S: Serializer, { serializer.serialize_u8(u8::from(*source)) } } // endregion serde_with-3.12.0/src/ser/mod.rs000064400000000000000000000131661046102023000145600ustar 00000000000000//! Module for [`SerializeAs`][] implementations //! //! The module contains the [`SerializeAs`][] trait and helper code. //! Additionally, it contains implementations of [`SerializeAs`][] for types defined in the Rust Standard Library or this crate. //! //! You can find more details on how to implement this trait for your types in the documentation of the [`SerializeAs`][] trait and details about the usage in the [user guide][]. //! //! [user guide]: crate::guide #[cfg(feature = "alloc")] mod duplicates; mod impls; #[cfg(feature = "alloc")] mod skip_error; use crate::prelude::*; /// A **data structure** that can be serialized into any data format supported by Serde, analogue to [`Serialize`]. /// /// The trait is analogue to the [`serde::Serialize`][`Serialize`] trait, with the same meaning of input and output arguments. /// It can and should be implemented using the same code structure as the [`Serialize`] trait. /// As such, the same advice for [implementing `Serialize`][impl-serialize] applies here. /// /// # Differences to [`Serialize`] /// /// The trait is only required for container-like types or types implementing specific conversion functions. /// Container-like types are [`Vec`], [`BTreeMap`], but also [`Option`] and [`Box`]. /// Conversion types serialize into a different serde data type. /// For example, [`DisplayFromStr`] uses the [`Display`] trait to serialize a String and [`DurationSeconds`] converts a [`Duration`] into either String or integer values. /// /// This code shows how to implement [`Serialize`] for [`Box`]: /// /// ```rust,ignore /// impl Serialize for Box /// where /// T: Serialize, /// { /// #[inline] /// fn serialize(&self, serializer: S) -> Result /// where /// S: Serializer, /// { /// (**self).serialize(serializer) /// } /// } /// ``` /// /// and this code shows how to do the same using [`SerializeAs`][]: /// /// ```rust,ignore /// impl SerializeAs> for Box /// where /// U: SerializeAs, /// { /// fn serialize_as(source: &Box, serializer: S) -> Result /// where /// S: Serializer, /// { /// SerializeAsWrap::::new(source).serialize(serializer) /// } /// } /// ``` /// /// It uses two type parameters, `T` and `U` instead of only one and performs the serialization step using the `SerializeAsWrap` type. /// The `T` type is the type on the Rust side before serialization, whereas the `U` type determines how the value will be serialized. /// These two changes are usually enough to make a container type implement [`SerializeAs`][]. /// /// [`SerializeAsWrap`] is a piece of glue code which turns [`SerializeAs`] into a serde compatible datatype, by converting all calls to `serialize` into `serialize_as`. /// This allows us to implement [`SerializeAs`] such that it can be applied recursively throughout the whole data structure. /// This is mostly important for container types, such as `Vec` or `BTreeMap`. /// In a `BTreeMap` this allows us to specify two different serialization behaviors, one for key and one for value, using the [`SerializeAs`] trait. /// /// ## Implementing a converter Type /// /// This shows a simplified implementation for [`DisplayFromStr`]. /// /// ```rust /// # #[cfg(feature = "macros")] { /// # use serde_with::{serde_as, SerializeAs}; /// # use std::fmt::Display; /// struct DisplayFromStr; /// /// impl SerializeAs for DisplayFromStr /// where /// T: Display, /// { /// fn serialize_as(source: &T, serializer: S) -> Result /// where /// S: serde::Serializer, /// { /// serializer.collect_str(&source) /// } /// } /// # /// # #[serde_as] /// # #[derive(serde::Serialize)] /// # struct S (#[serde_as(as = "DisplayFromStr")] bool); /// # /// # assert_eq!(r#""false""#, serde_json::to_string(&S(false)).unwrap()); /// # } /// ``` /// /// [`Box`]: std::boxed::Box /// [`BTreeMap`]: std::collections::BTreeMap /// [`Display`]: std::fmt::Display /// [`Duration`]: std::time::Duration /// [`Vec`]: std::vec::Vec /// [impl-serialize]: https://serde.rs/impl-serialize.html pub trait SerializeAs { /// Serialize this value into the given Serde serializer. fn serialize_as(source: &T, serializer: S) -> Result where S: Serializer; } /// Helper type to implement [`SerializeAs`] for container-like types. pub struct SerializeAsWrap<'a, T: ?Sized, U: ?Sized> { value: &'a T, marker: PhantomData, } impl<'a, T, U> SerializeAsWrap<'a, T, U> where T: ?Sized, U: ?Sized, { /// Create new instance with provided value. pub fn new(value: &'a T) -> Self { Self { value, marker: PhantomData, } } } impl Serialize for SerializeAsWrap<'_, T, U> where T: ?Sized, U: ?Sized, U: SerializeAs, { fn serialize(&self, serializer: S) -> Result where S: Serializer, { U::serialize_as(self.value, serializer) } } impl<'a, T, U> From<&'a T> for SerializeAsWrap<'a, T, U> where T: ?Sized, U: ?Sized, U: SerializeAs, { fn from(value: &'a T) -> Self { Self::new(value) } } impl As { /// Serialize type `T` using [`SerializeAs`][] /// /// The function signature is compatible with [serde's `with` annotation][with-annotation]. /// /// [with-annotation]: https://serde.rs/field-attrs.html#with pub fn serialize(value: &I, serializer: S) -> Result where S: Serializer, T: SerializeAs, I: ?Sized, { T::serialize_as(value, serializer) } } serde_with-3.12.0/src/ser/skip_error.rs000064400000000000000000000024271046102023000161560ustar 00000000000000use super::impls::macros::foreach_map; use crate::prelude::*; #[cfg(feature = "hashbrown_0_14")] use hashbrown_0_14::HashMap as HashbrownMap014; #[cfg(feature = "hashbrown_0_15")] use hashbrown_0_15::HashMap as HashbrownMap015; #[cfg(feature = "indexmap_1")] use indexmap_1::IndexMap; #[cfg(feature = "indexmap_2")] use indexmap_2::IndexMap as IndexMap2; impl SerializeAs> for VecSkipError where U: SerializeAs, { fn serialize_as(source: &Vec, serializer: S) -> Result where S: Serializer, { Vec::::serialize_as(source, serializer) } } macro_rules! map_skip_error_handling { ($tyorig:ident < K, V $(, $typaram:ident : $bound:ident)* >) => { impl SerializeAs<$tyorig> for MapSkipError where KAs: SerializeAs, VAs: SerializeAs, $($typaram: ?Sized + $bound,)* { fn serialize_as(value: &$tyorig, serializer: S) -> Result where S: Serializer, { <$tyorig>::serialize_as(value, serializer) } } } } foreach_map!(map_skip_error_handling); serde_with-3.12.0/src/serde_conv.rs000064400000000000000000000121311046102023000153260ustar 00000000000000/// Create new conversion adapters from functions /// /// The macro lets you create a new converter, which is usable for serde's with-attribute and `#[serde_as]`. /// Its main use case is to write simple converters for types, which are not serializable. /// Another use-case is to change the serialization behavior if the implemented `Serialize`/`Deserialize` trait is insufficient. /// /// The macro takes four arguments: /// /// 1. The name of the converter type. /// The type can be prefixed with a visibility modifies like `pub` or `pub(crate)`. /// By default, the type is not marked as public (`pub(self)`). /// 2. The type `T` we want to extend with custom behavior. /// 3. A function or macro taking a `&T` and returning a serializable type. /// 4. A function or macro taking a deserializable type and returning a `Result`. /// The error type `E` must implement [`Display`]. /// /// [`Display`]: std::fmt::Display /// /// # Example /// /// In this example, we write custom serialization behavior for a `Rgb` type. /// We want to serialize it as a `[u8; 3]`. /// /// ```rust /// # #[cfg(feature = "macros")] { /// # use serde::{Serialize, Deserialize}; /// # use serde_with::serde_as; /// /// #[derive(Clone, Copy, Debug, PartialEq)] /// struct Rgb { /// red: u8, /// green: u8, /// blue: u8, /// } /// /// serde_with::serde_conv!( /// RgbAsArray, /// Rgb, /// |rgb: &Rgb| [rgb.red, rgb.green, rgb.blue], /// |value: [u8; 3]| -> Result<_, std::convert::Infallible> { /// Ok(Rgb { /// red: value[0], /// green: value[1], /// blue: value[2], /// }) /// } /// ); /// /// ////////////////////////////////////////////////// /// /// // We define some colors to be used later /// /// let green = Rgb {red: 0, green: 255, blue: 0}; /// let orange = Rgb {red: 255, green: 128, blue: 0}; /// let pink = Rgb {red: 255, green: 0, blue: 255}; /// /// ////////////////////////////////////////////////// /// /// // We can now use the `RgbAsArray` adapter with `serde_as`. /// /// #[serde_as] /// #[derive(Debug, PartialEq, Serialize, Deserialize)] /// struct Colors { /// #[serde_as(as = "RgbAsArray")] /// one_rgb: Rgb, /// #[serde_as(as = "Vec")] /// rgbs_in_vec: Vec, /// } /// /// let data = Colors { /// one_rgb: orange, /// rgbs_in_vec: vec![green, pink], /// }; /// let json = serde_json::json!({ /// "one_rgb": [255, 128, 0], /// "rgbs_in_vec": [ /// [0, 255, 0], /// [255, 0, 255] /// ] /// }); /// /// assert_eq!(json, serde_json::to_value(&data).unwrap()); /// assert_eq!(data, serde_json::from_value(json).unwrap()); /// /// ////////////////////////////////////////////////// /// /// // The types generated by `serde_conv` is also compatible with serde's with attribute /// /// #[derive(Debug, PartialEq, Serialize, Deserialize)] /// struct ColorsWith { /// #[serde(with = "RgbAsArray")] /// rgb_with: Rgb, /// } /// /// let data = ColorsWith { /// rgb_with: pink, /// }; /// let json = serde_json::json!({ /// "rgb_with": [255, 0, 255] /// }); /// /// assert_eq!(json, serde_json::to_value(&data).unwrap()); /// assert_eq!(data, serde_json::from_value(json).unwrap()); /// # } /// ``` #[macro_export] macro_rules! serde_conv { ($m:ident, $t:ty, $ser:expr, $de:expr) => {$crate::serde_conv!(pub(self) $m, $t, $ser, $de);}; ($vis:vis $m:ident, $t:ty, $ser:expr, $de:expr) => { #[allow(non_camel_case_types)] $vis struct $m; // Prevent clippy lints triggering because of the template here // https://github.com/jonasbb/serde_with/pull/320 // https://github.com/jonasbb/serde_with/pull/729 #[allow(clippy::all)] const _:() = { impl $m { $vis fn serialize(x: &$t, serializer: S) -> $crate::__private__::Result where S: $crate::serde::Serializer, { let y = $ser(x); $crate::serde::Serialize::serialize(&y, serializer) } $vis fn deserialize<'de, D>(deserializer: D) -> $crate::__private__::Result<$t, D::Error> where D: $crate::serde::Deserializer<'de>, { let y = $crate::serde::Deserialize::deserialize(deserializer)?; $de(y).map_err($crate::serde::de::Error::custom) } } impl $crate::SerializeAs<$t> for $m { fn serialize_as(x: &$t, serializer: S) -> $crate::__private__::Result where S: $crate::serde::Serializer, { Self::serialize(x, serializer) } } impl<'de> $crate::DeserializeAs<'de, $t> for $m { fn deserialize_as(deserializer: D) -> $crate::__private__::Result<$t, D::Error> where D: $crate::serde::Deserializer<'de>, { Self::deserialize(deserializer) } } }; }; } serde_with-3.12.0/src/time_0_3.rs000064400000000000000000000507071046102023000146110ustar 00000000000000//! De/Serialization of [time v0.3][time] types //! //! This modules is only available if using the `time_0_3` feature of the crate. //! No extra types are exposed. Instead it enables support for [`time_0_3::Duration`] together with [`DurationSeconds`] and its variants. //! The types [`time_0_3::PrimitiveDateTime`] and [`time_0_3::OffsetDateTime`] are supported by [`TimestampSeconds`] and its variants. //! The well-known format descriptions [`Rfc2822`], [`Rfc3339`] and [`Iso8601`] are supported for [`OffsetDateTime`]. //! //! [time]: https://docs.rs/time/0.3/ // Serialization of large numbers can result in overflows // The time calculations are prone to this, so lint here extra // https://github.com/jonasbb/serde_with/issues/771 #![warn(clippy::as_conversions)] use crate::{ formats::{Flexible, Format, Strict, Strictness}, prelude::*, }; #[cfg(feature = "std")] use ::time_0_3::format_description::well_known::{ iso8601::EncodedConfig, Iso8601, Rfc2822, Rfc3339, }; use ::time_0_3::{Duration as Time03Duration, OffsetDateTime, PrimitiveDateTime}; /// Create a [`PrimitiveDateTime`] for the Unix Epoch fn unix_epoch_primitive() -> PrimitiveDateTime { PrimitiveDateTime::new( ::time_0_3::Date::from_ordinal_date(1970, 1).unwrap(), ::time_0_3::Time::from_hms_nano(0, 0, 0, 0).unwrap(), ) } /// Convert a [`time::Duration`][time_0_3::Duration] into a [`DurationSigned`] fn duration_into_duration_signed(dur: &Time03Duration) -> DurationSigned { let std_dur = Duration::new( dur.whole_seconds().unsigned_abs(), dur.subsec_nanoseconds().unsigned_abs(), ); DurationSigned::with_duration( // A duration of 0 is not positive, so check for negative value. if dur.is_negative() { Sign::Negative } else { Sign::Positive }, std_dur, ) } /// Convert a [`DurationSigned`] into a [`time_0_3::Duration`] fn duration_from_duration_signed<'de, D>(sdur: DurationSigned) -> Result where D: Deserializer<'de>, { let mut dur: Time03Duration = match sdur.duration.try_into() { Ok(dur) => dur, Err(msg) => { return Err(DeError::custom(format_args!( "Duration is outside of the representable range: {msg}" ))) } }; if sdur.sign.is_negative() { dur = -dur; } Ok(dur) } macro_rules! use_duration_signed_ser { ( $main_trait:ident $internal_trait:ident => { $ty:ty; $converter:ident => $({ $format:ty, $strictness:ty => $($tbound:ident: $bound:ident $(,)?)* })* } ) => { $( impl<$($tbound ,)*> SerializeAs<$ty> for $main_trait<$format, $strictness> where $($tbound: $bound,)* { fn serialize_as(source: &$ty, serializer: S) -> Result where S: Serializer, { let dur: DurationSigned = $converter(source); $internal_trait::<$format, $strictness>::serialize_as( &dur, serializer, ) } } )* }; ( $( $main_trait:ident $internal_trait:ident, )+ => $rest:tt ) => { $( use_duration_signed_ser!($main_trait $internal_trait => $rest); )+ }; } fn offset_datetime_to_duration(source: &OffsetDateTime) -> DurationSigned { duration_into_duration_signed(&(*source - OffsetDateTime::UNIX_EPOCH)) } fn primitive_datetime_to_duration(source: &PrimitiveDateTime) -> DurationSigned { duration_into_duration_signed(&(*source - unix_epoch_primitive())) } use_duration_signed_ser!( DurationSeconds DurationSeconds, DurationMilliSeconds DurationMilliSeconds, DurationMicroSeconds DurationMicroSeconds, DurationNanoSeconds DurationNanoSeconds, => { Time03Duration; duration_into_duration_signed => {i64, STRICTNESS => STRICTNESS: Strictness} } ); #[cfg(feature = "alloc")] use_duration_signed_ser!( DurationSeconds DurationSeconds, DurationMilliSeconds DurationMilliSeconds, DurationMicroSeconds DurationMicroSeconds, DurationNanoSeconds DurationNanoSeconds, => { Time03Duration; duration_into_duration_signed => {String, STRICTNESS => STRICTNESS: Strictness} } ); #[cfg(feature = "std")] use_duration_signed_ser!( DurationSeconds DurationSeconds, DurationMilliSeconds DurationMilliSeconds, DurationMicroSeconds DurationMicroSeconds, DurationNanoSeconds DurationNanoSeconds, => { Time03Duration; duration_into_duration_signed => {f64, STRICTNESS => STRICTNESS: Strictness} } ); use_duration_signed_ser!( TimestampSeconds DurationSeconds, TimestampMilliSeconds DurationMilliSeconds, TimestampMicroSeconds DurationMicroSeconds, TimestampNanoSeconds DurationNanoSeconds, => { OffsetDateTime; offset_datetime_to_duration => {i64, STRICTNESS => STRICTNESS: Strictness} } ); #[cfg(feature = "alloc")] use_duration_signed_ser!( TimestampSeconds DurationSeconds, TimestampMilliSeconds DurationMilliSeconds, TimestampMicroSeconds DurationMicroSeconds, TimestampNanoSeconds DurationNanoSeconds, => { OffsetDateTime; offset_datetime_to_duration => {String, STRICTNESS => STRICTNESS: Strictness} } ); #[cfg(feature = "std")] use_duration_signed_ser!( TimestampSeconds DurationSeconds, TimestampMilliSeconds DurationMilliSeconds, TimestampMicroSeconds DurationMicroSeconds, TimestampNanoSeconds DurationNanoSeconds, => { OffsetDateTime; offset_datetime_to_duration => {f64, STRICTNESS => STRICTNESS: Strictness} } ); use_duration_signed_ser!( TimestampSeconds DurationSeconds, TimestampMilliSeconds DurationMilliSeconds, TimestampMicroSeconds DurationMicroSeconds, TimestampNanoSeconds DurationNanoSeconds, => { PrimitiveDateTime; primitive_datetime_to_duration => {i64, STRICTNESS => STRICTNESS: Strictness} } ); #[cfg(feature = "alloc")] use_duration_signed_ser!( TimestampSeconds DurationSeconds, TimestampMilliSeconds DurationMilliSeconds, TimestampMicroSeconds DurationMicroSeconds, TimestampNanoSeconds DurationNanoSeconds, => { PrimitiveDateTime; primitive_datetime_to_duration => {String, STRICTNESS => STRICTNESS: Strictness} } ); #[cfg(feature = "std")] use_duration_signed_ser!( TimestampSeconds DurationSeconds, TimestampMilliSeconds DurationMilliSeconds, TimestampMicroSeconds DurationMicroSeconds, TimestampNanoSeconds DurationNanoSeconds, => { PrimitiveDateTime; primitive_datetime_to_duration => {f64, STRICTNESS => STRICTNESS: Strictness} } ); // Duration/Timestamp WITH FRACTIONS #[cfg(feature = "alloc")] use_duration_signed_ser!( DurationSecondsWithFrac DurationSecondsWithFrac, DurationMilliSecondsWithFrac DurationMilliSecondsWithFrac, DurationMicroSecondsWithFrac DurationMicroSecondsWithFrac, DurationNanoSecondsWithFrac DurationNanoSecondsWithFrac, => { Time03Duration; duration_into_duration_signed => {String, STRICTNESS => STRICTNESS: Strictness} } ); #[cfg(feature = "std")] use_duration_signed_ser!( DurationSecondsWithFrac DurationSecondsWithFrac, DurationMilliSecondsWithFrac DurationMilliSecondsWithFrac, DurationMicroSecondsWithFrac DurationMicroSecondsWithFrac, DurationNanoSecondsWithFrac DurationNanoSecondsWithFrac, => { Time03Duration; duration_into_duration_signed => {f64, STRICTNESS => STRICTNESS: Strictness} } ); #[cfg(feature = "alloc")] use_duration_signed_ser!( TimestampSecondsWithFrac DurationSecondsWithFrac, TimestampMilliSecondsWithFrac DurationMilliSecondsWithFrac, TimestampMicroSecondsWithFrac DurationMicroSecondsWithFrac, TimestampNanoSecondsWithFrac DurationNanoSecondsWithFrac, => { OffsetDateTime; offset_datetime_to_duration => {String, STRICTNESS => STRICTNESS: Strictness} } ); #[cfg(feature = "std")] use_duration_signed_ser!( TimestampSecondsWithFrac DurationSecondsWithFrac, TimestampMilliSecondsWithFrac DurationMilliSecondsWithFrac, TimestampMicroSecondsWithFrac DurationMicroSecondsWithFrac, TimestampNanoSecondsWithFrac DurationNanoSecondsWithFrac, => { OffsetDateTime; offset_datetime_to_duration => {f64, STRICTNESS => STRICTNESS: Strictness} } ); #[cfg(feature = "alloc")] use_duration_signed_ser!( TimestampSecondsWithFrac DurationSecondsWithFrac, TimestampMilliSecondsWithFrac DurationMilliSecondsWithFrac, TimestampMicroSecondsWithFrac DurationMicroSecondsWithFrac, TimestampNanoSecondsWithFrac DurationNanoSecondsWithFrac, => { PrimitiveDateTime; primitive_datetime_to_duration => {String, STRICTNESS => STRICTNESS: Strictness} } ); #[cfg(feature = "std")] use_duration_signed_ser!( TimestampSecondsWithFrac DurationSecondsWithFrac, TimestampMilliSecondsWithFrac DurationMilliSecondsWithFrac, TimestampMicroSecondsWithFrac DurationMicroSecondsWithFrac, TimestampNanoSecondsWithFrac DurationNanoSecondsWithFrac, => { PrimitiveDateTime; primitive_datetime_to_duration => {f64, STRICTNESS => STRICTNESS: Strictness} } ); macro_rules! use_duration_signed_de { ( $main_trait:ident $internal_trait:ident => { $ty:ty; $converter:ident => $({ $format:ty, $strictness:ty => $($tbound:ident: $bound:ident)* })* } ) =>{ $( impl<'de, $($tbound,)*> DeserializeAs<'de, $ty> for $main_trait<$format, $strictness> where $($tbound: $bound,)* { fn deserialize_as(deserializer: D) -> Result<$ty, D::Error> where D: Deserializer<'de>, { let dur: DurationSigned = $internal_trait::<$format, $strictness>::deserialize_as(deserializer)?; $converter::(dur) } } )* }; ( $( $main_trait:ident $internal_trait:ident, )+ => $rest:tt ) => { $( use_duration_signed_de!($main_trait $internal_trait => $rest); )+ }; } fn duration_to_offset_datetime<'de, D>(dur: DurationSigned) -> Result where D: Deserializer<'de>, { Ok(OffsetDateTime::UNIX_EPOCH + duration_from_duration_signed::(dur)?) } fn duration_to_primitive_datetime<'de, D>( dur: DurationSigned, ) -> Result where D: Deserializer<'de>, { Ok(unix_epoch_primitive() + duration_from_duration_signed::(dur)?) } // No sub-second precision use_duration_signed_de!( DurationSeconds DurationSeconds, DurationMilliSeconds DurationMilliSeconds, DurationMicroSeconds DurationMicroSeconds, DurationNanoSeconds DurationNanoSeconds, => { Time03Duration; duration_from_duration_signed => {i64, Strict =>} {FORMAT, Flexible => FORMAT: Format} } ); #[cfg(feature = "alloc")] use_duration_signed_de!( DurationSeconds DurationSeconds, DurationMilliSeconds DurationMilliSeconds, DurationMicroSeconds DurationMicroSeconds, DurationNanoSeconds DurationNanoSeconds, => { Time03Duration; duration_from_duration_signed => {String, Strict =>} } ); #[cfg(feature = "std")] use_duration_signed_de!( DurationSeconds DurationSeconds, DurationMilliSeconds DurationMilliSeconds, DurationMicroSeconds DurationMicroSeconds, DurationNanoSeconds DurationNanoSeconds, => { Time03Duration; duration_from_duration_signed => {f64, Strict =>} } ); use_duration_signed_de!( TimestampSeconds DurationSeconds, TimestampMilliSeconds DurationMilliSeconds, TimestampMicroSeconds DurationMicroSeconds, TimestampNanoSeconds DurationNanoSeconds, => { OffsetDateTime; duration_to_offset_datetime => {i64, Strict =>} {FORMAT, Flexible => FORMAT: Format} } ); #[cfg(feature = "alloc")] use_duration_signed_de!( TimestampSeconds DurationSeconds, TimestampMilliSeconds DurationMilliSeconds, TimestampMicroSeconds DurationMicroSeconds, TimestampNanoSeconds DurationNanoSeconds, => { OffsetDateTime; duration_to_offset_datetime => {String, Strict =>} } ); #[cfg(feature = "std")] use_duration_signed_de!( TimestampSeconds DurationSeconds, TimestampMilliSeconds DurationMilliSeconds, TimestampMicroSeconds DurationMicroSeconds, TimestampNanoSeconds DurationNanoSeconds, => { OffsetDateTime; duration_to_offset_datetime => {f64, Strict =>} } ); use_duration_signed_de!( TimestampSeconds DurationSeconds, TimestampMilliSeconds DurationMilliSeconds, TimestampMicroSeconds DurationMicroSeconds, TimestampNanoSeconds DurationNanoSeconds, => { PrimitiveDateTime; duration_to_primitive_datetime => {i64, Strict =>} {FORMAT, Flexible => FORMAT: Format} } ); #[cfg(feature = "alloc")] use_duration_signed_de!( TimestampSeconds DurationSeconds, TimestampMilliSeconds DurationMilliSeconds, TimestampMicroSeconds DurationMicroSeconds, TimestampNanoSeconds DurationNanoSeconds, => { PrimitiveDateTime; duration_to_primitive_datetime => {String, Strict =>} } ); #[cfg(feature = "std")] use_duration_signed_de!( TimestampSeconds DurationSeconds, TimestampMilliSeconds DurationMilliSeconds, TimestampMicroSeconds DurationMicroSeconds, TimestampNanoSeconds DurationNanoSeconds, => { PrimitiveDateTime; duration_to_primitive_datetime => {f64, Strict =>} } ); // Duration/Timestamp WITH FRACTIONS use_duration_signed_de!( DurationSecondsWithFrac DurationSecondsWithFrac, DurationMilliSecondsWithFrac DurationMilliSecondsWithFrac, DurationMicroSecondsWithFrac DurationMicroSecondsWithFrac, DurationNanoSecondsWithFrac DurationNanoSecondsWithFrac, => { Time03Duration; duration_from_duration_signed => {FORMAT, Flexible => FORMAT: Format} } ); #[cfg(feature = "alloc")] use_duration_signed_de!( DurationSecondsWithFrac DurationSecondsWithFrac, DurationMilliSecondsWithFrac DurationMilliSecondsWithFrac, DurationMicroSecondsWithFrac DurationMicroSecondsWithFrac, DurationNanoSecondsWithFrac DurationNanoSecondsWithFrac, => { Time03Duration; duration_from_duration_signed => {String, Strict =>} } ); #[cfg(feature = "std")] use_duration_signed_de!( DurationSecondsWithFrac DurationSecondsWithFrac, DurationMilliSecondsWithFrac DurationMilliSecondsWithFrac, DurationMicroSecondsWithFrac DurationMicroSecondsWithFrac, DurationNanoSecondsWithFrac DurationNanoSecondsWithFrac, => { Time03Duration; duration_from_duration_signed => {f64, Strict =>} } ); use_duration_signed_de!( TimestampSecondsWithFrac DurationSecondsWithFrac, TimestampMilliSecondsWithFrac DurationMilliSecondsWithFrac, TimestampMicroSecondsWithFrac DurationMicroSecondsWithFrac, TimestampNanoSecondsWithFrac DurationNanoSecondsWithFrac, => { OffsetDateTime; duration_to_offset_datetime => {FORMAT, Flexible => FORMAT: Format} } ); #[cfg(feature = "alloc")] use_duration_signed_de!( TimestampSecondsWithFrac DurationSecondsWithFrac, TimestampMilliSecondsWithFrac DurationMilliSecondsWithFrac, TimestampMicroSecondsWithFrac DurationMicroSecondsWithFrac, TimestampNanoSecondsWithFrac DurationNanoSecondsWithFrac, => { OffsetDateTime; duration_to_offset_datetime => {String, Strict =>} } ); #[cfg(feature = "std")] use_duration_signed_de!( TimestampSecondsWithFrac DurationSecondsWithFrac, TimestampMilliSecondsWithFrac DurationMilliSecondsWithFrac, TimestampMicroSecondsWithFrac DurationMicroSecondsWithFrac, TimestampNanoSecondsWithFrac DurationNanoSecondsWithFrac, => { OffsetDateTime; duration_to_offset_datetime => {f64, Strict =>} } ); use_duration_signed_de!( TimestampSecondsWithFrac DurationSecondsWithFrac, TimestampMilliSecondsWithFrac DurationMilliSecondsWithFrac, TimestampMicroSecondsWithFrac DurationMicroSecondsWithFrac, TimestampNanoSecondsWithFrac DurationNanoSecondsWithFrac, => { PrimitiveDateTime; duration_to_primitive_datetime => {FORMAT, Flexible => FORMAT: Format} } ); #[cfg(feature = "alloc")] use_duration_signed_de!( TimestampSecondsWithFrac DurationSecondsWithFrac, TimestampMilliSecondsWithFrac DurationMilliSecondsWithFrac, TimestampMicroSecondsWithFrac DurationMicroSecondsWithFrac, TimestampNanoSecondsWithFrac DurationNanoSecondsWithFrac, => { PrimitiveDateTime; duration_to_primitive_datetime => {String, Strict =>} } ); #[cfg(feature = "std")] use_duration_signed_de!( TimestampSecondsWithFrac DurationSecondsWithFrac, TimestampMilliSecondsWithFrac DurationMilliSecondsWithFrac, TimestampMicroSecondsWithFrac DurationMicroSecondsWithFrac, TimestampNanoSecondsWithFrac DurationNanoSecondsWithFrac, => { PrimitiveDateTime; duration_to_primitive_datetime => {f64, Strict =>} } ); #[cfg(feature = "std")] impl SerializeAs for Rfc2822 { fn serialize_as(datetime: &OffsetDateTime, serializer: S) -> Result where S: Serializer, { datetime .format(&Rfc2822) .map_err(S::Error::custom)? .serialize(serializer) } } #[cfg(feature = "std")] impl<'de> DeserializeAs<'de, OffsetDateTime> for Rfc2822 { fn deserialize_as(deserializer: D) -> Result where D: Deserializer<'de>, { struct Helper; impl Visitor<'_> for Helper { type Value = OffsetDateTime; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("a RFC2822-formatted `OffsetDateTime`") } fn visit_str(self, value: &str) -> Result { Self::Value::parse(value, &Rfc2822).map_err(E::custom) } } deserializer.deserialize_str(Helper) } } #[cfg(feature = "std")] impl SerializeAs for Rfc3339 { fn serialize_as(datetime: &OffsetDateTime, serializer: S) -> Result where S: Serializer, { datetime .format(&Rfc3339) .map_err(S::Error::custom)? .serialize(serializer) } } #[cfg(feature = "std")] impl<'de> DeserializeAs<'de, OffsetDateTime> for Rfc3339 { fn deserialize_as(deserializer: D) -> Result where D: Deserializer<'de>, { struct Helper; impl Visitor<'_> for Helper { type Value = OffsetDateTime; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("a RFC3339-formatted `OffsetDateTime`") } fn visit_str(self, value: &str) -> Result { Self::Value::parse(value, &Rfc3339).map_err(E::custom) } } deserializer.deserialize_str(Helper) } } #[cfg(feature = "std")] impl SerializeAs for Iso8601 { fn serialize_as(datetime: &OffsetDateTime, serializer: S) -> Result where S: Serializer, { datetime .format(&Iso8601::) .map_err(S::Error::custom)? .serialize(serializer) } } #[cfg(feature = "std")] impl<'de, const CONFIG: EncodedConfig> DeserializeAs<'de, OffsetDateTime> for Iso8601 { fn deserialize_as(deserializer: D) -> Result where D: Deserializer<'de>, { struct Helper; impl Visitor<'_> for Helper { type Value = OffsetDateTime; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("a ISO8601-formatted `OffsetDateTime`") } fn visit_str(self, value: &str) -> Result { Self::Value::parse(value, &Iso8601::).map_err(E::custom) } } deserializer.deserialize_str(Helper::) } } serde_with-3.12.0/src/utils/duration.rs000064400000000000000000000441151046102023000161730ustar 00000000000000//! Internal Helper types // Serialization of large numbers can result in overflows // The time calculations are prone to this, so lint here extra // https://github.com/jonasbb/serde_with/issues/771 #![warn(clippy::as_conversions)] use crate::{ formats::{Flexible, Format, Strict, Strictness}, prelude::*, }; #[derive(Copy, Clone, PartialEq, Eq)] #[cfg_attr(test, derive(Debug))] pub(crate) enum Sign { Positive, Negative, } impl Sign { #[allow(dead_code)] pub(crate) fn is_positive(&self) -> bool { *self == Sign::Positive } #[allow(dead_code)] pub(crate) fn is_negative(&self) -> bool { *self == Sign::Negative } pub(crate) fn apply_f64(&self, value: f64) -> f64 { match *self { Sign::Positive => value, Sign::Negative => -value, } } pub(crate) fn apply_i64(&self, value: i64) -> Option { match *self { Sign::Positive => Some(value), Sign::Negative => value.checked_neg(), } } } #[derive(Copy, Clone)] pub(crate) struct DurationSigned { pub(crate) sign: Sign, pub(crate) duration: Duration, } impl DurationSigned { pub(crate) fn new(sign: Sign, secs: u64, nanosecs: u32) -> Self { Self { sign, duration: Duration::new(secs, nanosecs), } } pub(crate) fn checked_mul(mut self, rhs: u32) -> Option { self.duration = self.duration.checked_mul(rhs)?; Some(self) } pub(crate) fn checked_div(mut self, rhs: u32) -> Option { self.duration = self.duration.checked_div(rhs)?; Some(self) } #[cfg(any(feature = "chrono_0_4", feature = "time_0_3"))] pub(crate) fn with_duration(sign: Sign, duration: Duration) -> Self { Self { sign, duration } } #[cfg(feature = "std")] pub(crate) fn to_system_time<'de, D>(self) -> Result where D: Deserializer<'de>, { match self.sign { Sign::Positive => SystemTime::UNIX_EPOCH.checked_add(self.duration), Sign::Negative => SystemTime::UNIX_EPOCH.checked_sub(self.duration), } .ok_or_else(|| DeError::custom("timestamp is outside the range for std::time::SystemTime")) } #[cfg(feature = "std")] pub(crate) fn to_std_duration<'de, D>(self) -> Result where D: Deserializer<'de>, { match self.sign { Sign::Positive => Ok(self.duration), Sign::Negative => Err(DeError::custom("std::time::Duration cannot be negative")), } } } impl From<&Duration> for DurationSigned { fn from(&duration: &Duration) -> Self { Self { sign: Sign::Positive, duration, } } } #[cfg(feature = "std")] impl From<&SystemTime> for DurationSigned { fn from(time: &SystemTime) -> Self { match time.duration_since(SystemTime::UNIX_EPOCH) { Ok(dur) => DurationSigned { sign: Sign::Positive, duration: dur, }, Err(err) => DurationSigned { sign: Sign::Negative, duration: err.duration(), }, } } } impl SerializeAs for DurationSeconds where STRICTNESS: Strictness, { fn serialize_as(source: &DurationSigned, serializer: S) -> Result where S: Serializer, { if source.sign.is_negative() { return Err(SerError::custom( "cannot serialize a negative Duration as u64", )); } let mut secs = source.duration.as_secs(); // Properly round the value if source.duration.subsec_millis() >= 500 { if source.sign.is_positive() { secs += 1; } else { secs -= 1; } } secs.serialize(serializer) } } impl SerializeAs for DurationSeconds where STRICTNESS: Strictness, { fn serialize_as(source: &DurationSigned, serializer: S) -> Result where S: Serializer, { let mut secs = source .sign .apply_i64(i64::try_from(source.duration.as_secs()).map_err(|_| { SerError::custom("The Duration of Timestamp is outside the supported range.") })?) .ok_or_else(|| { S::Error::custom("The Duration of Timestamp is outside the supported range.") })?; // Properly round the value // TODO check for overflows BUG771 if source.duration.subsec_millis() >= 500 { if source.sign.is_positive() { secs += 1; } else { secs -= 1; } } secs.serialize(serializer) } } impl SerializeAs for DurationSeconds where STRICTNESS: Strictness, { fn serialize_as(source: &DurationSigned, serializer: S) -> Result where S: Serializer, { // as conversions are necessary for floats #[allow(clippy::as_conversions)] let mut secs = source.sign.apply_f64(source.duration.as_secs() as f64); // Properly round the value if source.duration.subsec_millis() >= 500 { if source.sign.is_positive() { secs += 1.; } else { secs -= 1.; } } secs.serialize(serializer) } } #[cfg(feature = "alloc")] impl SerializeAs for DurationSeconds where STRICTNESS: Strictness, { fn serialize_as(source: &DurationSigned, serializer: S) -> Result where S: Serializer, { let mut secs = source .sign .apply_i64(i64::try_from(source.duration.as_secs()).map_err(|_| { SerError::custom("The Duration of Timestamp is outside the supported range.") })?) .ok_or_else(|| { S::Error::custom("The Duration of Timestamp is outside the supported range.") })?; // Properly round the value if source.duration.subsec_millis() >= 500 { if source.sign.is_positive() { secs += 1; } else { secs -= 1; } } secs.to_string().serialize(serializer) } } impl SerializeAs for DurationSecondsWithFrac where STRICTNESS: Strictness, { fn serialize_as(source: &DurationSigned, serializer: S) -> Result where S: Serializer, { source .sign .apply_f64(source.duration.as_secs_f64()) .serialize(serializer) } } #[cfg(feature = "alloc")] impl SerializeAs for DurationSecondsWithFrac where STRICTNESS: Strictness, { fn serialize_as(source: &DurationSigned, serializer: S) -> Result where S: Serializer, { source .sign .apply_f64(source.duration.as_secs_f64()) .to_string() .serialize(serializer) } } macro_rules! duration_impls { ($($inner:ident { $($factor:literal => $outer:ident,)+ })+) => { $($( impl SerializeAs for $outer where FORMAT: Format, STRICTNESS: Strictness, $inner: SerializeAs { fn serialize_as(source: &DurationSigned, serializer: S) -> Result where S: Serializer, { let value = source.checked_mul($factor).ok_or_else(|| S::Error::custom("Failed to serialize value as the value cannot be represented."))?; $inner::::serialize_as(&value, serializer) } } impl<'de, FORMAT, STRICTNESS> DeserializeAs<'de, DurationSigned> for $outer where FORMAT: Format, STRICTNESS: Strictness, $inner: DeserializeAs<'de, DurationSigned>, { fn deserialize_as(deserializer: D) -> Result where D: Deserializer<'de>, { let dur = $inner::::deserialize_as(deserializer)?; let dur = dur.checked_div($factor).ok_or_else(|| D::Error::custom("Failed to deserialize value as the value cannot be represented."))?; Ok(dur) } } )+)+ }; } duration_impls!( DurationSeconds { 1000u32 => DurationMilliSeconds, 1_000_000u32 => DurationMicroSeconds, 1_000_000_000u32 => DurationNanoSeconds, } DurationSecondsWithFrac { 1000u32 => DurationMilliSecondsWithFrac, 1_000_000u32 => DurationMicroSecondsWithFrac, 1_000_000_000u32 => DurationNanoSecondsWithFrac, } ); struct DurationVisitorFlexible; impl Visitor<'_> for DurationVisitorFlexible { type Value = DurationSigned; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("an integer, a float, or a string containing a number") } fn visit_i64(self, value: i64) -> Result where E: DeError, { let sign = if value >= 0 { Sign::Positive } else { Sign::Negative }; Ok(DurationSigned::new(sign, value.unsigned_abs(), 0)) } fn visit_u64(self, secs: u64) -> Result where E: DeError, { Ok(DurationSigned::new(Sign::Positive, secs, 0)) } fn visit_f64(self, secs: f64) -> Result where E: DeError, { utils::duration_signed_from_secs_f64(secs).map_err(DeError::custom) } fn visit_str(self, value: &str) -> Result where E: DeError, { match parse_float_into_time_parts(value) { Ok((sign, seconds, subseconds)) => Ok(DurationSigned::new(sign, seconds, subseconds)), Err(ParseFloatError::InvalidValue) => { Err(DeError::invalid_value(Unexpected::Str(value), &self)) } Err(ParseFloatError::Custom(msg)) => Err(DeError::custom(msg)), } } } impl<'de> DeserializeAs<'de, DurationSigned> for DurationSeconds { fn deserialize_as(deserializer: D) -> Result where D: Deserializer<'de>, { u64::deserialize(deserializer).map(|secs: u64| DurationSigned::new(Sign::Positive, secs, 0)) } } impl<'de> DeserializeAs<'de, DurationSigned> for DurationSeconds { fn deserialize_as(deserializer: D) -> Result where D: Deserializer<'de>, { i64::deserialize(deserializer).map(|secs: i64| { let sign = match secs.is_negative() { true => Sign::Negative, false => Sign::Positive, }; DurationSigned::new(sign, secs.abs_diff(0), 0) }) } } // round() only works on std #[cfg(feature = "std")] impl<'de> DeserializeAs<'de, DurationSigned> for DurationSeconds { fn deserialize_as(deserializer: D) -> Result where D: Deserializer<'de>, { let val = f64::deserialize(deserializer)?.round(); utils::duration_signed_from_secs_f64(val).map_err(DeError::custom) } } #[cfg(feature = "alloc")] impl<'de> DeserializeAs<'de, DurationSigned> for DurationSeconds { fn deserialize_as(deserializer: D) -> Result where D: Deserializer<'de>, { struct DurationDeserializationVisitor; impl Visitor<'_> for DurationDeserializationVisitor { type Value = DurationSigned; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("a string containing a number") } fn visit_str(self, value: &str) -> Result where E: DeError, { let secs: i64 = value.parse().map_err(DeError::custom)?; let sign = match secs.is_negative() { true => Sign::Negative, false => Sign::Positive, }; Ok(DurationSigned::new(sign, secs.abs_diff(0), 0)) } } deserializer.deserialize_str(DurationDeserializationVisitor) } } impl<'de, FORMAT> DeserializeAs<'de, DurationSigned> for DurationSeconds where FORMAT: Format, { fn deserialize_as(deserializer: D) -> Result where D: Deserializer<'de>, { deserializer.deserialize_any(DurationVisitorFlexible) } } impl<'de> DeserializeAs<'de, DurationSigned> for DurationSecondsWithFrac { fn deserialize_as(deserializer: D) -> Result where D: Deserializer<'de>, { let val = f64::deserialize(deserializer)?; utils::duration_signed_from_secs_f64(val).map_err(DeError::custom) } } #[cfg(feature = "alloc")] impl<'de> DeserializeAs<'de, DurationSigned> for DurationSecondsWithFrac { fn deserialize_as(deserializer: D) -> Result where D: Deserializer<'de>, { let value = String::deserialize(deserializer)?; match parse_float_into_time_parts(&value) { Ok((sign, seconds, subseconds)) => Ok(DurationSigned { sign, duration: Duration::new(seconds, subseconds), }), Err(ParseFloatError::InvalidValue) => Err(DeError::invalid_value( Unexpected::Str(&value), &"a string containing an integer or float", )), Err(ParseFloatError::Custom(msg)) => Err(DeError::custom(msg)), } } } impl<'de, FORMAT> DeserializeAs<'de, DurationSigned> for DurationSecondsWithFrac where FORMAT: Format, { fn deserialize_as(deserializer: D) -> Result where D: Deserializer<'de>, { deserializer.deserialize_any(DurationVisitorFlexible) } } #[cfg_attr(test, derive(Debug, PartialEq))] pub(crate) enum ParseFloatError { InvalidValue, #[cfg(not(feature = "alloc"))] Custom(&'static str), #[cfg(feature = "alloc")] Custom(String), } fn parse_float_into_time_parts(mut value: &str) -> Result<(Sign, u64, u32), ParseFloatError> { let sign = match value.chars().next() { // Advance by the size of the parsed char Some('+') => { value = &value[1..]; Sign::Positive } Some('-') => { value = &value[1..]; Sign::Negative } _ => Sign::Positive, }; let partslen = value.split('.').count(); let mut parts = value.split('.'); match partslen { 1 => { let seconds = parts.next().expect("Float contains exactly one part"); if let Ok(seconds) = seconds.parse() { Ok((sign, seconds, 0)) } else { Err(ParseFloatError::InvalidValue) } } 2 => { let seconds = parts.next().expect("Float contains exactly one part"); if let Ok(seconds) = seconds.parse() { let subseconds = parts.next().expect("Float contains exactly one part"); let subseclen = u32::try_from(subseconds.chars().count()).map_err(|_| { #[cfg(feature = "alloc")] return ParseFloatError::Custom(alloc::format!( "Duration and Timestamps with no more than 9 digits precision, but '{value}' has more" )); #[cfg(not(feature = "alloc"))] return ParseFloatError::Custom( "Duration and Timestamps with no more than 9 digits precision", ); })?; if subseclen > 9 { #[cfg(feature = "alloc")] return Err(ParseFloatError::Custom(alloc::format!( "Duration and Timestamps with no more than 9 digits precision, but '{value}' has more" ))); #[cfg(not(feature = "alloc"))] return Err(ParseFloatError::Custom( "Duration and Timestamps with no more than 9 digits precision", )); } if let Ok(mut subseconds) = subseconds.parse() { // convert subseconds to nanoseconds (10^-9), require 9 places for nanoseconds subseconds *= 10u32.pow(9 - subseclen); Ok((sign, seconds, subseconds)) } else { Err(ParseFloatError::InvalidValue) } } else { Err(ParseFloatError::InvalidValue) } } _ => Err(ParseFloatError::InvalidValue), } } #[test] fn test_parse_float_into_time_parts() { // Test normal behavior assert_eq!( Ok((Sign::Positive, 123, 456_000_000)), parse_float_into_time_parts("+123.456") ); assert_eq!( Ok((Sign::Negative, 123, 987_000)), parse_float_into_time_parts("-123.000987") ); assert_eq!( Ok((Sign::Positive, 18446744073709551615, 123_456_789)), parse_float_into_time_parts("18446744073709551615.123456789") ); // Test behavior around 0 assert_eq!( Ok((Sign::Positive, 0, 456_000_000)), parse_float_into_time_parts("+0.456") ); assert_eq!( Ok((Sign::Negative, 0, 987_000)), parse_float_into_time_parts("-0.000987") ); assert_eq!( Ok((Sign::Positive, 0, 123_456_789)), parse_float_into_time_parts("0.123456789") ); } serde_with-3.12.0/src/utils.rs000064400000000000000000000165311046102023000143470ustar 00000000000000pub(crate) mod duration; use crate::prelude::*; /// Re-Implementation of `serde::private::de::size_hint::cautious` #[cfg(feature = "alloc")] #[inline] pub(crate) fn size_hint_cautious(hint: Option) -> usize { const MAX_PREALLOC_BYTES: usize = 1024 * 1024; if core::mem::size_of::() == 0 { 0 } else { core::cmp::min( hint.unwrap_or(0), MAX_PREALLOC_BYTES / core::mem::size_of::(), ) } } /// Re-Implementation of `serde::private::de::size_hint::from_bounds` #[cfg(feature = "alloc")] #[inline] pub fn size_hint_from_bounds(iter: &I) -> Option where I: Iterator, { fn _size_hint_from_bounds(bounds: (usize, Option)) -> Option { match bounds { (lower, Some(upper)) if lower == upper => Some(upper), _ => None, } } _size_hint_from_bounds(iter.size_hint()) } pub(crate) const NANOS_PER_SEC: u128 = 1_000_000_000; pub(crate) const NANOS_PER_SEC_F64: f64 = 1_000_000_000.0; // pub(crate) const NANOS_PER_MILLI: u32 = 1_000_000; // pub(crate) const NANOS_PER_MICRO: u32 = 1_000; // pub(crate) const MILLIS_PER_SEC: u64 = 1_000; // pub(crate) const MICROS_PER_SEC: u64 = 1_000_000; pub(crate) const U64_MAX: u128 = u64::MAX as u128; pub(crate) struct MapIter<'de, A, K, V> { pub(crate) access: A, marker: PhantomData<(&'de (), K, V)>, } impl<'de, A, K, V> MapIter<'de, A, K, V> { pub(crate) fn new(access: A) -> Self where A: MapAccess<'de>, { Self { access, marker: PhantomData, } } } impl<'de, A, K, V> Iterator for MapIter<'de, A, K, V> where A: MapAccess<'de>, K: Deserialize<'de>, V: Deserialize<'de>, { type Item = Result<(K, V), A::Error>; fn next(&mut self) -> Option { self.access.next_entry().transpose() } fn size_hint(&self) -> (usize, Option) { match self.access.size_hint() { Some(size) => (size, Some(size)), None => (0, None), } } } pub(crate) struct SeqIter<'de, A, T> { access: A, marker: PhantomData<(&'de (), T)>, } impl<'de, A, T> SeqIter<'de, A, T> { pub(crate) fn new(access: A) -> Self where A: SeqAccess<'de>, { Self { access, marker: PhantomData, } } } impl<'de, A, T> Iterator for SeqIter<'de, A, T> where A: SeqAccess<'de>, T: Deserialize<'de>, { type Item = Result; fn next(&mut self) -> Option { self.access.next_element().transpose() } fn size_hint(&self) -> (usize, Option) { match self.access.size_hint() { Some(size) => (size, Some(size)), None => (0, None), } } } pub(crate) fn duration_signed_from_secs_f64(secs: f64) -> Result { const MAX_NANOS_F64: f64 = ((U64_MAX + 1) * NANOS_PER_SEC) as f64; // TODO why are the seconds converted to nanoseconds first? // Does it make sense to just truncate the value? let mut nanos = secs * NANOS_PER_SEC_F64; if !nanos.is_finite() { return Err("got non-finite value when converting float to duration"); } if nanos >= MAX_NANOS_F64 { return Err("overflow when converting float to duration"); } let mut sign = Sign::Positive; if nanos < 0.0 { nanos = -nanos; sign = Sign::Negative; } let nanos = nanos as u128; Ok(DurationSigned::new( sign, (nanos / NANOS_PER_SEC) as u64, (nanos % NANOS_PER_SEC) as u32, )) } /// Collect an array of a fixed size from an iterator. /// /// # Safety /// The code follow exactly the pattern of initializing an array element-by-element from the standard library. /// pub(crate) fn array_from_iterator( mut iter: I, expected: &dyn Expected, ) -> Result<[T; N], E> where I: Iterator>, E: DeError, { use core::mem::MaybeUninit; fn drop_array_elems(num: usize, mut arr: [MaybeUninit; 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; 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(DeError::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) }) } /// Writer that writes into a `&mut [u8]` while checking the length of the buffer struct BufWriter<'a> { bytes: &'a mut [u8], offset: usize, } impl<'a> BufWriter<'a> { fn new(bytes: &'a mut [u8]) -> Self { BufWriter { bytes, offset: 0 } } fn into_str(self) -> &'a str { let slice = &self.bytes[..self.offset]; core::str::from_utf8(slice) .unwrap_or("Failed to extract valid string from BufWriter. This should never happen.") } } impl core::fmt::Write for BufWriter<'_> { fn write_str(&mut self, s: &str) -> fmt::Result { if s.len() > self.bytes.len() - self.offset { Err(fmt::Error) } else { self.bytes[self.offset..self.offset + s.len()].copy_from_slice(s.as_bytes()); self.offset += s.len(); Ok(()) } } } // 58 chars is long enough for any i128 and u128 value pub(crate) fn get_unexpected_i128(value: i128, buf: &mut [u8; 58]) -> Unexpected<'_> { let mut writer = BufWriter::new(buf); fmt::Write::write_fmt(&mut writer, format_args!("integer `{value}` as i128")).unwrap(); Unexpected::Other(writer.into_str()) } // 58 chars is long enough for any i128 and u128 value pub(crate) fn get_unexpected_u128(value: u128, buf: &mut [u8; 58]) -> Unexpected<'_> { let mut writer = BufWriter::new(buf); fmt::Write::write_fmt(&mut writer, format_args!("integer `{value}` as u128")).unwrap(); Unexpected::Other(writer.into_str()) } serde_with-3.12.0/src/with_prefix.rs000064400000000000000000000413111046102023000155310ustar 00000000000000use crate::prelude::*; /// Serialize with an added prefix on every field name and deserialize by /// trimming away the prefix. /// /// You can set the visibility of the generated module by prefixing the module name with a module visibility. /// `with_prefix!(pub(crate) prefix_foo "foo_");` creates a module with `pub(crate)` visibility. /// The visibility is optional and by default `pub(self)`, i.e., private visibility is assumed. /// /// **Note:** Use of this macro is incompatible with applying the [`deny_unknown_fields`] attribute /// on the container. /// While deserializing, it will always warn about unknown fields, even though they are processed /// by the `with_prefix` wrapper. /// More details can be found in [this issue][issue-with_prefix-deny_unknown_fields]. /// /// # Example /// /// The [Challonge REST API] likes to use prefixes to group related fields. In /// simplified form, their JSON may resemble the following: /// /// [Challonge REST API]: https://api.challonge.com/v1/documents/matches/show /// /// ```json /// { /// "player1_name": "name1", /// "player1_votes": 1, /// "player2_name": "name2", /// "player2_votes": 2 /// } /// ``` /// /// In Rust, we would ideally like to model this data as a pair of `Player` /// structs, rather than repeating the fields of `Player` for each prefix. /// /// ```rust /// # #[allow(dead_code)] /// struct Match { /// player1: Player, /// player2: Player, /// } /// /// # #[allow(dead_code)] /// struct Player { /// name: String, /// votes: u64, /// } /// ``` /// /// This `with_prefix!` macro produces an adapter that adds a prefix onto field /// names during serialization and trims away the prefix during deserialization. /// An implementation of the Challonge API would use `with_prefix!` like this: /// /// ```rust /// use serde::{Deserialize, Serialize}; /// use serde_with::with_prefix; /// /// #[derive(Serialize, Deserialize)] /// struct Match { /// #[serde(flatten, with = "prefix_player1")] /// player1: Player, /// #[serde(flatten, with = "prefix_player2")] /// player2: Player, /// } /// /// #[derive(Serialize, Deserialize)] /// struct Player { /// name: String, /// votes: u64, /// } /// /// with_prefix!(prefix_player1 "player1_"); /// // You can also set the visibility of the generated prefix module, the default is private. /// with_prefix!(pub prefix_player2 "player2_"); /// # /// # const EXPECTED: &str = r#"{ /// # "player1_name": "name1", /// # "player1_votes": 1, /// # "player2_name": "name2", /// # "player2_votes": 2 /// # }"#; /// /// fn main() { /// let m = Match { /// player1: Player { /// name: "name1".to_owned(), /// votes: 1, /// }, /// player2: Player { /// name: "name2".to_owned(), /// votes: 2, /// }, /// }; /// /// let j = serde_json::to_string_pretty(&m).unwrap(); /// println!("{}", j); /// # /// # assert_eq!(j, EXPECTED); /// } /// ``` /// /// [`deny_unknown_fields`]: https://serde.rs/container-attrs.html#deny_unknown_fields /// [issue-with_prefix-deny_unknown_fields]: https://github.com/jonasbb/serde_with/issues/57 #[macro_export] macro_rules! with_prefix { ($module:ident $prefix:expr) => {$crate::with_prefix!(pub(self) $module $prefix);}; ($vis:vis $module:ident $prefix:expr) => { $vis mod $module { use $crate::serde::{Deserialize, Deserializer, Serialize, Serializer}; use $crate::with_prefix::WithPrefix; #[allow(dead_code)] pub fn serialize(object: &T, serializer: S) -> $crate::__private__::Result where T: Serialize, S: Serializer, { object.serialize(WithPrefix { delegate: serializer, prefix: $prefix, }) } #[allow(dead_code)] pub fn deserialize<'de, T, D>(deserializer: D) -> $crate::__private__::Result where T: Deserialize<'de>, D: Deserializer<'de>, { T::deserialize(WithPrefix { delegate: deserializer, prefix: $prefix, }) } } }; } pub struct WithPrefix<'a, T> { pub delegate: T, pub prefix: &'a str, } impl Serialize for WithPrefix<'_, T> where T: Serialize, { fn serialize(&self, serializer: S) -> Result where S: Serializer, { self.delegate.serialize(WithPrefix { delegate: serializer, prefix: self.prefix, }) } } impl<'a, S> Serializer for WithPrefix<'a, S> where S: Serializer, { type Ok = S::Ok; type Error = S::Error; type SerializeSeq = Impossible; type SerializeTuple = Impossible; type SerializeTupleStruct = Impossible; type SerializeTupleVariant = Impossible; type SerializeMap = WithPrefix<'a, S::SerializeMap>; type SerializeStruct = WithPrefix<'a, S::SerializeMap>; type SerializeStructVariant = Impossible; fn serialize_bool(self, _v: bool) -> Result { Err(SerError::custom("wrong type for with_prefix")) } fn serialize_i8(self, _v: i8) -> Result { Err(SerError::custom("wrong type for with_prefix")) } fn serialize_i16(self, _v: i16) -> Result { Err(SerError::custom("wrong type for with_prefix")) } fn serialize_i32(self, _v: i32) -> Result { Err(SerError::custom("wrong type for with_prefix")) } fn serialize_i64(self, _v: i64) -> Result { Err(SerError::custom("wrong type for with_prefix")) } fn serialize_u8(self, _v: u8) -> Result { Err(SerError::custom("wrong type for with_prefix")) } fn serialize_u16(self, _v: u16) -> Result { Err(SerError::custom("wrong type for with_prefix")) } fn serialize_u32(self, _v: u32) -> Result { Err(SerError::custom("wrong type for with_prefix")) } fn serialize_u64(self, _v: u64) -> Result { Err(SerError::custom("wrong type for with_prefix")) } fn serialize_f32(self, _v: f32) -> Result { Err(SerError::custom("wrong type for with_prefix")) } fn serialize_f64(self, _v: f64) -> Result { Err(SerError::custom("wrong type for with_prefix")) } fn serialize_char(self, _v: char) -> Result { Err(SerError::custom("wrong type for with_prefix")) } fn serialize_str(self, v: &str) -> Result { self.delegate .collect_str(&format_args!("{}{}", self.prefix, v)) } fn serialize_bytes(self, _v: &[u8]) -> Result { Err(SerError::custom("wrong type for with_prefix")) } fn serialize_none(self) -> Result { self.delegate.serialize_none() } fn serialize_some(self, value: &T) -> Result where T: ?Sized + Serialize, { self.delegate.serialize_some(&WithPrefix { delegate: value, prefix: self.prefix, }) } fn serialize_unit(self) -> Result { Err(SerError::custom("wrong type for with_prefix")) } fn serialize_unit_struct(self, _name: &'static str) -> Result { Err(SerError::custom("wrong type for with_prefix")) } fn serialize_unit_variant( self, _name: &'static str, _variant_index: u32, variant: &'static str, ) -> Result { self.serialize_str(variant) } fn serialize_newtype_struct( self, _name: &'static str, _value: &T, ) -> Result where T: ?Sized + Serialize, { Err(SerError::custom("wrong type for with_prefix")) } fn serialize_newtype_variant( self, _name: &'static str, _variant_index: u32, _variant: &'static str, _value: &T, ) -> Result where T: ?Sized + Serialize, { Err(SerError::custom("wrong type for with_prefix")) } fn serialize_seq(self, _len: Option) -> Result { Err(SerError::custom("wrong type for with_prefix")) } fn serialize_tuple(self, _len: usize) -> Result { Err(SerError::custom("wrong type for with_prefix")) } fn serialize_tuple_struct( self, _name: &'static str, _len: usize, ) -> Result { Err(SerError::custom("wrong type for with_prefix")) } fn serialize_tuple_variant( self, _name: &'static str, _variant_index: u32, _variant: &'static str, _len: usize, ) -> Result { Err(SerError::custom("wrong type for with_prefix")) } fn serialize_map(self, len: Option) -> Result { Ok(WithPrefix { delegate: self.delegate.serialize_map(len)?, prefix: self.prefix, }) } fn serialize_struct( self, _name: &'static str, len: usize, ) -> Result { self.serialize_map(Some(len)) } fn serialize_struct_variant( self, _name: &'static str, _variant_index: u32, _variant: &'static str, _len: usize, ) -> Result { Err(SerError::custom("wrong type for with_prefix")) } } impl SerializeMap for WithPrefix<'_, S> where S: SerializeMap, { type Ok = S::Ok; type Error = S::Error; fn serialize_key(&mut self, key: &T) -> Result<(), Self::Error> where T: ?Sized + Serialize, { self.delegate.serialize_key(&WithPrefix { delegate: key, prefix: self.prefix, }) } fn serialize_value(&mut self, value: &T) -> Result<(), Self::Error> where T: ?Sized + Serialize, { self.delegate.serialize_value(value) } fn serialize_entry(&mut self, key: &K, value: &V) -> Result<(), Self::Error> where K: ?Sized + Serialize, V: ?Sized + Serialize, { self.delegate.serialize_entry( &WithPrefix { delegate: key, prefix: self.prefix, }, value, ) } fn end(self) -> Result { self.delegate.end() } } impl SerializeStruct for WithPrefix<'_, S> where S: SerializeMap, { type Ok = S::Ok; type Error = S::Error; fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> where T: ?Sized + Serialize, { let mut prefixed_key = String::with_capacity(self.prefix.len() + key.len()); prefixed_key.push_str(self.prefix); prefixed_key.push_str(key); self.delegate.serialize_entry(&prefixed_key, value) } fn end(self) -> Result { self.delegate.end() } } impl<'de, T> DeserializeSeed<'de> for WithPrefix<'_, T> where T: DeserializeSeed<'de>, { type Value = T::Value; fn deserialize(self, deserializer: D) -> Result where D: Deserializer<'de>, { self.delegate.deserialize(WithPrefix { delegate: deserializer, prefix: self.prefix, }) } } impl<'de, D> Deserializer<'de> for WithPrefix<'_, D> where D: Deserializer<'de>, { type Error = D::Error; fn deserialize_any(self, visitor: V) -> Result where V: Visitor<'de>, { self.delegate.deserialize_map(WithPrefix { delegate: visitor, prefix: self.prefix, }) } fn deserialize_option(self, visitor: V) -> Result where V: Visitor<'de>, { self.delegate.deserialize_any(WithPrefixOption { first_key: None, delegate: visitor, prefix: self.prefix, }) } fn deserialize_identifier(self, visitor: V) -> Result where V: Visitor<'de>, { self.delegate.deserialize_identifier(WithPrefix { delegate: visitor, prefix: self.prefix, }) } forward_to_deserialize_any! { bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes byte_buf unit unit_struct newtype_struct seq tuple tuple_struct map struct enum ignored_any } } impl<'de, V> Visitor<'de> for WithPrefix<'_, V> where V: Visitor<'de>, { type Value = V::Value; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { self.delegate.expecting(formatter) } fn visit_map(self, map: A) -> Result where A: MapAccess<'de>, { self.delegate.visit_map(WithPrefix { delegate: map, prefix: self.prefix, }) } } impl<'de, A> MapAccess<'de> for WithPrefix<'_, A> where A: MapAccess<'de>, { type Error = A::Error; fn next_key_seed(&mut self, seed: K) -> Result, Self::Error> where K: DeserializeSeed<'de>, { while let Some(s) = self.delegate.next_key::()? { if let Some(without_prefix) = s.strip_prefix(self.prefix) { return seed .deserialize(without_prefix.into_deserializer()) .map(Some); } self.delegate.next_value::()?; } Ok(None) } fn next_value_seed(&mut self, seed: V) -> Result where V: DeserializeSeed<'de>, { self.delegate.next_value_seed(seed) } } pub struct WithPrefixOption<'a, T> { first_key: Option, delegate: T, prefix: &'a str, } impl<'de, V> Visitor<'de> for WithPrefixOption<'_, V> where V: Visitor<'de>, { type Value = V::Value; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { self.delegate.expecting(formatter) } fn visit_unit(self) -> Result where E: DeError, { self.delegate.visit_none() } fn visit_map(self, mut map: A) -> Result where A: MapAccess<'de>, { while let Some(s) = map.next_key::()? { if s.starts_with(self.prefix) { return self.delegate.visit_some(WithPrefixOption { first_key: Some(s), delegate: map, prefix: self.prefix, }); } map.next_value::()?; } self.delegate.visit_none() } } impl<'de, A> Deserializer<'de> for WithPrefixOption<'_, A> where A: MapAccess<'de>, { type Error = A::Error; fn deserialize_any(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_map(self) } forward_to_deserialize_any! { bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes byte_buf option unit unit_struct newtype_struct seq tuple tuple_struct map struct enum identifier ignored_any } } impl<'de, A> MapAccess<'de> for WithPrefixOption<'_, A> where A: MapAccess<'de>, { type Error = A::Error; fn next_key_seed(&mut self, seed: K) -> Result, Self::Error> where K: DeserializeSeed<'de>, { if let Some(s) = self.first_key.take() { let without_prefix = s[self.prefix.len()..].into_deserializer(); return seed.deserialize(without_prefix).map(Some); } while let Some(s) = self.delegate.next_key::()? { if let Some(without_prefix) = s.strip_prefix(self.prefix) { return seed .deserialize(without_prefix.into_deserializer()) .map(Some); } self.delegate.next_value::()?; } Ok(None) } fn next_value_seed(&mut self, seed: V) -> Result where V: DeserializeSeed<'de>, { self.delegate.next_value_seed(seed) } } serde_with-3.12.0/src/with_suffix.rs000064400000000000000000000423231046102023000155440ustar 00000000000000use crate::prelude::*; /// Serialize with an added suffix on every field name and deserialize by /// trimming away the suffix. /// /// You can set the visibility of the generated module by suffixing the module name with a module visibility. /// `with_suffix!(pub(crate) suffix_foo "_foo");` creates a module with `pub(crate)` visibility. /// The visibility is optional and by default `pub(self)`, i.e., private visibility is assumed. /// /// **Note:** Use of this macro is incompatible with applying the [`deny_unknown_fields`] attribute /// on the container. /// While deserializing, it will always warn about unknown fields, even though they are processed /// by the `with_suffix` wrapper. /// More details can be found in [this issue][issue-with_suffix-deny_unknown_fields]. /// /// # Example /// /// [Factorio Prototype tables] like to use suffixes to group related fields. In /// simplified form, their JSON representation may resemble the following: /// /// [Factorio Prototype tables]: https://lua-api.factorio.com/2.0.13/types/PipePictures.html /// /// ```json /// { /// "frames": 4, /// "spritesheet": "normal", /// "frames_frozen": 1, /// "spritesheet_frozen": "frozen", /// "frames_visualization": 2, /// "spritesheet_visualization": "vis", /// } /// ``` /// /// In Rust, we would ideally like to model this data as a couple of `SpriteData` /// structs, rather than repeating the fields of `SpriteData` for each suffix. /// /// ```rust /// # #[allow(dead_code)] /// struct Graphics { /// normal: SpriteData, /// frozen: SpriteData, /// visualization: SpriteData, /// } /// /// # #[allow(dead_code)] /// struct SpriteData { /// frames: u64, /// spritesheet: String, /// } /// ``` /// /// This `with_suffix!` macro produces an adapter that adds a suffix onto field /// names during serialization and trims away the suffix during deserialization. /// An implementation for the mentioned situation would use `with_suffix!` like this: /// /// ```rust /// use serde::{Deserialize, Serialize}; /// use serde_with::with_suffix; /// /// #[derive(Serialize, Deserialize)] /// struct Graphics { /// #[serde(flatten)] /// normal: SpriteData, /// #[serde(flatten, with = "suffix_frozen")] /// frozen: SpriteData, /// #[serde(flatten, with = "suffix_visualization")] /// visualization: SpriteData, /// } /// /// #[derive(Serialize, Deserialize)] /// struct SpriteData { /// frames: u64, /// spritesheet: String, /// } /// /// with_suffix!(suffix_frozen "_frozen"); /// // You can also set the visibility of the generated suffix module, the default is private. /// with_suffix!(pub suffix_visualization "_visualization"); /// # /// # const EXPECTED: &str = r#"{ /// # "frames": 4, /// # "spritesheet": "normal", /// # "frames_frozen": 1, /// # "spritesheet_frozen": "frozen", /// # "frames_visualization": 2, /// # "spritesheet_visualization": "vis" /// # }"#; /// /// fn main() { /// let g = Graphics { /// normal: SpriteData { /// frames: 4, /// spritesheet: "normal".to_owned(), /// }, /// frozen: SpriteData { /// frames: 1, /// spritesheet: "frozen".to_owned(), /// }, /// visualization: SpriteData { /// frames: 2, /// spritesheet: "vis".to_owned(), /// }, /// }; /// /// let j = serde_json::to_string_pretty(&g).unwrap(); /// println!("{}", j); /// # /// # assert_eq!(j, EXPECTED); /// } /// ``` /// /// [`deny_unknown_fields`]: https://serde.rs/container-attrs.html#deny_unknown_fields /// [issue-with_suffix-deny_unknown_fields]: https://github.com/jonasbb/serde_with/issues/57 #[macro_export] macro_rules! with_suffix { ($module:ident $suffix:expr) => {$crate::with_suffix!(pub(self) $module $suffix);}; ($vis:vis $module:ident $suffix:expr) => { $vis mod $module { use $crate::serde::{Deserialize, Deserializer, Serialize, Serializer}; use $crate::with_suffix::WithSuffix; #[allow(dead_code)] pub fn serialize(object: &T, serializer: S) -> $crate::__private__::Result where T: Serialize, S: Serializer, { object.serialize(WithSuffix { delegate: serializer, suffix: $suffix, }) } #[allow(dead_code)] pub fn deserialize<'de, T, D>(deserializer: D) -> $crate::__private__::Result where T: Deserialize<'de>, D: Deserializer<'de>, { T::deserialize(WithSuffix { delegate: deserializer, suffix: $suffix, }) } } }; } pub struct WithSuffix<'a, T> { pub delegate: T, pub suffix: &'a str, } impl Serialize for WithSuffix<'_, T> where T: Serialize, { fn serialize(&self, serializer: S) -> Result where S: Serializer, { self.delegate.serialize(WithSuffix { delegate: serializer, suffix: self.suffix, }) } } impl<'a, S> Serializer for WithSuffix<'a, S> where S: Serializer, { type Ok = S::Ok; type Error = S::Error; type SerializeSeq = Impossible; type SerializeTuple = Impossible; type SerializeTupleStruct = Impossible; type SerializeTupleVariant = Impossible; type SerializeMap = WithSuffix<'a, S::SerializeMap>; type SerializeStruct = WithSuffix<'a, S::SerializeMap>; type SerializeStructVariant = Impossible; fn serialize_bool(self, _v: bool) -> Result { Err(SerError::custom("wrong type for with_suffix")) } fn serialize_i8(self, _v: i8) -> Result { Err(SerError::custom("wrong type for with_suffix")) } fn serialize_i16(self, _v: i16) -> Result { Err(SerError::custom("wrong type for with_suffix")) } fn serialize_i32(self, _v: i32) -> Result { Err(SerError::custom("wrong type for with_suffix")) } fn serialize_i64(self, _v: i64) -> Result { Err(SerError::custom("wrong type for with_suffix")) } fn serialize_u8(self, _v: u8) -> Result { Err(SerError::custom("wrong type for with_suffix")) } fn serialize_u16(self, _v: u16) -> Result { Err(SerError::custom("wrong type for with_suffix")) } fn serialize_u32(self, _v: u32) -> Result { Err(SerError::custom("wrong type for with_suffix")) } fn serialize_u64(self, _v: u64) -> Result { Err(SerError::custom("wrong type for with_suffix")) } fn serialize_f32(self, _v: f32) -> Result { Err(SerError::custom("wrong type for with_suffix")) } fn serialize_f64(self, _v: f64) -> Result { Err(SerError::custom("wrong type for with_suffix")) } fn serialize_char(self, _v: char) -> Result { Err(SerError::custom("wrong type for with_suffix")) } fn serialize_str(self, v: &str) -> Result { self.delegate .collect_str(&format_args!("{}{}", v, self.suffix)) } fn serialize_bytes(self, _v: &[u8]) -> Result { Err(SerError::custom("wrong type for with_suffix")) } fn serialize_none(self) -> Result { self.delegate.serialize_none() } fn serialize_some(self, value: &T) -> Result where T: ?Sized + Serialize, { self.delegate.serialize_some(&WithSuffix { delegate: value, suffix: self.suffix, }) } fn serialize_unit(self) -> Result { Err(SerError::custom("wrong type for with_suffix")) } fn serialize_unit_struct(self, _name: &'static str) -> Result { Err(SerError::custom("wrong type for with_suffix")) } fn serialize_unit_variant( self, _name: &'static str, _variant_index: u32, variant: &'static str, ) -> Result { self.serialize_str(variant) } fn serialize_newtype_struct( self, _name: &'static str, _value: &T, ) -> Result where T: ?Sized + Serialize, { Err(SerError::custom("wrong type for with_suffix")) } fn serialize_newtype_variant( self, _name: &'static str, _variant_index: u32, _variant: &'static str, _value: &T, ) -> Result where T: ?Sized + Serialize, { Err(SerError::custom("wrong type for with_suffix")) } fn serialize_seq(self, _len: Option) -> Result { Err(SerError::custom("wrong type for with_suffix")) } fn serialize_tuple(self, _len: usize) -> Result { Err(SerError::custom("wrong type for with_suffix")) } fn serialize_tuple_struct( self, _name: &'static str, _len: usize, ) -> Result { Err(SerError::custom("wrong type for with_suffix")) } fn serialize_tuple_variant( self, _name: &'static str, _variant_index: u32, _variant: &'static str, _len: usize, ) -> Result { Err(SerError::custom("wrong type for with_suffix")) } fn serialize_map(self, len: Option) -> Result { Ok(WithSuffix { delegate: self.delegate.serialize_map(len)?, suffix: self.suffix, }) } fn serialize_struct( self, _name: &'static str, len: usize, ) -> Result { self.serialize_map(Some(len)) } fn serialize_struct_variant( self, _name: &'static str, _variant_index: u32, _variant: &'static str, _len: usize, ) -> Result { Err(SerError::custom("wrong type for with_suffix")) } } impl SerializeMap for WithSuffix<'_, S> where S: SerializeMap, { type Ok = S::Ok; type Error = S::Error; fn serialize_key(&mut self, key: &T) -> Result<(), Self::Error> where T: ?Sized + Serialize, { self.delegate.serialize_key(&WithSuffix { delegate: key, suffix: self.suffix, }) } fn serialize_value(&mut self, value: &T) -> Result<(), Self::Error> where T: ?Sized + Serialize, { self.delegate.serialize_value(value) } fn serialize_entry(&mut self, key: &K, value: &V) -> Result<(), Self::Error> where K: ?Sized + Serialize, V: ?Sized + Serialize, { self.delegate.serialize_entry( &WithSuffix { delegate: key, suffix: self.suffix, }, value, ) } fn end(self) -> Result { self.delegate.end() } } impl SerializeStruct for WithSuffix<'_, S> where S: SerializeMap, { type Ok = S::Ok; type Error = S::Error; fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> where T: ?Sized + Serialize, { let mut suffixed_key = String::with_capacity(key.len() + self.suffix.len()); suffixed_key.push_str(key); suffixed_key.push_str(self.suffix); self.delegate.serialize_entry(&suffixed_key, value) } fn end(self) -> Result { self.delegate.end() } } impl<'de, T> DeserializeSeed<'de> for WithSuffix<'_, T> where T: DeserializeSeed<'de>, { type Value = T::Value; fn deserialize(self, deserializer: D) -> Result where D: Deserializer<'de>, { self.delegate.deserialize(WithSuffix { delegate: deserializer, suffix: self.suffix, }) } } impl<'de, D> Deserializer<'de> for WithSuffix<'_, D> where D: Deserializer<'de>, { type Error = D::Error; fn deserialize_any(self, visitor: V) -> Result where V: Visitor<'de>, { self.delegate.deserialize_map(WithSuffix { delegate: visitor, suffix: self.suffix, }) } fn deserialize_option(self, visitor: V) -> Result where V: Visitor<'de>, { self.delegate.deserialize_any(WithSuffixOption { first_key: None, delegate: visitor, suffix: self.suffix, }) } fn deserialize_identifier(self, visitor: V) -> Result where V: Visitor<'de>, { self.delegate.deserialize_identifier(WithSuffix { delegate: visitor, suffix: self.suffix, }) } forward_to_deserialize_any! { bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes byte_buf unit unit_struct newtype_struct seq tuple tuple_struct map struct enum ignored_any } } impl<'de, V> Visitor<'de> for WithSuffix<'_, V> where V: Visitor<'de>, { type Value = V::Value; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { self.delegate.expecting(formatter) } fn visit_map(self, map: A) -> Result where A: MapAccess<'de>, { self.delegate.visit_map(WithSuffix { delegate: map, suffix: self.suffix, }) } } impl<'de, A> MapAccess<'de> for WithSuffix<'_, A> where A: MapAccess<'de>, { type Error = A::Error; fn next_key_seed(&mut self, seed: K) -> Result, Self::Error> where K: DeserializeSeed<'de>, { while let Some(s) = self.delegate.next_key::()? { if let Some(without_suffix) = s.strip_suffix(self.suffix) { return seed .deserialize(without_suffix.into_deserializer()) .map(Some); } self.delegate.next_value::()?; } Ok(None) } fn next_value_seed(&mut self, seed: V) -> Result where V: DeserializeSeed<'de>, { self.delegate.next_value_seed(seed) } } pub struct WithSuffixOption<'a, T> { first_key: Option, delegate: T, suffix: &'a str, } impl<'de, V> Visitor<'de> for WithSuffixOption<'_, V> where V: Visitor<'de>, { type Value = V::Value; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { self.delegate.expecting(formatter) } fn visit_unit(self) -> Result where E: DeError, { self.delegate.visit_none() } fn visit_map(self, mut map: A) -> Result where A: MapAccess<'de>, { while let Some(s) = map.next_key::()? { if s.ends_with(self.suffix) { return self.delegate.visit_some(WithSuffixOption { first_key: Some(s), delegate: map, suffix: self.suffix, }); } map.next_value::()?; } self.delegate.visit_none() } } impl<'de, A> Deserializer<'de> for WithSuffixOption<'_, A> where A: MapAccess<'de>, { type Error = A::Error; fn deserialize_any(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_map(self) } forward_to_deserialize_any! { bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes byte_buf option unit unit_struct newtype_struct seq tuple tuple_struct map struct enum identifier ignored_any } } impl<'de, A> MapAccess<'de> for WithSuffixOption<'_, A> where A: MapAccess<'de>, { type Error = A::Error; fn next_key_seed(&mut self, seed: K) -> Result, Self::Error> where K: DeserializeSeed<'de>, { if let Some(s) = self.first_key.take() { let without_suffix = s[0..s.len() - self.suffix.len()].into_deserializer(); return seed.deserialize(without_suffix).map(Some); } while let Some(s) = self.delegate.next_key::()? { if let Some(without_suffix) = s.strip_suffix(self.suffix) { return seed .deserialize(without_suffix.into_deserializer()) .map(Some); } self.delegate.next_value::()?; } Ok(None) } fn next_value_seed(&mut self, seed: V) -> Result where V: DeserializeSeed<'de>, { self.delegate.next_value_seed(seed) } } serde_with-3.12.0/tests/base64.rs000064400000000000000000000105511046102023000146420ustar 00000000000000//! Test Cases #![allow( // This allows the tests to be written more uniform and not have to special case the last clone(). clippy::redundant_clone, )] mod utils; use crate::utils::{check_deserialization, check_error_deserialization, is_equal}; use expect_test::expect; use serde::{Deserialize, Serialize}; use serde_with::{ base64::{Base64, Bcrypt, BinHex, Crypt, ImapMutf7, Standard, UrlSafe}, formats::{Padded, Unpadded}, serde_as, }; #[test] fn base64_vec() { let check_equal = vec![vec![0, 1, 2, 13], vec![14, 5, 6, 7]]; let check_deser = vec![vec![0xaa, 0xbc, 0xff], vec![0xe0, 0x7d], vec![0xe0, 0x7d]]; let check_deser_from = r#"["qrz/","4H0=","4H0"]"#; #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] pub struct BDefault(#[serde_as(as = "Vec")] Vec>); is_equal( BDefault(check_equal.clone()), expect![[r#" [ "AAECDQ==", "DgUGBw==" ]"#]], ); // Check mixed padding deserialization check_deserialization(BDefault(check_deser.clone()), check_deser_from); check_error_deserialization::( r#"["0"]"#, expect!["Invalid input length: 1 at line 1 column 4"], ); check_error_deserialization::( r#"["zz"]"#, expect!["Invalid last symbol 122, offset 1. at line 1 column 5"], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] pub struct BPadded(#[serde_as(as = "Vec>")] Vec>); is_equal( BPadded(check_equal.clone()), expect![[r#" [ "AAECDQ==", "DgUGBw==" ]"#]], ); check_deserialization(BPadded(check_deser.clone()), check_deser_from); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] pub struct BUnpadded(#[serde_as(as = "Vec>")] Vec>); is_equal( BUnpadded(check_equal.clone()), expect![[r#" [ "AAECDQ", "DgUGBw" ]"#]], ); check_deserialization(BUnpadded(check_deser.clone()), check_deser_from); } #[test] fn base64_different_charsets() { let bytes = [ 0x69_u8, 0xb7, 0x1d, 0x79, 0xf8, 0x21, 0x8a, 0x39, 0x25, 0x9a, 0x7a, 0x29, 0xaa, 0xbb, 0x2d, 0xba, 0xfc, 0x31, 0xcb, 0x30, 0x01, 0x08, 0x31, 0x05, 0x18, 0x72, 0x09, 0x28, 0xb3, 0x0d, 0x38, 0xf4, 0x11, 0x49, 0x35, 0x15, 0x59, 0x76, 0x19, 0xd3, 0x5d, 0xb7, 0xe3, 0x9e, 0xbb, 0xf3, 0xdf, 0xbf, 0x00, ]; #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] pub struct B64Standard(#[serde_as(as = "Base64")] Vec); is_equal( B64Standard(bytes.to_vec()), expect![[r#""abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/AA==""#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] pub struct B64UrlSafe(#[serde_as(as = "Base64")] Vec); is_equal( B64UrlSafe(bytes.to_vec()), expect![[r#""abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_AA==""#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] pub struct B64Crypt(#[serde_as(as = "Base64")] Vec); is_equal( B64Crypt(bytes.to_vec()), expect![[r#""OPQRSTUVWXYZabcdefghijklmn./0123456789ABCDEFGHIJKLMNopqrstuvwxyz..==""#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] pub struct B64Bcrypt(#[serde_as(as = "Base64")] Vec); is_equal( B64Bcrypt(bytes.to_vec()), expect![[r#""YZabcdefghijklmnopqrstuvwx./ABCDEFGHIJKLMNOPQRSTUVWXyz0123456789..==""#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] pub struct B64ImapMutf7(#[serde_as(as = "Base64")] Vec); is_equal( B64ImapMutf7(bytes.to_vec()), expect![[r#""abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+,AA==""#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] pub struct B64BinHex(#[serde_as(as = "Base64")] Vec); is_equal( B64BinHex(bytes.to_vec()), expect![[r##""DEFGHIJKLMNPQRSTUVXYZ[`abc!\"#$%&'()*+,-012345689@ABCdefhijklmpqr!!==""##]], ); } serde_with-3.12.0/tests/chrono_0_4.rs000064400000000000000000001011541046102023000155100ustar 00000000000000//! Test Cases extern crate alloc; mod utils; use crate::utils::{ check_deserialization, check_error_deserialization, check_serialization, is_equal, }; use alloc::collections::BTreeMap; use chrono_0_4::{DateTime, Duration, Local, NaiveDateTime, Utc}; use core::str::FromStr; use expect_test::expect; use serde::{Deserialize, Serialize}; use serde_with::{ formats::Flexible, serde_as, DurationMicroSeconds, DurationMicroSecondsWithFrac, DurationMilliSeconds, DurationMilliSecondsWithFrac, DurationNanoSeconds, DurationNanoSecondsWithFrac, DurationSeconds, DurationSecondsWithFrac, TimestampMicroSeconds, TimestampMicroSecondsWithFrac, TimestampMilliSeconds, TimestampMilliSecondsWithFrac, TimestampNanoSeconds, TimestampNanoSecondsWithFrac, TimestampSeconds, TimestampSecondsWithFrac, }; fn new_datetime(secs: i64, nsecs: u32) -> DateTime { DateTime::from_timestamp(secs, nsecs).unwrap() } fn unix_epoch_local() -> DateTime { DateTime::from_timestamp(0, 0) .unwrap() .with_timezone(&Local) } fn unix_epoch_naive() -> NaiveDateTime { DateTime::from_timestamp(0, 0).unwrap().naive_utc() } #[test] fn json_datetime_from_any_to_string_deserialization() { #[derive(Debug, PartialEq, Deserialize)] struct S( #[serde(with = "serde_with::chrono_0_4::datetime_utc_ts_seconds_from_any")] DateTime, ); // just integers check_deserialization( vec![ S(new_datetime(1_478_563_200, 0)), S(new_datetime(0, 0)), S(new_datetime(-86000, 0)), ], r#"[ 1478563200, 0, -86000 ]"#, ); // floats, shows precision errors in sub-second part check_deserialization( vec![ S(new_datetime(1_478_563_200, 122_999_906)), S(new_datetime(0, 0)), S(new_datetime(-86000, 998_999_999)), ], r#"[ 1478563200.123, 0.000, -86000.999 ]"#, ); // string representation of floats check_deserialization( vec![ S(new_datetime(1_478_563_200, 123_000_000)), S(new_datetime(0, 0)), S(new_datetime(-86000, 999_000_000)), ], r#"[ "1478563200.123", "0.000", "-86000.999" ]"#, ); } #[test] fn test_chrono_naive_date_time() { #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] pub struct S(#[serde_as(as = "DateTime")] NaiveDateTime); is_equal( S(NaiveDateTime::from_str("1994-11-05T08:15:30").unwrap()), expect![[r#""1994-11-05T08:15:30Z""#]], ); } #[test] fn test_chrono_option_naive_date_time() { #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] pub struct S(#[serde_as(as = "Option>")] Option); is_equal( S(NaiveDateTime::from_str("1994-11-05T08:15:30").ok()), expect![[r#""1994-11-05T08:15:30Z""#]], ); } #[test] fn test_chrono_vec_option_naive_date_time() { #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] pub struct S(#[serde_as(as = "Vec>>")] Vec>); is_equal( S(vec![ NaiveDateTime::from_str("1994-11-05T08:15:30").ok(), NaiveDateTime::from_str("1994-11-05T08:15:31").ok(), ]), expect![[r#" [ "1994-11-05T08:15:30Z", "1994-11-05T08:15:31Z" ]"#]], ); } #[test] fn test_chrono_btreemap_naive_date_time() { #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] pub struct S(#[serde_as(as = "BTreeMap<_, DateTime>")] BTreeMap); is_equal( S(BTreeMap::from_iter(vec![ (1, NaiveDateTime::from_str("1994-11-05T08:15:30").unwrap()), (2, NaiveDateTime::from_str("1994-11-05T08:15:31").unwrap()), ])), expect![[r#" { "1": "1994-11-05T08:15:30Z", "2": "1994-11-05T08:15:31Z" }"#]], ); } #[test] fn test_chrono_duration_seconds() { let zero = Duration::zero(); let one_second = Duration::seconds(1); let half_second = Duration::nanoseconds(500_000_000); let minus_one_second = zero - one_second; let minus_half_second = zero - half_second; #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct StructIntStrict(#[serde_as(as = "DurationSeconds")] Duration); is_equal(StructIntStrict(zero), expect![[r#"0"#]]); is_equal(StructIntStrict(one_second), expect![[r#"1"#]]); is_equal(StructIntStrict(minus_one_second), expect![[r#"-1"#]]); check_serialization(StructIntStrict(half_second), expect![[r#"1"#]]); check_serialization(StructIntStrict(minus_half_second), expect![[r#"-1"#]]); check_error_deserialization::( r#""1""#, expect![[r#"invalid type: string "1", expected i64 at line 1 column 3"#]], ); check_error_deserialization::( r#"9223372036854775808"#, expect![[ r#"invalid value: integer `9223372036854775808`, expected i64 at line 1 column 19"# ]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct StructIntFlexible(#[serde_as(as = "DurationSeconds")] Duration); is_equal(StructIntFlexible(zero), expect![[r#"0"#]]); is_equal(StructIntFlexible(one_second), expect![[r#"1"#]]); check_serialization(StructIntFlexible(half_second), expect![[r#"1"#]]); check_serialization(StructIntFlexible(minus_half_second), expect![[r#"-1"#]]); check_deserialization(StructIntFlexible(half_second), r#""0.5""#); check_deserialization(StructIntFlexible(minus_half_second), r#""-0.5""#); check_deserialization(StructIntFlexible(one_second), r#""1""#); check_deserialization(StructIntFlexible(minus_one_second), r#""-1""#); check_deserialization(StructIntFlexible(zero), r#""0""#); check_error_deserialization::( r#""a""#, expect![[ r#"invalid value: string "a", expected an integer, a float, or a string containing a number at line 1 column 3"# ]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct Structf64Strict(#[serde_as(as = "DurationSeconds")] Duration); is_equal(Structf64Strict(zero), expect![[r#"0.0"#]]); is_equal(Structf64Strict(one_second), expect![[r#"1.0"#]]); is_equal(Structf64Strict(minus_one_second), expect![[r#"-1.0"#]]); check_serialization(Structf64Strict(half_second), expect![[r#"1.0"#]]); check_serialization(Structf64Strict(minus_half_second), expect![[r#"-1.0"#]]); check_deserialization(Structf64Strict(one_second), r#"0.5"#); check_deserialization(Structf64Strict(minus_one_second), r#"-0.5"#); check_error_deserialization::( r#""1""#, expect![[r#"invalid type: string "1", expected f64 at line 1 column 3"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct Structf64Flexible(#[serde_as(as = "DurationSeconds")] Duration); is_equal(Structf64Flexible(zero), expect![[r#"0.0"#]]); is_equal(Structf64Flexible(one_second), expect![[r#"1.0"#]]); is_equal(Structf64Flexible(minus_one_second), expect![[r#"-1.0"#]]); check_serialization(Structf64Flexible(half_second), expect![[r#"1.0"#]]); check_serialization(Structf64Flexible(minus_half_second), expect![[r#"-1.0"#]]); check_deserialization(Structf64Flexible(half_second), r#""0.5""#); check_deserialization(Structf64Flexible(minus_half_second), r#""-0.5""#); check_deserialization(Structf64Flexible(one_second), r#""1""#); check_deserialization(Structf64Flexible(minus_one_second), r#""-1""#); check_deserialization(Structf64Flexible(zero), r#""0""#); check_error_deserialization::( r#""a""#, expect![[ r#"invalid value: string "a", expected an integer, a float, or a string containing a number at line 1 column 3"# ]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct StructStringStrict(#[serde_as(as = "DurationSeconds")] Duration); is_equal(StructStringStrict(zero), expect![[r#""0""#]]); is_equal(StructStringStrict(one_second), expect![[r#""1""#]]); is_equal(StructStringStrict(minus_one_second), expect![[r#""-1""#]]); check_serialization(StructStringStrict(half_second), expect![[r#""1""#]]); check_serialization(StructStringStrict(minus_half_second), expect![[r#""-1""#]]); check_error_deserialization::( r#"1"#, expect![[ r#"invalid type: integer `1`, expected a string containing a number at line 1 column 1"# ]], ); check_error_deserialization::( r#"-1"#, expect![[ r#"invalid type: integer `-1`, expected a string containing a number at line 1 column 2"# ]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct StructStringFlexible(#[serde_as(as = "DurationSeconds")] Duration); is_equal(StructStringFlexible(zero), expect![[r#""0""#]]); is_equal(StructStringFlexible(one_second), expect![[r#""1""#]]); is_equal(StructStringFlexible(minus_one_second), expect![[r#""-1""#]]); check_serialization(StructStringFlexible(half_second), expect![[r#""1""#]]); check_deserialization(StructStringFlexible(half_second), r#""0.5""#); check_deserialization(StructStringFlexible(one_second), r#""1""#); check_deserialization(StructStringFlexible(zero), r#""0""#); check_error_deserialization::( r#""a""#, expect![[ r#"invalid value: string "a", expected an integer, a float, or a string containing a number at line 1 column 3"# ]], ); } #[test] fn test_chrono_duration_seconds_with_frac() { let zero = Duration::zero(); let one_second = Duration::seconds(1); let half_second = Duration::nanoseconds(500_000_000); let minus_one_second = zero - one_second; let minus_half_second = zero - half_second; #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct Structf64Strict(#[serde_as(as = "DurationSecondsWithFrac")] Duration); is_equal(Structf64Strict(zero), expect![[r#"0.0"#]]); is_equal(Structf64Strict(one_second), expect![[r#"1.0"#]]); is_equal(Structf64Strict(minus_one_second), expect![[r#"-1.0"#]]); is_equal(Structf64Strict(half_second), expect![[r#"0.5"#]]); is_equal(Structf64Strict(minus_half_second), expect![[r#"-0.5"#]]); check_error_deserialization::( r#""1""#, expect![[r#"invalid type: string "1", expected f64 at line 1 column 3"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct Structf64Flexible(#[serde_as(as = "DurationSecondsWithFrac")] Duration); is_equal(Structf64Flexible(zero), expect![[r#"0.0"#]]); is_equal(Structf64Flexible(one_second), expect![[r#"1.0"#]]); is_equal(Structf64Flexible(minus_one_second), expect![[r#"-1.0"#]]); is_equal(Structf64Flexible(minus_half_second), expect![[r#"-0.5"#]]); check_deserialization(Structf64Flexible(one_second), r#""1""#); check_deserialization(Structf64Flexible(minus_one_second), r#""-1""#); check_deserialization(Structf64Flexible(half_second), r#""0.5""#); check_deserialization(Structf64Flexible(zero), r#""0""#); check_error_deserialization::( r#""a""#, expect![[ r#"invalid value: string "a", expected an integer, a float, or a string containing a number at line 1 column 3"# ]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct StructStringStrict(#[serde_as(as = "DurationSecondsWithFrac")] Duration); is_equal(StructStringStrict(zero), expect![[r#""0""#]]); is_equal(StructStringStrict(one_second), expect![[r#""1""#]]); is_equal(StructStringStrict(minus_one_second), expect![[r#""-1""#]]); is_equal(StructStringStrict(half_second), expect![[r#""0.5""#]]); is_equal( StructStringStrict(minus_half_second), expect![[r#""-0.5""#]], ); is_equal( StructStringStrict(minus_half_second), expect![[r#""-0.5""#]], ); check_error_deserialization::( r#"1"#, expect![[r#"invalid type: integer `1`, expected a string at line 1 column 1"#]], ); check_error_deserialization::( r#"-1"#, expect![[r#"invalid type: integer `-1`, expected a string at line 1 column 2"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct StructStringFlexible( #[serde_as(as = "DurationSecondsWithFrac")] Duration, ); is_equal(StructStringFlexible(zero), expect![[r#""0""#]]); is_equal(StructStringFlexible(one_second), expect![[r#""1""#]]); is_equal(StructStringFlexible(minus_one_second), expect![[r#""-1""#]]); is_equal(StructStringFlexible(half_second), expect![[r#""0.5""#]]); is_equal( StructStringFlexible(minus_half_second), expect![[r#""-0.5""#]], ); check_deserialization(StructStringFlexible(one_second), r#""1""#); check_deserialization(StructStringFlexible(zero), r#""0""#); check_error_deserialization::( r#""a""#, expect![[ r#"invalid value: string "a", expected an integer, a float, or a string containing a number at line 1 column 3"# ]], ); } #[test] fn test_chrono_timestamp_seconds() { let zero = new_datetime(0, 0); let one_second = zero + Duration::seconds(1); let half_second = zero + Duration::nanoseconds(500_000_000); let minus_one_second = zero - Duration::seconds(1); let minus_half_second = zero - Duration::nanoseconds(500_000_000); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct StructIntStrict(#[serde_as(as = "TimestampSeconds")] DateTime); is_equal(StructIntStrict(zero), expect![[r#"0"#]]); is_equal(StructIntStrict(one_second), expect![[r#"1"#]]); is_equal(StructIntStrict(minus_one_second), expect![[r#"-1"#]]); check_serialization(StructIntStrict(half_second), expect![[r#"1"#]]); check_serialization(StructIntStrict(minus_half_second), expect![[r#"-1"#]]); check_error_deserialization::( r#""1""#, expect![[r#"invalid type: string "1", expected i64 at line 1 column 3"#]], ); check_error_deserialization::( r#"0.123"#, expect![[r#"invalid type: floating point `0.123`, expected i64 at line 1 column 5"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct StructIntFlexible(#[serde_as(as = "TimestampSeconds")] DateTime); is_equal(StructIntFlexible(zero), expect![[r#"0"#]]); is_equal(StructIntFlexible(one_second), expect![[r#"1"#]]); is_equal(StructIntFlexible(minus_one_second), expect![[r#"-1"#]]); check_serialization(StructIntFlexible(half_second), expect![[r#"1"#]]); check_serialization(StructIntFlexible(minus_half_second), expect![[r#"-1"#]]); check_deserialization(StructIntFlexible(one_second), r#""1""#); check_deserialization(StructIntFlexible(one_second), r#"1.0"#); check_deserialization(StructIntFlexible(minus_half_second), r#""-0.5""#); check_deserialization(StructIntFlexible(half_second), r#"0.5"#); check_error_deserialization::( r#""a""#, expect![[ r#"invalid value: string "a", expected an integer, a float, or a string containing a number at line 1 column 3"# ]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct Structf64Strict(#[serde_as(as = "TimestampSeconds")] DateTime); is_equal(Structf64Strict(zero), expect![[r#"0.0"#]]); is_equal(Structf64Strict(one_second), expect![[r#"1.0"#]]); is_equal(Structf64Strict(minus_one_second), expect![[r#"-1.0"#]]); check_serialization(Structf64Strict(half_second), expect![[r#"1.0"#]]); check_serialization(Structf64Strict(minus_half_second), expect![[r#"-1.0"#]]); check_deserialization(Structf64Strict(one_second), r#"0.5"#); check_error_deserialization::( r#""1""#, expect![[r#"invalid type: string "1", expected f64 at line 1 column 3"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct Structf64Flexible(#[serde_as(as = "TimestampSeconds")] DateTime); is_equal(Structf64Flexible(zero), expect![[r#"0.0"#]]); is_equal(Structf64Flexible(one_second), expect![[r#"1.0"#]]); is_equal(Structf64Flexible(minus_one_second), expect![[r#"-1.0"#]]); check_serialization(Structf64Flexible(half_second), expect![[r#"1.0"#]]); check_serialization(Structf64Flexible(minus_half_second), expect![[r#"-1.0"#]]); check_deserialization(Structf64Flexible(one_second), r#""1""#); check_deserialization(Structf64Flexible(one_second), r#"1.0"#); check_deserialization(Structf64Flexible(minus_half_second), r#""-0.5""#); check_deserialization(Structf64Flexible(half_second), r#"0.5"#); check_error_deserialization::( r#""a""#, expect![[ r#"invalid value: string "a", expected an integer, a float, or a string containing a number at line 1 column 3"# ]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct StructStringStrict(#[serde_as(as = "TimestampSeconds")] DateTime); is_equal(StructStringStrict(zero), expect![[r#""0""#]]); is_equal(StructStringStrict(one_second), expect![[r#""1""#]]); is_equal(StructStringStrict(minus_one_second), expect![[r#""-1""#]]); check_serialization(StructStringStrict(half_second), expect![[r#""1""#]]); check_serialization(StructStringStrict(minus_half_second), expect![[r#""-1""#]]); check_deserialization(StructStringStrict(one_second), r#""1""#); check_error_deserialization::( r#""0.5""#, expect![[r#"invalid digit found in string at line 1 column 5"#]], ); check_error_deserialization::( r#""-0.5""#, expect![[r#"invalid digit found in string at line 1 column 6"#]], ); check_error_deserialization::( r#"1"#, expect![[ r#"invalid type: integer `1`, expected a string containing a number at line 1 column 1"# ]], ); check_error_deserialization::( r#"0.1"#, expect![[ r#"invalid type: floating point `0.1`, expected a string containing a number at line 1 column 3"# ]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct StructStringFlexible( #[serde_as(as = "TimestampSeconds")] DateTime, ); is_equal(StructStringFlexible(zero), expect![[r#""0""#]]); is_equal(StructStringFlexible(one_second), expect![[r#""1""#]]); is_equal(StructStringFlexible(minus_one_second), expect![[r#""-1""#]]); check_serialization(StructStringFlexible(half_second), expect![[r#""1""#]]); check_serialization( StructStringFlexible(minus_half_second), expect![[r#""-1""#]], ); check_deserialization(StructStringFlexible(one_second), r#"1"#); check_deserialization(StructStringFlexible(one_second), r#"1.0"#); check_deserialization(StructStringFlexible(minus_half_second), r#""-0.5""#); check_deserialization(StructStringFlexible(half_second), r#"0.5"#); check_error_deserialization::( r#""a""#, expect![[ r#"invalid value: string "a", expected an integer, a float, or a string containing a number at line 1 column 3"# ]], ); } #[test] fn test_chrono_timestamp_seconds_with_frac() { let zero = new_datetime(0, 0); let one_second = zero + Duration::seconds(1); let half_second = zero + Duration::nanoseconds(500_000_000); let minus_one_second = zero - Duration::seconds(1); let minus_half_second = zero - Duration::nanoseconds(500_000_000); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct Structf64Strict(#[serde_as(as = "TimestampSecondsWithFrac")] DateTime); is_equal(Structf64Strict(zero), expect![[r#"0.0"#]]); is_equal(Structf64Strict(one_second), expect![[r#"1.0"#]]); is_equal(Structf64Strict(minus_one_second), expect![[r#"-1.0"#]]); is_equal(Structf64Strict(half_second), expect![[r#"0.5"#]]); is_equal(Structf64Strict(minus_half_second), expect![[r#"-0.5"#]]); check_error_deserialization::( r#""1""#, expect![[r#"invalid type: string "1", expected f64 at line 1 column 3"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct Structf64Flexible( #[serde_as(as = "TimestampSecondsWithFrac")] DateTime, ); is_equal(Structf64Flexible(zero), expect![[r#"0.0"#]]); is_equal(Structf64Flexible(one_second), expect![[r#"1.0"#]]); is_equal(Structf64Flexible(minus_one_second), expect![[r#"-1.0"#]]); is_equal(Structf64Flexible(half_second), expect![[r#"0.5"#]]); is_equal(Structf64Flexible(minus_half_second), expect![[r#"-0.5"#]]); check_deserialization(Structf64Flexible(one_second), r#""1""#); check_deserialization(Structf64Flexible(minus_half_second), r#""-0.5""#); check_error_deserialization::( r#""a""#, expect![[ r#"invalid value: string "a", expected an integer, a float, or a string containing a number at line 1 column 3"# ]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct StructStringStrict(#[serde_as(as = "TimestampSecondsWithFrac")] DateTime); is_equal(StructStringStrict(zero), expect![[r#""0""#]]); is_equal(StructStringStrict(one_second), expect![[r#""1""#]]); is_equal(StructStringStrict(minus_one_second), expect![[r#""-1""#]]); is_equal(StructStringStrict(half_second), expect![[r#""0.5""#]]); is_equal( StructStringStrict(minus_half_second), expect![[r#""-0.5""#]], ); check_error_deserialization::( r#"1"#, expect![[r#"invalid type: integer `1`, expected a string at line 1 column 1"#]], ); check_error_deserialization::( r#"0.1"#, expect![[r#"invalid type: floating point `0.1`, expected a string at line 1 column 3"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct StructStringFlexible( #[serde_as(as = "TimestampSecondsWithFrac")] DateTime, ); is_equal(StructStringFlexible(zero), expect![[r#""0""#]]); is_equal(StructStringFlexible(one_second), expect![[r#""1""#]]); is_equal(StructStringFlexible(minus_one_second), expect![[r#""-1""#]]); is_equal(StructStringFlexible(half_second), expect![[r#""0.5""#]]); is_equal( StructStringFlexible(minus_half_second), expect![[r#""-0.5""#]], ); check_deserialization(StructStringFlexible(one_second), r#"1"#); check_deserialization(StructStringFlexible(one_second), r#"1.0"#); check_deserialization(StructStringFlexible(half_second), r#"0.5"#); check_error_deserialization::( r#""a""#, expect![[ r#"invalid value: string "a", expected an integer, a float, or a string containing a number at line 1 column 3"# ]], ); } macro_rules! smoketest { ($($valuety:ty, $adapter:literal, $value:expr, $expect:tt;)*) => { $({ #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S(#[serde_as(as = $adapter)] $valuety); #[allow(unused_braces)] is_equal(S($value), $expect); })* }; } #[test] fn test_duration_smoketest() { let zero = Duration::seconds(0); let one_second = Duration::seconds(1); smoketest! { Duration, "DurationSeconds", one_second, {expect![[r#"1"#]]}; Duration, "DurationSeconds", one_second, {expect![[r#"1.0"#]]}; Duration, "DurationMilliSeconds", one_second, {expect![[r#"1000"#]]}; Duration, "DurationMilliSeconds", one_second, {expect![[r#"1000.0"#]]}; Duration, "DurationMicroSeconds", one_second, {expect![[r#"1000000"#]]}; Duration, "DurationMicroSeconds", one_second, {expect![[r#"1000000.0"#]]}; Duration, "DurationNanoSeconds", one_second, {expect![[r#"1000000000"#]]}; Duration, "DurationNanoSeconds", one_second, {expect![[r#"1000000000.0"#]]}; }; smoketest! { Duration, "DurationSecondsWithFrac", one_second, {expect![[r#"1.0"#]]}; Duration, "DurationSecondsWithFrac", one_second, {expect![[r#""1""#]]}; Duration, "DurationMilliSecondsWithFrac", one_second, {expect![[r#"1000.0"#]]}; Duration, "DurationMilliSecondsWithFrac", one_second, {expect![[r#""1000""#]]}; Duration, "DurationMicroSecondsWithFrac", one_second, {expect![[r#"1000000.0"#]]}; Duration, "DurationMicroSecondsWithFrac", one_second, {expect![[r#""1000000""#]]}; Duration, "DurationNanoSecondsWithFrac", one_second, {expect![[r#"1000000000.0"#]]}; Duration, "DurationNanoSecondsWithFrac", one_second, {expect![[r#""1000000000""#]]}; }; smoketest! { Duration, "DurationSecondsWithFrac", zero, {expect![[r#"0.0"#]]}; Duration, "DurationSecondsWithFrac", zero + Duration::nanoseconds(500_000_000), {expect![[r#"0.5"#]]}; Duration, "DurationSecondsWithFrac", zero + Duration::seconds(1), {expect![[r#"1.0"#]]}; Duration, "DurationSecondsWithFrac", zero - Duration::nanoseconds(500_000_000), {expect![[r#"-0.5"#]]}; Duration, "DurationSecondsWithFrac", zero - Duration::seconds(1), {expect![[r#"-1.0"#]]}; }; } #[test] fn test_datetime_utc_smoketest() { let zero = new_datetime(0, 0); let one_second = zero + Duration::seconds(1); smoketest! { DateTime, "TimestampSeconds", one_second, {expect![[r#"1"#]]}; DateTime, "TimestampSeconds", one_second, {expect![[r#"1.0"#]]}; DateTime, "TimestampMilliSeconds", one_second, {expect![[r#"1000"#]]}; DateTime, "TimestampMilliSeconds", one_second, {expect![[r#"1000.0"#]]}; DateTime, "TimestampMicroSeconds", one_second, {expect![[r#"1000000"#]]}; DateTime, "TimestampMicroSeconds", one_second, {expect![[r#"1000000.0"#]]}; DateTime, "TimestampNanoSeconds", one_second, {expect![[r#"1000000000"#]]}; DateTime, "TimestampNanoSeconds", one_second, {expect![[r#"1000000000.0"#]]}; }; smoketest! { DateTime, "TimestampSecondsWithFrac", one_second, {expect![[r#"1.0"#]]}; DateTime, "TimestampSecondsWithFrac", one_second, {expect![[r#""1""#]]}; DateTime, "TimestampMilliSecondsWithFrac", one_second, {expect![[r#"1000.0"#]]}; DateTime, "TimestampMilliSecondsWithFrac", one_second, {expect![[r#""1000""#]]}; DateTime, "TimestampMicroSecondsWithFrac", one_second, {expect![[r#"1000000.0"#]]}; DateTime, "TimestampMicroSecondsWithFrac", one_second, {expect![[r#""1000000""#]]}; DateTime, "TimestampNanoSecondsWithFrac", one_second, {expect![[r#"1000000000.0"#]]}; DateTime, "TimestampNanoSecondsWithFrac", one_second, {expect![[r#""1000000000""#]]}; }; smoketest! { DateTime, "TimestampSecondsWithFrac", zero, {expect![[r#"0.0"#]]}; DateTime, "TimestampSecondsWithFrac", zero + Duration::nanoseconds(500_000_000), {expect![[r#"0.5"#]]}; DateTime, "TimestampSecondsWithFrac", zero + Duration::seconds(1), {expect![[r#"1.0"#]]}; DateTime, "TimestampSecondsWithFrac", zero - Duration::nanoseconds(500_000_000), {expect![[r#"-0.5"#]]}; DateTime, "TimestampSecondsWithFrac", zero - Duration::seconds(1), {expect![[r#"-1.0"#]]}; }; } #[test] fn test_datetime_local_smoketest() { let zero = unix_epoch_local(); let one_second = zero + Duration::seconds(1); smoketest! { DateTime, "TimestampSeconds", one_second, {expect![[r#"1"#]]}; DateTime, "TimestampSeconds", one_second, {expect![[r#"1.0"#]]}; DateTime, "TimestampMilliSeconds", one_second, {expect![[r#"1000"#]]}; DateTime, "TimestampMilliSeconds", one_second, {expect![[r#"1000.0"#]]}; DateTime, "TimestampMicroSeconds", one_second, {expect![[r#"1000000"#]]}; DateTime, "TimestampMicroSeconds", one_second, {expect![[r#"1000000.0"#]]}; DateTime, "TimestampNanoSeconds", one_second, {expect![[r#"1000000000"#]]}; DateTime, "TimestampNanoSeconds", one_second, {expect![[r#"1000000000.0"#]]}; }; smoketest! { DateTime, "TimestampSecondsWithFrac", one_second, {expect![[r#"1.0"#]]}; DateTime, "TimestampSecondsWithFrac", one_second, {expect![[r#""1""#]]}; DateTime, "TimestampMilliSecondsWithFrac", one_second, {expect![[r#"1000.0"#]]}; DateTime, "TimestampMilliSecondsWithFrac", one_second, {expect![[r#""1000""#]]}; DateTime, "TimestampMicroSecondsWithFrac", one_second, {expect![[r#"1000000.0"#]]}; DateTime, "TimestampMicroSecondsWithFrac", one_second, {expect![[r#""1000000""#]]}; DateTime, "TimestampNanoSecondsWithFrac", one_second, {expect![[r#"1000000000.0"#]]}; DateTime, "TimestampNanoSecondsWithFrac", one_second, {expect![[r#""1000000000""#]]}; }; smoketest! { DateTime, "TimestampSecondsWithFrac", zero, {expect![[r#"0.0"#]]}; DateTime, "TimestampSecondsWithFrac", zero + Duration::nanoseconds(500_000_000), {expect![[r#"0.5"#]]}; DateTime, "TimestampSecondsWithFrac", zero + Duration::seconds(1), {expect![[r#"1.0"#]]}; DateTime, "TimestampSecondsWithFrac", zero - Duration::nanoseconds(500_000_000), {expect![[r#"-0.5"#]]}; DateTime, "TimestampSecondsWithFrac", zero - Duration::seconds(1), {expect![[r#"-1.0"#]]}; }; } #[test] fn test_naive_datetime_smoketest() { let zero = unix_epoch_naive(); let one_second = zero + Duration::seconds(1); smoketest! { NaiveDateTime, "TimestampSeconds", one_second, {expect![[r#"1"#]]}; NaiveDateTime, "TimestampSeconds", one_second, {expect![[r#"1.0"#]]}; NaiveDateTime, "TimestampMilliSeconds", one_second, {expect![[r#"1000"#]]}; NaiveDateTime, "TimestampMilliSeconds", one_second, {expect![[r#"1000.0"#]]}; NaiveDateTime, "TimestampMicroSeconds", one_second, {expect![[r#"1000000"#]]}; NaiveDateTime, "TimestampMicroSeconds", one_second, {expect![[r#"1000000.0"#]]}; NaiveDateTime, "TimestampNanoSeconds", one_second, {expect![[r#"1000000000"#]]}; NaiveDateTime, "TimestampNanoSeconds", one_second, {expect![[r#"1000000000.0"#]]}; }; smoketest! { NaiveDateTime, "TimestampSecondsWithFrac", one_second, {expect![[r#"1.0"#]]}; NaiveDateTime, "TimestampSecondsWithFrac", one_second, {expect![[r#""1""#]]}; NaiveDateTime, "TimestampMilliSecondsWithFrac", one_second, {expect![[r#"1000.0"#]]}; NaiveDateTime, "TimestampMilliSecondsWithFrac", one_second, {expect![[r#""1000""#]]}; NaiveDateTime, "TimestampMicroSecondsWithFrac", one_second, {expect![[r#"1000000.0"#]]}; NaiveDateTime, "TimestampMicroSecondsWithFrac", one_second, {expect![[r#""1000000""#]]}; NaiveDateTime, "TimestampNanoSecondsWithFrac", one_second, {expect![[r#"1000000000.0"#]]}; NaiveDateTime, "TimestampNanoSecondsWithFrac", one_second, {expect![[r#""1000000000""#]]}; }; smoketest! { NaiveDateTime, "TimestampSecondsWithFrac", zero, {expect![[r#"0.0"#]]}; NaiveDateTime, "TimestampSecondsWithFrac", zero + Duration::nanoseconds(500_000_000), {expect![[r#"0.5"#]]}; NaiveDateTime, "TimestampSecondsWithFrac", zero + Duration::seconds(1), {expect![[r#"1.0"#]]}; NaiveDateTime, "TimestampSecondsWithFrac", zero - Duration::nanoseconds(500_000_000), {expect![[r#"-0.5"#]]}; NaiveDateTime, "TimestampSecondsWithFrac", zero - Duration::seconds(1), {expect![[r#"-1.0"#]]}; }; } serde_with-3.12.0/tests/derives/deserialize_fromstr.rs000064400000000000000000000053301046102023000212720ustar 00000000000000use super::*; use core::{ num::ParseIntError, str::{FromStr, ParseBoolError}, }; use pretty_assertions::assert_eq; use serde_with::DeserializeFromStr; #[derive(Debug, PartialEq, DeserializeFromStr)] struct A { a: u32, b: bool, } impl FromStr for A { type Err = String; /// Parse a value like `123<>true` fn from_str(s: &str) -> Result { let mut parts = s.split("<>"); let number = parts .next() .ok_or_else(|| "Missing first value".to_string())? .parse() .map_err(|err: ParseIntError| err.to_string())?; let bool = parts .next() .ok_or_else(|| "Missing second value".to_string())? .parse() .map_err(|err: ParseBoolError| err.to_string())?; Ok(Self { a: number, b: bool }) } } #[test] fn test_deserialize_fromstr() { check_deserialization(A { a: 159, b: true }, "\"159<>true\""); check_deserialization(A { a: 999, b: false }, "\"999<>false\""); check_deserialization(A { a: 0, b: true }, "\"0<>true\""); } #[test] fn test_deserialize_from_bytes() { use serde::de::{value::Error, Deserialize, Deserializer, Visitor}; // Unfortunately serde_json is too clever (i.e. handles bytes gracefully) // so instead create a custom deserializer which can only deserialize bytes. // All other deserialize_* fns are forwarded to deserialize_bytes struct ByteDeserializer(&'static [u8]); impl<'de> Deserializer<'de> for ByteDeserializer { type Error = Error; fn deserialize_any(self, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_bytes(visitor) } fn deserialize_bytes(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_bytes(self.0) } serde::forward_to_deserialize_any! { bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string byte_buf option unit unit_struct newtype_struct seq tuple tuple_struct map struct enum identifier ignored_any } } // callstack: A::deserialize -> deserialize_str -> deserialize_any -> // deserialize_bytes -> visit_bytes -> visit_str -> success! let a = A::deserialize(ByteDeserializer(b"159<>true")).unwrap(); assert_eq!(A { a: 159, b: true }, a); } #[test] fn test_deserialize_fromstr_in_vec() { check_deserialization( vec![ A { a: 123, b: false }, A { a: 0, b: true }, A { a: 999, b: true }, ], r#"[ "123<>false", "0<>true", "999<>true" ]"#, ); } serde_with-3.12.0/tests/derives/lib.rs000064400000000000000000000002131046102023000157570ustar 00000000000000//! Test Cases mod deserialize_fromstr; mod serialize_display; #[path = "../utils.rs"] mod utils; use expect_test::expect; use utils::*; serde_with-3.12.0/tests/derives/serialize_display.rs000064400000000000000000000016411046102023000207330ustar 00000000000000use super::*; use core::fmt; use serde_with::SerializeDisplay; #[derive(Debug, SerializeDisplay)] struct A { a: u32, b: bool, } impl fmt::Display for A { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_fmt(format_args!("->{} <> {}<-", self.a, self.b)) } } #[test] fn test_serialize_display() { check_serialization(A { a: 123, b: false }, expect![[r#""->123 <> false<-""#]]); check_serialization(A { a: 0, b: true }, expect![[r#""->0 <> true<-""#]]); check_serialization(A { a: 999, b: true }, expect![[r#""->999 <> true<-""#]]); } #[test] fn test_serialize_display_in_vec() { check_serialization( vec![ A { a: 123, b: false }, A { a: 0, b: true }, A { a: 999, b: true }, ], expect![[r#" [ "->123 <> false<-", "->0 <> true<-", "->999 <> true<-" ]"#]], ); } serde_with-3.12.0/tests/hashbrown_0_14.rs000064400000000000000000000224051046102023000162750ustar 00000000000000//! Test Cases mod utils; use crate::utils::{check_deserialization, check_error_deserialization, is_equal}; use expect_test::expect; use serde::{Deserialize, Serialize}; use serde_with::{serde_as, DisplayFromStr, Same}; use std::net::IpAddr; type HashMap = hashbrown_0_14::HashMap; type HashSet = hashbrown_0_14::HashSet; #[test] fn test_hashmap() { #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S(#[serde_as(as = "HashMap")] HashMap); // Normal is_equal( S([(1, 1), (3, 3), (111, 111)].iter().copied().collect()), expect![[r#" { "1": "1", "3": "3", "111": "111" }"#]], ); is_equal(S(HashMap::default()), expect![[r#"{}"#]]); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct SStd( #[serde_as( as = "HashMap>" )] HashMap>, ); // Normal is_equal( SStd([(1, 1)].iter().copied().collect()), expect![[r#" { "1": "1" }"#]], ); is_equal(SStd(HashMap::default()), expect![[r#"{}"#]]); } #[test] fn test_hashset() { #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S(#[serde_as(as = "HashSet")] HashSet); // Normal is_equal( S([1, 2, 3, 4, 5].iter().copied().collect()), expect![[r#" [ "5", "4", "1", "3", "2" ]"#]], ); is_equal(S(HashSet::default()), expect![[r#"[]"#]]); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct SStd( #[serde_as( as = "HashSet>" )] HashSet>, ); // Normal is_equal( SStd([1].iter().copied().collect()), expect![[r#" [ "1" ]"#]], ); is_equal(SStd(HashSet::default()), expect![[r#"[]"#]]); } #[test] fn test_map_as_tuple_list() { let ip = "1.2.3.4".parse().unwrap(); let ip2 = "255.255.255.255".parse().unwrap(); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct SI(#[serde_as(as = "Vec<(DisplayFromStr, DisplayFromStr)>")] HashMap); let map: HashMap<_, _> = vec![(1, ip), (10, ip), (200, ip2)].into_iter().collect(); is_equal( SI(map.clone()), expect![[r#" [ [ "1", "1.2.3.4" ], [ "200", "255.255.255.255" ], [ "10", "1.2.3.4" ] ]"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct SI2(#[serde_as(as = "Vec<(Same, DisplayFromStr)>")] HashMap); is_equal( SI2(map), expect![[r#" [ [ 1, "1.2.3.4" ], [ 200, "255.255.255.255" ], [ 10, "1.2.3.4" ] ]"#]], ); } #[test] fn duplicate_key_first_wins_hashmap() { #[derive(Debug, PartialEq, Deserialize, Serialize)] struct S(#[serde(with = "::serde_with::rust::maps_first_key_wins")] HashMap); // Different value and key always works is_equal( S(HashMap::from_iter(vec![(1, 1), (2, 2), (3, 3)])), expect![[r#" { "1": 1, "3": 3, "2": 2 }"#]], ); // Same value for different keys is ok is_equal( S(HashMap::from_iter(vec![(1, 1), (2, 1), (3, 1)])), expect![[r#" { "1": 1, "3": 1, "2": 1 }"#]], ); // Duplicate keys, the first one is used check_deserialization( S(HashMap::from_iter(vec![(1, 1), (2, 2)])), r#"{"1": 1, "2": 2, "1": 3}"#, ); } #[test] fn prohibit_duplicate_key_hashmap() { #[derive(Debug, Eq, PartialEq, Deserialize, Serialize)] struct S( #[serde(with = "::serde_with::rust::maps_duplicate_key_is_error")] HashMap, ); // Different value and key always works is_equal( S(HashMap::from_iter(vec![(1, 1), (2, 2), (3, 3)])), expect![[r#" { "1": 1, "3": 3, "2": 2 }"#]], ); // Same value for different keys is ok is_equal( S(HashMap::from_iter(vec![(1, 1), (2, 1), (3, 1)])), expect![[r#" { "1": 1, "3": 1, "2": 1 }"#]], ); // Duplicate keys are an error check_error_deserialization::( r#"{"1": 1, "2": 2, "1": 3}"#, expect![[r#"invalid entry: found duplicate key at line 1 column 24"#]], ); } #[test] fn duplicate_value_last_wins_hashset() { #[derive(Debug, PartialEq, Deserialize, Serialize)] struct S(#[serde(with = "::serde_with::rust::sets_last_value_wins")] HashSet); #[derive(Debug, Eq, Deserialize, Serialize)] struct W(i32, bool); impl PartialEq for W { fn eq(&self, other: &Self) -> bool { self.0 == other.0 } } impl std::hash::Hash for W { fn hash(&self, state: &mut H) where H: std::hash::Hasher, { self.0.hash(state); } } // Different values always work is_equal( S(HashSet::from_iter(vec![ W(1, true), W(2, false), W(3, true), ])), expect![[r#" [ [ 1, true ], [ 3, true ], [ 2, false ] ]"#]], ); let value: S = serde_json::from_str( r#"[ [1, false], [1, true], [2, true], [2, false] ]"#, ) .unwrap(); let entries: Vec<_> = value.0.into_iter().collect(); assert_eq!(1, entries[0].0); assert!(entries[0].1); assert_eq!(2, entries[1].0); assert!(!entries[1].1); } #[test] fn prohibit_duplicate_value_hashset() { #[derive(Debug, PartialEq, Deserialize, Serialize)] struct S(#[serde(with = "::serde_with::rust::sets_duplicate_value_is_error")] HashSet); is_equal( S(HashSet::from_iter(vec![1, 2, 3, 4])), expect![[r#" [ 4, 1, 3, 2 ]"#]], ); check_error_deserialization::( r#"[1, 2, 3, 4, 1]"#, expect![[r#"invalid entry: found duplicate value at line 1 column 15"#]], ); } #[test] fn test_map_skip_error_hashmap() { use serde_with::MapSkipError; #[serde_as] #[derive(Debug, PartialEq, Deserialize, Serialize)] struct S { tag: String, #[serde_as(as = "MapSkipError")] values: HashMap, } check_deserialization( S { tag: "type".into(), values: [(0, 1), (10, 20)].into_iter().collect(), }, r#" { "tag":"type", "values": { "0": 1, "str": 2, "3": "str", "4": [10, 11], "5": {}, "10": 20 } }"#, ); check_error_deserialization::( r#"{"tag":"type", "values":{"0": 1,}}"#, expect!["trailing comma at line 1 column 33"], ); is_equal( S { tag: "round-trip".into(), values: [(0, 0), (255, 255)].into_iter().collect(), }, expect![[r#" { "tag": "round-trip", "values": { "255": 255, "0": 0 } }"#]], ); } #[test] fn test_map_skip_error_hashmap_flatten() { use serde_with::MapSkipError; #[serde_as] #[derive(Debug, PartialEq, Deserialize, Serialize)] struct S { tag: String, #[serde_as(as = "MapSkipError")] #[serde(flatten)] values: HashMap, } check_deserialization( S { tag: "type".into(), values: [(0, 1), (10, 20)].into_iter().collect(), }, r#" { "tag":"type", "0": 1, "str": 2, "3": "str", "4": [10, 11], "5": {}, "10": 20 }"#, ); is_equal( S { tag: "round-trip".into(), values: [(0, 0), (255, 255)].into_iter().collect(), }, expect![[r#" { "tag": "round-trip", "255": 255, "0": 0 }"#]], ); } serde_with-3.12.0/tests/hashbrown_0_15.rs000064400000000000000000000224051046102023000162760ustar 00000000000000//! Test Cases mod utils; use crate::utils::{check_deserialization, check_error_deserialization, is_equal}; use expect_test::expect; use serde::{Deserialize, Serialize}; use serde_with::{serde_as, DisplayFromStr, Same}; use std::net::IpAddr; type HashMap = hashbrown_0_15::HashMap; type HashSet = hashbrown_0_15::HashSet; #[test] fn test_hashmap() { #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S(#[serde_as(as = "HashMap")] HashMap); // Normal is_equal( S([(1, 1), (3, 3), (111, 111)].iter().copied().collect()), expect![[r#" { "1": "1", "3": "3", "111": "111" }"#]], ); is_equal(S(HashMap::default()), expect![[r#"{}"#]]); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct SStd( #[serde_as( as = "HashMap>" )] HashMap>, ); // Normal is_equal( SStd([(1, 1)].iter().copied().collect()), expect![[r#" { "1": "1" }"#]], ); is_equal(SStd(HashMap::default()), expect![[r#"{}"#]]); } #[test] fn test_hashset() { #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S(#[serde_as(as = "HashSet")] HashSet); // Normal is_equal( S([1, 2, 3, 4, 5].iter().copied().collect()), expect![[r#" [ "5", "4", "1", "3", "2" ]"#]], ); is_equal(S(HashSet::default()), expect![[r#"[]"#]]); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct SStd( #[serde_as( as = "HashSet>" )] HashSet>, ); // Normal is_equal( SStd([1].iter().copied().collect()), expect![[r#" [ "1" ]"#]], ); is_equal(SStd(HashSet::default()), expect![[r#"[]"#]]); } #[test] fn test_map_as_tuple_list() { let ip = "1.2.3.4".parse().unwrap(); let ip2 = "255.255.255.255".parse().unwrap(); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct SI(#[serde_as(as = "Vec<(DisplayFromStr, DisplayFromStr)>")] HashMap); let map: HashMap<_, _> = vec![(1, ip), (10, ip), (200, ip2)].into_iter().collect(); is_equal( SI(map.clone()), expect![[r#" [ [ "1", "1.2.3.4" ], [ "200", "255.255.255.255" ], [ "10", "1.2.3.4" ] ]"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct SI2(#[serde_as(as = "Vec<(Same, DisplayFromStr)>")] HashMap); is_equal( SI2(map), expect![[r#" [ [ 1, "1.2.3.4" ], [ 200, "255.255.255.255" ], [ 10, "1.2.3.4" ] ]"#]], ); } #[test] fn duplicate_key_first_wins_hashmap() { #[derive(Debug, PartialEq, Deserialize, Serialize)] struct S(#[serde(with = "::serde_with::rust::maps_first_key_wins")] HashMap); // Different value and key always works is_equal( S(HashMap::from_iter(vec![(1, 1), (2, 2), (3, 3)])), expect![[r#" { "1": 1, "3": 3, "2": 2 }"#]], ); // Same value for different keys is ok is_equal( S(HashMap::from_iter(vec![(1, 1), (2, 1), (3, 1)])), expect![[r#" { "1": 1, "3": 1, "2": 1 }"#]], ); // Duplicate keys, the first one is used check_deserialization( S(HashMap::from_iter(vec![(1, 1), (2, 2)])), r#"{"1": 1, "2": 2, "1": 3}"#, ); } #[test] fn prohibit_duplicate_key_hashmap() { #[derive(Debug, Eq, PartialEq, Deserialize, Serialize)] struct S( #[serde(with = "::serde_with::rust::maps_duplicate_key_is_error")] HashMap, ); // Different value and key always works is_equal( S(HashMap::from_iter(vec![(1, 1), (2, 2), (3, 3)])), expect![[r#" { "1": 1, "3": 3, "2": 2 }"#]], ); // Same value for different keys is ok is_equal( S(HashMap::from_iter(vec![(1, 1), (2, 1), (3, 1)])), expect![[r#" { "1": 1, "3": 1, "2": 1 }"#]], ); // Duplicate keys are an error check_error_deserialization::( r#"{"1": 1, "2": 2, "1": 3}"#, expect![[r#"invalid entry: found duplicate key at line 1 column 24"#]], ); } #[test] fn duplicate_value_last_wins_hashset() { #[derive(Debug, PartialEq, Deserialize, Serialize)] struct S(#[serde(with = "::serde_with::rust::sets_last_value_wins")] HashSet); #[derive(Debug, Eq, Deserialize, Serialize)] struct W(i32, bool); impl PartialEq for W { fn eq(&self, other: &Self) -> bool { self.0 == other.0 } } impl std::hash::Hash for W { fn hash(&self, state: &mut H) where H: std::hash::Hasher, { self.0.hash(state); } } // Different values always work is_equal( S(HashSet::from_iter(vec![ W(1, true), W(2, false), W(3, true), ])), expect![[r#" [ [ 1, true ], [ 3, true ], [ 2, false ] ]"#]], ); let value: S = serde_json::from_str( r#"[ [1, false], [1, true], [2, true], [2, false] ]"#, ) .unwrap(); let entries: Vec<_> = value.0.into_iter().collect(); assert_eq!(1, entries[0].0); assert!(entries[0].1); assert_eq!(2, entries[1].0); assert!(!entries[1].1); } #[test] fn prohibit_duplicate_value_hashset() { #[derive(Debug, PartialEq, Deserialize, Serialize)] struct S(#[serde(with = "::serde_with::rust::sets_duplicate_value_is_error")] HashSet); is_equal( S(HashSet::from_iter(vec![1, 2, 3, 4])), expect![[r#" [ 4, 1, 3, 2 ]"#]], ); check_error_deserialization::( r#"[1, 2, 3, 4, 1]"#, expect![[r#"invalid entry: found duplicate value at line 1 column 15"#]], ); } #[test] fn test_map_skip_error_hashmap() { use serde_with::MapSkipError; #[serde_as] #[derive(Debug, PartialEq, Deserialize, Serialize)] struct S { tag: String, #[serde_as(as = "MapSkipError")] values: HashMap, } check_deserialization( S { tag: "type".into(), values: [(0, 1), (10, 20)].into_iter().collect(), }, r#" { "tag":"type", "values": { "0": 1, "str": 2, "3": "str", "4": [10, 11], "5": {}, "10": 20 } }"#, ); check_error_deserialization::( r#"{"tag":"type", "values":{"0": 1,}}"#, expect!["trailing comma at line 1 column 33"], ); is_equal( S { tag: "round-trip".into(), values: [(0, 0), (255, 255)].into_iter().collect(), }, expect![[r#" { "tag": "round-trip", "values": { "255": 255, "0": 0 } }"#]], ); } #[test] fn test_map_skip_error_hashmap_flatten() { use serde_with::MapSkipError; #[serde_as] #[derive(Debug, PartialEq, Deserialize, Serialize)] struct S { tag: String, #[serde_as(as = "MapSkipError")] #[serde(flatten)] values: HashMap, } check_deserialization( S { tag: "type".into(), values: [(0, 1), (10, 20)].into_iter().collect(), }, r#" { "tag":"type", "0": 1, "str": 2, "3": "str", "4": [10, 11], "5": {}, "10": 20 }"#, ); is_equal( S { tag: "round-trip".into(), values: [(0, 0), (255, 255)].into_iter().collect(), }, expect![[r#" { "tag": "round-trip", "255": 255, "0": 0 }"#]], ); } serde_with-3.12.0/tests/hex.rs000064400000000000000000000040621046102023000143420ustar 00000000000000//! Test Cases mod utils; use crate::utils::{check_deserialization, check_error_deserialization, is_equal}; use expect_test::expect; use serde::{Deserialize, Serialize}; use serde_with::{ formats::{Lowercase, Uppercase}, hex::Hex, serde_as, }; #[test] fn hex_vec() { #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] pub struct B(#[serde_as(as = "Vec")] Vec>); is_equal( B(vec![vec![0, 1, 2, 13], vec![14, 5, 6, 7]]), expect![[r#" [ "0001020d", "0e050607" ]"#]], ); // Check mixed case deserialization check_deserialization( B(vec![vec![0xaa, 0xbc, 0xff], vec![0xe0, 0x7d]]), r#"["aaBCff","E07d"]"#, ); check_error_deserialization::( r#"["0"]"#, expect![[r#"Odd number of digits at line 1 column 5"#]], ); check_error_deserialization::( r#"["zz"]"#, expect![[r#"Invalid character 'z' at position 0 at line 1 column 6"#]], ); } #[test] fn hex_vec_lowercase() { #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] pub struct B(#[serde_as(as = "Vec>")] Vec>); is_equal( B(vec![vec![0, 1, 2, 13], vec![14, 5, 6, 7]]), expect![[r#" [ "0001020d", "0e050607" ]"#]], ); // Check mixed case deserialization check_deserialization( B(vec![vec![0xaa, 0xbc, 0xff], vec![0xe0, 0x7d]]), r#"["aaBCff","E07d"]"#, ); } #[test] fn hex_vec_uppercase() { #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] pub struct B(#[serde_as(as = "Vec>")] Vec>); is_equal( B(vec![vec![0, 1, 2, 13], vec![14, 5, 6, 7]]), expect![[r#" [ "0001020D", "0E050607" ]"#]], ); // Check mixed case deserialization check_deserialization( B(vec![vec![0xaa, 0xbc, 0xff], vec![0xe0, 0x7d]]), r#"["aaBCff","E07d"]"#, ); } serde_with-3.12.0/tests/indexmap_1.rs000064400000000000000000000200541046102023000156020ustar 00000000000000//! Test Cases mod utils; use crate::utils::{check_deserialization, check_error_deserialization, is_equal}; use expect_test::expect; use indexmap_1::{IndexMap, IndexSet}; use serde::{Deserialize, Serialize}; use serde_with::{serde_as, DisplayFromStr, Same}; use std::net::IpAddr; #[test] fn test_indexmap() { #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S(#[serde_as(as = "IndexMap")] IndexMap); // Normal is_equal( S([(1, 1), (3, 3), (111, 111)].iter().copied().collect()), expect![[r#" { "1": "1", "3": "3", "111": "111" }"#]], ); is_equal(S(IndexMap::default()), expect![[r#"{}"#]]); } #[test] fn test_indexset() { #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S(#[serde_as(as = "IndexSet")] IndexSet); // Normal is_equal( S([1, 2, 3, 4, 5].iter().copied().collect()), expect![[r#" [ "1", "2", "3", "4", "5" ]"#]], ); is_equal(S(IndexSet::default()), expect![[r#"[]"#]]); } #[test] fn test_map_as_tuple_list() { let ip = "1.2.3.4".parse().unwrap(); let ip2 = "255.255.255.255".parse().unwrap(); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct SI(#[serde_as(as = "Vec<(DisplayFromStr, DisplayFromStr)>")] IndexMap); let map: IndexMap<_, _> = vec![(1, ip), (10, ip), (200, ip2)].into_iter().collect(); is_equal( SI(map.clone()), expect![[r#" [ [ "1", "1.2.3.4" ], [ "10", "1.2.3.4" ], [ "200", "255.255.255.255" ] ]"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct SI2(#[serde_as(as = "Vec<(Same, DisplayFromStr)>")] IndexMap); is_equal( SI2(map), expect![[r#" [ [ 1, "1.2.3.4" ], [ 10, "1.2.3.4" ], [ 200, "255.255.255.255" ] ]"#]], ); } #[test] fn duplicate_key_first_wins_indexmap() { #[derive(Debug, PartialEq, Deserialize, Serialize)] struct S(#[serde(with = "::serde_with::rust::maps_first_key_wins")] IndexMap); // Different value and key always works is_equal( S(IndexMap::from_iter(vec![(1, 1), (2, 2), (3, 3)])), expect![[r#" { "1": 1, "2": 2, "3": 3 }"#]], ); // Same value for different keys is ok is_equal( S(IndexMap::from_iter(vec![(1, 1), (2, 1), (3, 1)])), expect![[r#" { "1": 1, "2": 1, "3": 1 }"#]], ); // Duplicate keys, the first one is used check_deserialization( S(IndexMap::from_iter(vec![(1, 1), (2, 2)])), r#"{"1": 1, "2": 2, "1": 3}"#, ); } #[test] fn prohibit_duplicate_key_indexmap() { #[derive(Debug, Eq, PartialEq, Deserialize, Serialize)] struct S( #[serde(with = "::serde_with::rust::maps_duplicate_key_is_error")] IndexMap, ); // Different value and key always works is_equal( S(IndexMap::from_iter(vec![(1, 1), (2, 2), (3, 3)])), expect![[r#" { "1": 1, "2": 2, "3": 3 }"#]], ); // Same value for different keys is ok is_equal( S(IndexMap::from_iter(vec![(1, 1), (2, 1), (3, 1)])), expect![[r#" { "1": 1, "2": 1, "3": 1 }"#]], ); // Duplicate keys are an error check_error_deserialization::( r#"{"1": 1, "2": 2, "1": 3}"#, expect![[r#"invalid entry: found duplicate key at line 1 column 24"#]], ); } #[test] fn duplicate_value_last_wins_indexset() { #[derive(Debug, PartialEq, Deserialize, Serialize)] struct S(#[serde(with = "::serde_with::rust::sets_last_value_wins")] IndexSet); #[derive(Debug, Eq, Deserialize, Serialize)] struct W(i32, bool); impl PartialEq for W { fn eq(&self, other: &Self) -> bool { self.0 == other.0 } } impl std::hash::Hash for W { fn hash(&self, state: &mut H) where H: std::hash::Hasher, { self.0.hash(state); } } // Different values always work is_equal( S(IndexSet::from_iter(vec![ W(1, true), W(2, false), W(3, true), ])), expect![[r#" [ [ 1, true ], [ 2, false ], [ 3, true ] ]"#]], ); let value: S = serde_json::from_str( r#"[ [1, false], [1, true], [2, true], [2, false] ]"#, ) .unwrap(); let entries: Vec<_> = value.0.into_iter().collect(); assert_eq!(1, entries[0].0); assert!(entries[0].1); assert_eq!(2, entries[1].0); assert!(!entries[1].1); } #[test] fn prohibit_duplicate_value_indexset() { #[derive(Debug, PartialEq, Deserialize, Serialize)] struct S(#[serde(with = "::serde_with::rust::sets_duplicate_value_is_error")] IndexSet); is_equal( S(IndexSet::from_iter(vec![1, 2, 3, 4])), expect![[r#" [ 1, 2, 3, 4 ]"#]], ); check_error_deserialization::( r#"[1, 2, 3, 4, 1]"#, expect![[r#"invalid entry: found duplicate value at line 1 column 15"#]], ); } #[test] fn test_map_skip_error_indexmap() { use serde_with::MapSkipError; #[serde_as] #[derive(Debug, PartialEq, Deserialize, Serialize)] struct S { tag: String, #[serde_as(as = "MapSkipError")] values: IndexMap, } check_deserialization( S { tag: "type".into(), values: [(0, 1), (10, 20)].into_iter().collect(), }, r#" { "tag":"type", "values": { "0": 1, "str": 2, "3": "str", "4": [10, 11], "5": {}, "10": 20 } }"#, ); check_error_deserialization::( r#"{"tag":"type", "values":{"0": 1,}}"#, expect!["trailing comma at line 1 column 33"], ); is_equal( S { tag: "round-trip".into(), values: [(0, 0), (255, 255)].into_iter().collect(), }, expect![[r#" { "tag": "round-trip", "values": { "0": 0, "255": 255 } }"#]], ); } #[test] fn test_map_skip_error_indexmap_flatten() { use serde_with::MapSkipError; #[serde_as] #[derive(Debug, PartialEq, Deserialize, Serialize)] struct S { tag: String, #[serde_as(as = "MapSkipError")] #[serde(flatten)] values: IndexMap, } check_deserialization( S { tag: "type".into(), values: [(0, 1), (10, 20)].into_iter().collect(), }, r#" { "tag":"type", "0": 1, "str": 2, "3": "str", "4": [10, 11], "5": {}, "10": 20 }"#, ); is_equal( S { tag: "round-trip".into(), values: [(0, 0), (255, 255)].into_iter().collect(), }, expect![[r#" { "tag": "round-trip", "0": 0, "255": 255 }"#]], ); } serde_with-3.12.0/tests/indexmap_2.rs000064400000000000000000000200541046102023000156030ustar 00000000000000//! Test Cases mod utils; use crate::utils::{check_deserialization, check_error_deserialization, is_equal}; use expect_test::expect; use indexmap_2::{IndexMap, IndexSet}; use serde::{Deserialize, Serialize}; use serde_with::{serde_as, DisplayFromStr, Same}; use std::net::IpAddr; #[test] fn test_indexmap() { #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S(#[serde_as(as = "IndexMap")] IndexMap); // Normal is_equal( S([(1, 1), (3, 3), (111, 111)].iter().copied().collect()), expect![[r#" { "1": "1", "3": "3", "111": "111" }"#]], ); is_equal(S(IndexMap::default()), expect![[r#"{}"#]]); } #[test] fn test_indexset() { #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S(#[serde_as(as = "IndexSet")] IndexSet); // Normal is_equal( S([1, 2, 3, 4, 5].iter().copied().collect()), expect![[r#" [ "1", "2", "3", "4", "5" ]"#]], ); is_equal(S(IndexSet::default()), expect![[r#"[]"#]]); } #[test] fn test_map_as_tuple_list() { let ip = "1.2.3.4".parse().unwrap(); let ip2 = "255.255.255.255".parse().unwrap(); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct SI(#[serde_as(as = "Vec<(DisplayFromStr, DisplayFromStr)>")] IndexMap); let map: IndexMap<_, _> = vec![(1, ip), (10, ip), (200, ip2)].into_iter().collect(); is_equal( SI(map.clone()), expect![[r#" [ [ "1", "1.2.3.4" ], [ "10", "1.2.3.4" ], [ "200", "255.255.255.255" ] ]"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct SI2(#[serde_as(as = "Vec<(Same, DisplayFromStr)>")] IndexMap); is_equal( SI2(map), expect![[r#" [ [ 1, "1.2.3.4" ], [ 10, "1.2.3.4" ], [ 200, "255.255.255.255" ] ]"#]], ); } #[test] fn duplicate_key_first_wins_indexmap() { #[derive(Debug, PartialEq, Deserialize, Serialize)] struct S(#[serde(with = "::serde_with::rust::maps_first_key_wins")] IndexMap); // Different value and key always works is_equal( S(IndexMap::from_iter(vec![(1, 1), (2, 2), (3, 3)])), expect![[r#" { "1": 1, "2": 2, "3": 3 }"#]], ); // Same value for different keys is ok is_equal( S(IndexMap::from_iter(vec![(1, 1), (2, 1), (3, 1)])), expect![[r#" { "1": 1, "2": 1, "3": 1 }"#]], ); // Duplicate keys, the first one is used check_deserialization( S(IndexMap::from_iter(vec![(1, 1), (2, 2)])), r#"{"1": 1, "2": 2, "1": 3}"#, ); } #[test] fn prohibit_duplicate_key_indexmap() { #[derive(Debug, Eq, PartialEq, Deserialize, Serialize)] struct S( #[serde(with = "::serde_with::rust::maps_duplicate_key_is_error")] IndexMap, ); // Different value and key always works is_equal( S(IndexMap::from_iter(vec![(1, 1), (2, 2), (3, 3)])), expect![[r#" { "1": 1, "2": 2, "3": 3 }"#]], ); // Same value for different keys is ok is_equal( S(IndexMap::from_iter(vec![(1, 1), (2, 1), (3, 1)])), expect![[r#" { "1": 1, "2": 1, "3": 1 }"#]], ); // Duplicate keys are an error check_error_deserialization::( r#"{"1": 1, "2": 2, "1": 3}"#, expect![[r#"invalid entry: found duplicate key at line 1 column 24"#]], ); } #[test] fn duplicate_value_last_wins_indexset() { #[derive(Debug, PartialEq, Deserialize, Serialize)] struct S(#[serde(with = "::serde_with::rust::sets_last_value_wins")] IndexSet); #[derive(Debug, Eq, Deserialize, Serialize)] struct W(i32, bool); impl PartialEq for W { fn eq(&self, other: &Self) -> bool { self.0 == other.0 } } impl std::hash::Hash for W { fn hash(&self, state: &mut H) where H: std::hash::Hasher, { self.0.hash(state); } } // Different values always work is_equal( S(IndexSet::from_iter(vec![ W(1, true), W(2, false), W(3, true), ])), expect![[r#" [ [ 1, true ], [ 2, false ], [ 3, true ] ]"#]], ); let value: S = serde_json::from_str( r#"[ [1, false], [1, true], [2, true], [2, false] ]"#, ) .unwrap(); let entries: Vec<_> = value.0.into_iter().collect(); assert_eq!(1, entries[0].0); assert!(entries[0].1); assert_eq!(2, entries[1].0); assert!(!entries[1].1); } #[test] fn prohibit_duplicate_value_indexset() { #[derive(Debug, PartialEq, Deserialize, Serialize)] struct S(#[serde(with = "::serde_with::rust::sets_duplicate_value_is_error")] IndexSet); is_equal( S(IndexSet::from_iter(vec![1, 2, 3, 4])), expect![[r#" [ 1, 2, 3, 4 ]"#]], ); check_error_deserialization::( r#"[1, 2, 3, 4, 1]"#, expect![[r#"invalid entry: found duplicate value at line 1 column 15"#]], ); } #[test] fn test_map_skip_error_indexmap() { use serde_with::MapSkipError; #[serde_as] #[derive(Debug, PartialEq, Deserialize, Serialize)] struct S { tag: String, #[serde_as(as = "MapSkipError")] values: IndexMap, } check_deserialization( S { tag: "type".into(), values: [(0, 1), (10, 20)].into_iter().collect(), }, r#" { "tag":"type", "values": { "0": 1, "str": 2, "3": "str", "4": [10, 11], "5": {}, "10": 20 } }"#, ); check_error_deserialization::( r#"{"tag":"type", "values":{"0": 1,}}"#, expect!["trailing comma at line 1 column 33"], ); is_equal( S { tag: "round-trip".into(), values: [(0, 0), (255, 255)].into_iter().collect(), }, expect![[r#" { "tag": "round-trip", "values": { "0": 0, "255": 255 } }"#]], ); } #[test] fn test_map_skip_error_indexmap_flatten() { use serde_with::MapSkipError; #[serde_as] #[derive(Debug, PartialEq, Deserialize, Serialize)] struct S { tag: String, #[serde_as(as = "MapSkipError")] #[serde(flatten)] values: IndexMap, } check_deserialization( S { tag: "type".into(), values: [(0, 1), (10, 20)].into_iter().collect(), }, r#" { "tag":"type", "0": 1, "str": 2, "3": "str", "4": [10, 11], "5": {}, "10": 20 }"#, ); is_equal( S { tag: "round-trip".into(), values: [(0, 0), (255, 255)].into_iter().collect(), }, expect![[r#" { "tag": "round-trip", "0": 0, "255": 255 }"#]], ); } serde_with-3.12.0/tests/json.rs000064400000000000000000000023041046102023000145240ustar 00000000000000//! Test Cases mod utils; use crate::utils::is_equal; use expect_test::expect; use serde::{Deserialize, Serialize}; use serde_with::{json::JsonString, serde_as, DisplayFromStr}; use std::collections::BTreeMap; #[test] fn test_jsonstring() { #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct Struct { #[serde_as(as = "JsonString")] value: Nested, } #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct Nested { #[serde_as(as = "DisplayFromStr")] value: u32, } is_equal( Struct { value: Nested { value: 444 }, }, expect![[r#" { "value": "{\"value\":\"444\"}" }"#]], ); } #[test] fn test_jsonstring_nested() { #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct Struct { #[serde_as(as = "JsonString>")] value: BTreeMap<[u8; 2], u32>, } is_equal( Struct { value: BTreeMap::from([([1, 2], 3), ([4, 5], 6)]), }, expect![[r#" { "value": "[[\"[1,2]\",3],[\"[4,5]\",6]]" }"#]], ); } serde_with-3.12.0/tests/rust.rs000064400000000000000000000220641046102023000145550ustar 00000000000000//! Test Cases extern crate alloc; mod utils; use crate::utils::{check_deserialization, check_error_deserialization, is_equal}; use alloc::collections::{BTreeMap, BTreeSet}; use core::{cmp, iter::FromIterator as _}; use expect_test::expect; use fnv::{FnvHashMap as HashMap, FnvHashSet as HashSet}; use pretty_assertions::assert_eq; use serde::{Deserialize, Serialize}; #[test] fn prohibit_duplicate_value_hashset() { #[derive(Debug, PartialEq, Deserialize, Serialize)] struct S(#[serde(with = "::serde_with::rust::sets_duplicate_value_is_error")] HashSet); is_equal( S(HashSet::from_iter(vec![1, 2, 3, 4])), expect![[r#" [ 4, 1, 3, 2 ]"#]], ); check_error_deserialization::( r#"[1, 2, 3, 4, 1]"#, expect![[r#"invalid entry: found duplicate value at line 1 column 15"#]], ); } #[test] fn prohibit_duplicate_value_btreeset() { #[derive(Debug, PartialEq, Deserialize, Serialize)] struct S(#[serde(with = "::serde_with::rust::sets_duplicate_value_is_error")] BTreeSet); is_equal( S(BTreeSet::from_iter(vec![1, 2, 3, 4])), expect![[r#" [ 1, 2, 3, 4 ]"#]], ); check_error_deserialization::( r#"[1, 2, 3, 4, 1]"#, expect![[r#"invalid entry: found duplicate value at line 1 column 15"#]], ); } #[test] fn prohibit_duplicate_key_hashmap() { #[derive(Debug, Eq, PartialEq, Deserialize, Serialize)] struct S( #[serde(with = "::serde_with::rust::maps_duplicate_key_is_error")] HashMap, ); // Different value and key always works is_equal( S(HashMap::from_iter(vec![(1, 1), (2, 2), (3, 3)])), expect![[r#" { "1": 1, "3": 3, "2": 2 }"#]], ); // Same value for different keys is ok is_equal( S(HashMap::from_iter(vec![(1, 1), (2, 1), (3, 1)])), expect![[r#" { "1": 1, "3": 1, "2": 1 }"#]], ); // Duplicate keys are an error check_error_deserialization::( r#"{"1": 1, "2": 2, "1": 3}"#, expect![[r#"invalid entry: found duplicate key at line 1 column 24"#]], ); } #[test] fn prohibit_duplicate_key_btreemap() { #[derive(Debug, Eq, PartialEq, Deserialize, Serialize)] struct S( #[serde(with = "::serde_with::rust::maps_duplicate_key_is_error")] BTreeMap, ); // Different value and key always works is_equal( S(BTreeMap::from_iter(vec![(1, 1), (2, 2), (3, 3)])), expect![[r#" { "1": 1, "2": 2, "3": 3 }"#]], ); // Same value for different keys is ok is_equal( S(BTreeMap::from_iter(vec![(1, 1), (2, 1), (3, 1)])), expect![[r#" { "1": 1, "2": 1, "3": 1 }"#]], ); // Duplicate keys are an error check_error_deserialization::( r#"{"1": 1, "2": 2, "1": 3}"#, expect![[r#"invalid entry: found duplicate key at line 1 column 24"#]], ); } #[test] fn duplicate_key_first_wins_hashmap() { #[derive(Debug, PartialEq, Deserialize, Serialize)] struct S(#[serde(with = "::serde_with::rust::maps_first_key_wins")] HashMap); // Different value and key always works is_equal( S(HashMap::from_iter(vec![(1, 1), (2, 2), (3, 3)])), expect![[r#" { "1": 1, "3": 3, "2": 2 }"#]], ); // Same value for different keys is ok is_equal( S(HashMap::from_iter(vec![(1, 1), (2, 1), (3, 1)])), expect![[r#" { "1": 1, "3": 1, "2": 1 }"#]], ); // Duplicate keys, the first one is used check_deserialization( S(HashMap::from_iter(vec![(1, 1), (2, 2)])), r#"{"1": 1, "2": 2, "1": 3}"#, ); } #[test] fn duplicate_key_first_wins_btreemap() { #[derive(Debug, PartialEq, Deserialize, Serialize)] struct S(#[serde(with = "::serde_with::rust::maps_first_key_wins")] BTreeMap); // Different value and key always works is_equal( S(BTreeMap::from_iter(vec![(1, 1), (2, 2), (3, 3)])), expect![[r#" { "1": 1, "2": 2, "3": 3 }"#]], ); // Same value for different keys is ok is_equal( S(BTreeMap::from_iter(vec![(1, 1), (2, 1), (3, 1)])), expect![[r#" { "1": 1, "2": 1, "3": 1 }"#]], ); // Duplicate keys, the first one is used check_deserialization( S(BTreeMap::from_iter(vec![(1, 1), (2, 2)])), r#"{"1": 1, "2": 2, "1": 3}"#, ); } #[test] fn duplicate_value_first_wins_hashset() { #[derive(Debug, PartialEq, Deserialize, Serialize)] struct S(HashSet); // struct S(#[serde(with = "::serde_with::rust::sets_first_value_wins")] HashSet); #[derive(Debug, Eq, Deserialize, Serialize)] struct W(i32, bool); impl PartialEq for W { fn eq(&self, other: &Self) -> bool { self.0 == other.0 } } impl std::hash::Hash for W { fn hash(&self, state: &mut H) where H: std::hash::Hasher, { self.0.hash(state); } } // Different values always work is_equal( S(HashSet::from_iter(vec![ W(1, true), W(2, false), W(3, true), ])), expect![[r#" [ [ 1, true ], [ 3, true ], [ 2, false ] ]"#]], ); let value: S = serde_json::from_str( r#"[ [1, false], [1, true], [2, true], [2, false] ]"#, ) .unwrap(); let entries: Vec<_> = value.0.into_iter().collect(); assert_eq!(1, entries[0].0); assert!(!entries[0].1); assert_eq!(2, entries[1].0); assert!(entries[1].1); } #[test] fn duplicate_value_last_wins_hashset() { #[derive(Debug, PartialEq, Deserialize, Serialize)] struct S(#[serde(with = "::serde_with::rust::sets_last_value_wins")] HashSet); #[derive(Debug, Eq, Deserialize, Serialize)] struct W(i32, bool); impl PartialEq for W { fn eq(&self, other: &Self) -> bool { self.0 == other.0 } } impl std::hash::Hash for W { fn hash(&self, state: &mut H) where H: std::hash::Hasher, { self.0.hash(state); } } // Different values always work is_equal( S(HashSet::from_iter(vec![ W(1, true), W(2, false), W(3, true), ])), expect![[r#" [ [ 1, true ], [ 3, true ], [ 2, false ] ]"#]], ); let value: S = serde_json::from_str( r#"[ [1, false], [1, true], [2, true], [2, false] ]"#, ) .unwrap(); let entries: Vec<_> = value.0.into_iter().collect(); assert_eq!(1, entries[0].0); assert!(entries[0].1); assert_eq!(2, entries[1].0); assert!(!entries[1].1); } #[test] fn duplicate_value_last_wins_btreeset() { #[derive(Debug, PartialEq, Deserialize, Serialize)] struct S(#[serde(with = "::serde_with::rust::sets_last_value_wins")] BTreeSet); #[derive(Debug, Eq, Deserialize, Serialize)] struct W(i32, bool); impl PartialEq for W { fn eq(&self, other: &Self) -> bool { self.0 == other.0 } } impl Ord for W { fn cmp(&self, other: &Self) -> cmp::Ordering { self.0.cmp(&other.0) } } impl PartialOrd for W { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } // Different values always work is_equal( S(BTreeSet::from_iter(vec![ W(1, true), W(2, false), W(3, true), ])), expect![[r#" [ [ 1, true ], [ 2, false ], [ 3, true ] ]"#]], ); let value: S = serde_json::from_str( r#"[ [1, false], [1, true], [2, true], [2, false] ]"#, ) .unwrap(); let entries: Vec<_> = value.0.into_iter().collect(); assert_eq!(1, entries[0].0); assert!(entries[0].1); assert_eq!(2, entries[1].0); assert!(!entries[1].1); } serde_with-3.12.0/tests/schemars_0_8/schemars_basic.json000064400000000000000000000021311046102023000213170ustar 00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Basic", "type": "object", "required": [ "bare_field", "box_same", "display_from_str", "same", "vec_same" ], "properties": { "bare_field": { "description": "Basic field, no attribute", "type": "integer", "format": "uint32", "minimum": 0.0 }, "box_same": { "description": "This checks that Same still works when wrapped in a box.", "type": "integer", "format": "uint32", "minimum": 0.0 }, "display_from_str": { "description": "Field that directly uses DisplayFromStr", "type": "string" }, "same": { "description": "Same does not implement JsonSchema directly so this checks that the correct schemars attribute was injected.", "type": "integer", "format": "uint32", "minimum": 0.0 }, "vec_same": { "description": "Same thing, but with a Vec this time.", "type": "array", "items": { "type": "integer", "format": "uint32", "minimum": 0.0 } } } } serde_with-3.12.0/tests/schemars_0_8/schemars_deserialize_only_bug_735.json000064400000000000000000000027511046102023000250420ustar 00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Basic", "type": "object", "required": [ "bare_field", "both", "deserialize_only", "serialize_and_deserialize", "serialize_only" ], "properties": { "bare_field": { "description": "Basic field, no attribute", "type": "integer", "format": "uint32", "minimum": 0.0 }, "both": { "description": "Will emit matching schemars attribute", "allOf": [ { "$ref": "#/definitions/PickFirst(uint32String)" } ] }, "deserialize_only": { "description": "schemars doesn't support deserialize_as", "type": "integer", "format": "uint32", "minimum": 0.0 }, "serialize_and_deserialize": { "description": "Can emit schemars with serialize_as, but it will be ignored schemars doesn't support deserialize_as", "type": "integer", "format": "uint32", "minimum": 0.0 }, "serialize_only": { "description": "Can emit schemars with serialize_as, but it will be ignored", "type": "integer", "format": "uint32", "minimum": 0.0 } }, "definitions": { "PickFirst(uint32String)": { "anyOf": [ { "type": "integer", "format": "uint32", "minimum": 0.0 }, { "writeOnly": true, "allOf": [ { "type": "string" } ] } ] } } } serde_with-3.12.0/tests/schemars_0_8/snapshots/bytes.json000064400000000000000000000004541046102023000215270ustar 00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Test", "type": "object", "required": [ "bytes" ], "properties": { "bytes": { "type": "array", "items": { "type": "integer", "format": "uint8", "minimum": 0.0 } } } } serde_with-3.12.0/tests/schemars_0_8/snapshots/default_on_null.json000064400000000000000000000003461046102023000235530ustar 00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Test", "type": "object", "required": [ "data" ], "properties": { "data": { "type": [ "string", "null" ] } } } serde_with-3.12.0/tests/schemars_0_8/snapshots/duration.json000064400000000000000000000025311046102023000222240ustar 00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Test", "type": "object", "required": [ "flexible_string", "frac", "seconds", "seconds_u64_strict", "time_i64" ], "properties": { "flexible_string": { "oneOf": [ { "writeOnly": true, "type": "number", "minimum": 0.0 }, { "type": "string", "pattern": "^[0-9]+(\\.[0-9]+)?([eE][+-]?[0-9]+)?$" } ] }, "frac": { "oneOf": [ { "type": "number", "minimum": 0.0 }, { "writeOnly": true, "type": "string", "pattern": "^[0-9]+(\\.[0-9]+)?([eE][+-]?[0-9]+)?$" } ] }, "seconds": { "oneOf": [ { "type": "number", "minimum": 0.0 }, { "writeOnly": true, "type": "string", "pattern": "^[0-9]+(\\.[0-9]+)?([eE][+-]?[0-9]+)?$" } ] }, "seconds_u64_strict": { "type": "integer", "format": "uint64", "minimum": 0.0 }, "time_i64": { "oneOf": [ { "type": "number" }, { "writeOnly": true, "type": "string", "pattern": "^-?[0-9]+(\\.[0-9]+)?([eE][+-]?[0-9]+)?$" } ] } } } serde_with-3.12.0/tests/schemars_0_8/snapshots/enum_map.json000064400000000000000000000016471046102023000222070ustar 00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Test", "type": "object", "required": [ "data" ], "properties": { "data": { "$ref": "#/definitions/EnumMap(Mappable)" } }, "definitions": { "EnumMap(Mappable)": { "type": "object", "properties": { "A": { "type": "integer", "format": "int32" }, "B": { "type": "string" }, "C": { "type": "object", "required": [ "c" ], "properties": { "b": { "type": [ "integer", "null" ], "format": "uint64", "minimum": 0.0 }, "c": { "type": "integer", "format": "int32" } } } }, "additionalProperties": false } } } serde_with-3.12.0/tests/schemars_0_8/snapshots/from_into.json000064400000000000000000000003651046102023000223760ustar 00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Test", "type": "object", "required": [ "data" ], "properties": { "data": { "type": "integer", "format": "uint64", "minimum": 0.0 } } } serde_with-3.12.0/tests/schemars_0_8/snapshots/key_value_map.json000064400000000000000000000015001046102023000232130ustar 00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Test", "type": "object", "required": [ "data" ], "properties": { "data": { "$ref": "#/definitions/KeyValueMap(KvMapData)" } }, "definitions": { "KeyValueMap(KvMapData)": { "type": "object", "additionalProperties": { "type": "object", "required": [ "a", "b", "c", "d" ], "properties": { "a": { "type": "integer", "format": "uint32", "minimum": 0.0 }, "b": { "type": "string" }, "c": { "type": "number", "format": "float" }, "d": { "type": "boolean" } } } } } } serde_with-3.12.0/tests/schemars_0_8/snapshots/key_value_map_enum.json000064400000000000000000000021501046102023000242410ustar 00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Test", "type": "object", "required": [ "data" ], "properties": { "data": { "$ref": "#/definitions/KeyValueMap(KvMapEnum)" } }, "definitions": { "KeyValueMap(KvMapEnum)": { "type": "object", "additionalProperties": { "oneOf": [ { "type": "object", "required": [ "a" ], "properties": { "a": { "type": "integer", "format": "uint32", "minimum": 0.0 } } }, { "type": "object", "required": [ "b" ], "properties": { "b": { "type": "string" } } }, { "type": "object", "required": [ "c" ], "properties": { "c": { "type": "boolean" } } } ] } } } } serde_with-3.12.0/tests/schemars_0_8/snapshots/key_value_map_flatten.json000064400000000000000000000024351046102023000247400ustar 00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Test", "type": "object", "required": [ "data" ], "properties": { "data": { "$ref": "#/definitions/KeyValueMap(KvMapFlatten)" } }, "definitions": { "KeyValueMap(KvMapFlatten)": { "type": "object", "additionalProperties": { "type": "object", "oneOf": [ { "type": "object", "required": [ "a" ], "properties": { "a": { "type": "integer", "format": "uint32", "minimum": 0.0 } } }, { "type": "object", "required": [ "b" ], "properties": { "b": { "type": "string" } } }, { "type": "object", "required": [ "c" ], "properties": { "c": { "type": "boolean" } } } ], "required": [ "extra" ], "properties": { "extra": { "type": "boolean" } } } } } } serde_with-3.12.0/tests/schemars_0_8/snapshots/map.json000064400000000000000000000004731046102023000211570ustar 00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Test", "type": "object", "required": [ "data" ], "properties": { "data": { "type": "object", "additionalProperties": { "type": "integer", "format": "uint32", "minimum": 0.0 } } } } serde_with-3.12.0/tests/schemars_0_8/snapshots/map_fixed.json000064400000000000000000000004731046102023000223360ustar 00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Test", "type": "object", "required": [ "data" ], "properties": { "data": { "type": "object", "additionalProperties": { "type": "integer", "format": "uint32", "minimum": 0.0 } } } } serde_with-3.12.0/tests/schemars_0_8/snapshots/one_or_many_nested.json000064400000000000000000000010771046102023000242520ustar 00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Test", "type": "object", "properties": { "optional_many": { "default": null, "anyOf": [ { "$ref": "#/definitions/OneOrMany(String,PreferOne)" }, { "type": "null" } ] } }, "definitions": { "OneOrMany(String,PreferOne)": { "anyOf": [ { "type": "string" }, { "type": "array", "items": { "type": "string" } } ] } } } serde_with-3.12.0/tests/schemars_0_8/snapshots/one_or_many_prefer_one.json000064400000000000000000000004451046102023000251120ustar 00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "title": "OneOrMany(int32,PreferOne)", "anyOf": [ { "type": "integer", "format": "int32" }, { "type": "array", "items": { "type": "integer", "format": "int32" } } ] } serde_with-3.12.0/tests/schemars_0_8/snapshots/pickfirst.json000064400000000000000000000004651046102023000224010ustar 00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "title": "PickFirst(uint32String)", "anyOf": [ { "type": "integer", "format": "uint32", "minimum": 0.0 }, { "writeOnly": true, "allOf": [ { "type": "string" } ] } ] } serde_with-3.12.0/tests/schemars_0_8/snapshots/pickfirst_nested.json000064400000000000000000000012151046102023000237350ustar 00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "title": "OneOrMany(PickFirst(uint32String),PreferOne)", "anyOf": [ { "$ref": "#/definitions/PickFirst(uint32String)" }, { "type": "array", "items": { "$ref": "#/definitions/PickFirst(uint32String)" } } ], "definitions": { "PickFirst(uint32String)": { "anyOf": [ { "type": "integer", "format": "uint32", "minimum": 0.0 }, { "writeOnly": true, "allOf": [ { "type": "string" } ] } ] } } } serde_with-3.12.0/tests/schemars_0_8/snapshots/set_last_value_wins.json000064400000000000000000000004531046102023000244520ustar 00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Test", "type": "object", "required": [ "data" ], "properties": { "data": { "type": "array", "items": { "type": "integer", "format": "uint32", "minimum": 0.0 } } } } serde_with-3.12.0/tests/schemars_0_8/snapshots/set_prevent_duplicates.json000064400000000000000000000005061046102023000251520ustar 00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Test", "type": "object", "required": [ "data" ], "properties": { "data": { "type": "array", "items": { "type": "integer", "format": "uint32", "minimum": 0.0 }, "uniqueItems": true } } } serde_with-3.12.0/tests/schemars_0_8/snapshots/string_with_separator.json000064400000000000000000000003041046102023000250140ustar 00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Test", "type": "object", "required": [ "data" ], "properties": { "data": { "type": "string" } } } serde_with-3.12.0/tests/schemars_0_8/test_std/map.json000064400000000000000000000004401046102023000207600ustar 00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Test", "type": "object", "required": [ "map" ], "properties": { "map": { "type": "object", "additionalProperties": { "type": "integer", "format": "int32" } } } } serde_with-3.12.0/tests/schemars_0_8/test_std/option.json000064400000000000000000000003441046102023000215160ustar 00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Test", "type": "object", "properties": { "optional": { "type": [ "integer", "null" ], "format": "int32" } } } serde_with-3.12.0/tests/schemars_0_8/test_std/set.json000064400000000000000000000004171046102023000210020ustar 00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Test", "type": "object", "required": [ "map" ], "properties": { "map": { "type": "array", "items": { "type": "string" }, "uniqueItems": true } } } serde_with-3.12.0/tests/schemars_0_8/test_std/tuples.json000064400000000000000000000017661046102023000215330ustar 00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Test", "type": "object", "required": [ "tuple0", "tuple1", "tuple2", "tuple3" ], "properties": { "tuple0": { "type": "null" }, "tuple1": { "type": "array", "items": [ { "type": "integer", "format": "int32" } ], "maxItems": 1, "minItems": 1 }, "tuple2": { "type": "array", "items": [ { "type": "integer", "format": "int32" }, { "type": "integer", "format": "int32" } ], "maxItems": 2, "minItems": 2 }, "tuple3": { "type": "array", "items": [ { "type": "integer", "format": "int32" }, { "type": "integer", "format": "int32" }, { "type": "string" } ], "maxItems": 3, "minItems": 3 } } } serde_with-3.12.0/tests/schemars_0_8/test_std/vec.json000064400000000000000000000003641046102023000207650ustar 00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Test", "type": "object", "required": [ "vec" ], "properties": { "vec": { "type": "array", "items": { "type": "string" } } } } serde_with-3.12.0/tests/schemars_0_8/test_std/vec_deque.json000064400000000000000000000004001046102023000221370ustar 00000000000000{ "$schema": "http://json-schema.org/draft-07/schema#", "title": "Test", "type": "object", "required": [ "vec_deque" ], "properties": { "vec_deque": { "type": "array", "items": { "type": "string" } } } } serde_with-3.12.0/tests/schemars_0_8.rs000064400000000000000000000617631046102023000160440ustar 00000000000000//! Test Cases use crate::utils::{check_matches_schema, check_valid_json_schema}; use expect_test::expect_file; use schemars::JsonSchema; use serde::Serialize; use serde_json::json; use serde_with::*; use std::collections::BTreeSet; // This avoids us having to add `#[schemars(crate = "::schemars_0_8")]` all // over the place. We're not testing that and it is inconvenient. extern crate schemars_0_8 as schemars; mod utils; /// Declare a snapshot tests for a struct. /// /// The snapshot files are stored under the `schemars_0_8` folder alongside /// this test file. macro_rules! declare_snapshot_test { {$( $( #[$tattr:meta] )* $test:ident { $( #[$stattr:meta] )* struct $name:ident { $( $( #[ $fattr:meta ] )* $field:ident : $ty:ty ),* $(,)? } } )*} => {$( #[test] $(#[$tattr])* fn $test() { #[serde_as] #[derive(JsonSchema, Serialize)] $( #[$stattr] )* struct $name { $( $( #[$fattr] )* $field: $ty, )* } let schema = schemars::schema_for!($name); let _ = jsonschema::Validator::new( &serde_json::to_value(&schema).expect("generated schema is not valid json"), ) .expect("generated schema is not valid"); let mut schema = serde_json::to_string_pretty(&schema) .expect("schema could not be serialized"); schema.push('\n'); let filename = concat!("./", module_path!(), "::", stringify!($test), ".json") .replace("::", "/"); let expected = expect_file![filename]; expected.assert_eq(&schema); } )*} } #[test] fn schemars_basic() { #[serde_as] #[derive(JsonSchema, Serialize)] #[schemars(crate = "::schemars_0_8")] struct Basic { /// Basic field, no attribute bare_field: u32, /// Field that directly uses DisplayFromStr #[serde_as(as = "DisplayFromStr")] display_from_str: u32, /// Same does not implement JsonSchema directly so this checks that the /// correct schemars attribute was injected. #[serde_as(as = "Same")] same: u32, /// This checks that Same still works when wrapped in a box. #[serde_as(as = "Box")] box_same: Box, /// Same thing, but with a Vec this time. #[serde_as(as = "Vec<_>")] vec_same: Vec, } let schema = schemars::schema_for!(Basic); let mut schema = serde_json::to_string_pretty(&schema).expect("schema could not be serialized"); schema.push('\n'); let expected = expect_file!["./schemars_0_8/schemars_basic.json"]; expected.assert_eq(&schema); } #[test] fn schemars_other_cfg_attrs() { #[serde_as] #[derive(JsonSchema, Serialize)] struct Test { #[serde_as(as = "DisplayFromStr")] #[cfg_attr(any(), arbitrary("some" |weird| syntax::()))] #[cfg_attr(any(), schemars(with = "i32"))] custom: i32, } check_matches_schema::(&json!({ "custom": "23", })); } #[test] fn schemars_custom_with() { #[serde_as] #[derive(JsonSchema, Serialize)] struct Test { #[serde_as(as = "DisplayFromStr")] #[schemars(with = "i32")] custom: i32, #[serde_as(as = "DisplayFromStr")] #[cfg_attr(any(), schemars(with = "i32"))] with_disabled: i32, #[serde_as(as = "DisplayFromStr")] #[cfg_attr(all(), schemars(with = "i32"))] always_enabled: i32, } check_matches_schema::(&json!({ "custom": 3, "with_disabled": "5", "always_enabled": 7, })); } #[test] fn schemars_deserialize_only_bug_735() { #[serde_as] #[derive(JsonSchema, Serialize)] #[schemars(crate = "::schemars_0_8")] struct Basic { /// Basic field, no attribute bare_field: u32, /// Will emit matching schemars attribute #[serde_as(as = "PickFirst<(_, DisplayFromStr)>")] both: u32, /// Can emit schemars with serialize_as, but it will be ignored #[serde_as(serialize_as = "PickFirst<(_, DisplayFromStr)>")] serialize_only: u32, /// schemars doesn't support deserialize_as #[serde_as(deserialize_as = "PickFirst<(_, DisplayFromStr)>")] deserialize_only: u32, /// Can emit schemars with serialize_as, but it will be ignored /// schemars doesn't support deserialize_as #[serde_as( serialize_as = "PickFirst<(_, DisplayFromStr)>", deserialize_as = "PickFirst<(_, DisplayFromStr)>" )] serialize_and_deserialize: u32, } let schema = schemars::schema_for!(Basic); let mut schema = serde_json::to_string_pretty(&schema).expect("schema could not be serialized"); schema.push('\n'); let expected = expect_file!["./schemars_0_8/schemars_deserialize_only_bug_735.json"]; expected.assert_eq(&schema); } #[test] fn schemars_custom_schema_with() { fn custom_int(_: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { use schemars::schema::*; SchemaObject { instance_type: Some(InstanceType::Integer.into()), ..Default::default() } .into() } #[serde_as] #[derive(JsonSchema, Serialize)] struct Test { #[serde_as(as = "DisplayFromStr")] #[schemars(schema_with = "custom_int")] custom: i32, #[serde_as(as = "DisplayFromStr")] #[cfg_attr(any(), schemars(schema_with = "custom_int"))] with_disabled: i32, #[serde_as(as = "DisplayFromStr")] #[cfg_attr(all(), schemars(schema_with = "custom_int"))] always_enabled: i32, } check_matches_schema::(&json!({ "custom": 3, "with_disabled": "5", "always_enabled": 7, })); } mod test_std { use super::*; use std::collections::{BTreeMap, VecDeque}; declare_snapshot_test! { option { struct Test { #[serde_with(as = "Option<_>")] optional: Option, } } vec { struct Test { #[serde_with(as = "Vec<_>")] vec: Vec } } vec_deque { struct Test { #[serde_with(as = "VecDeque<_>")] vec_deque: VecDeque } } map { struct Test { #[serde_with(as = "BTreeMap<_, _>")] map: BTreeMap } } set { struct Test { #[serde_with(as = "BTreeSet<_>")] map: BTreeSet, } } tuples { struct Test { #[serde_with(as = "()")] tuple0: (), #[serde_with(as = "(_ ,)")] tuple1: (i32,), #[serde_with(as = "(_, _)")] tuple2: (i32, i32), #[serde_with(as = "(_, _, _)")] tuple3: (i32, i32, String) } } } } mod snapshots { use super::*; use serde_with::formats::*; #[allow(dead_code)] #[derive(JsonSchema, Serialize)] enum Mappable { A(i32), B(String), C { c: i32, b: Option }, } #[derive(JsonSchema, Serialize)] struct KvMapData { #[serde(rename = "$key$")] key: String, a: u32, b: String, c: f32, d: bool, } #[allow(dead_code, variant_size_differences)] #[derive(JsonSchema, Serialize)] #[serde(tag = "$key$")] enum KvMapEnum { TypeA { a: u32 }, TypeB { b: String }, TypeC { c: bool }, } #[derive(JsonSchema, Serialize)] struct KvMapFlatten { #[serde(flatten)] data: KvMapEnum, extra: bool, } declare_snapshot_test! { bytes { struct Test { #[serde_as(as = "Bytes")] bytes: Vec, } } default_on_null { struct Test { #[serde_as(as = "DefaultOnNull<_>")] data: String, } } string_with_separator { struct Test { #[serde_as(as = "StringWithSeparator")] data: Vec, } } from_into { struct Test { #[serde_as(as = "FromInto")] data: u32, } } map { struct Test { #[serde_as(as = "Map<_, _>")] data: Vec<(String, u32)>, } } map_fixed { struct Test { #[serde_as(as = "Map<_, _>")] data: [(String, u32); 4], } } set_last_value_wins { struct Test { #[serde_as(as = "SetLastValueWins<_>")] data: BTreeSet, } } set_prevent_duplicates { struct Test { #[serde_as(as = "SetPreventDuplicates<_>")] data: BTreeSet, } } duration { struct Test { #[serde_as(as = "DurationSeconds")] seconds: std::time::Duration, #[serde_as(as = "DurationSecondsWithFrac")] frac: std::time::Duration, #[serde_as(as = "DurationSeconds")] flexible_string: std::time::Duration, #[serde_as(as = "DurationSeconds")] seconds_u64_strict: std::time::Duration, #[serde_as(as = "TimestampSeconds")] time_i64: std::time::SystemTime, } } enum_map { struct Test { #[serde_as(as = "EnumMap")] data: Vec, } } key_value_map { struct Test { #[serde_as(as = "KeyValueMap<_>")] data: Vec, } } key_value_map_enum { struct Test { #[serde_as(as = "KeyValueMap<_>")] data: Vec, } } key_value_map_flatten { struct Test { #[serde_as(as = "KeyValueMap<_>")] data: Vec, } } one_or_many_prefer_one { #[serde(transparent)] struct Test { #[serde_as(as = "OneOrMany<_, PreferOne>")] data: Vec, } } pickfirst { #[serde(transparent)] struct Test { #[serde_as(as = "PickFirst<(_, DisplayFromStr)>")] value: u32 } } pickfirst_nested { #[serde(transparent)] struct Test { #[serde_as(as = "OneOrMany>")] optional_value: Vec } } one_or_many_nested { struct Test { #[serde_as(as = "Option>")] optional_many: Option>, } } } } mod derive { use super::*; #[serde_as] #[derive(Serialize)] #[cfg_attr(all(), derive(JsonSchema))] struct Enabled { #[serde_as(as = "DisplayFromStr")] field: u32, } #[allow(dead_code)] #[serde_as] #[derive(Serialize)] #[cfg_attr(any(), derive(JsonSchema))] struct Disabled { // If we are incorrectly adding `#[schemars(with = ...)]` attributes // then we should get an error on this field. #[serde_as(as = "DisplayFromStr")] field: u32, } #[test] fn test_enabled_has_correct_schema() { check_valid_json_schema(&Enabled { field: 77 }); } } mod array { use super::*; #[serde_as] #[derive(JsonSchema, Serialize)] struct FixedArray { #[serde_as(as = "[_; 3]")] array: [u32; 3], } #[test] fn test_serialized_is_valid() { let array = FixedArray { array: [1, 2, 3] }; check_valid_json_schema(&array); } #[test] fn test_valid_json() { let value = json!({ "array": [1, 2, 3] }); check_matches_schema::(&value); } #[test] #[should_panic] fn test_too_short() { check_matches_schema::(&json!({ "array": [1], })); } #[test] #[should_panic] fn test_too_long() { check_matches_schema::(&json!({ "array": [1, 2, 3, 4] })); } #[test] #[should_panic] fn test_wrong_item_type() { check_matches_schema::(&json!({ "array": ["1", "2", "3"] })); } #[test] #[should_panic] fn test_oob_item() { check_matches_schema::(&json!({ "array": [-1, 0x1_0000_0000i64, 32] })); } } mod bool_from_int { use super::*; use serde_with::formats::{Flexible, Strict}; #[serde_as] #[derive(Serialize, JsonSchema)] struct BoolStrict { #[serde_as(as = "BoolFromInt")] value: bool, } #[serde_as] #[derive(Serialize, JsonSchema)] struct BoolFlexible { #[serde_as(as = "BoolFromInt")] value: bool, } #[test] fn test_serialized_strict_is_valid() { check_valid_json_schema(&vec![ BoolStrict { value: true }, BoolStrict { value: false }, ]); } #[test] fn test_serialized_flexible_is_valid() { check_valid_json_schema(&vec![ BoolFlexible { value: true }, BoolFlexible { value: false }, ]); } #[test] #[should_panic] fn strict_out_of_range() { check_matches_schema::(&json!({ "value": 5 })); } #[test] fn flexible_out_of_range() { check_matches_schema::(&json!({ "value": 5 })); } #[test] #[should_panic] fn flexible_wrong_type() { check_matches_schema::(&json!({ "value": "seven" })); } #[test] #[should_panic] fn test_fractional_value_strict() { check_matches_schema::(&json!({ "value": 0.5 })); } #[test] #[should_panic] fn test_fractional_value_flexible() { check_matches_schema::(&json!({ "value": 0.5 })); } } mod bytes_or_string { use super::*; #[serde_as] #[derive(Serialize, JsonSchema)] struct Test { #[serde_as(as = "BytesOrString")] bytes: Vec, } #[test] fn test_serialized_is_valid() { check_valid_json_schema(&Test { bytes: b"test".to_vec(), }); } #[test] fn test_string_valid_json() { check_matches_schema::(&json!({ "bytes": "test string" })); } #[test] fn test_bytes_valid_json() { check_matches_schema::(&json!({ "bytes": [1, 2, 3, 4] })); } #[test] #[should_panic] fn test_int_not_valid_json() { check_matches_schema::(&json!({ "bytes": 5 })); } } mod enum_map { use super::*; #[derive(Serialize, JsonSchema)] struct InnerStruct { c: String, d: f64, } #[derive(Serialize, JsonSchema)] enum Inner { A(i32), B(String), C(InnerStruct), } #[serde_as] #[derive(Serialize, JsonSchema)] #[serde(transparent)] struct Outer(#[serde_as(as = "EnumMap")] Vec); #[test] fn test_serialized_is_valid() { check_valid_json_schema(&Outer(vec![ Inner::A(5), Inner::B("test".into()), Inner::C(InnerStruct { c: "c".into(), d: -34.0, }), ])); } #[test] fn test_matches_expected() { check_matches_schema::(&json!({ "A": 75, "B": "BBBBBB", "C": { "c": "inner C", "d": 777 } })); } #[test] fn test_no_fields_required() { check_matches_schema::(&json!({})); } #[test] #[should_panic] fn test_mixed_up_schemas() { check_matches_schema::(&json!({ "A": "b", "B": 5 })); } #[test] #[should_panic] fn test_invalid_key() { check_matches_schema::(&json!({ "invalid": 4 })); } } mod duration { use super::*; use serde_with::formats::{Flexible, Strict}; use std::time::{Duration, SystemTime}; #[serde_as] #[derive(Serialize, JsonSchema)] struct DurationTest { #[serde_as(as = "DurationSeconds")] strict_u64: Duration, #[serde_as(as = "DurationSeconds")] strict_str: Duration, #[serde_as(as = "DurationSecondsWithFrac")] strict_f64: Duration, #[serde_as(as = "DurationSeconds")] flexible_u64: Duration, #[serde_as(as = "DurationSeconds")] flexible_f64: Duration, #[serde_as(as = "DurationSeconds")] flexible_str: Duration, } #[test] fn test_serialized_is_valid() { check_valid_json_schema(&DurationTest { strict_u64: Duration::from_millis(2500), strict_str: Duration::from_millis(2500), strict_f64: Duration::from_millis(2500), flexible_u64: Duration::from_millis(2500), flexible_f64: Duration::from_millis(2500), flexible_str: Duration::from_millis(2500), }); } #[serde_as] #[derive(Serialize, JsonSchema)] struct FlexibleU64Duration(#[serde_as(as = "DurationSeconds")] Duration); #[serde_as] #[derive(Serialize, JsonSchema)] struct FlexibleStringDuration(#[serde_as(as = "DurationSeconds")] Duration); #[serde_as] #[derive(Serialize, JsonSchema)] struct FlexibleTimestamp(#[serde_as(as = "TimestampSeconds")] SystemTime); #[test] fn test_string_as_flexible_u64() { check_matches_schema::(&json!("32")); } #[test] fn test_integer_as_flexible_u64() { check_matches_schema::(&json!(16)); } #[test] fn test_number_as_flexible_u64() { check_matches_schema::(&json!(54.1)); } #[test] #[should_panic] fn test_negative_as_flexible_u64() { check_matches_schema::(&json!(-5)); } #[test] fn test_string_as_flexible_string() { check_matches_schema::(&json!("32")); } #[test] fn test_integer_as_flexible_string() { check_matches_schema::(&json!(16)); } #[test] fn test_number_as_flexible_string() { check_matches_schema::(&json!(54.1)); } #[test] #[should_panic] fn test_negative_as_flexible_string() { check_matches_schema::(&json!(-5)); } #[test] fn test_negative_as_flexible_timestamp() { check_matches_schema::(&json!(-50000)); } #[test] fn test_negative_string_as_flexible_timestamp() { check_matches_schema::(&json!("-50000")); } } #[test] fn test_borrow_cow() { use std::borrow::Cow; #[serde_as] #[derive(Serialize, JsonSchema)] struct Borrowed<'a> { #[serde_as(as = "BorrowCow")] data: Cow<'a, str>, } check_valid_json_schema(&Borrowed { data: Cow::Borrowed("test"), }); } #[test] fn test_map() { #[serde_as] #[derive(Serialize, JsonSchema)] struct Test { map: [(&'static str, u32); 2], } check_valid_json_schema(&Test { map: [("a", 1), ("b", 2)], }); } #[test] fn test_if_is_human_readable() { #[serde_as] #[derive(Serialize, JsonSchema)] struct Test { #[serde_as(as = "IfIsHumanReadable")] data: i32, } check_valid_json_schema(&Test { data: 5 }); check_matches_schema::(&json!({ "data": "5" })); } #[test] fn test_set_last_value_wins_with_duplicates() { #[serde_as] #[derive(Serialize, JsonSchema)] struct Test { #[serde_as(as = "SetLastValueWins<_>")] set: BTreeSet, } check_matches_schema::(&json!({ "set": [ 1, 2, 3, 1, 4, 2 ] })); } #[test] #[should_panic] fn test_set_prevent_duplicates_with_duplicates() { #[serde_as] #[derive(Serialize, JsonSchema)] struct Test { #[serde_as(as = "SetPreventDuplicates<_>")] set: BTreeSet, } check_matches_schema::(&json!({ "set": [ 1, 1 ] })); } mod key_value_map { use super::*; use std::collections::BTreeMap; #[serde_as] #[derive(Clone, Debug, JsonSchema, Serialize)] #[serde(transparent)] struct KVMap( #[serde_as(as = "KeyValueMap<_>")] #[serde(bound(serialize = "E: Serialize", deserialize = "E: Deserialize<'de>"))] Vec, ); #[derive(Clone, Debug, JsonSchema, Serialize)] #[serde(untagged)] enum UntaggedEnum { A { #[serde(rename = "$key$")] key: String, field1: String, }, B(String, i32), } #[test] fn test_untagged_enum() { let value = KVMap(vec![ UntaggedEnum::A { key: "v1".into(), field1: "field".into(), }, UntaggedEnum::B("v2".into(), 7), ]); check_valid_json_schema(&value); } #[derive(Clone, Debug, JsonSchema, Serialize)] #[serde(untagged)] enum UntaggedNestedEnum { Nested(UntaggedEnum), C { #[serde(rename = "$key$")] key: String, field2: i32, }, } #[test] fn test_untagged_nested_enum() { let value = KVMap(vec![ UntaggedNestedEnum::Nested(UntaggedEnum::A { key: "v1".into(), field1: "field".into(), }), UntaggedNestedEnum::Nested(UntaggedEnum::B("v2".into(), 7)), UntaggedNestedEnum::C { key: "v2".into(), field2: 222, }, ]); check_valid_json_schema(&value); } #[test] fn test_btreemap() { let value = KVMap(vec![ BTreeMap::from_iter([("$key$", "a"), ("value", "b")]), BTreeMap::from_iter([("$key$", "b"), ("value", "d")]), ]); check_valid_json_schema(&value); } } mod one_or_many { use super::*; use serde_with::formats::{PreferMany, PreferOne}; #[serde_as] #[derive(Clone, Debug, JsonSchema, Serialize)] #[serde(transparent)] struct WithPreferOne(#[serde_as(as = "OneOrMany<_, PreferOne>")] Vec); #[serde_as] #[derive(Clone, Debug, JsonSchema, Serialize)] #[serde(transparent)] struct WithPreferMany(#[serde_as(as = "OneOrMany<_, PreferMany>")] Vec); #[test] fn test_prefer_one() { let single = WithPreferOne(vec![7]); let multiple = WithPreferOne(vec![1, 2, 3]); check_valid_json_schema(&single); check_valid_json_schema(&multiple); } #[test] fn test_prefer_one_matches() { check_matches_schema::(&json!(7)); check_matches_schema::(&json!([1, 2, 3])); } #[test] #[should_panic] fn test_prefer_one_no_invalid_type_one() { check_matches_schema::(&json!("test")); } #[test] #[should_panic] fn test_prefer_one_no_invalid_type_many() { check_matches_schema::(&json!(["test", 1])); } #[test] fn test_prefer_many() { let single = WithPreferMany(vec![7]); let multiple = WithPreferMany(vec![1, 2, 3]); check_valid_json_schema(&single); check_valid_json_schema(&multiple); } #[test] #[should_panic] fn test_prefer_many_no_invalid_type_one() { check_matches_schema::(&json!("test")); } #[test] #[should_panic] fn test_prefer_many_no_invalid_type_many() { check_matches_schema::(&json!(["test", 1])); } } #[test] fn test_pickfirst() { #[serde_as] #[derive(JsonSchema, Serialize)] #[serde(transparent)] struct IntOrDisplay(#[serde_as(as = "PickFirst<(_, DisplayFromStr)>")] u32); check_matches_schema::(&json!(7)); check_matches_schema::(&json!("17")); } serde_with-3.12.0/tests/serde_as/collections.rs000064400000000000000000000022211046102023000176540ustar 00000000000000use super::*; use fnv::{FnvHashMap, FnvHashSet}; /// Test that `HashSet`s are also supported with non-default hashers. #[test] fn test_fnv_hashset() { #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S(#[serde_as(as = "FnvHashSet")] FnvHashSet); // Normal is_equal( S([1, 2, 3, 4, 5].iter().copied().collect()), expect![[r#" [ "5", "4", "1", "3", "2" ]"#]], ); is_equal(S(FnvHashSet::default()), expect![[r#"[]"#]]); } /// Test that `HashSet`s are also supported with non-default hashers. #[test] fn test_fnv_hashmap() { #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S(#[serde_as(as = "FnvHashMap")] FnvHashMap); // Normal is_equal( S([(1, 1), (3, 3), (111, 111)].iter().copied().collect()), expect![[r#" { "1": "1", "3": "3", "111": "111" }"#]], ); is_equal(S(FnvHashMap::default()), expect![[r#"{}"#]]); } serde_with-3.12.0/tests/serde_as/default_on.rs000064400000000000000000000071061046102023000174650ustar 00000000000000use super::*; use serde_with::{DefaultOnError, DefaultOnNull}; #[test] fn test_default_on_error() { #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S(#[serde_as(as = "DefaultOnError")] u32); // Normal is_equal(S(123), expect![[r#""123""#]]); is_equal(S(0), expect![[r#""0""#]]); // Error cases check_deserialization(S(0), r#""""#); check_deserialization(S(0), r#""12+3""#); check_deserialization(S(0), r#""abc""#); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S2(#[serde_as(as = "DefaultOnError>")] Vec); // Normal is_equal( S2(vec![1, 2, 3]), expect![[r#" [ "1", "2", "3" ]"#]], ); is_equal(S2(vec![]), expect![[r#"[]"#]]); // Error cases check_deserialization(S2(vec![]), r#"2"#); check_deserialization(S2(vec![]), r#""not_a_list""#); check_deserialization(S2(vec![]), r#"{}"#); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct Struct2 { #[serde_as(as = "DefaultOnError>")] value: Vec, } check_deserialization(Struct2 { value: vec![] }, r#"{"value":}"#); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S3(#[serde_as(as = "Vec>")] Vec); // Normal is_equal( S3(vec![1, 2, 3]), expect![[r#" [ "1", "2", "3" ]"#]], ); is_equal(S3(vec![]), expect![[r#"[]"#]]); // Error cases check_deserialization(S3(vec![0, 3, 0]), r#"[2,"3",4]"#); check_deserialization(S3(vec![0, 0]), r#"["AA",5]"#); } #[test] fn test_default_on_null() { #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S(#[serde_as(as = "DefaultOnNull")] u32); // Normal is_equal(S(123), expect![[r#""123""#]]); is_equal(S(0), expect![[r#""0""#]]); // Null case check_deserialization(S(0), r#"null"#); // Error cases check_error_deserialization::( r#""12+3""#, expect![[r#"invalid digit found in string at line 1 column 6"#]], ); check_error_deserialization::( r#""abc""#, expect![[r#"invalid digit found in string at line 1 column 5"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S2(#[serde_as(as = "Vec")] Vec); // Normal is_equal( S2(vec![1, 2, 0, 3]), expect![[r#" [ 1, 2, 0, 3 ]"#]], ); is_equal(S2(vec![]), expect![[r#"[]"#]]); // Null cases check_deserialization(S2(vec![1, 0, 2]), r#"[1, null, 2]"#); check_error_deserialization::( r#"["not_a_number"]"#, expect![[r#"invalid type: string "not_a_number", expected u32 at line 1 column 15"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S3(#[serde_as(as = "Vec>")] Vec); // Normal is_equal( S3(vec![1, 2, 3]), expect![[r#" [ "1", "2", "3" ]"#]], ); // Null case check_deserialization(S3(vec![0, 3, 0]), r#"[null,"3",null]"#); check_error_deserialization::( r#"[null,3,null]"#, expect![[r#"invalid type: integer `3`, expected a string at line 1 column 7"#]], ); } serde_with-3.12.0/tests/serde_as/enum_map.rs000064400000000000000000000332561046102023000171530ustar 00000000000000use super::*; use core::{fmt::Write as _, str::FromStr}; use serde_test::Configure; use serde_with::EnumMap; use std::net::IpAddr; fn bytes_debug_readable(bytes: &[u8]) -> String { let mut result = String::with_capacity(bytes.len() * 2); for &byte in bytes { match byte { non_printable if !(0x20..0x7f).contains(&non_printable) => { result.write_fmt(format_args!("\\x{byte:02x}")).unwrap(); } b'\\' => result.push_str("\\\\"), _ => { result.push(char::from(byte)); } } } result } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] enum EnumValue { Int(i32), String(String), Unit, Tuple(i32, String, bool), Struct { a: i32, b: String, c: bool, }, Ip(IpAddr, IpAddr), #[serde(rename = "$value")] Extra(serde_json::Value), } #[serde_as] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] struct VecEnumValues { #[serde_as(as = "EnumMap")] vec: Vec, } #[test] fn json_round_trip() { let values = VecEnumValues { vec: vec![ EnumValue::Int(123), EnumValue::String("FooBar".to_string()), EnumValue::Int(456), EnumValue::String("XXX".to_string()), EnumValue::Unit, EnumValue::Tuple(1, "Middle".to_string(), false), EnumValue::Struct { a: 666, b: "BBB".to_string(), c: true, }, ], }; let json = serde_json::to_string_pretty(&values).unwrap(); expect_test::expect![[r#" { "vec": { "Int": 123, "String": "FooBar", "Int": 456, "String": "XXX", "Unit": null, "Tuple": [ 1, "Middle", false ], "Struct": { "a": 666, "b": "BBB", "c": true } } }"#]] .assert_eq(&json); let deser_values: VecEnumValues = serde_json::from_str(&json).unwrap(); assert_eq!(values, deser_values); } #[test] fn ron_serialize() { let values = VecEnumValues { vec: vec![ EnumValue::Int(123), EnumValue::String("FooBar".to_string()), EnumValue::Int(456), EnumValue::String("XXX".to_string()), EnumValue::Unit, EnumValue::Tuple(1, "Middle".to_string(), false), EnumValue::Struct { a: 666, b: "BBB".to_string(), c: true, }, ], }; let pretty_config = ron::ser::PrettyConfig::new().new_line("\n".into()); let ron = ron::ser::to_string_pretty(&values, pretty_config).unwrap(); expect_test::expect![[r#" ( vec: { "Int": 123, "String": "FooBar", "Int": 456, "String": "XXX", "Unit": (), "Tuple": (1, "Middle", false), "Struct": ( a: 666, b: "BBB", c: true, ), }, )"#]] .assert_eq(&ron); // TODO deserializing a Strings as an Identifier seems unsupported let deser_values: ron::Value = ron::de::from_str(&ron).unwrap(); expect_test::expect![[r#" Map( Map( { String( "vec", ): Map( Map( { String( "Int", ): Number( Integer( 456, ), ), String( "String", ): String( "XXX", ), String( "Struct", ): Map( Map( { String( "a", ): Number( Integer( 666, ), ), String( "b", ): String( "BBB", ), String( "c", ): Bool( true, ), }, ), ), String( "Tuple", ): Seq( [ Number( Integer( 1, ), ), String( "Middle", ), Bool( false, ), ], ), String( "Unit", ): Unit, }, ), ), }, ), ) "#]] .assert_debug_eq(&deser_values); } #[test] fn xml_round_trip() { let values = VecEnumValues { vec: vec![ EnumValue::Int(123), EnumValue::String("FooBar".to_string()), EnumValue::Int(456), EnumValue::String("XXX".to_string()), EnumValue::Unit, // serialize_tuple and variants are not supported by XML // EnumValue::Tuple(1, "Middle".to_string(), false), // Cannot be deserialized. It serializes to: // 666BBBtrue // EnumValue::Struct { // a: 666, // b: "BBB".to_string(), // c: true, // }, ], }; let xml = serde_xml_rs::to_string(&values).unwrap(); expect_test::expect![[r#"123FooBar456XXX"#]] .assert_eq(&xml); let deser_values: VecEnumValues = serde_xml_rs::from_str(&xml).unwrap(); assert_eq!(values, deser_values); } #[test] fn serde_test_round_trip() { let values = VecEnumValues { vec: vec![ EnumValue::Int(123), EnumValue::String("FooBar".to_string()), EnumValue::Int(456), EnumValue::String("XXX".to_string()), EnumValue::Unit, EnumValue::Tuple(1, "Middle".to_string(), false), EnumValue::Struct { a: 666, b: "BBB".to_string(), c: true, }, ], }; use serde_test::Token::*; serde_test::assert_tokens( &values.readable(), &[ Struct { name: "VecEnumValues", len: 1, }, Str("vec"), Map { len: Option::Some(7), }, Str("Int"), I32(123), Str("String"), Str("FooBar"), Str("Int"), I32(456), Str("String"), Str("XXX"), Str("Unit"), Unit, Str("Tuple"), TupleStruct { name: "EnumValue", len: 3, }, I32(1), Str("Middle"), Bool(false), TupleStructEnd, Str("Struct"), Struct { name: "EnumValue", len: 3, }, Str("a"), I32(666), Str("b"), Str("BBB"), Str("c"), Bool(true), StructEnd, MapEnd, StructEnd, ], ); } #[test] fn serde_test_round_trip_human_readable() { let values = VecEnumValues { vec: vec![EnumValue::Ip( IpAddr::from_str("127.0.0.1").unwrap(), IpAddr::from_str("::7777:dead:beef").unwrap(), )], }; use serde_test::Token::*; serde_test::assert_tokens( &values.clone().readable(), &[ Struct { name: "VecEnumValues", len: 1, }, Str("vec"), Map { len: Option::Some(1), }, Str("Ip"), TupleStruct { name: "EnumValue", len: 2, }, Str("127.0.0.1"), Str("::7777:dead:beef"), TupleStructEnd, MapEnd, StructEnd, ], ); serde_test::assert_tokens( &values.compact(), &[ Struct { name: "VecEnumValues", len: 1, }, Str("vec"), Map { len: Option::Some(1), }, Str("Ip"), TupleStruct { name: "EnumValue", len: 2, }, NewtypeVariant { name: "IpAddr", variant: "V4", }, Tuple { len: 4 }, U8(127), U8(0), U8(0), U8(1), TupleEnd, NewtypeVariant { name: "IpAddr", variant: "V6", }, Tuple { len: 16 }, U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0x77), U8(0x77), U8(0xde), U8(0xad), U8(0xbe), U8(0xef), TupleEnd, TupleStructEnd, MapEnd, StructEnd, ], ); } // Bincode does not support Deserializer::deserialize_identifier // https://github.com/bincode-org/bincode/blob/e0ac3245162ba668ba04591897dd88ff5b3096b8/src/de/mod.rs#L442 #[test] fn rmp_round_trip() { let values = VecEnumValues { vec: vec![ EnumValue::Int(123), EnumValue::String("FooBar".to_string()), EnumValue::Int(456), EnumValue::String("XXX".to_string()), EnumValue::Unit, EnumValue::Tuple(1, "Middle".to_string(), false), EnumValue::Struct { a: 666, b: "BBB".to_string(), c: true, }, EnumValue::Ip( IpAddr::from_str("127.0.0.1").unwrap(), IpAddr::from_str("::7777:dead:beef").unwrap(), ), ], }; let rmp = rmp_serde::to_vec(&values).unwrap(); expect_test::expect![[r"\x91\x88\xa3Int{\xa6String\xa6FooBar\xa3Int\xcd\x01\xc8\xa6String\xa3XXX\xa4Unit\xc0\xa5Tuple\x93\x01\xa6Middle\xc2\xa6Struct\x93\xcd\x02\x9a\xa3BBB\xc3\xa2Ip\x92\x81\xa2V4\x94\x7f\x00\x00\x01\x81\xa2V6\xdc\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00ww\xcc\xde\xcc\xad\xcc\xbe\xcc\xef"]] .assert_eq(&bytes_debug_readable(&rmp)); let deser_values: VecEnumValues = rmp_serde::from_read(&*rmp).unwrap(); assert_eq!(values, deser_values); } #[test] fn yaml_round_trip() { // Duplicate enum variants do not work with YAML let values = VecEnumValues { vec: vec![ EnumValue::Int(123), EnumValue::String("FooBar".to_string()), // EnumValue::Int(456), // EnumValue::String("XXX".to_string()), EnumValue::Unit, EnumValue::Tuple(1, "Middle".to_string(), false), EnumValue::Struct { a: 666, b: "BBB".to_string(), c: true, }, ], }; let yaml = serde_yaml::to_string(&values).unwrap(); expect_test::expect![[r#" vec: Int: 123 String: FooBar Unit: null Tuple: - 1 - Middle - false Struct: a: 666 b: BBB c: true "#]] .assert_eq(&yaml); let deser_values: VecEnumValues = serde_yaml::from_str(&yaml).unwrap(); assert_eq!(values, deser_values); } serde_with-3.12.0/tests/serde_as/frominto.rs000064400000000000000000000121441046102023000172000ustar 00000000000000use super::*; use serde_with::{FromInto, TryFromInto}; #[derive(Clone, Debug, PartialEq)] enum IntoSerializable { A, B, C, } impl From for String { fn from(value: IntoSerializable) -> Self { match value { IntoSerializable::A => "String A", IntoSerializable::B => "Some other value", IntoSerializable::C => "Looks like 123", } .to_string() } } #[derive(Debug, PartialEq)] enum FromDeserializable { Zero, Odd(u32), Even(u32), } impl From for FromDeserializable { fn from(value: u32) -> Self { match value { 0 => FromDeserializable::Zero, e if e % 2 == 0 => FromDeserializable::Even(e), o => FromDeserializable::Odd(o), } } } #[derive(Clone, Debug, PartialEq)] enum LikeBool { Trueish, Falseisch, } impl From for LikeBool { fn from(b: bool) -> Self { if b { LikeBool::Trueish } else { LikeBool::Falseisch } } } impl From for bool { fn from(lb: LikeBool) -> Self { match lb { LikeBool::Trueish => true, LikeBool::Falseisch => false, } } } #[test] fn test_frominto_ser() { #[serde_as] #[derive(Debug, PartialEq, Serialize)] struct S(#[serde_as(serialize_as = "FromInto")] IntoSerializable); check_serialization(S(IntoSerializable::A), expect![[r#""String A""#]]); check_serialization(S(IntoSerializable::B), expect![[r#""Some other value""#]]); check_serialization(S(IntoSerializable::C), expect![[r#""Looks like 123""#]]); } #[test] fn test_tryfrominto_ser() { #[serde_as] #[derive(Debug, PartialEq, Serialize)] struct S(#[serde_as(serialize_as = "TryFromInto")] IntoSerializable); check_serialization(S(IntoSerializable::A), expect![[r#""String A""#]]); check_serialization(S(IntoSerializable::B), expect![[r#""Some other value""#]]); check_serialization(S(IntoSerializable::C), expect![[r#""Looks like 123""#]]); } #[test] fn test_frominto_de() { #[serde_as] #[derive(Debug, PartialEq, Deserialize)] struct S(#[serde_as(deserialize_as = "FromInto")] FromDeserializable); check_deserialization(S(FromDeserializable::Zero), "0"); check_deserialization(S(FromDeserializable::Odd(1)), "1"); check_deserialization(S(FromDeserializable::Odd(101)), "101"); check_deserialization(S(FromDeserializable::Even(2)), "2"); check_deserialization(S(FromDeserializable::Even(202)), "202"); } #[test] fn test_tryfrominto_de() { #[serde_as] #[derive(Debug, PartialEq, Deserialize)] struct S(#[serde_as(deserialize_as = "TryFromInto")] FromDeserializable); check_deserialization(S(FromDeserializable::Zero), "0"); check_deserialization(S(FromDeserializable::Odd(1)), "1"); check_deserialization(S(FromDeserializable::Odd(101)), "101"); check_deserialization(S(FromDeserializable::Even(2)), "2"); check_deserialization(S(FromDeserializable::Even(202)), "202"); } #[test] fn test_frominto_de_and_ser() { #[serde_as] #[derive(Debug, PartialEq, Deserialize, Serialize)] struct S(#[serde_as(as = "FromInto")] LikeBool); is_equal(S(LikeBool::Trueish), expect![[r#"true"#]]); is_equal(S(LikeBool::Falseisch), expect![[r#"false"#]]); } #[test] fn test_tryfrominto_de_and_ser() { #[serde_as] #[derive(Debug, PartialEq, Deserialize, Serialize)] struct S(#[serde_as(as = "TryFromInto")] LikeBool); is_equal(S(LikeBool::Trueish), expect![[r#"true"#]]); is_equal(S(LikeBool::Falseisch), expect![[r#"false"#]]); } #[derive(Clone, Debug, PartialEq)] enum TryIntoSerializable { Works, Fails, } impl TryFrom for String { type Error = &'static str; fn try_from(value: TryIntoSerializable) -> Result { match value { TryIntoSerializable::Works => Ok("Works".to_string()), TryIntoSerializable::Fails => Err("Fails cannot be turned into String"), } } } #[derive(Debug, PartialEq)] enum TryFromDeserializable { Zero, } impl TryFrom for TryFromDeserializable { type Error = &'static str; fn try_from(value: u32) -> Result { match value { 0 => Ok(TryFromDeserializable::Zero), _ => Err("Number is not zero"), } } } #[test] fn test_tryfrominto_ser_with_error() { #[serde_as] #[derive(Debug, PartialEq, Serialize)] struct S(#[serde_as(serialize_as = "TryFromInto")] TryIntoSerializable); check_serialization(S(TryIntoSerializable::Works), expect![[r#""Works""#]]); check_error_serialization( S(TryIntoSerializable::Fails), expect![[r#"Fails cannot be turned into String"#]], ); } #[test] fn test_tryfrominto_de_with_error() { #[serde_as] #[derive(Debug, PartialEq, Deserialize)] struct S(#[serde_as(deserialize_as = "TryFromInto")] TryFromDeserializable); check_deserialization(S(TryFromDeserializable::Zero), "0"); check_error_deserialization::("1", expect![[r#"Number is not zero"#]]); } serde_with-3.12.0/tests/serde_as/fromintoref.rs000064400000000000000000000122211046102023000176710ustar 00000000000000use super::*; use serde_with::{FromIntoRef, TryFromIntoRef}; #[derive(Debug, PartialEq)] enum IntoSerializable { A, B, C, } impl<'a> From<&'a IntoSerializable> for String { fn from(value: &'a IntoSerializable) -> Self { match value { IntoSerializable::A => "String A", IntoSerializable::B => "Some other value", IntoSerializable::C => "Looks like 123", } .to_string() } } #[derive(Debug, PartialEq)] enum FromDeserializable { Zero, Odd(u32), Even(u32), } impl From for FromDeserializable { fn from(value: u32) -> Self { match value { 0 => FromDeserializable::Zero, e if e % 2 == 0 => FromDeserializable::Even(e), o => FromDeserializable::Odd(o), } } } #[derive(Debug, PartialEq)] enum LikeBool { Trueish, Falseisch, } impl From for LikeBool { fn from(b: bool) -> Self { if b { LikeBool::Trueish } else { LikeBool::Falseisch } } } impl<'a> From<&'a LikeBool> for bool { fn from(lb: &'a LikeBool) -> Self { match lb { LikeBool::Trueish => true, LikeBool::Falseisch => false, } } } #[test] fn test_frominto_ser() { #[serde_as] #[derive(Debug, PartialEq, Serialize)] struct S(#[serde_as(serialize_as = "FromIntoRef")] IntoSerializable); check_serialization(S(IntoSerializable::A), expect![[r#""String A""#]]); check_serialization(S(IntoSerializable::B), expect![[r#""Some other value""#]]); check_serialization(S(IntoSerializable::C), expect![[r#""Looks like 123""#]]); } #[test] fn test_tryfrominto_ser() { #[serde_as] #[derive(Debug, PartialEq, Serialize)] struct S(#[serde_as(serialize_as = "TryFromIntoRef")] IntoSerializable); check_serialization(S(IntoSerializable::A), expect![[r#""String A""#]]); check_serialization(S(IntoSerializable::B), expect![[r#""Some other value""#]]); check_serialization(S(IntoSerializable::C), expect![[r#""Looks like 123""#]]); } #[test] fn test_frominto_de() { #[serde_as] #[derive(Debug, PartialEq, Deserialize)] struct S(#[serde_as(deserialize_as = "FromIntoRef")] FromDeserializable); check_deserialization(S(FromDeserializable::Zero), "0"); check_deserialization(S(FromDeserializable::Odd(1)), "1"); check_deserialization(S(FromDeserializable::Odd(101)), "101"); check_deserialization(S(FromDeserializable::Even(2)), "2"); check_deserialization(S(FromDeserializable::Even(202)), "202"); } #[test] fn test_tryfrominto_de() { #[serde_as] #[derive(Debug, PartialEq, Deserialize)] struct S(#[serde_as(deserialize_as = "TryFromIntoRef")] FromDeserializable); check_deserialization(S(FromDeserializable::Zero), "0"); check_deserialization(S(FromDeserializable::Odd(1)), "1"); check_deserialization(S(FromDeserializable::Odd(101)), "101"); check_deserialization(S(FromDeserializable::Even(2)), "2"); check_deserialization(S(FromDeserializable::Even(202)), "202"); } #[test] fn test_frominto_de_and_ser() { #[serde_as] #[derive(Debug, PartialEq, Deserialize, Serialize)] struct S(#[serde_as(as = "FromIntoRef")] LikeBool); is_equal(S(LikeBool::Trueish), expect![[r#"true"#]]); is_equal(S(LikeBool::Falseisch), expect![[r#"false"#]]); } #[test] fn test_tryfrominto_de_and_ser() { #[serde_as] #[derive(Debug, PartialEq, Deserialize, Serialize)] struct S(#[serde_as(as = "TryFromIntoRef")] LikeBool); is_equal(S(LikeBool::Trueish), expect![[r#"true"#]]); is_equal(S(LikeBool::Falseisch), expect![[r#"false"#]]); } #[derive(Debug, PartialEq)] enum TryIntoSerializable { Works, Fails, } impl<'a> TryFrom<&'a TryIntoSerializable> for String { type Error = &'static str; fn try_from(value: &'a TryIntoSerializable) -> Result { match value { TryIntoSerializable::Works => Ok("Works".to_string()), TryIntoSerializable::Fails => Err("Fails cannot be turned into String"), } } } #[derive(Debug, PartialEq)] enum TryFromDeserializable { Zero, } impl TryFrom for TryFromDeserializable { type Error = &'static str; fn try_from(value: u32) -> Result { match value { 0 => Ok(TryFromDeserializable::Zero), _ => Err("Number is not zero"), } } } #[test] fn test_tryfrominto_ser_with_error() { #[serde_as] #[derive(Debug, PartialEq, Serialize)] struct S(#[serde_as(serialize_as = "TryFromIntoRef")] TryIntoSerializable); check_serialization(S(TryIntoSerializable::Works), expect![[r#""Works""#]]); check_error_serialization( S(TryIntoSerializable::Fails), expect![[r#"Fails cannot be turned into String"#]], ); } #[test] fn test_tryfrominto_de_with_error() { #[serde_as] #[derive(Debug, PartialEq, Deserialize)] struct S(#[serde_as(deserialize_as = "TryFromIntoRef")] TryFromDeserializable); check_deserialization(S(TryFromDeserializable::Zero), "0"); check_error_deserialization::("1", expect![[r#"Number is not zero"#]]); } serde_with-3.12.0/tests/serde_as/key_value_map.rs000064400000000000000000000202031046102023000201570ustar 00000000000000use super::*; use core::str::FromStr; use serde_test::Configure; use serde_with::{serde_as, KeyValueMap}; use std::net::IpAddr; #[serde_as] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(transparent)] struct KVMap { #[serde_as(as = "KeyValueMap<_>")] #[serde(bound(serialize = "E: Serialize", deserialize = "E: Deserialize<'de>"))] foo: Vec, } #[test] fn test_kvmap_struct_json() { #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] struct Struct { #[serde(rename = "$key$")] key: String, bar: String, baz: i32, } let kvmap = KVMap { foo: vec![ Struct { key: "a".to_string(), bar: "b".to_string(), baz: 1, }, Struct { key: "c".to_string(), bar: "d".to_string(), baz: 2, }, ], }; is_equal( kvmap, expect![[r#" { "a": { "bar": "b", "baz": 1 }, "c": { "bar": "d", "baz": 2 } }"#]], ); } #[test] fn test_kvmap_tuple_struct_json() { #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] struct TupleStruct(String, String, i32); let kvmap = KVMap { foo: vec![ TupleStruct("a".to_string(), "b".to_string(), 1), TupleStruct("c".to_string(), "d".to_string(), 2), ], }; is_equal( kvmap, expect![[r#" { "a": [ "b", 1 ], "c": [ "d", 2 ] }"#]], ); } #[test] fn test_kvmap_tuple_json() { #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(transparent)] struct Tuple((String, String, i32)); let kvmap = KVMap { foo: vec![ Tuple(("a".to_string(), "b".to_string(), 1)), Tuple(("c".to_string(), "d".to_string(), 2)), ], }; is_equal( kvmap, expect![[r#" { "a": [ "b", 1 ], "c": [ "d", 2 ] }"#]], ); } #[test] fn test_kvmap_map_json() { #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(transparent)] struct Map(BTreeMap); let kvmap = KVMap { foo: vec![ Map(BTreeMap::from([ ("$key$".to_string(), "a".to_string()), ("bar".to_string(), "b".to_string()), ("baz".to_string(), "1".to_string()), ])), Map(BTreeMap::from([ ("$key$".to_string(), "c".to_string()), ("bar".to_string(), "d".to_string()), ("baz".to_string(), "2".to_string()), ])), ], }; is_equal( kvmap, expect![[r#" { "a": { "bar": "b", "baz": "1" }, "c": { "bar": "d", "baz": "2" } }"#]], ); } #[test] fn test_kvmap_seq_json() { #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(transparent)] struct Seq(Vec); let kvmap = KVMap { foo: vec![ Seq(vec!["a".to_string(), "b".to_string(), "1".to_string()]), Seq(vec!["c".to_string(), "d".to_string(), "2".to_string()]), ], }; is_equal( kvmap, expect![[r#" { "a": [ "b", "1" ], "c": [ "d", "2" ] }"#]], ); } #[test] fn test_kvmap_complex_key_serde_test() { #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] struct ComplexKey { #[serde(rename = "$key$")] net: (IpAddr, u8), bar: String, baz: i32, } let kvmap = KVMap { foo: vec![ ComplexKey { net: (IpAddr::from_str("127.0.0.0").unwrap(), 24), bar: "b".to_string(), baz: 1, }, ComplexKey { net: (IpAddr::from_str("::7777:dead:beef").unwrap(), 48), bar: "d".to_string(), baz: 2, }, ], }; { use serde_test::Token::*; serde_test::assert_tokens( &kvmap.clone().readable(), &[ Map { len: Option::Some(2), }, Tuple { len: 2 }, Str("127.0.0.0"), U8(24), TupleEnd, Struct { name: "ComplexKey", len: 2, }, Str("bar"), Str("b"), Str("baz"), I32(1), StructEnd, Tuple { len: 2 }, Str("::7777:dead:beef"), U8(48), TupleEnd, Struct { name: "ComplexKey", len: 2, }, Str("bar"), Str("d"), Str("baz"), I32(2), StructEnd, MapEnd, ], ); serde_test::assert_tokens( &kvmap.compact(), &[ Map { len: Option::Some(2), }, Tuple { len: 2 }, NewtypeVariant { name: "IpAddr", variant: "V4", }, Tuple { len: 4 }, U8(127), U8(0), U8(0), U8(0), TupleEnd, U8(24), TupleEnd, Struct { name: "ComplexKey", len: 2, }, Str("bar"), Str("b"), Str("baz"), I32(1), StructEnd, Tuple { len: 2 }, NewtypeVariant { name: "IpAddr", variant: "V6", }, Tuple { len: 16 }, U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0x77), U8(0x77), U8(0xde), U8(0xad), U8(0xbe), U8(0xef), TupleEnd, U8(48), TupleEnd, Struct { name: "ComplexKey", len: 2, }, Str("bar"), Str("d"), Str("baz"), I32(2), StructEnd, MapEnd, ], ); } } #[test] fn test_kvmap_complex_key_yaml() { #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] struct ComplexKey { #[serde(rename = "$key$")] net: (IpAddr, u8), bar: String, baz: i32, } let kvmap = KVMap { foo: vec![ ComplexKey { net: (IpAddr::from_str("127.0.0.0").unwrap(), 24), bar: "b".to_string(), baz: 1, }, ComplexKey { net: (IpAddr::from_str("::7777:dead:beef").unwrap(), 48), bar: "d".to_string(), baz: 2, }, ], }; let yaml = serde_yaml::to_string(&kvmap).unwrap(); expect_test::expect![[r#" ? - 127.0.0.0 - 24 : bar: b baz: 1 ? - ::7777:dead:beef - 48 : bar: d baz: 2 "#]] .assert_eq(&yaml); let deser_values = serde_yaml::from_str(&yaml).unwrap(); assert_eq!(kvmap, deser_values); } serde_with-3.12.0/tests/serde_as/lib.rs000064400000000000000000001215571046102023000161220ustar 00000000000000//! Test Cases extern crate alloc; mod collections; mod default_on; mod enum_map; mod frominto; mod fromintoref; mod key_value_map; mod map_tuple_list; mod pickfirst; mod serde_as_macro; mod serde_conv; mod time; #[path = "../utils.rs"] mod utils; use crate::utils::*; use alloc::{ collections::{BTreeMap, BTreeSet, LinkedList, VecDeque}, rc::{Rc, Weak as RcWeak}, sync::{Arc, Weak as ArcWeak}, }; use core::{ cell::{Cell, RefCell}, ops::Bound, pin::Pin, }; use expect_test::expect; use serde::{Deserialize, Serialize}; use serde_with::{ formats::{CommaSeparator, Flexible, Strict}, serde_as, BoolFromInt, BytesOrString, DisplayFromStr, IfIsHumanReadable, Map, NoneAsEmptyString, OneOrMany, Same, Seq, StringWithSeparator, }; use std::{ collections::HashMap, sync::{Mutex, RwLock}, }; #[test] fn test_basic_wrappers() { #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct SBox(#[serde_as(as = "Box")] Box); is_equal(SBox(Box::new(123)), expect![[r#""123""#]]); // Deserialization in generally is not possible, only for unpin types #[serde_as] #[derive(Debug, Serialize, PartialEq)] struct SPin<'a>(#[serde_as(as = "Pin<&DisplayFromStr>")] Pin<&'a u32>); let tmp = 123; check_serialization(SPin(Pin::new(&tmp)), expect![[r#""123""#]]); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct SPinBox(#[serde_as(as = "Pin>")] Pin>); is_equal(SPinBox(Box::pin(123)), expect![[r#""123""#]]); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct SRc(#[serde_as(as = "Rc")] Rc); is_equal(SRc(Rc::new(123)), expect![[r#""123""#]]); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct SPinRc(#[serde_as(as = "Pin>")] Pin>); is_equal(SPinRc(Rc::pin(123)), expect![[r#""123""#]]); #[serde_as] #[derive(Debug, Serialize, Deserialize)] struct SRcWeak(#[serde_as(as = "RcWeak")] RcWeak); check_serialization(SRcWeak(RcWeak::new()), expect![[r#"null"#]]); let s: SRcWeak = serde_json::from_str("null").unwrap(); assert!(s.0.upgrade().is_none()); let s: SRcWeak = serde_json::from_str("\"123\"").unwrap(); assert!(s.0.upgrade().is_none()); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct SArc(#[serde_as(as = "Arc")] Arc); is_equal(SArc(Arc::new(123)), expect![[r#""123""#]]); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct SPinArc(#[serde_as(as = "Pin>")] Pin>); is_equal(SPinArc(Arc::pin(123)), expect![[r#""123""#]]); #[serde_as] #[derive(Debug, Serialize, Deserialize)] struct SArcWeak(#[serde_as(as = "ArcWeak")] ArcWeak); check_serialization(SArcWeak(ArcWeak::new()), expect![[r#"null"#]]); let s: SArcWeak = serde_json::from_str("null").unwrap(); assert!(s.0.upgrade().is_none()); let s: SArcWeak = serde_json::from_str("\"123\"").unwrap(); assert!(s.0.upgrade().is_none()); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct SCell(#[serde_as(as = "Cell")] Cell); is_equal(SCell(Cell::new(123)), expect![[r#""123""#]]); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct SRefCell(#[serde_as(as = "RefCell")] RefCell); is_equal(SRefCell(RefCell::new(123)), expect![[r#""123""#]]); #[serde_as] #[derive(Debug, Serialize, Deserialize)] struct SMutex(#[serde_as(as = "Mutex")] Mutex); check_serialization(SMutex(Mutex::new(123)), expect![[r#""123""#]]); let s: SMutex = serde_json::from_str("\"123\"").unwrap(); assert_eq!(*s.0.lock().unwrap(), 123); #[serde_as] #[derive(Debug, Serialize, Deserialize)] struct SRwLock(#[serde_as(as = "RwLock")] RwLock); let expected = expect![[r#""123""#]]; check_serialization(SRwLock(RwLock::new(123)), expected); let s: SRwLock = serde_json::from_str("\"123\"").unwrap(); assert_eq!(*s.0.read().unwrap(), 123); } #[test] fn test_option() { #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S(#[serde_as(as = "_")] Option); is_equal(S(None), expect![[r#"null"#]]); is_equal(S(Some(9)), expect![[r#"9"#]]); check_error_deserialization::( r#"{}"#, expect![[r#"invalid type: map, expected u32 at line 1 column 0"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct Struct { #[serde_as(as = "_")] value: Option, } check_error_deserialization::( r#"{}"#, expect![[r#"missing field `value` at line 1 column 2"#]], ); } #[test] fn test_bound() { #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S(#[serde_as(as = "Bound")] Bound); is_equal(S(Bound::Unbounded), expect![[r#""Unbounded""#]]); is_equal( S(Bound::Included(42)), expect![[r#" { "Included": "42" }"#]], ); is_equal( S(Bound::Excluded(42)), expect![[r#" { "Excluded": "42" }"#]], ); check_error_deserialization::(r#"{}"#, expect![[r#"expected value at line 1 column 2"#]]); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct Struct { #[serde_as(as = "Bound")] value: Bound, } check_error_deserialization::( r#"{}"#, expect![[r#"missing field `value` at line 1 column 2"#]], ); } #[test] fn test_result() { #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S( #[serde_as(as = "Result, DisplayFromStr>")] Result, u32>, ); is_equal( S(Ok(vec![1, 2, 3])), expect![[r#" { "Ok": "1,2,3" }"#]], ); is_equal( S(Err(9)), expect![[r#" { "Err": "9" }"#]], ); check_error_deserialization::(r#"{}"#, expect![[r#"expected value at line 1 column 2"#]]); } #[test] fn test_display_fromstr() { #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S(#[serde_as(as = "DisplayFromStr")] u32); is_equal(S(123), expect![[r#""123""#]]); } #[test] fn test_if_is_human_readable() { #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S(#[serde_as(as = "IfIsHumanReadable")] u32); let ser_json = serde_json::to_string(&S(123)).unwrap(); assert_eq!(ser_json, r#""123""#); let ser_rmp = rmp_serde::to_vec(&S(123)).unwrap(); assert_eq!(ser_rmp, vec![123]); let de_json: S = serde_json::from_str(r#""123""#).unwrap(); assert_eq!(S(123), de_json); let de_rmp: S = rmp_serde::from_read(&*vec![123]).unwrap(); assert_eq!(S(123), de_rmp); } #[test] fn test_tuples() { use std::net::IpAddr; let ip = "1.2.3.4".parse().unwrap(); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S1(#[serde_as(as = "(DisplayFromStr,)")] (u32,)); is_equal( S1((1,)), expect![[r#" [ "1" ]"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S2a(#[serde_as(as = "(DisplayFromStr, DisplayFromStr)")] (u32, IpAddr)); is_equal( S2a((555_888, ip)), expect![[r#" [ "555888", "1.2.3.4" ]"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S2b(#[serde_as(as = "(_, DisplayFromStr)")] (u32, IpAddr)); is_equal( S2b((987, ip)), expect![[r#" [ 987, "1.2.3.4" ]"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S2c(#[serde_as(as = "(Same, DisplayFromStr)")] (u32, IpAddr)); is_equal( S2c((987, ip)), expect![[r#" [ 987, "1.2.3.4" ]"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S6( #[serde_as(as = "(Same, Same, Same, Same, Same, Same)")] (u8, u16, u32, i8, i16, i32), ); is_equal( S6((8, 16, 32, -8, 16, -32)), expect![[r#" [ 8, 16, 32, -8, 16, -32 ]"#]], ); } #[test] fn test_arrays() { #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S0(#[serde_as(as = "[DisplayFromStr; 0]")] [u32; 0]); is_equal(S0([]), expect![[r#"[]"#]]); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S1(#[serde_as(as = "[DisplayFromStr; 1]")] [u32; 1]); is_equal( S1([1]), expect![[r#" [ "1" ]"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S2(#[serde_as(as = "[Same; 2]")] [u32; 2]); is_equal( S2([11, 22]), expect![[r#" [ 11, 22 ]"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S32(#[serde_as(as = "[Same; 32]")] [u32; 32]); is_equal( S32([ 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, ]), expect![[r#" [ 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 ]"#]], ); } #[test] fn test_sequence_like_types() { #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S1(#[serde_as(as = "Box<[Same]>")] Box<[u32]>); is_equal( S1(vec![1, 2, 3, 99].into()), expect![[r#" [ 1, 2, 3, 99 ]"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S2(#[serde_as(as = "BTreeSet")] BTreeSet); is_equal( S2(vec![1, 2, 3, 99].into_iter().collect()), expect![[r#" [ 1, 2, 3, 99 ]"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S3(#[serde_as(as = "LinkedList")] LinkedList); is_equal( S3(vec![1, 2, 3, 99].into_iter().collect()), expect![[r#" [ 1, 2, 3, 99 ]"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S4(#[serde_as(as = "Vec")] Vec); is_equal( S4(vec![1, 2, 3, 99]), expect![[r#" [ 1, 2, 3, 99 ]"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S5(#[serde_as(as = "VecDeque")] VecDeque); is_equal( S5(vec![1, 2, 3, 99].into()), expect![[r#" [ 1, 2, 3, 99 ]"#]], ); } #[test] fn test_none_as_empty_string() { #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S(#[serde_as(as = "NoneAsEmptyString")] Option); is_equal(S(None), expect![[r#""""#]]); is_equal(S(Some("Hello".to_string())), expect![[r#""Hello""#]]); } #[test] fn test_bytes_or_string() { #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S(#[serde_as(as = "BytesOrString")] Vec); is_equal( S(vec![1, 2, 3]), expect![[r#" [ 1, 2, 3 ]"#]], ); check_deserialization(S(vec![72, 101, 108, 108, 111]), r#""Hello""#); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct SVec(#[serde_as(as = "Vec")] Vec>); is_equal( SVec(vec![vec![1, 2, 3]]), expect![[r#" [ [ 1, 2, 3 ] ]"#]], ); check_deserialization( SVec(vec![ vec![72, 101, 108, 108, 111], vec![87, 111, 114, 108, 100], vec![1, 2, 3], ]), r#"["Hello","World",[1,2,3]]"#, ); } #[test] fn string_with_separator() { use serde_with::{ formats::{CommaSeparator, DosLineSeparator, SpaceSeparator, UnixLineSeparator}, StringWithSeparator, }; #[serde_as] #[derive(Deserialize, Serialize)] struct A { #[serde_as(as = "StringWithSeparator::")] tags: Vec, #[serde_as(as = "StringWithSeparator::")] more_tags: BTreeSet, #[serde_as(as = "StringWithSeparator::")] lf_tags: BTreeSet, #[serde_as(as = "StringWithSeparator::")] crlf_tags: BTreeSet, } let v: A = serde_json::from_str( r##"{ "tags": "#hello #world", "more_tags": "foo,bar,bar", "lf_tags": "foo\nbar\nbar", "crlf_tags": "foo\r\nbar\r\nbar" }"##, ) .unwrap(); assert_eq!(vec!["#hello", "#world"], v.tags); assert_eq!( BTreeSet::from(["foo".to_string(), "bar".to_string()]), v.more_tags ); assert_eq!( BTreeSet::from(["foo".to_string(), "bar".to_string()]), v.lf_tags ); assert_eq!( BTreeSet::from(["foo".to_string(), "bar".to_string()]), v.crlf_tags ); let x = A { tags: vec!["1".to_string(), "2".to_string(), "3".to_string()], more_tags: BTreeSet::default(), lf_tags: BTreeSet::default(), crlf_tags: BTreeSet::default(), }; assert_eq!( r#"{"tags":"1 2 3","more_tags":"","lf_tags":"","crlf_tags":""}"#, serde_json::to_string(&x).unwrap() ); } #[test] fn test_vec_skip_error() { use serde_with::VecSkipError; #[serde_as] #[derive(Debug, PartialEq, Deserialize, Serialize)] struct S { tag: String, #[serde_as(as = "VecSkipError<_>")] values: Vec, } check_deserialization( S { tag: "type".into(), values: vec![0, 1], }, r#"{"tag":"type","values":[0, "str", 1, [10, 11], -2, {}, 300]}"#, ); check_error_deserialization::( r#"{"tag":"type", "values":[0, "str", 1, , 300]}"#, expect!["expected value at line 1 column 39"], ); is_equal( S { tag: "round-trip".into(), values: vec![0, 255], }, expect![[r#" { "tag": "round-trip", "values": [ 0, 255 ] }"#]], ); } #[test] fn test_map_skip_error_btreemap() { use serde_with::MapSkipError; #[serde_as] #[derive(Debug, PartialEq, Deserialize, Serialize)] struct S { tag: String, #[serde_as(as = "MapSkipError")] values: BTreeMap, } check_deserialization( S { tag: "type".into(), values: [(0, 1), (10, 20)].into_iter().collect(), }, r#" { "tag":"type", "values": { "0": 1, "str": 2, "3": "str", "4": [10, 11], "5": {}, "10": 20 } }"#, ); check_error_deserialization::( r#"{"tag":"type", "values":{"0": 1,}}"#, expect!["trailing comma at line 1 column 33"], ); is_equal( S { tag: "round-trip".into(), values: [(0, 0), (255, 255)].into_iter().collect(), }, expect![[r#" { "tag": "round-trip", "values": { "0": 0, "255": 255 } }"#]], ); } #[test] fn test_map_skip_error_btreemap_flatten() { use serde_with::MapSkipError; #[serde_as] #[derive(Debug, PartialEq, Deserialize, Serialize)] struct S { tag: String, #[serde_as(as = "MapSkipError")] #[serde(flatten)] values: BTreeMap, } check_deserialization( S { tag: "type".into(), values: [(0, 1), (10, 20)].into_iter().collect(), }, r#" { "tag":"type", "0": 1, "str": 2, "3": "str", "4": [10, 11], "5": {}, "10": 20 }"#, ); is_equal( S { tag: "round-trip".into(), values: [(0, 0), (255, 255)].into_iter().collect(), }, expect![[r#" { "tag": "round-trip", "0": 0, "255": 255 }"#]], ); } #[test] fn test_map_skip_error_hashmap() { use serde_with::MapSkipError; #[serde_as] #[derive(Debug, PartialEq, Deserialize, Serialize)] struct S { tag: String, #[serde_as(as = "MapSkipError")] values: HashMap, } check_deserialization( S { tag: "type".into(), values: [(0, 1)].into_iter().collect(), }, r#" { "tag":"type", "values": { "0": 1, "str": 2, "3": "str", "4": [10, 11], "5": {} } }"#, ); check_error_deserialization::( r#"{"tag":"type", "values":{"0": 1,}}"#, expect!["trailing comma at line 1 column 33"], ); is_equal( S { tag: "round-trip".into(), values: [(255, 0)].into_iter().collect(), }, expect![[r#" { "tag": "round-trip", "values": { "255": 0 } }"#]], ); } #[test] fn test_map_skip_error_hashmap_flatten() { use serde_with::MapSkipError; #[serde_as] #[derive(Debug, PartialEq, Deserialize, Serialize)] struct S { tag: String, #[serde_as(as = "MapSkipError")] #[serde(flatten)] values: HashMap, } check_deserialization( S { tag: "type".into(), values: [(0, 1)].into_iter().collect(), }, r#" { "tag":"type", "0": 1, "str": 2, "3": "str", "4": [10, 11], "5": {} }"#, ); is_equal( S { tag: "round-trip".into(), values: [(255, 0)].into_iter().collect(), }, expect![[r#" { "tag": "round-trip", "255": 0 }"#]], ); } #[test] fn test_serialize_reference() { #[serde_as] #[derive(Debug, Serialize)] struct S1<'a>(#[serde_as(as = "Vec")] &'a Vec); check_serialization( S1(&vec![1, 2]), expect![[r#" [ "1", "2" ]"#]], ); #[serde_as] #[derive(Debug, Serialize)] struct S1a<'a>(#[serde_as(as = "&Vec")] &'a Vec); check_serialization( S1a(&vec![1, 2]), expect![[r#" [ "1", "2" ]"#]], ); #[serde_as] #[derive(Debug, Serialize)] struct S1Mut<'a>(#[serde_as(as = "Vec")] &'a mut Vec); check_serialization( S1Mut(&mut vec![1, 2]), expect![[r#" [ "1", "2" ]"#]], ); #[serde_as] #[derive(Debug, Serialize)] struct S1aMut<'a>(#[serde_as(as = "&mut Vec")] &'a mut Vec); check_serialization( S1aMut(&mut vec![1, 2]), expect![[r#" [ "1", "2" ]"#]], ); #[serde_as] #[derive(Debug, Serialize)] struct S2<'a>(#[serde_as(as = "&[DisplayFromStr]")] &'a [u32]); check_serialization( S2(&[1, 2]), expect![[r#" [ "1", "2" ]"#]], ); #[serde_as] #[derive(Debug, Serialize)] struct S3<'a>( #[serde_as(as = "&BTreeMap")] &'a BTreeMap, ); let bmap = vec![(false, 123), (true, 456)].into_iter().collect(); check_serialization( S3(&bmap), expect![[r#" { "false": "123", "true": "456" }"#]], ); #[serde_as] #[derive(Debug, Serialize)] struct S4<'a>(#[serde_as(as = "&Vec<(_, DisplayFromStr)>")] &'a BTreeMap); let bmap = vec![(false, 123), (true, 456)].into_iter().collect(); check_serialization( S4(&bmap), expect![[r#" [ [ false, "123" ], [ true, "456" ] ]"#]], ); #[serde_as] #[derive(Debug, Serialize)] struct S5<'a>( #[serde_as(as = "&BTreeMap>")] &'a Vec<(u32, &'a BTreeMap)>, ); let bmap0 = vec![(false, "123".to_string()), (true, "456".to_string())] .into_iter() .collect(); let bmap1 = vec![(true, "Hello".to_string()), (false, "World".to_string())] .into_iter() .collect(); let vec = vec![(111, &bmap0), (999, &bmap1)]; check_serialization( S5(&vec), expect![[r#" { "111": [ [ false, "123" ], [ true, "456" ] ], "999": [ [ false, "World" ], [ true, "Hello" ] ] }"#]], ); } #[test] fn test_big_arrays() { // Single Big Array #[serde_as] #[derive(Debug, PartialEq, Serialize, Deserialize)] struct S1(#[serde_as(as = "[_; 64]")] [u8; 64]); is_equal_compact( S1([0; 64]), expect![[ r#"[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]"# ]], ); // Too few entries check_error_deserialization::( r#"[0,0,0,0,0,0,0,0,0,0,0,0,0,0]"#, expect![[r#"invalid length 14, expected an array of size 64 at line 1 column 29"#]], ); // Too many entries check_error_deserialization::( r#"[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]"#, expect![[r#"trailing characters at line 1 column 130"#]], ); // Single Big Array #[serde_as] #[derive(Debug, PartialEq, Serialize, Deserialize)] struct S2(#[serde_as(as = "[DisplayFromStr; 40]")] [u8; 40]); is_equal_compact( S2([0; 40]), expect![[ r#"["0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0"]"# ]], ); // Nested Big Arrays #[serde_as] #[derive(Debug, PartialEq, Serialize, Deserialize)] struct S3(#[serde_as(as = "[[_; 34]; 33]")] [[u8; 34]; 33]); is_equal_compact( S3([[0; 34]; 33]), expect![[ r#"[[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]"# ]], ); } #[test] fn test_bytes() { // The test case is copied from // https://github.com/serde-rs/bytes/blob/cbae606b9dc225fc094b031cc84eac9493da2058/tests/test_derive.rs // Original code by @dtolnay use alloc::borrow::Cow; use serde_test::{assert_de_tokens, assert_tokens, Token}; use serde_with::Bytes; #[serde_as] #[derive(Serialize, Deserialize, PartialEq, Debug)] struct Test<'a> { #[serde_as(as = "Bytes")] array: [u8; 52], #[serde_as(as = "Bytes")] slice: &'a [u8], #[serde_as(as = "Bytes")] vec: Vec, #[serde_as(as = "Bytes")] cow_slice: Cow<'a, [u8]>, #[serde_as(as = "Box")] boxed_array: Box<[u8; 52]>, #[serde_as(as = "Bytes")] boxed_array2: Box<[u8; 52]>, #[serde_as(as = "Bytes")] boxed_slice: Box<[u8]>, #[serde_as(as = "Option")] opt_slice: Option<&'a [u8]>, #[serde_as(as = "Option")] opt_vec: Option>, #[serde_as(as = "Option")] opt_cow_slice: Option>, } let test = Test { array: *b"ABCDEFGHIJKLMNOPQRSTUVWXZYabcdefghijklmnopqrstuvwxyz", slice: b"...", vec: b"...".to_vec(), cow_slice: Cow::Borrowed(b"..."), boxed_array: Box::new(*b"ABCDEFGHIJKLMNOPQRSTUVWXZYabcdefghijklmnopqrstuvwxyz"), boxed_array2: Box::new(*b"ABCDEFGHIJKLMNOPQRSTUVWXZYabcdefghijklmnopqrstuvwxyz"), boxed_slice: b"...".to_vec().into_boxed_slice(), opt_slice: Some(b"..."), opt_vec: Some(b"...".to_vec()), opt_cow_slice: Some(Cow::Borrowed(b"...")), }; assert_tokens( &test, &[ Token::Struct { name: "Test", len: 10, }, Token::Str("array"), Token::BorrowedBytes(b"ABCDEFGHIJKLMNOPQRSTUVWXZYabcdefghijklmnopqrstuvwxyz"), Token::Str("slice"), Token::BorrowedBytes(b"..."), Token::Str("vec"), Token::Bytes(b"..."), Token::Str("cow_slice"), Token::BorrowedBytes(b"..."), Token::Str("boxed_array"), Token::BorrowedBytes(b"ABCDEFGHIJKLMNOPQRSTUVWXZYabcdefghijklmnopqrstuvwxyz"), Token::Str("boxed_array2"), Token::BorrowedBytes(b"ABCDEFGHIJKLMNOPQRSTUVWXZYabcdefghijklmnopqrstuvwxyz"), Token::Str("boxed_slice"), Token::Bytes(b"..."), Token::Str("opt_slice"), Token::Some, Token::BorrowedBytes(b"..."), Token::Str("opt_vec"), Token::Some, Token::Bytes(b"..."), Token::Str("opt_cow_slice"), Token::Some, Token::BorrowedBytes(b"..."), Token::StructEnd, ], ); // Test string deserialization assert_de_tokens( &test, &[ Token::Struct { name: "Test", len: 10, }, Token::Str("array"), Token::BorrowedStr("ABCDEFGHIJKLMNOPQRSTUVWXZYabcdefghijklmnopqrstuvwxyz"), Token::Str("slice"), Token::BorrowedStr("..."), Token::Str("vec"), Token::Bytes(b"..."), Token::Str("cow_slice"), Token::BorrowedStr("..."), Token::Str("boxed_array"), Token::BorrowedStr("ABCDEFGHIJKLMNOPQRSTUVWXZYabcdefghijklmnopqrstuvwxyz"), Token::Str("boxed_array2"), Token::BorrowedStr("ABCDEFGHIJKLMNOPQRSTUVWXZYabcdefghijklmnopqrstuvwxyz"), Token::Str("boxed_slice"), Token::Bytes(b"..."), Token::Str("opt_slice"), Token::Some, Token::BorrowedStr("..."), Token::Str("opt_vec"), Token::Some, Token::Bytes(b"..."), Token::Str("opt_cow_slice"), Token::Some, Token::BorrowedStr("..."), Token::StructEnd, ], ); } #[test] fn test_one_or_many_prefer_one() { #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S1Vec(#[serde_as(as = "OneOrMany<_>")] Vec); // Normal is_equal(S1Vec(vec![]), expect![[r#"[]"#]]); is_equal(S1Vec(vec![1]), expect![[r#"1"#]]); is_equal( S1Vec(vec![1, 2, 3]), expect![[r#" [ 1, 2, 3 ]"#]], ); check_deserialization(S1Vec(vec![1]), r#"1"#); check_deserialization(S1Vec(vec![1]), r#"[1]"#); check_error_deserialization::( r#"{}"#, expect![[r#" OneOrMany could not deserialize any variant: One: invalid type: map, expected u32 Many: invalid type: map, expected a sequence"#]], ); check_error_deserialization::( r#""xx""#, expect![[r#" OneOrMany could not deserialize any variant: One: invalid type: string "xx", expected u32 Many: invalid type: string "xx", expected a sequence"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S2Vec(#[serde_as(as = "OneOrMany")] Vec); // Normal is_equal(S2Vec(vec![]), expect![[r#"[]"#]]); is_equal(S2Vec(vec![1]), expect![[r#""1""#]]); is_equal( S2Vec(vec![1, 2, 3]), expect![[r#" [ "1", "2", "3" ]"#]], ); check_deserialization(S2Vec(vec![1]), r#""1""#); check_deserialization(S2Vec(vec![1]), r#"["1"]"#); check_error_deserialization::( r#"{}"#, expect![[r#" OneOrMany could not deserialize any variant: One: invalid type: map, expected a string Many: invalid type: map, expected a sequence"#]], ); check_error_deserialization::( r#""xx""#, expect![[r#" OneOrMany could not deserialize any variant: One: invalid digit found in string Many: invalid type: string "xx", expected a sequence"#]], ); } #[test] fn test_one_or_many_prefer_many() { use serde_with::formats::PreferMany; #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S1Vec(#[serde_as(as = "OneOrMany<_, PreferMany>")] Vec); // Normal is_equal(S1Vec(vec![]), expect![[r#"[]"#]]); is_equal( S1Vec(vec![1]), expect![[r#" [ 1 ]"#]], ); is_equal( S1Vec(vec![1, 2, 3]), expect![[r#" [ 1, 2, 3 ]"#]], ); check_deserialization(S1Vec(vec![1]), r#"1"#); check_deserialization(S1Vec(vec![1]), r#"[1]"#); check_error_deserialization::( r#"{}"#, expect![[r#" OneOrMany could not deserialize any variant: One: invalid type: map, expected u32 Many: invalid type: map, expected a sequence"#]], ); check_error_deserialization::( r#""xx""#, expect![[r#" OneOrMany could not deserialize any variant: One: invalid type: string "xx", expected u32 Many: invalid type: string "xx", expected a sequence"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S2Vec(#[serde_as(as = "OneOrMany")] Vec); // Normal is_equal(S2Vec(vec![]), expect![[r#"[]"#]]); is_equal( S2Vec(vec![1]), expect![[r#" [ "1" ]"#]], ); is_equal( S2Vec(vec![1, 2, 3]), expect![[r#" [ "1", "2", "3" ]"#]], ); check_deserialization(S2Vec(vec![1]), r#""1""#); check_deserialization(S2Vec(vec![1]), r#"["1"]"#); check_error_deserialization::( r#"{}"#, expect![[r#" OneOrMany could not deserialize any variant: One: invalid type: map, expected a string Many: invalid type: map, expected a sequence"#]], ); check_error_deserialization::( r#""xx""#, expect![[r#" OneOrMany could not deserialize any variant: One: invalid digit found in string Many: invalid type: string "xx", expected a sequence"#]], ); } /// Test that Cow borrows from the input #[test] fn test_borrow_cow_str() { use alloc::borrow::Cow; use serde::de::{ value::{BorrowedStrDeserializer, MapDeserializer}, IntoDeserializer, }; use serde_test::{assert_ser_tokens, Token}; use serde_with::BorrowCow; #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S1<'a> { #[serde_as(as = "BorrowCow")] cow: Cow<'a, str>, #[serde_as(as = "Option")] opt: Option>, #[serde_as(as = "Box")] b: Box>, #[serde_as(as = "[BorrowCow; 1]")] arr: [Cow<'a, str>; 1], } assert_ser_tokens( &S1 { cow: "abc".into(), opt: Some("foo".into()), b: Box::new("bar".into()), arr: ["def".into()], }, &[ Token::Struct { name: "S1", len: 4 }, Token::Str("cow"), Token::BorrowedStr("abc"), Token::Str("opt"), Token::Some, Token::BorrowedStr("foo"), Token::Str("b"), Token::BorrowedStr("bar"), Token::Str("arr"), Token::Tuple { len: 1 }, Token::BorrowedStr("def"), Token::TupleEnd, Token::StructEnd, ], ); let s1: S1<'_> = serde_json::from_str( r#"{ "cow": "abc", "opt": "foo", "b": "bar", "arr": ["def"] }"#, ) .unwrap(); assert!(matches!(s1.cow, Cow::Borrowed(_))); assert!(matches!(s1.opt, Some(Cow::Borrowed(_)))); assert!(matches!(*s1.b, Cow::Borrowed(_))); assert!(matches!(s1.arr, [Cow::Borrowed(_)])); let s1: S1<'_> = serde_json::from_str( r#"{ "cow": "a\"c", "opt": "f\"o", "b": "b\"r", "arr": ["d\"f"] }"#, ) .unwrap(); assert!(matches!(s1.cow, Cow::Owned(_))); assert!(matches!(s1.opt, Some(Cow::Owned(_)))); assert!(matches!(*s1.b, Cow::Owned(_))); assert!(matches!(s1.arr, [Cow::Owned(_)])); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S2<'a> { #[serde_as(as = "BorrowCow")] cow: Cow<'a, [u8]>, #[serde_as(as = "Option")] opt: Option>, } assert_ser_tokens( &S2 { cow: b"abc"[..].into(), opt: Some(b"foo"[..].into()), }, &[ Token::Struct { name: "S2", len: 2 }, Token::Str("cow"), Token::Seq { len: Some(3) }, Token::U8(b'a'), Token::U8(b'b'), Token::U8(b'c'), Token::SeqEnd, Token::Str("opt"), Token::Some, Token::Seq { len: Some(3) }, Token::U8(b'f'), Token::U8(b'o'), Token::U8(b'o'), Token::SeqEnd, Token::StructEnd, ], ); // Check that a manual borrow works too #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S3<'a> { #[serde(borrow = "'a")] #[serde_as(as = "BorrowCow")] borrowed: Cow<'a, [u8]>, // TODO add a test for Cow<'b, [u8; N]> // #[serde_as(as = "BorrowCow")] // Cow<'b, [u8; N]>, } struct BorrowedStr(&'static str); impl<'de> IntoDeserializer<'de> for BorrowedStr { type Deserializer = BorrowedStrDeserializer<'de, serde::de::value::Error>; fn into_deserializer(self) -> Self::Deserializer { BorrowedStrDeserializer::new(self.0) } } let deser = MapDeserializer::new(IntoIterator::into_iter([ ("copied", BorrowedStr("copied")), ("borrowed", BorrowedStr("borrowed")), ])); let s3 = S3::deserialize(deser).unwrap(); assert!(matches!(s3.borrowed, Cow::Borrowed(_))); } #[test] fn test_boolfromint() { #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S(#[serde_as(as = "BoolFromInt")] bool); is_equal(S(false), expect![[r#"0"#]]); is_equal(S(true), expect![[r#"1"#]]); check_error_deserialization::( "2", expect![[r#"invalid value: integer `2`, expected 0 or 1 at line 1 column 1"#]], ); check_error_deserialization::( "-100", expect![[r#"invalid value: integer `-100`, expected 0 or 1 at line 1 column 4"#]], ); check_error_deserialization::( "18446744073709551615", expect![[ r#"invalid value: integer `18446744073709551615`, expected 0 or 1 at line 1 column 20"# ]], ); check_error_deserialization::( r#""""#, expect![[r#"invalid type: string "", expected an integer 0 or 1 at line 1 column 2"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct SStrict(#[serde_as(as = "BoolFromInt")] bool); is_equal(SStrict(false), expect![[r#"0"#]]); is_equal(SStrict(true), expect![[r#"1"#]]); check_error_deserialization::( "2", expect![[r#"invalid value: integer `2`, expected 0 or 1 at line 1 column 1"#]], ); check_error_deserialization::( "-100", expect![[r#"invalid value: integer `-100`, expected 0 or 1 at line 1 column 4"#]], ); check_error_deserialization::( "18446744073709551615", expect![[ r#"invalid value: integer `18446744073709551615`, expected 0 or 1 at line 1 column 20"# ]], ); check_error_deserialization::( r#""""#, expect![[r#"invalid type: string "", expected an integer 0 or 1 at line 1 column 2"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct SFlexible(#[serde_as(as = "BoolFromInt")] bool); is_equal(SFlexible(false), expect![[r#"0"#]]); is_equal(SFlexible(true), expect![[r#"1"#]]); check_deserialization::(SFlexible(true), "2"); check_deserialization::(SFlexible(true), "-100"); check_deserialization::(SFlexible(true), "18446744073709551615"); check_error_deserialization::( r#""""#, expect![[r#"invalid type: string "", expected an integer at line 1 column 2"#]], ); } serde_with-3.12.0/tests/serde_as/map_tuple_list.rs000064400000000000000000000420611046102023000203650ustar 00000000000000use super::*; use std::net::IpAddr; #[test] fn test_map_as_tuple_list() { let ip = "1.2.3.4".parse().unwrap(); let ip2 = "255.255.255.255".parse().unwrap(); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct SM(#[serde_as(as = "Seq<(DisplayFromStr, DisplayFromStr)>")] BTreeMap); let map: BTreeMap<_, _> = vec![(1, ip), (10, ip), (200, ip2)].into_iter().collect(); is_equal( SM(map), expect![[r#" [ [ "1", "1.2.3.4" ], [ "10", "1.2.3.4" ], [ "200", "255.255.255.255" ] ]"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct SB(#[serde_as(as = "Vec<(DisplayFromStr, DisplayFromStr)>")] BTreeMap); let map: BTreeMap<_, _> = vec![(1, ip), (10, ip), (200, ip2)].into_iter().collect(); is_equal( SB(map.clone()), expect![[r#" [ [ "1", "1.2.3.4" ], [ "10", "1.2.3.4" ], [ "200", "255.255.255.255" ] ]"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct SB2(#[serde_as(as = "Vec<(Same, DisplayFromStr)>")] BTreeMap); is_equal( SB2(map), expect![[r#" [ [ 1, "1.2.3.4" ], [ 10, "1.2.3.4" ], [ 200, "255.255.255.255" ] ]"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct SH(#[serde_as(as = "Vec<(DisplayFromStr, DisplayFromStr)>")] HashMap); // HashMap serialization tests with more than 1 entry are unreliable let map1: HashMap<_, _> = vec![(200, ip2)].into_iter().collect(); let map: HashMap<_, _> = vec![(1, ip), (10, ip), (200, ip2)].into_iter().collect(); is_equal( SH(map1.clone()), expect![[r#" [ [ "200", "255.255.255.255" ] ]"#]], ); check_deserialization( SH(map.clone()), r#"[["1","1.2.3.4"],["10","1.2.3.4"],["200","255.255.255.255"]]"#, ); check_error_deserialization::( r#"{"200":"255.255.255.255"}"#, expect![[r#"invalid type: map, expected a sequence at line 1 column 0"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct SH2(#[serde_as(as = "Vec<(Same, DisplayFromStr)>")] HashMap); is_equal( SH2(map1), expect![[r#" [ [ 200, "255.255.255.255" ] ]"#]], ); check_deserialization( SH2(map), r#"[[1,"1.2.3.4"],[10,"1.2.3.4"],[200,"255.255.255.255"]]"#, ); check_error_deserialization::( r#"1"#, expect![[r#"invalid type: integer `1`, expected a sequence at line 1 column 1"#]], ); } #[test] fn test_tuple_list_as_map() { let ip = "1.2.3.4".parse().unwrap(); let ip2 = "255.255.255.255".parse().unwrap(); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct SM(#[serde_as(as = "Map")] Vec<(u32, IpAddr)>); is_equal( SM(vec![(1, ip), (10, ip), (200, ip2)]), expect![[r#" { "1": "1.2.3.4", "10": "1.2.3.4", "200": "255.255.255.255" }"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct SH(#[serde_as(as = "HashMap")] Vec<(u32, IpAddr)>); is_equal( SH(vec![(1, ip), (10, ip), (200, ip2)]), expect![[r#" { "1": "1.2.3.4", "10": "1.2.3.4", "200": "255.255.255.255" }"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct SB(#[serde_as(as = "BTreeMap")] Vec<(u32, IpAddr)>); is_equal( SB(vec![(1, ip), (10, ip), (200, ip2)]), expect![[r#" { "1": "1.2.3.4", "10": "1.2.3.4", "200": "255.255.255.255" }"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct SD(#[serde_as(as = "BTreeMap")] VecDeque<(u32, IpAddr)>); is_equal( SD(vec![(1, ip), (10, ip), (200, ip2)].into()), expect![[r#" { "1": "1.2.3.4", "10": "1.2.3.4", "200": "255.255.255.255" }"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct Sll( #[serde_as(as = "HashMap")] LinkedList<(u32, IpAddr)>, ); is_equal( Sll(vec![(1, ip), (10, ip), (200, ip2)].into_iter().collect()), expect![[r#" { "1": "1.2.3.4", "10": "1.2.3.4", "200": "255.255.255.255" }"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct SO(#[serde_as(as = "HashMap")] Option<(u32, IpAddr)>); is_equal( SO(Some((1, ip))), expect![[r#" { "1": "1.2.3.4" }"#]], ); is_equal(SO(None), expect![[r#"{}"#]]); } #[test] fn test_tuple_array_as_map() { #[serde_as] #[derive(Debug, PartialEq, Serialize, Deserialize)] struct S0(#[serde_as(as = "Map<_, _>")] [(u8, u8); 1]); is_equal( S0([(1, 2)]), expect![[r#" { "1": 2 }"#]], ); #[serde_as] #[derive(Debug, PartialEq, Serialize, Deserialize)] struct S1(#[serde_as(as = "BTreeMap<_, _>")] [(u8, u8); 1]); is_equal( S1([(1, 2)]), expect![[r#" { "1": 2 }"#]], ); #[serde_as] #[derive(Debug, PartialEq, Serialize, Deserialize)] struct S2(#[serde_as(as = "HashMap<_, _>")] [(u8, u8); 33]); is_equal( S2([ (0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8, 8), (9, 9), (10, 10), (11, 11), (12, 12), (13, 13), (14, 14), (15, 15), (16, 16), (17, 17), (18, 18), (19, 19), (20, 20), (21, 21), (22, 22), (23, 23), (24, 24), (25, 25), (26, 26), (27, 27), (28, 28), (29, 29), (30, 30), (31, 31), (32, 32), ]), expect![[r#" { "0": 0, "1": 1, "2": 2, "3": 3, "4": 4, "5": 5, "6": 6, "7": 7, "8": 8, "9": 9, "10": 10, "11": 11, "12": 12, "13": 13, "14": 14, "15": 15, "16": 16, "17": 17, "18": 18, "19": 19, "20": 20, "21": 21, "22": 22, "23": 23, "24": 24, "25": 25, "26": 26, "27": 27, "28": 28, "29": 29, "30": 30, "31": 31, "32": 32 }"#]], ); } // Test that the `Seq` conversion works when the inner type is explicitly specified. #[test] fn test_map_as_tuple_with_nested_complex_type() { #[serde_as] #[derive(Debug, PartialEq, Serialize, Deserialize)] struct S0(#[serde_as(as = "Seq<(_, Vec<_>)>")] BTreeMap>); let value = S0(BTreeMap::from([ (1, Vec::from([1, 2])), (2, Vec::from([3, 4])), ])); is_equal( value, expect![[r#" [ [ 1, [ 1, 2 ] ], [ 2, [ 3, 4 ] ] ]"#]], ); } // Problematic handling around fundamental types: https://github.com/rust-lang/rust/issues/121621 #[allow(unknown_lints, non_local_definitions)] #[test] fn test_map_as_tuple_list_works_with_serializer_that_needs_length_to_serialize_sequence() { use serde::{ ser::{ SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant, SerializeTuple, SerializeTupleStruct, SerializeTupleVariant, }, Serializer, }; use std::fmt::{self, Debug, Display}; #[derive(Debug)] enum TestError { LengthRequired, Other, } impl Display for TestError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Debug::fmt(self, f) } } impl std::error::Error for TestError {} impl serde::ser::Error for TestError { fn custom(_: T) -> Self where T: Display, { TestError::Other } } struct TestSerializer; impl Serializer for &mut TestSerializer { type Ok = (); type Error = TestError; type SerializeSeq = Self; type SerializeTuple = Self; type SerializeTupleStruct = Self; type SerializeTupleVariant = Self; type SerializeMap = Self; type SerializeStruct = Self; type SerializeStructVariant = Self; fn serialize_bool(self, _: bool) -> Result { Ok(()) } fn serialize_i8(self, _: i8) -> Result { Ok(()) } fn serialize_i16(self, _: i16) -> Result { Ok(()) } fn serialize_i32(self, _: i32) -> Result { Ok(()) } fn serialize_i64(self, _: i64) -> Result { Ok(()) } fn serialize_u8(self, _: u8) -> Result { Ok(()) } fn serialize_u16(self, _: u16) -> Result { Ok(()) } fn serialize_u32(self, _: u32) -> Result { Ok(()) } fn serialize_u64(self, _: u64) -> Result { Ok(()) } fn serialize_f32(self, _: f32) -> Result { Ok(()) } fn serialize_f64(self, _: f64) -> Result { Ok(()) } fn serialize_char(self, _: char) -> Result { Ok(()) } fn serialize_str(self, _: &str) -> Result { Ok(()) } fn serialize_bytes(self, _: &[u8]) -> Result { Ok(()) } fn serialize_none(self) -> Result { Ok(()) } fn serialize_some(self, v: &T) -> Result where T: Serialize + ?Sized, { v.serialize(self) } fn serialize_unit(self) -> Result { Ok(()) } fn serialize_unit_struct(self, _: &'static str) -> Result { Ok(()) } fn serialize_unit_variant( self, _: &'static str, _: u32, _: &'static str, ) -> Result { Ok(()) } fn serialize_newtype_struct( self, _: &'static str, value: &T, ) -> Result where T: Serialize + ?Sized, { value.serialize(self) } fn serialize_newtype_variant( self, _: &'static str, _: u32, _: &'static str, value: &T, ) -> Result where T: Serialize + ?Sized, { value.serialize(self) } fn serialize_seq(self, len: Option) -> Result { len.map(|_| self).ok_or(TestError::LengthRequired) } fn serialize_tuple(self, _: usize) -> Result { Ok(self) } fn serialize_tuple_struct( self, _: &'static str, _: usize, ) -> Result { Ok(self) } fn serialize_tuple_variant( self, _: &'static str, _: u32, _: &'static str, _: usize, ) -> Result { Ok(self) } fn serialize_map(self, _: Option) -> Result { Ok(self) } fn serialize_struct( self, _: &'static str, _: usize, ) -> Result { Ok(self) } fn serialize_struct_variant( self, _: &'static str, _: u32, _: &'static str, _: usize, ) -> Result { Ok(self) } } impl SerializeMap for &mut TestSerializer { type Ok = (); type Error = TestError; fn serialize_key(&mut self, key: &T) -> Result<(), Self::Error> where T: Serialize + ?Sized, { key.serialize(&mut **self) } fn serialize_value(&mut self, value: &T) -> Result<(), Self::Error> where T: Serialize + ?Sized, { value.serialize(&mut **self) } fn end(self) -> Result { Ok(()) } } impl SerializeSeq for &mut TestSerializer { type Ok = (); type Error = TestError; fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> where T: Serialize + ?Sized, { value.serialize(&mut **self) } fn end(self) -> Result { Ok(()) } } impl SerializeStruct for &mut TestSerializer { type Ok = (); type Error = TestError; fn serialize_field(&mut self, _: &'static str, value: &T) -> Result<(), Self::Error> where T: Serialize + ?Sized, { value.serialize(&mut **self) } fn end(self) -> Result { Ok(()) } } impl SerializeStructVariant for &mut TestSerializer { type Ok = (); type Error = TestError; fn serialize_field(&mut self, _: &'static str, value: &T) -> Result<(), Self::Error> where T: Serialize + ?Sized, { value.serialize(&mut **self) } fn end(self) -> Result { Ok(()) } } impl SerializeTuple for &mut TestSerializer { type Ok = (); type Error = TestError; fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> where T: Serialize + ?Sized, { value.serialize(&mut **self) } fn end(self) -> Result { Ok(()) } } impl SerializeTupleStruct for &mut TestSerializer { type Ok = (); type Error = TestError; fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> where T: Serialize + ?Sized, { value.serialize(&mut **self) } fn end(self) -> Result { Ok(()) } } impl SerializeTupleVariant for &mut TestSerializer { type Ok = (); type Error = TestError; fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> where T: Serialize + ?Sized, { value.serialize(&mut **self) } fn end(self) -> Result { Ok(()) } } #[serde_as] #[derive(Debug, Default, Serialize)] struct Data { #[serde_as(as = "Seq<(_, _)>")] xs: HashMap, } Data::default().serialize(&mut TestSerializer).unwrap(); } serde_with-3.12.0/tests/serde_as/pickfirst.rs000064400000000000000000000076041046102023000173460ustar 00000000000000use super::*; use serde_with::{formats::SpaceSeparator, PickFirst}; #[test] fn test_pick_first_two() { #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S(#[serde_as(as = "PickFirst<(_, DisplayFromStr)>")] u32); is_equal(S(123), expect![[r#"123"#]]); check_deserialization(S(123), r#""123""#); check_error_deserialization::( r#""Abc""#, expect![[r#" PickFirst could not deserialize any variant: First: invalid type: string "Abc", expected u32 Second: invalid digit found in string"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S2(#[serde_as(as = "PickFirst<(DisplayFromStr, _)>")] u32); is_equal(S2(123), expect![[r#""123""#]]); check_deserialization(S2(123), r#"123"#); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S3( #[serde_as(as = "PickFirst<(_, StringWithSeparator::,)>")] Vec, ); is_equal( S3(vec!["A".to_string(), "B".to_string(), "C".to_string()]), expect![[r#" [ "A", "B", "C" ]"#]], ); check_deserialization( S3(vec!["A".to_string(), "B".to_string(), "C".to_string()]), r#""A B C""#, ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S4( #[serde_as(as = "PickFirst<(StringWithSeparator::, _,)>")] Vec, ); is_equal( S4(vec!["A".to_string(), "B".to_string(), "C".to_string()]), expect![[r#""A,B,C""#]], ); check_deserialization( S4(vec!["A".to_string(), "B".to_string(), "C".to_string()]), r#"["A", "B", "C"]"#, ); } #[test] fn test_pick_first_three() { #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S( #[serde_as( as = "PickFirst<(_, Vec, StringWithSeparator::)>" )] Vec, ); is_equal( S(vec![1, 2, 3]), expect![[r#" [ 1, 2, 3 ]"#]], ); check_deserialization( S(vec![1, 2, 3]), r#" [ "1", "2", "3" ]"#, ); check_deserialization(S(vec![1, 2, 3]), r#""1,2,3""#); check_error_deserialization::( r#""Abc""#, expect![[r#" PickFirst could not deserialize any variant: First: invalid type: string "Abc", expected a sequence Second: invalid type: string "Abc", expected a sequence Third: invalid digit found in string"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S2( #[serde_as( as = "PickFirst<(StringWithSeparator::, _, Vec)>" )] Vec, ); is_equal(S2(vec![1, 2, 3]), expect![[r#""1,2,3""#]]); check_deserialization( S2(vec![1, 2, 3]), r#" [ "1", "2", "3" ]"#, ); check_deserialization( S2(vec![1, 2, 3]), r#" [ 1, 2, 3 ]"#, ); } #[test] fn test_pick_first_four() { #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S(#[serde_as(as = "PickFirst<(_, _, _, _)>")] u32); is_equal(S(123), expect![[r#"123"#]]); check_error_deserialization::( r#""Abc""#, expect![[r#" PickFirst could not deserialize any variant: First: invalid type: string "Abc", expected u32 Second: invalid type: string "Abc", expected u32 Third: invalid type: string "Abc", expected u32 Fourth: invalid type: string "Abc", expected u32"#]], ); } serde_with-3.12.0/tests/serde_as/serde_as_macro.rs000064400000000000000000000152601046102023000203130ustar 00000000000000use super::*; /// Test that the [`serde_as`] macro can replace the `_` type and the resulting code compiles. #[test] fn test_serde_as_macro_replace_infer_type() { #[serde_as] #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)] struct Data { #[serde_as(as = "_")] a: u32, #[serde_as(as = "std::vec::Vec<_>")] b: Vec, #[serde_as(as = "Vec<(_, _)>")] c: Vec<(u32, String)>, #[serde_as(as = "[_; 2]")] d: [u32; 2], #[serde_as(as = "Box<[_]>")] e: Box<[u32]>, } is_equal( Data { a: 10, b: vec![20, 33], c: vec![(40, "Hello".into()), (55, "World".into()), (60, "!".into())], d: [70, 88], e: vec![99, 100, 110].into_boxed_slice(), }, expect![[r#" { "a": 10, "b": [ 20, 33 ], "c": [ [ 40, "Hello" ], [ 55, "World" ], [ 60, "!" ] ], "d": [ 70, 88 ], "e": [ 99, 100, 110 ] }"#]], ); } /// Test that the [`serde_as`] macro supports `deserialize_as` #[test] fn test_serde_as_macro_deserialize() { #[serde_as] #[derive(Debug, Eq, PartialEq, Deserialize)] struct Data { #[serde_as(deserialize_as = "DisplayFromStr")] a: u32, #[serde_as(deserialize_as = "Vec")] b: Vec, #[serde_as(deserialize_as = "(DisplayFromStr, _)")] c: (u32, u32), } check_deserialization( Data { a: 10, b: vec![20, 33], c: (40, 55), }, r#"{ "a": "10", "b": [ "20", "33" ], "c": [ "40", 55 ] }"#, ); } /// Test that the [`serde_as`] macro supports `serialize_as` #[test] fn test_serde_as_macro_serialize() { #[serde_as] #[derive(Debug, Eq, PartialEq, Serialize)] struct Data { #[serde_as(serialize_as = "DisplayFromStr")] a: u32, #[serde_as(serialize_as = "Vec")] b: Vec, #[serde_as(serialize_as = "(DisplayFromStr, _)")] c: (u32, u32), } check_serialization( Data { a: 10, b: vec![20, 33], c: (40, 55), }, expect![[r#" { "a": "10", "b": [ "20", "33" ], "c": [ "40", 55 ] }"#]], ); } /// Test that the [`serde_as`] macro supports `serialize_as` and `deserialize_as` #[test] fn test_serde_as_macro_serialize_deserialize() { #[serde_as] #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)] struct Data { #[serde_as(serialize_as = "DisplayFromStr", deserialize_as = "DisplayFromStr")] a: u32, #[serde_as( serialize_as = "Vec", deserialize_as = "Vec" )] b: Vec, #[serde_as( serialize_as = "(DisplayFromStr, _)", deserialize_as = "(DisplayFromStr, _)" )] c: (u32, u32), } is_equal( Data { a: 10, b: vec![20, 33], c: (40, 55), }, expect![[r#" { "a": "10", "b": [ "20", "33" ], "c": [ "40", 55 ] }"#]], ); } /// Test that the [`serde_as`] macro works correctly if applied multiple times to a field #[test] fn test_serde_as_macro_multiple_field_attributes() { #[serde_as] #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)] struct Data { #[serde_as(serialize_as = "DisplayFromStr")] #[serde_as(deserialize_as = "DisplayFromStr")] a: u32, } is_equal( Data { a: 10 }, expect![[r#" { "a": "10" }"#]], ); } /// Ensure that `serde_as` applies `default` if both the field and the conversion are option. #[test] fn test_default_on_option() { #[serde_as] #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)] struct Data { #[serde_as(as = "Option")] a: Option, } is_equal( Data { a: None }, expect![[r#" { "a": null }"#]], ); is_equal( Data { a: Some(123) }, expect![[r#" { "a": "123" }"#]], ); check_deserialization(Data { a: None }, "{}"); #[serde_as] #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)] struct DataNoDefault { #[serde_as(as = "Option", no_default)] a: Option, } is_equal( DataNoDefault { a: None }, expect![[r#" { "a": null }"#]], ); is_equal( DataNoDefault { a: Some(123) }, expect![[r#" { "a": "123" }"#]], ); check_error_deserialization::( "{}", expect!["missing field `a` at line 1 column 2"], ); fn default_555() -> Option { Some(555) } #[serde_as] #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)] struct DataExplicitDefault { #[serde_as(as = "Option")] #[serde(default = "default_555")] a: Option, } is_equal( DataExplicitDefault { a: None }, expect![[r#" { "a": null }"#]], ); is_equal( DataExplicitDefault { a: Some(123) }, expect![[r#" { "a": "123" }"#]], ); check_deserialization(DataExplicitDefault { a: Some(555) }, "{}"); #[serde_as] #[derive(Debug, Eq, PartialEq, Serialize, Deserialize)] struct DataString { #[serde_as(as = "NoneAsEmptyString")] a: Option, } is_equal( DataString { a: None }, expect![[r#" { "a": "" }"#]], ); is_equal( DataString { a: Some("123".to_string()), }, expect![[r#" { "a": "123" }"#]], ); check_deserialization(DataString { a: None }, r#"{"a": ""}"#); check_deserialization( DataString { a: Some("555".to_string()), }, r#"{"a": "555"}"#, ); check_error_deserialization::( "{}", expect!["missing field `a` at line 1 column 2"], ); } serde_with-3.12.0/tests/serde_as/serde_conv.rs000064400000000000000000000026601046102023000174740ustar 00000000000000use super::*; use serde_with::serde_conv; #[test] fn test_bool_as_string() { serde_conv!(BoolAsString, bool, |x: &bool| x.to_string(), |x: String| x .parse()); #[derive(Debug, PartialEq, Serialize, Deserialize)] struct SWith(#[serde(with = "BoolAsString")] bool); is_equal(SWith(false), expect![[r#""false""#]]); is_equal(SWith(true), expect![[r#""true""#]]); check_error_deserialization::( "123", expect![[r#"invalid type: integer `123`, expected a string at line 1 column 3"#]], ); #[serde_as] #[derive(Debug, PartialEq, Serialize, Deserialize)] struct SAs(#[serde_as(as = "BoolAsString")] bool); is_equal(SAs(false), expect![[r#""false""#]]); is_equal(SAs(true), expect![[r#""true""#]]); check_error_deserialization::( "123", expect![[r#"invalid type: integer `123`, expected a string at line 1 column 3"#]], ); #[serde_as] #[derive(Debug, PartialEq, Serialize, Deserialize)] struct SAsVec(#[serde_as(as = "Vec")] Vec); is_equal( SAsVec(vec![false]), expect![[r#" [ "false" ]"#]], ); is_equal( SAsVec(vec![true]), expect![[r#" [ "true" ]"#]], ); check_error_deserialization::( "123", expect![[r#"invalid type: integer `123`, expected a sequence at line 1 column 3"#]], ); } serde_with-3.12.0/tests/serde_as/time.rs000064400000000000000000000545311046102023000163070ustar 00000000000000use super::*; use core::time::Duration; use serde_with::{ DurationMicroSeconds, DurationMicroSecondsWithFrac, DurationMilliSeconds, DurationMilliSecondsWithFrac, DurationNanoSeconds, DurationNanoSecondsWithFrac, DurationSeconds, DurationSecondsWithFrac, TimestampMicroSeconds, TimestampMicroSecondsWithFrac, TimestampMilliSeconds, TimestampMilliSecondsWithFrac, TimestampNanoSeconds, TimestampNanoSecondsWithFrac, TimestampSeconds, TimestampSecondsWithFrac, }; use std::time::SystemTime; #[test] fn test_duration_seconds() { let zero = Duration::new(0, 0); let one_second = Duration::new(1, 0); let half_second = Duration::new(0, 500_000_000); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct IntStrict(#[serde_as(as = "DurationSeconds")] Duration); is_equal(IntStrict(zero), expect![[r#"0"#]]); is_equal(IntStrict(one_second), expect![[r#"1"#]]); check_serialization(IntStrict(half_second), expect![[r#"1"#]]); check_error_deserialization::( r#""1""#, expect![[r#"invalid type: string "1", expected u64 at line 1 column 3"#]], ); check_error_deserialization::( r#"-1"#, expect![[r#"invalid value: integer `-1`, expected u64 at line 1 column 2"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct IntFlexible(#[serde_as(as = "DurationSeconds")] Duration); is_equal(IntFlexible(zero), expect![[r#"0"#]]); is_equal(IntFlexible(one_second), expect![[r#"1"#]]); check_serialization(IntFlexible(half_second), expect![[r#"1"#]]); check_deserialization(IntFlexible(half_second), r#""0.5""#); check_deserialization(IntFlexible(one_second), r#""1""#); check_deserialization(IntFlexible(zero), r#""0""#); check_error_deserialization::( r#""a""#, expect![[ r#"invalid value: string "a", expected an integer, a float, or a string containing a number at line 1 column 3"# ]], ); check_error_deserialization::( r#"-1"#, expect![[r#"std::time::Duration cannot be negative"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct F64Strict(#[serde_as(as = "DurationSeconds")] Duration); is_equal(F64Strict(zero), expect![[r#"0.0"#]]); is_equal(F64Strict(one_second), expect![[r#"1.0"#]]); check_serialization(F64Strict(half_second), expect![[r#"1.0"#]]); check_deserialization(F64Strict(one_second), r#"0.5"#); check_error_deserialization::( r#""1""#, expect![[r#"invalid type: string "1", expected f64 at line 1 column 3"#]], ); check_error_deserialization::( r#"-1.0"#, expect![[r#"std::time::Duration cannot be negative"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct F64Flexible(#[serde_as(as = "DurationSeconds")] Duration); is_equal(F64Flexible(zero), expect![[r#"0.0"#]]); is_equal(F64Flexible(one_second), expect![[r#"1.0"#]]); check_serialization(F64Flexible(half_second), expect![[r#"1.0"#]]); check_deserialization(F64Flexible(half_second), r#""0.5""#); check_deserialization(F64Flexible(one_second), r#""1""#); check_deserialization(F64Flexible(zero), r#""0""#); check_error_deserialization::( r#""a""#, expect![[ r#"invalid value: string "a", expected an integer, a float, or a string containing a number at line 1 column 3"# ]], ); check_error_deserialization::( r#"-1"#, expect![[r#"std::time::Duration cannot be negative"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct StringStrict(#[serde_as(as = "DurationSeconds")] Duration); is_equal(StringStrict(zero), expect![[r#""0""#]]); is_equal(StringStrict(one_second), expect![[r#""1""#]]); check_serialization(StringStrict(half_second), expect![[r#""1""#]]); check_error_deserialization::( r#"1"#, expect![[ r#"invalid type: integer `1`, expected a string containing a number at line 1 column 1"# ]], ); check_error_deserialization::( r#"-1"#, expect![[ r#"invalid type: integer `-1`, expected a string containing a number at line 1 column 2"# ]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct StringFlexible(#[serde_as(as = "DurationSeconds")] Duration); is_equal(StringFlexible(zero), expect![[r#""0""#]]); is_equal(StringFlexible(one_second), expect![[r#""1""#]]); check_serialization(StringFlexible(half_second), expect![[r#""1""#]]); check_deserialization(StringFlexible(half_second), r#""0.5""#); check_deserialization(StringFlexible(one_second), r#""1""#); check_deserialization(StringFlexible(zero), r#""0""#); check_error_deserialization::( r#""a""#, expect![[ r#"invalid value: string "a", expected an integer, a float, or a string containing a number at line 1 column 3"# ]], ); check_error_deserialization::( r#"-1"#, expect![[r#"std::time::Duration cannot be negative"#]], ); } #[test] fn test_duration_seconds_with_frac() { let zero = Duration::new(0, 0); let one_second = Duration::new(1, 0); let half_second = Duration::new(0, 500_000_000); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct F64Strict(#[serde_as(as = "DurationSecondsWithFrac")] Duration); is_equal(F64Strict(zero), expect![[r#"0.0"#]]); is_equal(F64Strict(one_second), expect![[r#"1.0"#]]); is_equal(F64Strict(half_second), expect![[r#"0.5"#]]); check_error_deserialization::( r#""1""#, expect![[r#"invalid type: string "1", expected f64 at line 1 column 3"#]], ); check_error_deserialization::( r#"-1.0"#, expect![[r#"std::time::Duration cannot be negative"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct F64Flexible(#[serde_as(as = "DurationSecondsWithFrac")] Duration); is_equal(F64Flexible(zero), expect![[r#"0.0"#]]); is_equal(F64Flexible(one_second), expect![[r#"1.0"#]]); is_equal(F64Flexible(half_second), expect![[r#"0.5"#]]); check_deserialization(F64Flexible(one_second), r#""1""#); check_deserialization(F64Flexible(zero), r#""0""#); check_error_deserialization::( r#""a""#, expect![[ r#"invalid value: string "a", expected an integer, a float, or a string containing a number at line 1 column 3"# ]], ); check_error_deserialization::( r#"-1"#, expect![[r#"std::time::Duration cannot be negative"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct StringStrict(#[serde_as(as = "DurationSecondsWithFrac")] Duration); is_equal(StringStrict(zero), expect![[r#""0""#]]); is_equal(StringStrict(one_second), expect![[r#""1""#]]); is_equal(StringStrict(half_second), expect![[r#""0.5""#]]); check_error_deserialization::( r#"1"#, expect![[r#"invalid type: integer `1`, expected a string at line 1 column 1"#]], ); check_error_deserialization::( r#"-1"#, expect![[r#"invalid type: integer `-1`, expected a string at line 1 column 2"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct StringFlexible(#[serde_as(as = "DurationSecondsWithFrac")] Duration); is_equal(StringFlexible(zero), expect![[r#""0""#]]); is_equal(StringFlexible(one_second), expect![[r#""1""#]]); is_equal(StringFlexible(half_second), expect![[r#""0.5""#]]); check_deserialization(StringFlexible(zero), r#""0""#); check_error_deserialization::( r#""a""#, expect![[ r#"invalid value: string "a", expected an integer, a float, or a string containing a number at line 1 column 3"# ]], ); check_error_deserialization::( r#"-1"#, expect![[r#"std::time::Duration cannot be negative"#]], ); } #[test] fn test_timestamp_seconds_systemtime() { let zero = SystemTime::UNIX_EPOCH; let one_second = SystemTime::UNIX_EPOCH .checked_add(Duration::new(1, 0)) .unwrap(); let half_second = SystemTime::UNIX_EPOCH .checked_add(Duration::new(0, 500_000_000)) .unwrap(); let minus_one_second = SystemTime::UNIX_EPOCH .checked_sub(Duration::new(1, 0)) .unwrap(); let minus_half_second = SystemTime::UNIX_EPOCH .checked_sub(Duration::new(0, 500_000_000)) .unwrap(); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct StructIntStrict(#[serde_as(as = "TimestampSeconds")] SystemTime); is_equal(StructIntStrict(zero), expect![[r#"0"#]]); is_equal(StructIntStrict(one_second), expect![[r#"1"#]]); is_equal(StructIntStrict(minus_one_second), expect![[r#"-1"#]]); check_serialization(StructIntStrict(half_second), expect![[r#"1"#]]); check_serialization(StructIntStrict(minus_half_second), expect![[r#"-1"#]]); check_error_deserialization::( r#""1""#, expect![[r#"invalid type: string "1", expected i64 at line 1 column 3"#]], ); check_error_deserialization::( r#"0.123"#, expect![[r#"invalid type: floating point `0.123`, expected i64 at line 1 column 5"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct StructIntFlexible(#[serde_as(as = "TimestampSeconds")] SystemTime); is_equal(StructIntFlexible(zero), expect![[r#"0"#]]); is_equal(StructIntFlexible(one_second), expect![[r#"1"#]]); is_equal(StructIntFlexible(minus_one_second), expect![[r#"-1"#]]); check_serialization(StructIntFlexible(half_second), expect![[r#"1"#]]); check_serialization(StructIntFlexible(minus_half_second), expect![[r#"-1"#]]); check_deserialization(StructIntFlexible(one_second), r#""1""#); check_deserialization(StructIntFlexible(one_second), r#"1.0"#); check_deserialization(StructIntFlexible(minus_half_second), r#""-0.5""#); check_deserialization(StructIntFlexible(half_second), r#"0.5"#); check_error_deserialization::( r#""a""#, expect![[ r#"invalid value: string "a", expected an integer, a float, or a string containing a number at line 1 column 3"# ]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct Structf64Strict(#[serde_as(as = "TimestampSeconds")] SystemTime); is_equal(Structf64Strict(zero), expect![[r#"0.0"#]]); is_equal(Structf64Strict(one_second), expect![[r#"1.0"#]]); is_equal(Structf64Strict(minus_one_second), expect![[r#"-1.0"#]]); check_serialization(Structf64Strict(half_second), expect![[r#"1.0"#]]); check_serialization(Structf64Strict(minus_half_second), expect![[r#"-1.0"#]]); check_deserialization(Structf64Strict(one_second), r#"0.5"#); check_error_deserialization::( r#""1""#, expect![[r#"invalid type: string "1", expected f64 at line 1 column 3"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct Structf64Flexible(#[serde_as(as = "TimestampSeconds")] SystemTime); is_equal(Structf64Flexible(zero), expect![[r#"0.0"#]]); is_equal(Structf64Flexible(one_second), expect![[r#"1.0"#]]); is_equal(Structf64Flexible(minus_one_second), expect![[r#"-1.0"#]]); check_serialization(Structf64Flexible(half_second), expect![[r#"1.0"#]]); check_serialization(Structf64Flexible(minus_half_second), expect![[r#"-1.0"#]]); check_deserialization(Structf64Flexible(one_second), r#""1""#); check_deserialization(Structf64Flexible(one_second), r#"1.0"#); check_deserialization(Structf64Flexible(minus_half_second), r#""-0.5""#); check_deserialization(Structf64Flexible(half_second), r#"0.5"#); check_error_deserialization::( r#""a""#, expect![[ r#"invalid value: string "a", expected an integer, a float, or a string containing a number at line 1 column 3"# ]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct StructStringStrict(#[serde_as(as = "TimestampSeconds")] SystemTime); is_equal(StructStringStrict(zero), expect![[r#""0""#]]); is_equal(StructStringStrict(one_second), expect![[r#""1""#]]); is_equal(StructStringStrict(minus_one_second), expect![[r#""-1""#]]); check_serialization(StructStringStrict(half_second), expect![[r#""1""#]]); check_serialization(StructStringStrict(minus_half_second), expect![[r#""-1""#]]); check_deserialization(StructStringStrict(one_second), r#""1""#); check_error_deserialization::( r#""0.5""#, expect![[r#"invalid digit found in string at line 1 column 5"#]], ); check_error_deserialization::( r#""-0.5""#, expect![[r#"invalid digit found in string at line 1 column 6"#]], ); check_error_deserialization::( r#"1"#, expect![[ r#"invalid type: integer `1`, expected a string containing a number at line 1 column 1"# ]], ); check_error_deserialization::( r#"0.1"#, expect![[ r#"invalid type: floating point `0.1`, expected a string containing a number at line 1 column 3"# ]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct StructStringFlexible(#[serde_as(as = "TimestampSeconds")] SystemTime); is_equal(StructStringFlexible(zero), expect![[r#""0""#]]); is_equal(StructStringFlexible(one_second), expect![[r#""1""#]]); is_equal(StructStringFlexible(minus_one_second), expect![[r#""-1""#]]); check_serialization(StructStringFlexible(half_second), expect![[r#""1""#]]); check_serialization( StructStringFlexible(minus_half_second), expect![[r#""-1""#]], ); check_deserialization(StructStringFlexible(one_second), r#"1"#); check_deserialization(StructStringFlexible(one_second), r#"1.0"#); check_deserialization(StructStringFlexible(minus_half_second), r#""-0.5""#); check_deserialization(StructStringFlexible(half_second), r#"0.5"#); check_error_deserialization::( r#""a""#, expect![[ r#"invalid value: string "a", expected an integer, a float, or a string containing a number at line 1 column 3"# ]], ); } #[test] fn test_timestamp_seconds_with_frac_systemtime() { let zero = SystemTime::UNIX_EPOCH; let one_second = SystemTime::UNIX_EPOCH .checked_add(Duration::new(1, 0)) .unwrap(); let half_second = SystemTime::UNIX_EPOCH .checked_add(Duration::new(0, 500_000_000)) .unwrap(); let minus_one_second = SystemTime::UNIX_EPOCH .checked_sub(Duration::new(1, 0)) .unwrap(); let minus_half_second = SystemTime::UNIX_EPOCH .checked_sub(Duration::new(0, 500_000_000)) .unwrap(); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct Structf64Strict(#[serde_as(as = "TimestampSecondsWithFrac")] SystemTime); is_equal(Structf64Strict(zero), expect![[r#"0.0"#]]); is_equal(Structf64Strict(one_second), expect![[r#"1.0"#]]); is_equal(Structf64Strict(minus_one_second), expect![[r#"-1.0"#]]); is_equal(Structf64Strict(half_second), expect![[r#"0.5"#]]); is_equal(Structf64Strict(minus_half_second), expect![[r#"-0.5"#]]); check_error_deserialization::( r#""1""#, expect![[r#"invalid type: string "1", expected f64 at line 1 column 3"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct Structf64Flexible( #[serde_as(as = "TimestampSecondsWithFrac")] SystemTime, ); is_equal(Structf64Flexible(zero), expect![[r#"0.0"#]]); is_equal(Structf64Flexible(one_second), expect![[r#"1.0"#]]); is_equal(Structf64Flexible(minus_one_second), expect![[r#"-1.0"#]]); is_equal(Structf64Flexible(half_second), expect![[r#"0.5"#]]); is_equal(Structf64Flexible(minus_half_second), expect![[r#"-0.5"#]]); check_deserialization(Structf64Flexible(one_second), r#""1""#); check_deserialization(Structf64Flexible(one_second), r#"1.0"#); check_deserialization(Structf64Flexible(minus_half_second), r#""-0.5""#); check_deserialization(Structf64Flexible(half_second), r#"0.5"#); check_error_deserialization::( r#""a""#, expect![[ r#"invalid value: string "a", expected an integer, a float, or a string containing a number at line 1 column 3"# ]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct StructStringStrict(#[serde_as(as = "TimestampSecondsWithFrac")] SystemTime); is_equal(StructStringStrict(zero), expect![[r#""0""#]]); is_equal(StructStringStrict(one_second), expect![[r#""1""#]]); is_equal(StructStringStrict(minus_one_second), expect![[r#""-1""#]]); is_equal(StructStringStrict(half_second), expect![[r#""0.5""#]]); is_equal( StructStringStrict(minus_half_second), expect![[r#""-0.5""#]], ); check_error_deserialization::( r#"1"#, expect![[r#"invalid type: integer `1`, expected a string at line 1 column 1"#]], ); check_error_deserialization::( r#"0.1"#, expect![[r#"invalid type: floating point `0.1`, expected a string at line 1 column 3"#]], ); #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct StructStringFlexible( #[serde_as(as = "TimestampSecondsWithFrac")] SystemTime, ); is_equal(StructStringFlexible(zero), expect![[r#""0""#]]); is_equal(StructStringFlexible(one_second), expect![[r#""1""#]]); is_equal(StructStringFlexible(minus_one_second), expect![[r#""-1""#]]); is_equal(StructStringFlexible(half_second), expect![[r#""0.5""#]]); is_equal( StructStringFlexible(minus_half_second), expect![[r#""-0.5""#]], ); check_deserialization(StructStringFlexible(one_second), r#"1"#); check_deserialization(StructStringFlexible(one_second), r#"1.0"#); check_deserialization(StructStringFlexible(half_second), r#"0.5"#); check_error_deserialization::( r#""a""#, expect![[ r#"invalid value: string "a", expected an integer, a float, or a string containing a number at line 1 column 3"# ]], ); } macro_rules! smoketest { ($($valuety:ty, $adapter:literal, $value:ident, $expect:tt;)*) => { $({ #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S(#[serde_as(as = $adapter)] $valuety); #[allow(unused_braces)] is_equal(S($value), $expect); })* }; } #[test] fn test_duration_smoketest() { let one_second = Duration::new(1, 0); smoketest! { Duration, "DurationSeconds", one_second, {expect![[r#"1"#]]}; Duration, "DurationSeconds", one_second, {expect![[r#"1.0"#]]}; Duration, "DurationMilliSeconds", one_second, {expect![[r#"1000"#]]}; Duration, "DurationMilliSeconds", one_second, {expect![[r#"1000.0"#]]}; Duration, "DurationMicroSeconds", one_second, {expect![[r#"1000000"#]]}; Duration, "DurationMicroSeconds", one_second, {expect![[r#"1000000.0"#]]}; Duration, "DurationNanoSeconds", one_second, {expect![[r#"1000000000"#]]}; Duration, "DurationNanoSeconds", one_second, {expect![[r#"1000000000.0"#]]}; }; smoketest! { Duration, "DurationSecondsWithFrac", one_second, {expect![[r#"1.0"#]]}; Duration, "DurationSecondsWithFrac", one_second, {expect![[r#""1""#]]}; Duration, "DurationMilliSecondsWithFrac", one_second, {expect![[r#"1000.0"#]]}; Duration, "DurationMilliSecondsWithFrac", one_second, {expect![[r#""1000""#]]}; Duration, "DurationMicroSecondsWithFrac", one_second, {expect![[r#"1000000.0"#]]}; Duration, "DurationMicroSecondsWithFrac", one_second, {expect![[r#""1000000""#]]}; Duration, "DurationNanoSecondsWithFrac", one_second, {expect![[r#"1000000000.0"#]]}; Duration, "DurationNanoSecondsWithFrac", one_second, {expect![[r#""1000000000""#]]}; }; } #[test] fn test_timestamp_systemtime_smoketest() { let one_second = SystemTime::UNIX_EPOCH .checked_add(Duration::new(1, 0)) .unwrap(); smoketest! { SystemTime, "TimestampSeconds", one_second, {expect![[r#"1"#]]}; SystemTime, "TimestampSeconds", one_second, {expect![[r#"1.0"#]]}; SystemTime, "TimestampMilliSeconds", one_second, {expect![[r#"1000"#]]}; SystemTime, "TimestampMilliSeconds", one_second, {expect![[r#"1000.0"#]]}; SystemTime, "TimestampMicroSeconds", one_second, {expect![[r#"1000000"#]]}; SystemTime, "TimestampMicroSeconds", one_second, {expect![[r#"1000000.0"#]]}; SystemTime, "TimestampNanoSeconds", one_second, {expect![[r#"1000000000"#]]}; SystemTime, "TimestampNanoSeconds", one_second, {expect![[r#"1000000000.0"#]]}; }; smoketest! { SystemTime, "TimestampSecondsWithFrac", one_second, {expect![[r#"1.0"#]]}; SystemTime, "TimestampSecondsWithFrac", one_second, {expect![[r#""1""#]]}; SystemTime, "TimestampMilliSecondsWithFrac", one_second, {expect![[r#"1000.0"#]]}; SystemTime, "TimestampMilliSecondsWithFrac", one_second, {expect![[r#""1000""#]]}; SystemTime, "TimestampMicroSecondsWithFrac", one_second, {expect![[r#"1000000.0"#]]}; SystemTime, "TimestampMicroSecondsWithFrac", one_second, {expect![[r#""1000000""#]]}; SystemTime, "TimestampNanoSecondsWithFrac", one_second, {expect![[r#"1000000000.0"#]]}; SystemTime, "TimestampNanoSecondsWithFrac", one_second, {expect![[r#""1000000000""#]]}; }; } serde_with-3.12.0/tests/time_0_3.rs000064400000000000000000000300221046102023000151500ustar 00000000000000//! Test Cases mod utils; use crate::utils::{check_deserialization, check_error_deserialization, is_equal}; use expect_test::expect; use serde::{Deserialize, Serialize}; use serde_with::{ serde_as, DurationMicroSeconds, DurationMicroSecondsWithFrac, DurationMilliSeconds, DurationMilliSecondsWithFrac, DurationNanoSeconds, DurationNanoSecondsWithFrac, DurationSeconds, DurationSecondsWithFrac, TimestampMicroSeconds, TimestampMicroSecondsWithFrac, TimestampMilliSeconds, TimestampMilliSecondsWithFrac, TimestampNanoSeconds, TimestampNanoSecondsWithFrac, TimestampSeconds, TimestampSecondsWithFrac, }; use time_0_3::{Duration, OffsetDateTime, PrimitiveDateTime, UtcOffset}; /// Create a [`PrimitiveDateTime`] for the Unix Epoch fn unix_epoch_primitive() -> PrimitiveDateTime { PrimitiveDateTime::new( time_0_3::Date::from_ordinal_date(1970, 1).unwrap(), time_0_3::Time::from_hms_nano(0, 0, 0, 0).unwrap(), ) } macro_rules! smoketest { ($($valuety:ty, $adapter:literal, $value:expr, $expect:tt;)*) => { $({ #[serde_as] #[derive(Debug, Serialize, Deserialize, PartialEq)] struct S(#[serde_as(as = $adapter)] $valuety); #[allow(unused_braces)] is_equal(S($value), $expect); })* }; } #[test] fn test_duration_smoketest() { let zero = Duration::seconds(0); let one_second = Duration::seconds(1); smoketest! { Duration, "DurationSeconds", one_second, {expect![[r#"1"#]]}; Duration, "DurationSeconds", one_second, {expect![[r#"1.0"#]]}; Duration, "DurationMilliSeconds", one_second, {expect![[r#"1000"#]]}; Duration, "DurationMilliSeconds", one_second, {expect![[r#"1000.0"#]]}; Duration, "DurationMicroSeconds", one_second, {expect![[r#"1000000"#]]}; Duration, "DurationMicroSeconds", one_second, {expect![[r#"1000000.0"#]]}; Duration, "DurationNanoSeconds", one_second, {expect![[r#"1000000000"#]]}; Duration, "DurationNanoSeconds", one_second, {expect![[r#"1000000000.0"#]]}; }; smoketest! { Duration, "DurationSecondsWithFrac", one_second, {expect![[r#"1.0"#]]}; Duration, "DurationSecondsWithFrac", one_second, {expect![[r#""1""#]]}; Duration, "DurationMilliSecondsWithFrac", one_second, {expect![[r#"1000.0"#]]}; Duration, "DurationMilliSecondsWithFrac", one_second, {expect![[r#""1000""#]]}; Duration, "DurationMicroSecondsWithFrac", one_second, {expect![[r#"1000000.0"#]]}; Duration, "DurationMicroSecondsWithFrac", one_second, {expect![[r#""1000000""#]]}; Duration, "DurationNanoSecondsWithFrac", one_second, {expect![[r#"1000000000.0"#]]}; Duration, "DurationNanoSecondsWithFrac", one_second, {expect![[r#""1000000000""#]]}; }; smoketest! { Duration, "DurationSecondsWithFrac", zero, {expect![[r#"0.0"#]]}; Duration, "DurationSecondsWithFrac", zero + Duration::nanoseconds(500_000_000), {expect![[r#"0.5"#]]}; Duration, "DurationSecondsWithFrac", zero + Duration::seconds(1), {expect![[r#"1.0"#]]}; Duration, "DurationSecondsWithFrac", zero - Duration::nanoseconds(500_000_000), {expect![[r#"-0.5"#]]}; Duration, "DurationSecondsWithFrac", zero - Duration::seconds(1), {expect![[r#"-1.0"#]]}; }; } #[test] fn test_datetime_utc_smoketest() { let zero = OffsetDateTime::UNIX_EPOCH; let one_second = zero + Duration::seconds(1); smoketest! { OffsetDateTime, "TimestampSeconds", one_second, {expect![[r#"1"#]]}; OffsetDateTime, "TimestampSeconds", one_second, {expect![[r#"1.0"#]]}; OffsetDateTime, "TimestampMilliSeconds", one_second, {expect![[r#"1000"#]]}; OffsetDateTime, "TimestampMilliSeconds", one_second, {expect![[r#"1000.0"#]]}; OffsetDateTime, "TimestampMicroSeconds", one_second, {expect![[r#"1000000"#]]}; OffsetDateTime, "TimestampMicroSeconds", one_second, {expect![[r#"1000000.0"#]]}; OffsetDateTime, "TimestampNanoSeconds", one_second, {expect![[r#"1000000000"#]]}; OffsetDateTime, "TimestampNanoSeconds", one_second, {expect![[r#"1000000000.0"#]]}; }; smoketest! { OffsetDateTime, "TimestampSecondsWithFrac", one_second, {expect![[r#"1.0"#]]}; OffsetDateTime, "TimestampSecondsWithFrac", one_second, {expect![[r#""1""#]]}; OffsetDateTime, "TimestampMilliSecondsWithFrac", one_second, {expect![[r#"1000.0"#]]}; OffsetDateTime, "TimestampMilliSecondsWithFrac", one_second, {expect![[r#""1000""#]]}; OffsetDateTime, "TimestampMicroSecondsWithFrac", one_second, {expect![[r#"1000000.0"#]]}; OffsetDateTime, "TimestampMicroSecondsWithFrac", one_second, {expect![[r#""1000000""#]]}; OffsetDateTime, "TimestampNanoSecondsWithFrac", one_second, {expect![[r#"1000000000.0"#]]}; OffsetDateTime, "TimestampNanoSecondsWithFrac", one_second, {expect![[r#""1000000000""#]]}; }; smoketest! { OffsetDateTime, "TimestampSecondsWithFrac", zero, {expect![[r#"0.0"#]]}; OffsetDateTime, "TimestampSecondsWithFrac", zero + Duration::nanoseconds(500_000_000), {expect![[r#"0.5"#]]}; OffsetDateTime, "TimestampSecondsWithFrac", zero + Duration::seconds(1), {expect![[r#"1.0"#]]}; OffsetDateTime, "TimestampSecondsWithFrac", zero - Duration::nanoseconds(500_000_000), {expect![[r#"-0.5"#]]}; OffsetDateTime, "TimestampSecondsWithFrac", zero - Duration::seconds(1), {expect![[r#"-1.0"#]]}; }; } #[test] fn test_naive_datetime_smoketest() { let zero = unix_epoch_primitive(); let one_second = zero + Duration::seconds(1); smoketest! { PrimitiveDateTime, "TimestampSeconds", one_second, {expect![[r#"1"#]]}; PrimitiveDateTime, "TimestampSeconds", one_second, {expect![[r#"1.0"#]]}; PrimitiveDateTime, "TimestampMilliSeconds", one_second, {expect![[r#"1000"#]]}; PrimitiveDateTime, "TimestampMilliSeconds", one_second, {expect![[r#"1000.0"#]]}; PrimitiveDateTime, "TimestampMicroSeconds", one_second, {expect![[r#"1000000"#]]}; PrimitiveDateTime, "TimestampMicroSeconds", one_second, {expect![[r#"1000000.0"#]]}; PrimitiveDateTime, "TimestampNanoSeconds", one_second, {expect![[r#"1000000000"#]]}; PrimitiveDateTime, "TimestampNanoSeconds", one_second, {expect![[r#"1000000000.0"#]]}; }; smoketest! { PrimitiveDateTime, "TimestampSecondsWithFrac", one_second, {expect![[r#"1.0"#]]}; PrimitiveDateTime, "TimestampSecondsWithFrac", one_second, {expect![[r#""1""#]]}; PrimitiveDateTime, "TimestampMilliSecondsWithFrac", one_second, {expect![[r#"1000.0"#]]}; PrimitiveDateTime, "TimestampMilliSecondsWithFrac", one_second, {expect![[r#""1000""#]]}; PrimitiveDateTime, "TimestampMicroSecondsWithFrac", one_second, {expect![[r#"1000000.0"#]]}; PrimitiveDateTime, "TimestampMicroSecondsWithFrac", one_second, {expect![[r#""1000000""#]]}; PrimitiveDateTime, "TimestampNanoSecondsWithFrac", one_second, {expect![[r#"1000000000.0"#]]}; PrimitiveDateTime, "TimestampNanoSecondsWithFrac", one_second, {expect![[r#""1000000000""#]]}; }; smoketest! { PrimitiveDateTime, "TimestampSecondsWithFrac", zero, {expect![[r#"0.0"#]]}; PrimitiveDateTime, "TimestampSecondsWithFrac", zero + Duration::nanoseconds(500_000_000), {expect![[r#"0.5"#]]}; PrimitiveDateTime, "TimestampSecondsWithFrac", zero + Duration::seconds(1), {expect![[r#"1.0"#]]}; PrimitiveDateTime, "TimestampSecondsWithFrac", zero - Duration::nanoseconds(500_000_000), {expect![[r#"-0.5"#]]}; PrimitiveDateTime, "TimestampSecondsWithFrac", zero - Duration::seconds(1), {expect![[r#"-1.0"#]]}; }; } #[test] fn test_offset_datetime_rfc2822() { #[serde_as] #[derive(Debug, PartialEq, Deserialize, Serialize)] struct S(#[serde_as(as = "time_0_3::format_description::well_known::Rfc2822")] OffsetDateTime); is_equal( S(OffsetDateTime::UNIX_EPOCH), expect![[r#""Thu, 01 Jan 1970 00:00:00 +0000""#]], ); check_error_deserialization::( r#""Foobar""#, expect!["the 'day' component could not be parsed at line 1 column 8"], ); check_error_deserialization::( r#""Fri, 2000""#, expect![[r#"a character literal was not valid at line 1 column 11"#]], ); } #[test] fn test_offset_datetime_rfc3339() { #[serde_as] #[derive(Debug, PartialEq, Deserialize, Serialize)] struct S(#[serde_as(as = "time_0_3::format_description::well_known::Rfc3339")] OffsetDateTime); is_equal( S(OffsetDateTime::UNIX_EPOCH), expect![[r#""1970-01-01T00:00:00Z""#]], ); check_deserialization::( S( OffsetDateTime::from_unix_timestamp_nanos(482_196_050_520_000_000) .unwrap() .to_offset(UtcOffset::from_hms(0, 0, 0).unwrap()), ), r#""1985-04-12T23:20:50.52Z""#, ); check_deserialization::( S(OffsetDateTime::from_unix_timestamp(851_042_397) .unwrap() .to_offset(UtcOffset::from_hms(-8, 0, 0).unwrap())), r#""1996-12-19T16:39:57-08:00""#, ); check_deserialization::( S( OffsetDateTime::from_unix_timestamp_nanos(662_687_999_999_999_999) .unwrap() .to_offset(UtcOffset::from_hms(0, 0, 0).unwrap()), ), r#""1990-12-31T23:59:60Z""#, ); check_deserialization::( S( OffsetDateTime::from_unix_timestamp_nanos(662_687_999_999_999_999) .unwrap() .to_offset(UtcOffset::from_hms(-8, 0, 0).unwrap()), ), r#""1990-12-31T15:59:60-08:00""#, ); check_deserialization::( S( OffsetDateTime::from_unix_timestamp_nanos(-1_041_337_172_130_000_000) .unwrap() .to_offset(UtcOffset::from_hms(0, 20, 0).unwrap()), ), r#""1937-01-01T12:00:27.87+00:20""#, ); check_error_deserialization::( r#""Foobar""#, expect![[r#"the 'year' component could not be parsed at line 1 column 8"#]], ); check_error_deserialization::( r#""2000-AA""#, expect![[r#"the 'month' component could not be parsed at line 1 column 9"#]], ); } #[test] fn test_offset_datetime_iso8601() { /// The default configuration for [`Iso8601`]. const DEFAULT_CONFIG: time_0_3::format_description::well_known::iso8601::EncodedConfig = time_0_3::format_description::well_known::iso8601::Config::DEFAULT.encode(); #[serde_as] #[derive(Debug, PartialEq, Deserialize, Serialize)] struct S( #[serde_as(as = "time_0_3::format_description::well_known::Iso8601")] OffsetDateTime, ); is_equal( S(OffsetDateTime::UNIX_EPOCH), expect![[r#""1970-01-01T00:00:00.000000000Z""#]], ); check_deserialization::( S( OffsetDateTime::from_unix_timestamp_nanos(482_196_050_520_000_000) .unwrap() .to_offset(UtcOffset::from_hms(0, 0, 0).unwrap()), ), r#""1985-04-12T23:20:50.52Z""#, ); check_deserialization::( S(OffsetDateTime::from_unix_timestamp(851_042_397) .unwrap() .to_offset(UtcOffset::from_hms(-8, 0, 0).unwrap())), r#""1996-12-19T16:39:57-08:00""#, ); check_deserialization::( S( OffsetDateTime::from_unix_timestamp_nanos(-1_041_337_172_130_000_000) .unwrap() .to_offset(UtcOffset::from_hms(0, 20, 0).unwrap()), ), r#""1937-01-01T12:00:27.87+00:20""#, ); check_error_deserialization::( r#""Foobar""#, expect![[r#"the 'year' component could not be parsed at line 1 column 8"#]], ); check_error_deserialization::( r#""2000-AA""#, expect!["unexpected trailing characters; the end of input was expected at line 1 column 9"], ); } serde_with-3.12.0/tests/utils.rs000064400000000000000000000072571046102023000147270ustar 00000000000000#![allow(dead_code, missing_docs)] use core::fmt::Debug; use expect_test::Expect; use pretty_assertions::assert_eq; use serde::{de::DeserializeOwned, Serialize}; #[track_caller] pub fn is_equal(value: T, expected: Expect) where T: Debug + DeserializeOwned + PartialEq + Serialize, { let serialized = serde_json::to_string_pretty(&value).unwrap(); expected.assert_eq(&serialized); assert_eq!( value, serde_json::from_str::(&serialized).unwrap(), "Deserialization differs from expected value." ); } /// Like [`is_equal`] but not pretty-print #[track_caller] pub fn is_equal_compact(value: T, expected: Expect) where T: Debug + DeserializeOwned + PartialEq + Serialize, { let serialized = serde_json::to_string(&value).unwrap(); expected.assert_eq(&serialized); assert_eq!( value, serde_json::from_str::(&serialized).unwrap(), "Deserialization differs from expected value." ); } #[track_caller] pub fn check_deserialization(value: T, deserialize_from: &str) where T: Debug + DeserializeOwned + PartialEq, { assert_eq!( value, serde_json::from_str::(deserialize_from).unwrap(), "Deserialization differs from expected value." ); } #[track_caller] pub fn check_serialization(value: T, serialize_to: Expect) where T: Debug + Serialize, { serialize_to.assert_eq(&serde_json::to_string_pretty(&value).unwrap()); } #[track_caller] pub fn check_error_serialization(value: T, error_msg: Expect) where T: Debug + Serialize, { error_msg.assert_eq( &serde_json::to_string_pretty(&value) .unwrap_err() .to_string(), ); } #[track_caller] pub fn check_error_deserialization(deserialize_from: &str, error_msg: Expect) where T: Debug + DeserializeOwned, { error_msg.assert_eq( &serde_json::from_str::(deserialize_from) .unwrap_err() .to_string(), ); } #[track_caller] pub fn check_matches_schema(value: &serde_json::Value) where T: schemars_0_8::JsonSchema, { use jsonschema::Validator; use std::fmt::Write; if cfg!(feature = "schemars_0_8") { let schema_object = serde_json::to_value(schemars_0_8::schema_for!(T)) .expect("schema for T could not be serialized to json"); let schema = match Validator::new(&schema_object) { Ok(schema) => schema, Err(e) => panic!("schema for T was not a valid JSON schema: {e}"), }; if let Err(err) = schema.validate(value) { let mut message = String::new(); let _ = writeln!( &mut message, "Object was not valid according to its own schema:" ); let _ = writeln!(&mut message, " -> {}", err); let _ = writeln!(&mut message); let _ = writeln!(&mut message, "Object Value:"); let _ = writeln!( &mut message, "{}", serde_json::to_string_pretty(&value).unwrap_or_else(|e| format!("> error: {e}")) ); let _ = writeln!(&mut message); let _ = writeln!(&mut message, "JSON Schema:"); let _ = writeln!( &mut message, "{}", serde_json::to_string_pretty(&schema_object) .unwrap_or_else(|e| format!("> error: {e}")) ); panic!("{}", message); }; } } #[track_caller] pub fn check_valid_json_schema(value: &T) where T: schemars_0_8::JsonSchema + Serialize, { let value = serde_json::to_value(value).expect("could not serialize T to json"); check_matches_schema::(&value); } serde_with-3.12.0/tests/version_numbers.rs000064400000000000000000000046621046102023000170040ustar 00000000000000//! Test Cases use version_sync::{assert_contains_regex, assert_html_root_url_updated}; #[test] fn test_changelog() { assert_contains_regex!("CHANGELOG.md", r"## \[{version}\]"); } #[test] fn test_html_root_url() { assert_html_root_url_updated!("src/lib.rs"); } #[test] fn test_serde_with_macros_dependency() { version_sync::assert_contains_regex!( "../serde_with/Cargo.toml", r#"^serde_with_macros = .*? version = "={version}""# ); version_sync::assert_contains_regex!("../Cargo.toml", r#"^version = "{version}""#); } /// Check that all docs.rs links point to the current version /// /// Parse all docs.rs links in `*.rs` and `*.md` files and check that they point to the current version. /// If a link should point to latest version this can be done by using `latest` in the version. /// The `*` version specifier is not allowed. /// /// Arguably this should be part of version-sync. There is an open issue for this feature: /// #[test] fn test_docs_rs_url_point_to_current_version() -> Result<(), Box> { let pkg_name = env!("CARGO_PKG_NAME"); let pkg_version = env!("CARGO_PKG_VERSION"); let re = regex::Regex::new(&format!( "https?://docs.rs/{pkg_name}/((\\d[^/]+|\\*|latest))/" ))?; let mut error = false; for entry in glob::glob("**/*.rs")?.chain(glob::glob("**/README.md")?) { let entry = entry?; let content = std::fs::read_to_string(&entry)?; for (line_number, line) in content.split('\n').enumerate() { for capture in re.captures_iter(line) { match capture .get(1) .expect("Will exist if regex matches") .as_str() { "latest" => {} version if version != pkg_version => { error = true; println!( "{}:{} pkg_version is {} but found URL {}", entry.display(), line_number + 1, pkg_version, capture.get(0).expect("Group 0 always exists").as_str() ); } _ => {} } } } } if error { panic!("Found wrong URLs in file(s)"); } else { Ok(()) } } serde_with-3.12.0/tests/with_prefix.rs000064400000000000000000000073371046102023000161160ustar 00000000000000//! Test Cases extern crate alloc; mod utils; use crate::utils::is_equal; use alloc::collections::BTreeMap; use expect_test::expect; use serde::{Deserialize, Serialize}; use serde_with::with_prefix; use std::collections::HashMap; #[test] fn test_flatten_with_prefix() { #[derive(Serialize, Deserialize, PartialEq, Debug)] struct Match { #[serde(flatten, with = "prefix_player1")] player1: Player, #[serde(flatten, with = "prefix_player2")] player2: Option, #[serde(flatten, with = "prefix_player3")] player3: Option, #[serde(flatten, with = "prefix_tag")] tags: HashMap, } #[derive(Serialize, Deserialize, PartialEq, Debug)] struct Player { name: String, votes: u64, } with_prefix!(prefix_player1 "player1_"); with_prefix!(prefix_player2 "player2_"); with_prefix!(prefix_player3 "player3_"); with_prefix!(prefix_tag "tag_"); let m = Match { player1: Player { name: "name1".to_owned(), votes: 1, }, player2: Some(Player { name: "name2".to_owned(), votes: 2, }), player3: None, tags: HashMap::from_iter(vec![("t".to_owned(), "T".to_owned())]), }; is_equal( m, expect![[r#" { "player1_name": "name1", "player1_votes": 1, "player2_name": "name2", "player2_votes": 2, "tag_t": "T" }"#]], ); } #[test] fn test_plain_with_prefix() { #[derive(Serialize, Deserialize, PartialEq, Debug)] struct Match { #[serde(with = "prefix_player1")] player1: Player, #[serde(with = "prefix_player2")] player2: Option, #[serde(with = "prefix_player3")] player3: Option, #[serde(with = "prefix_tag")] tags: HashMap, } #[derive(Serialize, Deserialize, PartialEq, Debug)] struct Player { name: String, votes: u64, } with_prefix!(prefix_player1 "player1_"); with_prefix!(prefix_player2 "player2_"); with_prefix!(prefix_player3 "player3_"); with_prefix!(prefix_tag "tag_"); let m = Match { player1: Player { name: "name1".to_owned(), votes: 1, }, player2: Some(Player { name: "name2".to_owned(), votes: 2, }), player3: None, tags: HashMap::from_iter(vec![("t".to_owned(), "T".to_owned())]), }; is_equal( m, expect![[r#" { "player1": { "player1_name": "name1", "player1_votes": 1 }, "player2": { "player2_name": "name2", "player2_votes": 2 }, "player3": null, "tags": { "tag_t": "T" } }"#]], ); } /// Ensure that `with_prefix` works for unit type enum variants. #[test] fn test_enum_unit_variant_with_prefix() { #[derive(Hash, PartialEq, Eq, Debug, Serialize, Deserialize, Ord, PartialOrd)] enum Foo { One, Two, Three, } #[derive(Hash, PartialEq, Eq, Debug, Serialize, Deserialize, Ord, PartialOrd)] struct Data { stuff: String, #[serde(flatten, with = "foo")] foo: BTreeMap, } with_prefix!(foo "foo_"); let d = Data { stuff: "Stuff".to_owned(), foo: BTreeMap::from_iter(vec![(Foo::One, 1), (Foo::Two, 2), (Foo::Three, 3)]), }; is_equal( d, expect![[r#" { "stuff": "Stuff", "foo_One": 1, "foo_Two": 2, "foo_Three": 3 }"#]], ); } serde_with-3.12.0/tests/with_suffix.rs000064400000000000000000000073371046102023000161250ustar 00000000000000//! Test Cases extern crate alloc; mod utils; use crate::utils::is_equal; use alloc::collections::BTreeMap; use expect_test::expect; use serde::{Deserialize, Serialize}; use serde_with::with_suffix; use std::collections::HashMap; #[test] fn test_flatten_with_suffix() { #[derive(Serialize, Deserialize, PartialEq, Debug)] struct Match { #[serde(flatten, with = "suffix_player1")] player1: Player, #[serde(flatten, with = "suffix_player2")] player2: Option, #[serde(flatten, with = "suffix_player3")] player3: Option, #[serde(flatten, with = "suffix_tag")] tags: HashMap, } #[derive(Serialize, Deserialize, PartialEq, Debug)] struct Player { name: String, votes: u64, } with_suffix!(suffix_player1 "_player1"); with_suffix!(suffix_player2 "_player2"); with_suffix!(suffix_player3 "_player3"); with_suffix!(suffix_tag "_tag"); let m = Match { player1: Player { name: "name1".to_owned(), votes: 1, }, player2: Some(Player { name: "name2".to_owned(), votes: 2, }), player3: None, tags: HashMap::from_iter(vec![("t".to_owned(), "T".to_owned())]), }; is_equal( m, expect![[r#" { "name_player1": "name1", "votes_player1": 1, "name_player2": "name2", "votes_player2": 2, "t_tag": "T" }"#]], ); } #[test] fn test_plain_with_suffix() { #[derive(Serialize, Deserialize, PartialEq, Debug)] struct Match { #[serde(with = "suffix_player1")] player1: Player, #[serde(with = "suffix_player2")] player2: Option, #[serde(with = "suffix_player3")] player3: Option, #[serde(with = "suffix_tag")] tags: HashMap, } #[derive(Serialize, Deserialize, PartialEq, Debug)] struct Player { name: String, votes: u64, } with_suffix!(suffix_player1 "_player1"); with_suffix!(suffix_player2 "_player2"); with_suffix!(suffix_player3 "_player3"); with_suffix!(suffix_tag "_tag"); let m = Match { player1: Player { name: "name1".to_owned(), votes: 1, }, player2: Some(Player { name: "name2".to_owned(), votes: 2, }), player3: None, tags: HashMap::from_iter(vec![("t".to_owned(), "T".to_owned())]), }; is_equal( m, expect![[r#" { "player1": { "name_player1": "name1", "votes_player1": 1 }, "player2": { "name_player2": "name2", "votes_player2": 2 }, "player3": null, "tags": { "t_tag": "T" } }"#]], ); } /// Ensure that `with_suffix` works for unit type enum variants. #[test] fn test_enum_unit_variant_with_suffix() { #[derive(Hash, PartialEq, Eq, Debug, Serialize, Deserialize, Ord, PartialOrd)] enum Foo { One, Two, Three, } #[derive(Hash, PartialEq, Eq, Debug, Serialize, Deserialize, Ord, PartialOrd)] struct Data { stuff: String, #[serde(flatten, with = "foo")] foo: BTreeMap, } with_suffix!(foo "_foo"); let d = Data { stuff: "Stuff".to_owned(), foo: BTreeMap::from_iter(vec![(Foo::One, 1), (Foo::Two, 2), (Foo::Three, 3)]), }; is_equal( d, expect![[r#" { "stuff": "Stuff", "One_foo": 1, "Two_foo": 2, "Three_foo": 3 }"#]], ); }