Compare commits

...

156 Commits

Author SHA1 Message Date
David Tolnay 78e74886be Update syntex to 0.39 2016-07-26 09:22:33 -07:00
David Tolnay ed603d4580 Merge pull request #442 from serde-rs/override
Use cargo override instead of relative paths
2016-07-15 01:56:14 -07:00
David Tolnay 7220029055 Use cargo override instead of relative paths
This makes it possible to use `cargo clone` + `cargo build`.
2016-07-15 01:17:23 -07:00
David Tolnay 35676305da Merge pull request #401 from erickt/contributing
Add a contribution guide
2016-07-15 01:06:54 -07:00
David Tolnay 4ec0a7e672 Merge pull request #433 from softprops/add_envy
add envy
2016-07-10 20:59:50 -07:00
softprops a41dae45a5 add envy 2016-07-10 23:55:32 -04:00
David Tolnay cb9e1cfb54 Merge pull request #431 from serde-rs/version
Drop support for 1.7.0
2016-07-10 14:40:15 -07:00
David Tolnay 54ce7f2e90 Drop support for 1.7.0 2016-07-10 14:25:17 -07:00
David Tolnay ddbd139793 Merge pull request #430 from serde-rs/bump
Update syntex to 0.38
2016-07-09 11:51:38 -07:00
David Tolnay a070de28e2 Update syntex to 0.38 2016-07-09 11:37:01 -07:00
David Tolnay 57aeb26728 Merge pull request #428 from erickt/readme
Rewrite readme to front-load stable rust usage
2016-07-07 20:26:00 -07:00
Erick Tryzelaar a592828808 Rewrite readme to front-load stable rust usage 2016-07-07 14:31:24 -07:00
David Tolnay 67d86dcc4f Merge pull request #426 from serde-rs/display
impl Display for de::Type
2016-07-07 00:59:17 -07:00
David Tolnay 15764cb955 impl Display for de::Type 2016-07-06 23:33:59 -07:00
David Tolnay 97bc1e08e7 Release 0.7.13 2016-07-05 20:06:10 -07:00
Oliver Schneider 7ffb74f5bb Merge pull request #423 from dtolnay/seq
Close the seq before failing on invalid length
2016-07-05 22:44:52 +02:00
David Tolnay f25e6d3ea9 Close the seq before failing on invalid length
Equivalent to what 0f9a930 does for maps.
2016-07-05 10:08:36 -07:00
David Tolnay 431cbe48b7 Merge pull request #422 from serde-rs/missing
Close the map before checking for missing fields
2016-07-05 07:11:03 -07:00
Homu 5405ab319d Auto merge of #421 - serde-rs:length, r=oli-obk
Invalid-length when enum seq is too short

Fixes https://github.com/serde-rs/json/issues/96.
2016-07-05 18:27:33 +09:00
David Tolnay 0f9a930c4f Close the map before checking for missing fields 2016-07-05 01:42:38 -07:00
David Tolnay 1a449bb3d0 Invalid-length when enum seq is too short 2016-07-05 01:35:20 -07:00
David Tolnay c0e8164792 Merge pull request #418 from serde-rs/expninfo
Record expansion info
2016-07-04 15:22:09 -07:00
David Tolnay a3a7e4085f Add run-pass test for expansion info 2016-07-04 15:02:15 -07:00
David Tolnay 149c87d7c2 Record expansion info 2016-07-03 10:53:33 -07:00
David Tolnay 5deba439c3 Merge pull request #417 from serde-rs/bump
Update syntex to 0.37
2016-07-03 08:53:56 -07:00
David Tolnay d450de1482 Update syntex to 0.37 2016-07-02 20:20:44 -07:00
David Tolnay 47c7387279 Merge pull request #416 from dtolnay/filter
Remove filter no longer necessary for rustc >=1.7
2016-07-01 22:53:45 -07:00
David Tolnay dc8d209f29 Remove filter no longer necessary for rustc >=1.7 2016-07-01 22:34:15 -07:00
David Tolnay 831802adc8 Merge pull request #407 from dtolnay/internals
Rename serde_item to serde_codegen_internals
2016-06-30 20:06:12 -07:00
David Tolnay 2d5a26dfc0 Rename serde_internals to serde_codegen_internals 2016-06-30 19:53:57 -07:00
David Tolnay c6b6e2a5d9 Rename serde_item to serde_internals 2016-06-30 19:42:55 -07:00
Oliver Schneider d1be5ef187 Merge pull request #412 from dtolnay/test
Move Token De/Serializer to serde_test crate
2016-06-29 10:05:36 +02:00
David Tolnay f531be1524 Turn comments into doc comments 2016-06-29 00:58:33 -07:00
David Tolnay 10b1508d4a Sync serde_test version with the other crates 2016-06-29 00:17:50 -07:00
David Tolnay 041d5c0842 Make serde_test asserts more consistent 2016-06-28 23:50:19 -07:00
David Tolnay 7d09053bb8 Fix declare_ser_tests to work on old rustc 2016-06-28 23:09:37 -07:00
David Tolnay 8e87926bc2 Round out the Error variants 2016-06-28 22:57:53 -07:00
David Tolnay d6a462b862 Add serde_test to dev dependencies of serde_macros 2016-06-28 22:50:58 -07:00
David Tolnay cfc2f9adc0 Remove Error from name of Error variants 2016-06-28 22:49:13 -07:00
David Tolnay 00f94290a6 Add message to CustomError 2016-06-28 22:36:29 -07:00
David Tolnay 093201abfb Assert tokens are empty after reaching error 2016-06-28 22:23:38 -07:00
David Tolnay 6d64104375 Factor the Token De/Serializer into serde_test 2016-06-28 21:54:51 -07:00
David Tolnay fb0e62951f Rename serde_tests to testing 2016-06-28 20:43:59 -07:00
David Tolnay 6ab508a93c Release 0.7.11 2016-06-23 19:58:31 -07:00
David Tolnay 9f38c2ab59 Merge pull request #409 from knsd/patch-1
Fix typo
2016-06-23 09:52:50 -07:00
Fedor Gogolev 021ce5be88 Fix typo 2016-06-23 20:50:27 +04:00
Homu f1f8b4622b Auto merge of #402 - erickt:clippy, r=erickt
Update link to clippy lint report
2016-06-23 12:52:36 +09:00
Erick Tryzelaar 1fb2172a25 Merge pull request #405 from erickt/rustup
Version bump the syntex stack
2016-06-22 20:34:15 -07:00
Erick Tryzelaar 5941f1d071 Version bump the syntex stack 2016-06-22 20:19:47 -07:00
Erick Tryzelaar 8d6cc4dfa5 Update link to clippy lint report 2016-06-22 10:13:28 -07:00
Erick Tryzelaar 18e077eda9 Add a contributing guide 2016-06-22 07:04:13 -07:00
Homu c9e2e518ba Auto merge of #397 - dtolnay:item, r=oli-obk
Factor attr parsing into serde_item crate

Fixes #396. @KodrAus [let me know whether this fits the bill.](https://github.com/dtolnay/serde/tree/5c6a0e12e95974e3c131386e8e0528b6b9cfa6fa/serde_item/src)

a few other changes to make the API a little more presentable:

- Rename attr::{ContainerAttrs,VariantAttrs,FieldAttrs} to remove the "Attrs" (I see you worked on the corresponding [clippy lint](https://github.com/Manishearth/rust-clippy/issues/904)).
- Rename attr::Container* to attr::Item to correspond with item::Item and ast::Item. The others already had a correspondence (attr::Variant/item::Variant/ast::Variant, attr::Field/item::Field/ast::Field). Also a unit struct isn't much of a "container."
- Change item::Item::from_ast to return a meaningful error enum instead of printing a message that was hard to generalize to other uses.
- Add item::Variant.span for consistency because Item and Field already had span.
- Remove the "ident" field from attr::Name because we can just fold it into the other two fields.
- Remove attr::Name::(de)serialize_name_expr because it wasn't using the right AstBuilder in the first place.
- Rename the attr:: constructors from_item/from_variant/from_field to from_ast to line up with the item:: constructors; the signatures match.
- Remove attr's dependency on aster because we were only using it for two very simple things.
2016-06-20 16:45:25 +09:00
David Tolnay 51042bde50 Remove ident from Name struct 2016-06-19 20:31:12 -07:00
David Tolnay 5c6a0e12e9 Factor attr parsing into serde_item crate 2016-06-19 20:15:49 -07:00
Homu d4e1ef659a Auto merge of #389 - dtolnay:rule, r=oli-obk
Remove unused macro rule
2016-06-17 16:52:37 +09:00
David Tolnay f5f35677f0 Remove unused macro rule 2016-06-17 00:06:11 -07:00
David Tolnay 90d9c77186 Merge pull request #385 from dtolnay/dup
Error on duplicate attributes
2016-06-17 00:02:04 -07:00
David Tolnay 882d130e19 Test duplicates within a single attribute 2016-06-16 23:45:34 -07:00
David Tolnay 5f84e601b6 Add explanation of ignored errors in attr.rs 2016-06-16 23:25:46 -07:00
David Tolnay 0bf6a17428 Merge pull request #387 from erickt/master
Extend tuple impls up to 16 elements
2016-06-16 09:47:58 -07:00
Erick Tryzelaar db8881d845 Extend tuple impls up to 16 elements 2016-06-16 11:22:15 +01:00
David Tolnay 3403352749 Merge pull request #377 from dtolnay/travis
Remove some low-risk travis builds
2016-06-15 08:54:04 -07:00
David Tolnay ac69524258 Gather attrs at the beginning 2016-06-15 02:38:47 -07:00
David Tolnay 28589620f6 Error on duplicate attributes 2016-06-14 20:39:21 -07:00
David Tolnay 8e4da7f36b Merge pull request #383 from oli-obk/compile-fail
more cfail tests
2016-06-13 10:46:35 -07:00
Oliver Schneider 4c4a27f53c more cfail tests 2016-06-13 18:56:45 +02:00
Homu b838651ac9 Auto merge of #378 - dtolnay:str, r=oli-obk
Better error when deriving Deserialize for struct containing &str

Fixes #360. The error looks like this:

```rust
#[derive(Serialize, Deserialize)]
struct Test<'a> {
    s: &'a str,
}
```

```
src/main.rs:6:5: 6:15 error: Serde does not support deserializing fields of type &str; consider using String instead
src/main.rs:6     s: &'a str,
                  ^~~~~~~~~~
```
2016-06-14 00:49:26 +09:00
David Tolnay 5cd8212a61 Merge pull request #381 from dtolnay/nosyntex
Remove unnecessary Syntexes
2016-06-12 22:25:50 -07:00
David Tolnay c5f606f10f Remove unnecessary Syntexes 2016-06-12 15:50:45 -07:00
David Tolnay c21bea8b30 Better error when deriving Deserialize for struct containing &str 2016-06-11 21:32:53 -07:00
David Tolnay 22a26a33f4 Remove some low-risk travis builds 2016-06-11 16:21:20 -07:00
David Tolnay 8a09f05644 Release 0.7.10 2016-06-11 13:08:33 -07:00
David Tolnay 5923a0cd2f Merge pull request #371 from dtolnay/hasher
De/serialize for HashMap<K, V, S> and HashSet<T, S>
2016-06-11 11:51:57 -07:00
David Tolnay 1576b5a8a0 Serde_macros tests depend on fnv 2016-06-11 11:15:10 -07:00
Homu 2c4dbf5a84 Auto merge of #370 - dtolnay:expand, r=erickt
Use serde_codegen::expand in serde_tests/build.rs
2016-06-12 02:51:08 +09:00
David Tolnay 021f4f2d70 Use serde_codegen::expand in serde_tests/build.rs 2016-06-11 10:02:10 -07:00
David Tolnay decc571988 De/serialize for HashSet<T, S> 2016-06-11 10:00:33 -07:00
David Tolnay 322d7a90db Add ser tests for normal HashMap 2016-06-11 10:00:33 -07:00
David Tolnay dd3f653103 Move bounds to where-clause to increase legibility 2016-06-11 10:00:33 -07:00
David Tolnay 46a1860601 De/serialize for HashMap<K, V, S> 2016-06-11 10:00:33 -07:00
Homu 84a573c926 Auto merge of #372 - dtolnay:old, r=erickt
Stop building on 1.5.0

Syntex no longer supports this version of Rust.
2016-06-12 01:09:49 +09:00
Erick Tryzelaar 7dfa8f43f4 Merge pull request #373 from erickt/master
Updating to rustc 1.11.0-nightly (7d2f75a95 2016-06-09)
2016-06-11 09:09:22 -07:00
David Tolnay 7375b4e847 Add travis builds for 1.6.0 and 1.7.0 2016-06-11 08:59:02 -07:00
Erick Tryzelaar 48da62ed07 Updating to rustc 1.11.0-nightly (7d2f75a95 2016-06-09) 2016-06-11 08:19:51 -07:00
David Tolnay 9834af7ed9 Stop building on 1.5.0
Syntex no longer supports this version of Rust.
2016-06-11 01:31:17 -07:00
Homu 6b404d8529 Auto merge of #367 - dtolnay:default, r=oli-obk
Simplify implementation of #[serde(default=...)]
2016-06-10 17:57:39 +09:00
David Tolnay 3119cc8857 Simplify implementation of #[serde(default=...)] 2016-06-09 23:21:42 -07:00
David Tolnay bb059b97c0 Release 0.7.9 2016-06-09 20:16:49 -07:00
Homu b7188f7022 Auto merge of #362 - dtolnay:expand, r=oli-obk
Add serde_codegen::expand to avoid public Syntex dependency

Required for #358. We can remove `serde_codegen::register` in the next breaking release.

This allows Syntex users to avoid being broken by Serde bumping its Syntex dependency.
2016-06-10 07:46:33 +09:00
David Tolnay a64fe99d1b Add cargo override for building examples 2016-06-09 11:23:43 -07:00
David Tolnay c716c4e261 Use AsRef to accept paths in serde_codegen::expand 2016-06-09 11:23:43 -07:00
David Tolnay 3d2e3beafe Add serde_codegen::expand to avoid public Syntex dependency 2016-06-09 11:23:43 -07:00
Homu 1917e54a6e Auto merge of #363 - dtolnay:example, r=oli-obk
Fix nightly check in serde-syntex-example

Fixes #361.
2016-06-10 01:58:51 +09:00
David Tolnay 898b346d48 1.5 does not have a stable libcore 2016-06-09 09:17:21 -07:00
David Tolnay e90adb20ef Run serde-syntex-example in Travis 2016-06-09 01:42:00 -07:00
Homu a52e7f5554 Auto merge of #364 - dtolnay:rustup, r=oli-obk
Use rustup in serde-syntex-example instead of multirust

Multirust is deprecated.
2016-06-09 17:36:01 +09:00
David Tolnay 7afb8b52ae Use rustup in serde-syntex-example instead of multirust 2016-06-09 01:16:31 -07:00
David Tolnay cb4694387e Fix nightly check in serde-syntex-example 2016-06-09 01:08:12 -07:00
David Tolnay 58fa302007 Release 0.7.8 2016-06-06 10:13:52 -07:00
Homu bf33daf124 Auto merge of #354 - dtolnay:attr, r=oli-obk
Fix attributes canceling each other

Fixes #353.
2016-06-06 18:08:20 +09:00
Homu 4b472be56e Auto merge of #352 - dtolnay:where, r=oli-obk
Attribute for handwritten where clauses

Addresses (2) and (3) in https://github.com/serde-rs/serde/issues/336#issuecomment-220378916.

- If there is a `#[serde(bound="...")]` attribute on the type, use the union of that and the actual type's `where` clause as the `where` clause for the impl and do not attempt to generate any additional `where` clauses whatsoever.
- If there is a `#[serde(bound="...")]` attribute on a field, use that and do not attempt to generate any additional `where` clauses for the field.

The `bound` attribute behaves similar to `rename` in that you can specify a single attribute that applies to both ser and de, or individual ones.

```
#[serde(bound="D: Serialize + Deserialize")]

#[serde(bound(serialize="D: Serialize", deserialize="D: Deserialize"))]
```

EDIT: now addresses (4) from https://github.com/serde-rs/serde/issues/336#issuecomment-220378916 as well.

- If a field contains direct recursion, do not generate any bounds based on that field except from `bound` attributes.
2016-06-06 17:47:45 +09:00
David Tolnay bdffaf3ea1 Re-enable clippy lint "useless_let_if_seq"
This reverts commit 4e6cd2d63f.
2016-06-05 13:01:22 -07:00
David Tolnay f197c3ce96 Readme for "bound" attribute 2016-06-05 11:54:36 -07:00
David Tolnay 01dfad6705 Fix attributes canceling each other 2016-06-05 11:40:30 -07:00
David Tolnay 2e06786262 Remove unnecessary clones 2016-06-05 11:23:01 -07:00
David Tolnay 578f34ecaf Use "bound" attribute instead of "where" 2016-06-05 11:17:43 -07:00
David Tolnay 2c8767cb46 Remove changelog in favor of github release notes 2016-06-05 10:05:56 -07:00
David Tolnay 45c51d3198 Fix build on 1.5.0 which does not have Vec::as_slice 2016-06-04 16:53:45 -07:00
David Tolnay bd40830905 Do not generate bounds from recursive types 2016-06-04 16:12:01 -07:00
David Tolnay 4e6cd2d63f Disable clippy lint "useless_let_if_seq" 2016-06-04 15:48:44 -07:00
David Tolnay 2256a04926 Address clippy lint "ptr_arg" 2016-06-04 15:48:44 -07:00
David Tolnay 660ea7bd7b Attribute for handwritten where clauses 2016-06-04 15:48:42 -07:00
Homu 7052833512 Auto merge of #351 - oli-obk:publish_nits, r=oli-obk
also publish the `.in` file used by the build script
2016-06-02 01:47:39 +09:00
Oliver Schneider 5c2cf5778f also publish the .in file used by the build script 2016-06-01 13:09:43 +02:00
Homu b5c0406afe Auto merge of #349 - oli-obk:undo, r=oli-obk
undo the breaking change introduced in 0.7.6

I should probably yank 0.7.6, too

cc @alexcrichton this should unbreak your setup, sorry about that.
2016-06-01 20:01:53 +09:00
Oliver Schneider 96cd910c92 undo the breaking change introduced in 0.7.6 2016-06-01 11:08:59 +02:00
Homu e0bd57d63c Auto merge of #347 - erickt:master, r=erickt
Add "include" to Cargo.toml files

This filters out junk files from the crates.

Closes #331.
2016-05-30 23:23:57 +09:00
Erick Tryzelaar 7c784f592e Add a changelog 2016-05-30 07:23:18 -07:00
Erick Tryzelaar 2c69ccdca4 Add the build script to the Cargo include section 2016-05-30 07:23:09 -07:00
Erick Tryzelaar 66eddd4d9b Add "include" to Cargo.toml files
This filters out junk files from the crates.

Closes #331.
2016-05-30 07:00:13 -07:00
Homu 1485f0a448 Auto merge of #346 - serde-rs:rustup, r=erickt
Rustup too rustc 1.11.0-nightly (7746a334d 2016-05-28)

cc @erickt
2016-05-30 22:45:47 +09:00
Manish Goregaokar 379c9e7148 Rustup too rustc 1.11.0-nightly (7746a334d 2016-05-28) 2016-05-30 14:01:57 +05:30
Homu 6c2af4da7a Auto merge of #343 - pyfisch:patch-1, r=oli-obk
Better documentation for de::Error::invalid_length

Closes #342
2016-05-24 20:32:57 +09:00
Pyfisch 2ff7d003ee Better documentation for de::Error::invalid_length
Closes #342
2016-05-24 13:07:26 +02:00
David Tolnay ea182e2561 Merge pull request #335 from dtolnay/tuples
Support (de)serialize_with in tuples
2016-05-23 13:33:58 -07:00
David Tolnay 938f42faf6 Support (de)serialize_with in tuples 2016-05-19 10:53:38 -07:00
Homu cc115ca43a Auto merge of #338 - dtolnay:refs, r=oli-obk
Strip more references

Fixes #337.
2016-05-20 02:45:31 +09:00
David Tolnay f1b4072444 Strip more references 2016-05-18 23:48:56 -07:00
Homu 16d3e96b77 Auto merge of #334 - dtolnay:dupl, r=oli-obk
Reduce code duplication in Deserialize generator

This combines `deserialize_newtype_struct`, `deserialize_tuple_struct`, and `deserialize_tuple_variant` into a single method `deserialize_tuple`, as well as `deserialize_struct` and `deserialize_struct_variant` into a single method `deserialize_struct`. No behavior changes.
2016-05-16 19:41:59 +09:00
David Tolnay 7d2423e856 Reduce code duplication in Deserialize generator
This combines deserialize_newtype_struct, deserialize_tuple_struct,
and deserialize_tuple_variant into a single method deserialize_tuple,
as well as deserialize_struct and deserialize_struct_variant into a
single method deserialize_struct. No behavior changes.
2016-05-15 13:32:54 -07:00
David Tolnay 9865ec23c7 Merge pull request #322 from dtolnay/unused
Remove unused imports and needless borrows
2016-05-13 11:23:15 -07:00
David Tolnay 76a321db5c Simplify redundant closure 2016-05-13 10:53:03 -07:00
David Tolnay 004dcaec3b Remove unused imports and needless borrows 2016-05-13 10:47:09 -07:00
Homu 74eb2f52b8 Auto merge of #316 - cmr:no_std, r=oli-obk
Enable use in no_std environments

Re-opening of #302 after bust merge.
2016-05-13 17:13:08 +09:00
Corey Richardson 9b7317fbb1 Address nits 2016-05-13 11:17:17 +10:00
Erick Tryzelaar 41142d41ee Merge pull request #319 from dtolnay/release
Release 0.7.5
2016-05-12 09:30:41 -07:00
Homu fd328c2f2a Auto merge of #320 - dtolnay:syntexv, r=oli-obk
Do not depend on multiple syntex versions

serde_codegen has syntex 0.32 under dependencies, but syntex 0.31 under build-dependencies...
2016-05-12 04:05:47 +09:00
David Tolnay 3ad276944a Do not depend on multiple syntex versions 2016-05-11 09:52:28 -07:00
David Tolnay bb20796e9d Bump version numbers in serde-syntex-example 2016-05-11 09:46:16 -07:00
David Tolnay 709ac64dfc Release 0.7.5 2016-05-11 09:36:30 -07:00
Homu a9a4b2d8e2 Auto merge of #293 - dtolnay:duplicate, r=oli-obk
feat(codegen): Detect repeated struct field when deserializing

Addresses #59. Let me know whether you think we need an escape hatch to opt out of this check.
2016-05-11 19:44:34 +09:00
Oliver Schneider 7374ac499d Merge pull request #311 from dtolnay/deserialize_with
Field with deserialize_with should not implement Deserialize
2016-05-11 11:48:19 +02:00
Oliver Schneider 6596f77e91 Merge pull request #315 from dtolnay/underscore
Prefix type parameters and lifetimes with double underscore
2016-05-11 11:06:25 +02:00
David Tolnay eeb4efc19c feat(codegen): Detect repeated struct field when deserializing 2016-05-10 09:52:51 -07:00
David Tolnay 76b70455ec Field with deserialize_with should not implement Deserialize 2016-05-10 09:50:32 -07:00
David Tolnay f43c8a6267 Prefix type parameters and lifetimes with double underscore 2016-05-10 09:12:38 -07:00
Corey Richardson ae806af644 Enable use in no_std environments
These changes are fairly invasive to imports and uses of non-libcore types,
but allow for some or none of the freestanding crates (core, rustc_unicode,
alloc, collections) to be supported by serde.
2016-05-10 10:23:41 +10:00
Homu 7aa0b6ce27 Auto merge of #314 - dtolnay:revert, r=erickt
Revert "Enable use in no_std environments"

This commit failed to build even before it was merged. See #313.
2016-05-09 12:47:09 +09:00
David Tolnay efdbf5795f Fix version in serde/Cargo.toml 2016-05-08 20:43:46 -07:00
David Tolnay 55355b6680 Revert "Enable use in no_std environments"
This reverts commit 9c0140968d.
2016-05-08 20:30:21 -07:00
Erick Tryzelaar f8a91e5176 Merge pull request #302 from cmr/no_std
Enable use in no_std environments
2016-05-08 19:24:48 -07:00
Erick Tryzelaar aa0cd9b3dc Merge pull request #308 from dtolnay/inference
Reduce dependence on type inference
2016-05-08 19:21:45 -07:00
Erick Tryzelaar 1f82cd6e3d Merge pull request #310 from dtolnay/withdoc
Clarify serialize_with and deserialize_with documentation
2016-05-08 19:20:12 -07:00
David Tolnay 3caac4e6f3 Clarify serialize_with and deserialize_with documentation 2016-05-08 10:52:23 -07:00
David Tolnay f4414bfc14 Reduce dependence on type inference 2016-05-07 15:25:13 -07:00
Corey Richardson 9c0140968d Enable use in no_std environments
These changes are fairly invasive to imports and uses of non-libcore types,
but allow for some or none of the freestanding crates (core, rustc_unicode,
alloc, collections) to be supported by serde.
2016-05-04 01:26:43 +10:00
65 changed files with 4162 additions and 2792 deletions
+7
View File
@@ -0,0 +1,7 @@
paths = [
"serde",
"serde_codegen",
"serde_codegen_internals",
"serde_macros",
"serde_test",
]
+13 -12
View File
@@ -1,35 +1,36 @@
sudo: false
language: rust
rust:
- stable
- beta
- nightly
- 1.5.0
- 1.8.0
- 1.9.0
- beta
addons:
apt:
packages:
- libcurl4-openssl-dev
- libelf-dev
- libdw-dev
- binutils-dev
before_script:
- pip install 'travis-cargo<0.2' --user
- export PATH=$HOME/.local/bin:$PATH
script:
- (cd serde && travis-cargo build)
- (cd serde && travis-cargo test)
- (cd serde && travis-cargo --skip nightly test)
- (cd serde && travis-cargo --only nightly test -- --features nightly-testing)
- (cd serde_tests && travis-cargo test)
- (cd serde_tests && travis-cargo --only nightly test -- --features nightly-testing)
- (cd serde && travis-cargo build -- --no-default-features)
- (cd serde && travis-cargo --only nightly build -- --no-default-features --features alloc)
- (cd serde && travis-cargo --only nightly build -- --no-default-features --features collections)
- (cd testing && travis-cargo --skip nightly test)
- (cd testing && travis-cargo --only nightly test -- --features nightly-testing)
- (cd serde_macros && travis-cargo --only nightly test -- --features nightly-testing)
- (cd serde_macros && travis-cargo --only nightly bench -- --features nightly-testing)
- (cd examples/serde-syntex-example && travis-cargo --skip nightly run)
- (cd examples/serde-syntex-example && travis-cargo --only nightly run -- --no-default-features --features nightly)
- (cd serde && travis-cargo --only stable doc)
- (cd serde_codegen && travis-cargo --only stable doc)
- (cd serde_macros && travis-cargo --only nightly doc)
after_success:
- (cd serde && travis-cargo --only stable doc-upload)
#- (cd serde_codegen && travis-cargo --only stable doc-upload)
#- (cd serde_macros && travis-cargo --only nightly doc-upload)
- (cd serde_tests && travis-cargo coveralls --no-sudo)
- (cd testing && travis-cargo --only stable coveralls --no-sudo)
env:
global:
- TRAVIS_CARGO_NIGHTLY_FEATURE=""
+45
View File
@@ -0,0 +1,45 @@
# Contributing to Serde
Serde welcomes contribution from everyone. Here are the guidelines if you are
thinking of helping us:
## Contributions
Contributions to Serde or its dependencies should be made in the form of GitHub
pull requests. Each pull request will be reviewed by a core contributor
(someone with permission to land patches) and either landed in the main tree or
given feedback for changes that would be required. All contributions should
follow this format, even those from core contributors.
Should you wish to work on an issue, please claim it first by commenting on
the GitHub issue that you want to work on it. This is to prevent duplicated
efforts from contributors on the same issue.
## Pull Request Checklist
- Branch from the master branch and, if needed, rebase to the current master
branch before submitting your pull request. If it doesn't merge cleanly with
master you may be asked to rebase your changes.
- Commits should be as small as possible, while ensuring that each commit is
correct independently (i.e., each commit should compile and pass tests).
- If your patch is not getting reviewed or you need a specific person to review
it, you can @-reply a reviewer asking for a review in the pull request or a
comment, or you can ask for a review in `#serde` on `irc.mozilla.org`.
- Add tests relevant to the fixed bug or new feature.
## Conduct
In all Serde-related forums, we follow the [Rust Code of
Conduct](https://www.rust-lang.org/conduct.html). For escalation or moderation
issues, please contact Erick (erick.tryzelaar@gmail.com) instead of the Rust
moderation team.
## Communication
Beyond opening tickets on the
[serde-rs/serde](https://github.com/serde-rs/serde) project, Serde contributors
frequent the `#serde` channel on
[`irc.mozilla.org`](https://wiki.mozilla.org/IRC).
+219 -124
View File
@@ -4,7 +4,7 @@ Serde Rust Serialization Framework
[![Build Status](https://api.travis-ci.org/serde-rs/serde.svg?branch=master)](https://travis-ci.org/serde-rs/serde)
[![Coverage Status](https://coveralls.io/repos/serde-rs/serde/badge.svg?branch=master&service=github)](https://coveralls.io/github/serde-rs/serde?branch=master)
[![Latest Version](https://img.shields.io/crates/v/serde.svg)](https://crates.io/crates/serde)
[![Clippy Linting Result](http://clippy.bashy.io/github/serde-rs/serde/master/badge.svg)](http://clippy.bashy.io/github/serde-rs/serde/master/log)
[![Clippy Linting Result](https://clippy.bashy.io/github/serde-rs/serde/master/badge.svg)](https://clippy.bashy.io/github/serde-rs/serde/master/log)
Serde is a powerful framework that enables serialization libraries to
generically serialize Rust data structures without the overhead of runtime type
@@ -12,20 +12,184 @@ information. In many situations, the handshake protocol between serializers and
serializees can be completely optimized away, leaving Serde to perform roughly
the same speed as a hand written serializer for a specific type.
Documentation is available at:
[Documentation](https://serde-rs.github.io/serde/serde/index.html)
* [serde](https://serde-rs.github.io/serde/serde/index.html)
Simple Serde Example
====================
Here is a simple example that uses
[serde_json](https://github.com/serde-rs/json), which uses Serde under the
covers, to generate and parse JSON. First, lets start off with the `Cargo.toml`
file:
```toml
[package]
name = "serde_example"
version = "0.1.0"
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>"]
[dependencies]
serde_json = "*"
```
Next, the `src/main.rs` file itself:
```rust,ignore
extern crate serde_json;
use std::collections::HashMap;
use serde_json::Value;
use serde_json::builder::{ArrayBuilder, ObjectBuilder};
fn main() {
// Serde has support for many of the builtin Rust types, like arrays..:
let v = vec![1, 2];
let serialized = serde_json::to_string(&v).unwrap();
println!("serialized vec: {:?}", serialized);
let deserialized: Vec<u32> = serde_json::from_str(&serialized).unwrap();
println!("deserialized vec: {:?}", deserialized);
// ... and maps:
let mut map = HashMap::new();
map.insert("x".to_string(), 1);
map.insert("y".to_string(), 2);
let serialized = serde_json::to_string(&map).unwrap();
println!("serialized map: {:?}", serialized);
let deserialized: HashMap<String, u32> = serde_json::from_str(&serialized).unwrap();
println!("deserialized map: {:?}", deserialized);
// It also can handle complex objects:
let value = ObjectBuilder::new()
.insert("int", 1)
.insert("string", "a string")
.insert("array", ArrayBuilder::new()
.push(1)
.push(2)
.unwrap())
.unwrap();
let serialized = serde_json::to_string(&value).unwrap();
println!("serialized value: {:?}", serialized);
let deserialized: serde_json::Value = serde_json::from_str(&serialized).unwrap();
println!("deserialized value: {:?}", deserialized);
}
```
This produces the following output when run:
```
% cargo run
serialized vec: "[1,2]"
deserialized vec: [1, 2]
serialized map: "{\"y\":2,\"x\":1}"
deserialized map: {"y": 2, "x": 1}
serialized value: "{\"array\":[1,2],\"int\":1,\"string\":\"a string\"}"
deserialized value: {"array":[1,2],"int":1,"string":"a string"}
```
Using Serde with Stable Rust and serde\_codegen
===============================================
The example before used `serde_json::Value` as the in-memory representation of
the JSON value, but it's also possible for Serde to serialize to and from
regular Rust types. However, the code to do this can be a bit complicated to
write. So instead, Serde also has some powerful code generation libraries that
work with Stable and Nightly Rust that eliminate much of the complexity of hand
rolling serialization and deserialization for a given type.
First lets see how we would use Stable Rust, which is currently a tad more
complicated than Nightly Rust due to having to work around compiler plugins
being unstable. We will use `serde_codegen` which is based on the code
generation library [syntex](https://github.com/serde-rs/syntex). First we need
to setup the `Cargo.toml` that builds the project:
```toml
[package]
name = "serde_example"
version = "0.1.0"
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>"]
build = "build.rs"
[build-dependencies]
serde_codegen = "*"
[dependencies]
serde = "*"
serde_json = "*"
```
Next, we define our source file, `src/main.rs.in`. Note this is a different
extension than usual becaues we need to do code generation:
```rust,ignore
#[derive(Serialize, Deserialize, Debug)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let point = Point { x: 1, y: 2 };
let serialized = serde_json::to_string(&point).unwrap();
println!("{}", serialized);
let deserialized: Point = serde_json::from_str(&serialized).unwrap();
println!("{:?}", deserialized);
}
```
To finish up the main source code, we define a very simple `src/main.rs` that
uses the generated code.
`src/main.rs`:
```rust,ignore
extern crate serde;
extern crate serde_json;
include!(concat!(env!("OUT_DIR"), "/main.rs"));
```
The last step is to actually drive the code generation, with the `build.rs` script:
```rust,ignore
extern crate serde_codegen;
use std::env;
use std::path::Path;
pub fn main() {
let out_dir = env::var_os("OUT_DIR").unwrap();
let src = Path::new("src/main.rs.in");
let dst = Path::new(&out_dir).join("main.rs");
serde_codegen::expand(&src, &dst).unwrap();
}
```
All this produces this when run:
```
% cargo run
{"x":1,"y":2}
Point { x: 1, y: 2 }
```
While this works well with Stable Rust, be aware that the error locations
currently are reported in the generated file instead of in the source file.
Using Serde with Nightly Rust and serde\_macros
===============================================
Here is a simple example that demonstrates how to use Serde by serializing and
deserializing to JSON. Serde comes with some powerful code generation libraries
that work with Stable and Nightly Rust that eliminate much of the complexity of
hand rolling serialization and deserialization for a given type. First lets see
how we would use Nightly Rust, which is currently a bit simpler than Stable
Rust:
The prior example is a bit more complicated than it needs to be due to compiler
plugins being unstable. However, if you are already using Nightly Rust, you can
use `serde_macros`, which has a much simpler interface. First, here is the new
`Cargo.toml`:
```toml
@@ -40,7 +204,8 @@ serde_json = "*"
serde_macros = "*"
```
`src/main.rs`
Note that it doesn't need a build script. Now the `src/main.rs`, which enables
the plugin feature, and registers the `serde_macros` plugin:
```rust
#![feature(custom_derive, plugin)]
@@ -56,17 +221,16 @@ struct Point {
fn main() {
let point = Point { x: 1, y: 2 };
let serialized = serde_json::to_string(&point).unwrap();
let serialized = serde_json::to_string(&point).unwrap();
println!("{}", serialized);
let deserialized: Point = serde_json::from_str(&serialized).unwrap();
println!("{:?}", deserialized);
}
```
When run, it produces:
This also produces the same output:
```
% cargo run
@@ -74,92 +238,7 @@ When run, it produces:
Point { x: 1, y: 2 }
```
Using Serde with Stable Rust, syntex, and serde\_codegen
========================================================
Stable Rust is a little more complicated because it does not yet support
compiler plugins. Instead we need to use the code generation library
[syntex](https://github.com/serde-rs/syntex) for this:
```toml
[package]
name = "serde_example"
version = "0.1.0"
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>"]
build = "build.rs"
[build-dependencies]
serde_codegen = "*"
syntex = "*"
[dependencies]
serde = "*"
serde_json = "*"
```
`src/main.rs`:
```rust,ignore
extern crate serde;
extern crate serde_json;
include!(concat!(env!("OUT_DIR"), "/main.rs"));
```
`src/main.rs.in`:
```rust,ignore
#[derive(Serialize, Deserialize, Debug)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let point = Point { x: 1, y: 2 };
let serialized = serde_json::to_string(&point).unwrap();
println!("{}", serialized);
let deserialized: Point = serde_json::from_str(&serialized).unwrap();
println!("{:?}", deserialized);
}
```
`build.rs`
```rust,ignore
extern crate syntex;
extern crate serde_codegen;
use std::env;
use std::path::Path;
pub fn main() {
let out_dir = env::var_os("OUT_DIR").unwrap();
let src = Path::new("src/main.rs.in");
let dst = Path::new(&out_dir).join("main.rs");
let mut registry = syntex::Registry::new();
serde_codegen::register(&mut registry);
registry.expand("", &src, &dst).unwrap();
}
```
This also produces:
```
% cargo run
{"x":1,"y":2}
Point { x: 1, y: 2 }
```
While this works well with Stable Rust, be aware that the error locations
currently are reported in the generated file instead of in the source file. You
may find it easier to develop with Nightly Rust and `serde\_macros`, then
You may find it easier to develop with Nightly Rust and `serde\_macros`, then
deploy with Stable Rust and `serde_codegen`. It's possible to combine both
approaches in one setup:
@@ -178,7 +257,6 @@ nightly = ["serde_macros"]
[build-dependencies]
serde_codegen = { version = "*", optional = true }
syntex = "*"
[dependencies]
serde = "*"
@@ -191,7 +269,6 @@ serde_macros = { version = "*", optional = true }
```rust,ignore
#[cfg(not(feature = "serde_macros"))]
mod inner {
extern crate syntex;
extern crate serde_codegen;
use std::env;
@@ -203,10 +280,7 @@ mod inner {
let src = Path::new("src/main.rs.in");
let dst = Path::new(&out_dir).join("main.rs");
let mut registry = syntex::Registry::new();
serde_codegen::register(&mut registry);
registry.expand("", &src, &dst).unwrap();
serde_codegen::expand(&src, &dst).unwrap();
}
}
@@ -688,12 +762,15 @@ how types are serialized. Here are the supported annotations:
Container Annotations:
| Annotation | Function |
| ---------- | -------- |
| `#[serde(rename="name")]` | Serialize and deserialize this container with the given name |
| `#[serde(rename(serialize="name1"))]` | Serialize this container with the given name |
| `#[serde(rename(deserialize="name1"))]` | Deserialize this container with the given name |
| `#[serde(deny_unknown_fields)]` | Always error during serialization when encountering unknown fields. When absent, unknown fields are ignored for self-describing formats like JSON. |
| Annotation | Function |
| ---------- | -------- |
| `#[serde(rename="name")]` | Serialize and deserialize this container with the given name |
| `#[serde(rename(serialize="name1"))]` | Serialize this container with the given name |
| `#[serde(rename(deserialize="name1"))]` | Deserialize this container with the given name |
| `#[serde(deny_unknown_fields)]` | Always error during serialization when encountering unknown fields. When absent, unknown fields are ignored for self-describing formats like JSON. |
| `#[serde(bound="T: MyTrait")]` | Where-clause for the Serialize and Deserialize impls. This replaces any bounds inferred by Serde. |
| `#[serde(bound(serialize="T: MyTrait"))]` | Where-clause for the Serialize impl. |
| `#[serde(bound(deserialize="T: MyTrait"))]` | Where-clause for the Deserialize impl. |
Variant Annotations:
@@ -705,18 +782,35 @@ Variant Annotations:
Field Annotations:
| Annotation | Function |
| ---------- | -------- |
| `#[serde(rename="name")]` | Serialize and deserialize this field with the given name |
| `#[serde(rename(serialize="name1"))]` | Serialize this field with the given name |
| `#[serde(rename(deserialize="name1"))]` | Deserialize this field with the given name |
| `#[serde(default)]` | If the value is not specified, use the `Default::default()` |
| `#[serde(default="$path")]` | Call the path to a function `fn() -> T` to build the value |
| `#[serde(skip_serializing)]` | Do not serialize this value |
| `#[serde(skip_deserializing)]` | Always use `Default::default()` or `#[serde(default="$path")]` instead of deserializing this value |
| `#[serde(skip_serializing_if="$path")]` | Do not serialize this value if this function `fn(&T) -> bool` returns `true` |
| `#[serde(serialize_with="$path")]` | Call a function `fn<T, S>(&T, &mut S) -> Result<(), S::Error> where S: Serializer` to serialize this value |
| `#[serde(deserialize_with="$path")]` | Call a function `fn<T, D>(&mut D) -> Result<T, D::Error> where D: Deserializer` to deserialize this value |
| Annotation | Function |
| ---------- | -------- |
| `#[serde(rename="name")]` | Serialize and deserialize this field with the given name |
| `#[serde(rename(serialize="name1"))]` | Serialize this field with the given name |
| `#[serde(rename(deserialize="name1"))]` | Deserialize this field with the given name |
| `#[serde(default)]` | If the value is not specified, use the `Default::default()` |
| `#[serde(default="$path")]` | Call the path to a function `fn() -> T` to build the value |
| `#[serde(skip_serializing)]` | Do not serialize this value |
| `#[serde(skip_deserializing)]` | Always use `Default::default()` or `#[serde(default="$path")]` instead of deserializing this value |
| `#[serde(skip_serializing_if="$path")]` | Do not serialize this value if this function `fn(&T) -> bool` returns `true` |
| `#[serde(serialize_with="$path")]` | Call a function `fn<S>(&T, &mut S) -> Result<(), S::Error> where S: Serializer` to serialize this value of type `T` |
| `#[serde(deserialize_with="$path")]` | Call a function `fn<D>(&mut D) -> Result<T, D::Error> where D: Deserializer` to deserialize this value of type `T` |
| `#[serde(bound="T: MyTrait")]` | Where-clause for the Serialize and Deserialize impls. This replaces any bounds inferred by Serde for the current field. |
| `#[serde(bound(serialize="T: MyTrait"))]` | Where-clause for the Serialize impl. |
| `#[serde(bound(deserialize="T: MyTrait"))]` | Where-clause for the Deserialize impl. |
Using in `no_std` crates
========================
The core `serde` package defines a number of features to enable usage in a
variety of freestanding environments. Enable any or none of the following
features, and use `default-features = false` in your `Cargo.toml`:
- `alloc` (implies `nightly`)
- `collections` (implies `alloc` and `nightly`)
- `std` (default)
If you only use `default-features = false`, you will receive a stock `no_std`
serde with no support for any of the collection types.
Upgrading from Serde 0.6
========================
@@ -730,6 +824,7 @@ Serialization Formats Using Serde
| Format | Name |
| ------ | ---- |
| Bincode | [bincode](https://crates.io/crates/bincode) |
| env vars | [envy](https://crates.io/crates/envy) |
| JSON | [serde\_json](https://crates.io/crates/serde_json) |
| MessagePack | [rmp](https://crates.io/crates/rmp) |
| XML | [serde\_xml](https://github.com/serde-rs/xml) |
+4 -5
View File
@@ -9,10 +9,9 @@ default = ["serde_codegen"]
nightly = ["serde_macros"]
[build-dependencies]
serde_codegen = { version = "^0.6.4", optional = true }
syntex = "^0.22.0"
serde_codegen = { version = "^0.7", optional = true }
[dependencies]
serde = "^0.6.1"
serde_json = "^0.6.0"
serde_macros = { version = "^0.6.1", optional = true }
serde = "^0.7"
serde_json = "^0.7"
serde_macros = { version = "^0.7", optional = true }
+3 -3
View File
@@ -2,12 +2,12 @@ This example demonstrates how to use Serde with Syntex. On stable or nightly
with Syntex, it can be built with:
```
% multirust run stable cargo run
% rustup run stable cargo run
Running `target/debug/serde-syntex-example`
{"x":1,"y":2}
Point { x: 1, y: 2 }
% multirust run nightly cargo run
% rustup run nightly cargo run
Running `target/debug/serde-syntex-example`
{"x":1,"y":2}
Point { x: 1, y: 2 }
@@ -16,5 +16,5 @@ Point { x: 1, y: 2 }
On nightly, it can use a plugin with:
```
% multirust run nightly cargo run --features nightly --no-default-features
% rustup run nightly cargo run --features nightly --no-default-features
```
+1 -5
View File
@@ -1,6 +1,5 @@
#[cfg(not(feature = "serde_macros"))]
mod inner {
extern crate syntex;
extern crate serde_codegen;
use std::env;
@@ -12,10 +11,7 @@ mod inner {
let src = Path::new("src/main.rs.in");
let dst = Path::new(&out_dir).join("main.rs");
let mut registry = syntex::Registry::new();
serde_codegen::register(&mut registry);
registry.expand("", &src, &dst).unwrap();
serde_codegen::expand(&src, &dst).unwrap();
}
}
+2 -2
View File
@@ -1,5 +1,5 @@
#![cfg_attr(nightly, feature(custom_derive, plugin))]
#![cfg_attr(nightly, plugin(serde_macros))]
#![cfg_attr(feature = "serde_macros", feature(custom_derive, plugin))]
#![cfg_attr(feature = "serde_macros", plugin(serde_macros))]
extern crate serde;
extern crate serde_json;
+8 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "serde"
version = "0.7.4"
version = "0.7.15"
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>"]
license = "MIT/Apache-2.0"
description = "A generic serialization/deserialization framework"
@@ -8,10 +8,16 @@ repository = "https://github.com/serde-rs/serde"
documentation = "https://serde-rs.github.io/serde/serde/"
readme = "../README.md"
keywords = ["serde", "serialization"]
include = ["Cargo.toml", "src/**/*.rs"]
[features]
default = ["std"]
std = []
nightly = []
nightly-testing = ["clippy", "nightly"]
alloc = ["nightly"]
collections = ["alloc"]
nightly-testing = ["clippy", "nightly", "std"]
[dependencies]
clippy = { version = "^0.*", optional = true }
+159 -135
View File
@@ -1,11 +1,15 @@
//! Helper module to enable serializing bytes more efficiently
use std::ops;
use std::fmt;
use std::ascii;
use core::{ops, fmt, char, iter, slice};
use core::fmt::Write;
use ser;
use de;
#[cfg(any(feature = "std", feature = "collections"))]
pub use self::bytebuf::{ByteBuf, ByteBufVisitor};
#[cfg(feature = "collections")]
use collections::Vec;
///////////////////////////////////////////////////////////////////////////////
@@ -17,7 +21,11 @@ pub struct Bytes<'a> {
impl<'a> fmt::Debug for Bytes<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "b\"{}\"", escape_bytestring(self.bytes))
try!(f.write_str("b\""));
for c in escape_bytestring(self.bytes) {
try!(f.write_char(c));
}
f.write_char('"')
}
}
@@ -29,10 +37,11 @@ impl<'a> From<&'a [u8]> for Bytes<'a> {
}
}
#[cfg(any(feature = "std", feature = "collections"))]
impl<'a> From<&'a Vec<u8>> for Bytes<'a> {
fn from(bytes: &'a Vec<u8>) -> Self {
Bytes {
bytes: &bytes,
bytes: bytes,
}
}
}
@@ -60,157 +69,172 @@ impl<'a> ser::Serialize for Bytes<'a> {
///////////////////////////////////////////////////////////////////////////////
/// `ByteBuf` wraps a `Vec<u8>` and serializes as a byte array.
#[derive(Clone, Default, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct ByteBuf {
bytes: Vec<u8>,
}
#[cfg(any(feature = "std", feature = "collections"))]
mod bytebuf {
use core::ops;
use core::fmt;
use core::fmt::Write;
impl ByteBuf {
/// Construct a new, empty `ByteBuf`.
pub fn new() -> Self {
ByteBuf {
bytes: Vec::new(),
use ser;
use de;
#[cfg(feature = "collections")]
use collections::Vec;
/// `ByteBuf` wraps a `Vec<u8>` and serializes as a byte array.
#[derive(Clone, Default, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct ByteBuf {
bytes: Vec<u8>,
}
impl ByteBuf {
/// Construct a new, empty `ByteBuf`.
pub fn new() -> Self {
ByteBuf {
bytes: Vec::new(),
}
}
/// Construct a new, empty `ByteBuf` with the specified capacity.
pub fn with_capacity(cap: usize) -> Self {
ByteBuf {
bytes: Vec::with_capacity(cap)
}
}
}
/// Construct a new, empty `ByteBuf` with the specified capacity.
pub fn with_capacity(cap: usize) -> Self {
ByteBuf {
bytes: Vec::with_capacity(cap)
impl fmt::Debug for ByteBuf {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(f.write_str("b\""));
for c in super::escape_bytestring(self.bytes.as_ref()) {
try!(f.write_char(c));
}
f.write_char('"')
}
}
}
impl fmt::Debug for ByteBuf {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "b\"{}\"", escape_bytestring(self.bytes.as_ref()))
}
}
impl Into<Vec<u8>> for ByteBuf {
fn into(self) -> Vec<u8> {
self.bytes
}
}
impl From<Vec<u8>> for ByteBuf {
fn from(bytes: Vec<u8>) -> Self {
ByteBuf {
bytes: bytes,
impl Into<Vec<u8>> for ByteBuf {
fn into(self) -> Vec<u8> {
self.bytes
}
}
}
impl AsRef<Vec<u8>> for ByteBuf {
fn as_ref(&self) -> &Vec<u8> {
&self.bytes
}
}
impl AsRef<[u8]> for ByteBuf {
fn as_ref(&self) -> &[u8] {
&self.bytes
}
}
impl AsMut<Vec<u8>> for ByteBuf {
fn as_mut(&mut self) -> &mut Vec<u8> {
&mut self.bytes
}
}
impl AsMut<[u8]> for ByteBuf {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.bytes
}
}
impl ops::Deref for ByteBuf {
type Target = [u8];
fn deref(&self) -> &[u8] { &self.bytes[..] }
}
impl ops::DerefMut for ByteBuf {
fn deref_mut(&mut self) -> &mut [u8] { &mut self.bytes[..] }
}
impl ser::Serialize for ByteBuf {
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
where S: ser::Serializer
{
serializer.serialize_bytes(&self)
}
}
/// This type implements the `serde::de::Visitor` trait for a `ByteBuf`.
pub struct ByteBufVisitor;
impl de::Visitor for ByteBufVisitor {
type Value = ByteBuf;
#[inline]
fn visit_unit<E>(&mut self) -> Result<ByteBuf, E>
where E: de::Error,
{
Ok(ByteBuf {
bytes: Vec::new(),
})
impl From<Vec<u8>> for ByteBuf {
fn from(bytes: Vec<u8>) -> Self {
ByteBuf {
bytes: bytes,
}
}
}
#[inline]
fn visit_seq<V>(&mut self, mut visitor: V) -> Result<ByteBuf, V::Error>
where V: de::SeqVisitor,
{
let (len, _) = visitor.size_hint();
let mut values = Vec::with_capacity(len);
impl AsRef<Vec<u8>> for ByteBuf {
fn as_ref(&self) -> &Vec<u8> {
&self.bytes
}
}
while let Some(value) = try!(visitor.visit()) {
values.push(value);
impl AsRef<[u8]> for ByteBuf {
fn as_ref(&self) -> &[u8] {
&self.bytes
}
}
impl AsMut<Vec<u8>> for ByteBuf {
fn as_mut(&mut self) -> &mut Vec<u8> {
&mut self.bytes
}
}
impl AsMut<[u8]> for ByteBuf {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.bytes
}
}
impl ops::Deref for ByteBuf {
type Target = [u8];
fn deref(&self) -> &[u8] { &self.bytes[..] }
}
impl ops::DerefMut for ByteBuf {
fn deref_mut(&mut self) -> &mut [u8] { &mut self.bytes[..] }
}
impl ser::Serialize for ByteBuf {
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
where S: ser::Serializer
{
serializer.serialize_bytes(self)
}
}
/// This type implements the `serde::de::Visitor` trait for a `ByteBuf`.
pub struct ByteBufVisitor;
impl de::Visitor for ByteBufVisitor {
type Value = ByteBuf;
#[inline]
fn visit_unit<E>(&mut self) -> Result<ByteBuf, E>
where E: de::Error,
{
Ok(ByteBuf {
bytes: Vec::new(),
})
}
try!(visitor.end());
#[inline]
fn visit_seq<V>(&mut self, mut visitor: V) -> Result<ByteBuf, V::Error>
where V: de::SeqVisitor,
{
let (len, _) = visitor.size_hint();
let mut values = Vec::with_capacity(len);
Ok(ByteBuf {
bytes: values,
})
while let Some(value) = try!(visitor.visit()) {
values.push(value);
}
try!(visitor.end());
Ok(ByteBuf {
bytes: values,
})
}
#[inline]
fn visit_bytes<E>(&mut self, v: &[u8]) -> Result<ByteBuf, E>
where E: de::Error,
{
self.visit_byte_buf(v.to_vec())
}
#[inline]
fn visit_byte_buf<E>(&mut self, v: Vec<u8>) -> Result<ByteBuf, E>
where E: de::Error,
{
Ok(ByteBuf {
bytes: v,
})
}
}
#[inline]
fn visit_bytes<E>(&mut self, v: &[u8]) -> Result<ByteBuf, E>
where E: de::Error,
{
self.visit_byte_buf(v.to_vec())
}
#[inline]
fn visit_byte_buf<E>(&mut self, v: Vec<u8>) -> Result<ByteBuf, E>
where E: de::Error,
{
Ok(ByteBuf {
bytes: v,
})
}
}
impl de::Deserialize for ByteBuf {
#[inline]
fn deserialize<D>(deserializer: &mut D) -> Result<ByteBuf, D::Error>
where D: de::Deserializer
{
deserializer.deserialize_bytes(ByteBufVisitor)
impl de::Deserialize for ByteBuf {
#[inline]
fn deserialize<D>(deserializer: &mut D) -> Result<ByteBuf, D::Error>
where D: de::Deserializer
{
deserializer.deserialize_bytes(ByteBufVisitor)
}
}
}
///////////////////////////////////////////////////////////////////////////////
fn escape_bytestring(bytes: &[u8]) -> String {
let mut result = String::new();
for &b in bytes {
for esc in ascii::escape_default(b) {
result.push(esc as char);
}
#[inline]
fn escape_bytestring<'a>(bytes: &'a [u8]) -> iter::FlatMap<slice::Iter<'a, u8>, char::EscapeDefault, fn(&u8) -> char::EscapeDefault> {
fn f(b: &u8) -> char::EscapeDefault {
char::from_u32(*b as u32).unwrap().escape_default()
}
result
bytes.iter().flat_map(f as fn(&u8) -> char::EscapeDefault)
}
+5 -5
View File
@@ -13,10 +13,10 @@
// Rust 1.5 is unhappy that this private module is undocumented.
#![allow(missing_docs)]
use std::{usize, u8, u16, u32, u64};
use std::{isize, i8, i16, i32, i64};
use std::{f32, f64};
use std::mem::size_of;
use core::{usize, u8, u16, u32, u64};
use core::{isize, i8, i16, i32, i64};
use core::{f32, f64};
use core::mem::size_of;
/// Numbers which have upper and lower bounds
pub trait Bounded {
@@ -274,7 +274,7 @@ macro_rules! impl_to_primitive_float_to_float {
Some($slf as $DstT)
} else {
let n = $slf as f64;
let max_value: $SrcT = ::std::$SrcT::MAX;
let max_value: $SrcT = ::core::$SrcT::MAX;
if -max_value as f64 <= n && n <= max_value as f64 {
Some($slf as $DstT)
} else {
+129 -65
View File
@@ -1,30 +1,63 @@
//! This module contains `Deserialize` and `Visitor` implementations.
#[cfg(feature = "std")]
use std::borrow::Cow;
use std::collections::{
#[cfg(all(feature = "nightly", feature = "collections", not(feature = "std")))]
use collections::borrow::Cow;
#[cfg(all(feature = "collections", not(feature = "std")))]
use collections::{
BinaryHeap,
BTreeMap,
BTreeSet,
LinkedList,
VecDeque,
Vec,
String,
};
#[cfg(feature = "std")]
use std::collections::{
HashMap,
HashSet,
BinaryHeap,
BTreeMap,
BTreeSet,
LinkedList,
VecDeque,
};
#[cfg(feature = "nightly")]
#[cfg(all(feature = "nightly", feature = "collections"))]
use collections::enum_set::{CLike, EnumSet};
use std::hash::Hash;
use std::marker::PhantomData;
#[cfg(all(feature = "nightly", feature = "collections"))]
use collections::borrow::ToOwned;
use core::hash::{Hash, BuildHasher};
use core::marker::PhantomData;
#[cfg(feature = "std")]
use std::net;
#[cfg(feature = "std")]
use std::path;
use core::str;
#[cfg(feature = "std")]
use std::rc::Rc;
use std::str;
#[cfg(all(feature = "nightly", feature = "alloc", not(feature = "std")))]
use alloc::rc::Rc;
#[cfg(feature = "std")]
use std::sync::Arc;
#[cfg(all(feature = "nightly", feature = "alloc", not(feature = "std")))]
use alloc::arc::Arc;
#[cfg(all(feature = "nightly", feature = "alloc", not(feature = "std")))]
use alloc::boxed::Box;
#[cfg(feature = "nightly")]
use core::nonzero::{NonZero, Zeroable};
#[cfg(feature = "nightly")]
use std::num::Zero;
use core::num::Zero;
use de::{
Deserialize,
@@ -85,7 +118,7 @@ impl Visitor for BoolVisitor {
fn visit_str<E>(&mut self, s: &str) -> Result<bool, E>
where E: Error,
{
match s.trim() {
match s.trim_matches(::utils::Pattern_White_Space) {
"true" => Ok(true),
"false" => Ok(false),
_ => Err(Error::invalid_type(Type::Bool)),
@@ -151,10 +184,10 @@ impl<T> Visitor for PrimitiveVisitor<T>
impl_deserialize_num_method!(f64, visit_f64, from_f64, Type::F64);
#[inline]
fn visit_str<E>(&mut self, v: &str) -> Result<T, E>
fn visit_str<E>(&mut self, s: &str) -> Result<T, E>
where E: Error,
{
str::FromStr::from_str(v.trim()).or_else(|_| {
str::FromStr::from_str(s.trim_matches(::utils::Pattern_White_Space)).or_else(|_| {
Err(Error::invalid_type(Type::Str))
})
}
@@ -228,8 +261,10 @@ impl Deserialize for char {
///////////////////////////////////////////////////////////////////////////////
#[cfg(any(feature = "std", feature = "collections"))]
struct StringVisitor;
#[cfg(any(feature = "std", feature = "collections"))]
impl Visitor for StringVisitor {
type Value = String;
@@ -264,6 +299,7 @@ impl Visitor for StringVisitor {
}
}
#[cfg(any(feature = "std", feature = "collections"))]
impl Deserialize for String {
fn deserialize<D>(deserializer: &mut D) -> Result<String, D::Error>
where D: Deserializer,
@@ -345,29 +381,30 @@ impl<T> Deserialize for PhantomData<T> where T: Deserialize {
macro_rules! seq_impl {
(
$ty:ty,
< $($constraints:ident),* >,
$visitor_name:ident,
$visitor_ty:ident < $($typaram:ident : $bound1:ident $(+ $bound2:ident)*),* >,
$visitor:ident,
$ctor:expr,
$with_capacity:expr,
$insert:expr
) => {
/// A visitor that produces a sequence.
pub struct $visitor_name<T> {
marker: PhantomData<T>,
pub struct $visitor_ty<$($typaram),*> {
marker: PhantomData<$ty>,
}
impl<T> $visitor_name<T> {
impl<$($typaram),*> $visitor_ty<$($typaram),*>
where $($typaram: $bound1 $(+ $bound2)*),*
{
/// Construct a new sequence visitor.
pub fn new() -> Self {
$visitor_name {
$visitor_ty {
marker: PhantomData,
}
}
}
impl<T> Visitor for $visitor_name<T>
where T: $($constraints +)*,
impl<$($typaram),*> Visitor for $visitor_ty<$($typaram),*>
where $($typaram: $bound1 $(+ $bound2)*),*
{
type Value = $ty;
@@ -394,77 +431,77 @@ macro_rules! seq_impl {
}
}
impl<T> Deserialize for $ty
where T: $($constraints +)*,
impl<$($typaram),*> Deserialize for $ty
where $($typaram: $bound1 $(+ $bound2)*),*
{
fn deserialize<D>(deserializer: &mut D) -> Result<$ty, D::Error>
where D: Deserializer,
{
deserializer.deserialize_seq($visitor_name::new())
deserializer.deserialize_seq($visitor_ty::new())
}
}
}
}
#[cfg(any(feature = "std", feature = "collections"))]
seq_impl!(
BinaryHeap<T>,
<Deserialize, Ord>,
BinaryHeapVisitor,
BinaryHeapVisitor<T: Deserialize + Ord>,
visitor,
BinaryHeap::new(),
BinaryHeap::with_capacity(visitor.size_hint().0),
BinaryHeap::push);
#[cfg(any(feature = "std", feature = "collections"))]
seq_impl!(
BTreeSet<T>,
<Deserialize, Eq, Ord>,
BTreeSetVisitor,
BTreeSetVisitor<T: Deserialize + Eq + Ord>,
visitor,
BTreeSet::new(),
BTreeSet::new(),
BTreeSet::insert);
#[cfg(feature = "nightly")]
#[cfg(all(feature = "nightly", feature = "collections"))]
seq_impl!(
EnumSet<T>,
<Deserialize, CLike>,
EnumSetVisitor,
EnumSetVisitor<T: Deserialize + CLike>,
visitor,
EnumSet::new(),
EnumSet::new(),
EnumSet::insert);
#[cfg(any(feature = "std", feature = "collections"))]
seq_impl!(
LinkedList<T>,
<Deserialize>,
LinkedListVisitor,
LinkedListVisitor<T: Deserialize>,
visitor,
LinkedList::new(),
LinkedList::new(),
LinkedList::push_back);
#[cfg(feature = "std")]
seq_impl!(
HashSet<T>,
<Deserialize, Eq, Hash>,
HashSetVisitor,
HashSet<T, S>,
HashSetVisitor<T: Deserialize + Eq + Hash,
S: BuildHasher + Default>,
visitor,
HashSet::new(),
HashSet::with_capacity(visitor.size_hint().0),
HashSet::with_hasher(S::default()),
HashSet::with_capacity_and_hasher(visitor.size_hint().0, S::default()),
HashSet::insert);
#[cfg(any(feature = "std", feature = "collections"))]
seq_impl!(
Vec<T>,
<Deserialize>,
VecVisitor,
VecVisitor<T: Deserialize>,
visitor,
Vec::new(),
Vec::with_capacity(visitor.size_hint().0),
Vec::push);
#[cfg(any(feature = "std", feature = "collections"))]
seq_impl!(
VecDeque<T>,
<Deserialize>,
VecDequeVisitor,
VecDequeVisitor<T: Deserialize>,
visitor,
VecDeque::new(),
VecDeque::with_capacity(visitor.size_hint().0),
@@ -609,7 +646,6 @@ array_impls! {
///////////////////////////////////////////////////////////////////////////////
macro_rules! tuple_impls {
() => {};
($($len:expr => $visitor:ident => ($($name:ident),+),)+) => {
$(
/// Construct a tuple visitor.
@@ -677,6 +713,10 @@ tuple_impls! {
10 => TupleVisitor10 => (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9),
11 => TupleVisitor11 => (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10),
12 => TupleVisitor12 => (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11),
13 => TupleVisitor13 => (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12),
14 => TupleVisitor14 => (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13),
15 => TupleVisitor15 => (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14),
16 => TupleVisitor16 => (T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15),
}
///////////////////////////////////////////////////////////////////////////////
@@ -684,30 +724,29 @@ tuple_impls! {
macro_rules! map_impl {
(
$ty:ty,
< $($constraints:ident),* >,
$visitor_name:ident,
$visitor_ty:ident < $($typaram:ident : $bound1:ident $(+ $bound2:ident)*),* >,
$visitor:ident,
$ctor:expr,
$with_capacity:expr,
$insert:expr
$with_capacity:expr
) => {
/// A visitor that produces a map.
pub struct $visitor_name<K, V> {
pub struct $visitor_ty<$($typaram),*> {
marker: PhantomData<$ty>,
}
impl<K, V> $visitor_name<K, V> {
impl<$($typaram),*> $visitor_ty<$($typaram),*>
where $($typaram: $bound1 $(+ $bound2)*),*
{
/// Construct a `MapVisitor*<T>`.
pub fn new() -> Self {
$visitor_name {
$visitor_ty {
marker: PhantomData,
}
}
}
impl<K, V> Visitor for $visitor_name<K, V>
where K: $($constraints +)*,
V: Deserialize,
impl<$($typaram),*> Visitor for $visitor_ty<$($typaram),*>
where $($typaram: $bound1 $(+ $bound2)*),*
{
type Value = $ty;
@@ -725,7 +764,7 @@ macro_rules! map_impl {
let mut values = $with_capacity;
while let Some((key, value)) = try!($visitor.visit()) {
$insert(&mut values, key, value);
values.insert(key, value);
}
try!($visitor.end());
@@ -734,40 +773,40 @@ macro_rules! map_impl {
}
}
impl<K, V> Deserialize for $ty
where K: $($constraints +)*,
V: Deserialize,
impl<$($typaram),*> Deserialize for $ty
where $($typaram: $bound1 $(+ $bound2)*),*
{
fn deserialize<D>(deserializer: &mut D) -> Result<$ty, D::Error>
where D: Deserializer,
{
deserializer.deserialize_map($visitor_name::new())
deserializer.deserialize_map($visitor_ty::new())
}
}
}
}
#[cfg(any(feature = "std", feature = "collections"))]
map_impl!(
BTreeMap<K, V>,
<Deserialize, Eq, Ord>,
BTreeMapVisitor,
BTreeMapVisitor<K: Deserialize + Eq + Ord,
V: Deserialize>,
visitor,
BTreeMap::new(),
BTreeMap::new(),
BTreeMap::insert);
BTreeMap::new());
#[cfg(feature = "std")]
map_impl!(
HashMap<K, V>,
<Deserialize, Eq, Hash>,
HashMapVisitor,
HashMap<K, V, S>,
HashMapVisitor<K: Deserialize + Eq + Hash,
V: Deserialize,
S: BuildHasher + Default>,
visitor,
HashMap::new(),
HashMap::with_capacity(visitor.size_hint().0),
HashMap::insert);
HashMap::with_hasher(S::default()),
HashMap::with_capacity_and_hasher(visitor.size_hint().0, S::default()));
///////////////////////////////////////////////////////////////////////////////
#[cfg(feature = "nightly")]
#[cfg(all(feature = "nightly", feature = "std"))]
impl Deserialize for net::IpAddr {
fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
where D: Deserializer,
@@ -780,6 +819,7 @@ impl Deserialize for net::IpAddr {
}
}
#[cfg(feature = "std")]
impl Deserialize for net::Ipv4Addr {
fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
where D: Deserializer,
@@ -792,6 +832,7 @@ impl Deserialize for net::Ipv4Addr {
}
}
#[cfg(feature = "std")]
impl Deserialize for net::Ipv6Addr {
fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
where D: Deserializer,
@@ -806,6 +847,7 @@ impl Deserialize for net::Ipv6Addr {
///////////////////////////////////////////////////////////////////////////////
#[cfg(feature = "std")]
impl Deserialize for net::SocketAddr {
fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
where D: Deserializer,
@@ -818,6 +860,7 @@ impl Deserialize for net::SocketAddr {
}
}
#[cfg(feature = "std")]
impl Deserialize for net::SocketAddrV4 {
fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
where D: Deserializer,
@@ -830,6 +873,7 @@ impl Deserialize for net::SocketAddrV4 {
}
}
#[cfg(feature = "std")]
impl Deserialize for net::SocketAddrV6 {
fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
where D: Deserializer,
@@ -844,8 +888,10 @@ impl Deserialize for net::SocketAddrV6 {
///////////////////////////////////////////////////////////////////////////////
#[cfg(feature = "std")]
struct PathBufVisitor;
#[cfg(feature = "std")]
impl Visitor for PathBufVisitor {
type Value = path::PathBuf;
@@ -862,6 +908,7 @@ impl Visitor for PathBufVisitor {
}
}
#[cfg(feature = "std")]
impl Deserialize for path::PathBuf {
fn deserialize<D>(deserializer: &mut D) -> Result<path::PathBuf, D::Error>
where D: Deserializer,
@@ -872,6 +919,7 @@ impl Deserialize for path::PathBuf {
///////////////////////////////////////////////////////////////////////////////
#[cfg(any(feature = "std", feature = "alloc"))]
impl<T: Deserialize> Deserialize for Box<T> {
fn deserialize<D>(deserializer: &mut D) -> Result<Box<T>, D::Error>
where D: Deserializer,
@@ -881,6 +929,7 @@ impl<T: Deserialize> Deserialize for Box<T> {
}
}
#[cfg(any(feature = "std", feature = "collections"))]
impl<T: Deserialize> Deserialize for Box<[T]> {
fn deserialize<D>(deserializer: &mut D) -> Result<Box<[T]>, D::Error>
where D: Deserializer,
@@ -890,6 +939,7 @@ impl<T: Deserialize> Deserialize for Box<[T]> {
}
}
#[cfg(any(feature = "std", feature = "alloc"))]
impl<T: Deserialize> Deserialize for Arc<T> {
fn deserialize<D>(deserializer: &mut D) -> Result<Arc<T>, D::Error>
where D: Deserializer,
@@ -899,6 +949,7 @@ impl<T: Deserialize> Deserialize for Arc<T> {
}
}
#[cfg(any(feature = "std", feature = "alloc"))]
impl<T: Deserialize> Deserialize for Rc<T> {
fn deserialize<D>(deserializer: &mut D) -> Result<Rc<T>, D::Error>
where D: Deserializer,
@@ -908,6 +959,7 @@ impl<T: Deserialize> Deserialize for Rc<T> {
}
}
#[cfg(any(feature = "std", feature = "collections"))]
impl<'a, T: ?Sized> Deserialize for Cow<'a, T> where T: ToOwned, T::Owned: Deserialize, {
#[inline]
fn deserialize<D>(deserializer: &mut D) -> Result<Cow<'a, T>, D::Error>
@@ -954,7 +1006,10 @@ impl<T, E> Deserialize for Result<T, E> where T: Deserialize, E: Deserialize {
impl ::de::Visitor for FieldVisitor {
type Value = Field;
#[cfg(any(feature = "std", feature = "collections"))]
fn visit_usize<E>(&mut self, value: usize) -> Result<Field, E> where E: Error {
#[cfg(feature = "collections")]
use collections::string::ToString;
match value {
0 => Ok(Field::Ok),
1 => Ok(Field::Err),
@@ -962,6 +1017,15 @@ impl<T, E> Deserialize for Result<T, E> where T: Deserialize, E: Deserialize {
}
}
#[cfg(all(not(feature = "std"), not(feature = "collections")))]
fn visit_usize<E>(&mut self, value: usize) -> Result<Field, E> where E: Error {
match value {
0 => Ok(Field::Ok),
1 => Ok(Field::Err),
_ => Err(Error::unknown_field("some number")),
}
}
fn visit_str<E>(&mut self, value: &str) -> Result<Field, E> where E: Error {
match value {
"Ok" => Ok(Field::Ok),
+66 -4
View File
@@ -1,6 +1,14 @@
//! Generic deserialization framework.
#[cfg(feature = "std")]
use std::error;
#[cfg(not(feature = "std"))]
use error;
#[cfg(all(not(feature = "std"), feature = "collections"))]
use collections::{String, Vec};
use core::fmt;
pub mod impls;
pub mod value;
@@ -12,8 +20,13 @@ mod from_primitive;
/// `Deserializer` error.
pub trait Error: Sized + error::Error {
/// Raised when there is general error when deserializing a type.
#[cfg(any(feature = "std", feature = "collections"))]
fn custom<T: Into<String>>(msg: T) -> Self;
/// Raised when there is general error when deserializing a type.
#[cfg(all(not(feature = "std"), not(feature = "collections")))]
fn custom<T: Into<&'static str>>(msg: T) -> Self;
/// Raised when a `Deserialize` type unexpectedly hit the end of the stream.
fn end_of_stream() -> Self;
@@ -28,6 +41,9 @@ pub trait Error: Sized + error::Error {
}
/// Raised when a fixed sized sequence or map was passed in the wrong amount of arguments.
///
/// The parameter `len` is the number of arguments found in the serialization. The sequence
/// may either expect more arguments or less arguments.
fn invalid_length(len: usize) -> Self {
Error::custom(format!("Invalid length: {}", len))
}
@@ -46,6 +62,12 @@ pub trait Error: Sized + error::Error {
fn missing_field(field: &'static str) -> Self {
Error::custom(format!("Missing field `{}`", field))
}
/// Raised when a `Deserialize` struct type received more than one of the
/// same struct field.
fn duplicate_field(field: &'static str) -> Self {
Error::custom(format!("Duplicate field `{}`", field))
}
}
/// `Type` represents all the primitive types that can be deserialized. This is used by
@@ -149,6 +171,46 @@ pub enum Type {
Bytes,
}
impl fmt::Display for Type {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
let display = match *self {
Type::Bool => "bool",
Type::Usize => "usize",
Type::U8 => "u8",
Type::U16 => "u16",
Type::U32 => "u32",
Type::U64 => "u64",
Type::Isize => "isize",
Type::I8 => "i8",
Type::I16 => "i16",
Type::I32 => "i32",
Type::I64 => "i64",
Type::F32 => "f32",
Type::F64 => "f64",
Type::Char => "char",
Type::Str => "str",
Type::String => "string",
Type::Unit => "unit",
Type::Option => "option",
Type::Seq => "seq",
Type::Map => "map",
Type::UnitStruct => "unit struct",
Type::NewtypeStruct => "newtype struct",
Type::TupleStruct => "tuple struct",
Type::Struct => "struct",
Type::FieldName => "field name",
Type::Tuple => "tuple",
Type::Enum => "enum",
Type::VariantName => "variant name",
Type::StructVariant => "struct variant",
Type::TupleVariant => "tuple variant",
Type::UnitVariant => "unit variant",
Type::Bytes => "bytes",
};
display.fmt(formatter)
}
}
///////////////////////////////////////////////////////////////////////////////
/// `Deserialize` represents a type that can be deserialized.
@@ -558,9 +620,7 @@ pub trait Visitor {
fn visit_char<E>(&mut self, v: char) -> Result<Self::Value, E>
where E: Error,
{
// FIXME: this allocation is required in order to be compatible with stable rust, which
// doesn't support encoding a `char` into a stack buffer.
self.visit_string(v.to_string())
self.visit_str(::utils::encode_utf8(v).as_str())
}
/// `visit_str` deserializes a `&str` into a `Value`.
@@ -574,6 +634,7 @@ pub trait Visitor {
/// a copy if it is deserializing a string from a `String` type. By default it passes a `&str`
/// to the `visit_str` method.
#[inline]
#[cfg(any(feature = "std", feature = "collections"))]
fn visit_string<E>(&mut self, v: String) -> Result<Self::Value, E>
where E: Error,
{
@@ -616,7 +677,7 @@ pub trait Visitor {
Err(Error::invalid_type(Type::NewtypeStruct))
}
/// `visit_bool` deserializes a `SeqVisitor` into a `Value`.
/// `visit_seq` deserializes a `SeqVisitor` into a `Value`.
fn visit_seq<V>(&mut self, _visitor: V) -> Result<Self::Value, V::Error>
where V: SeqVisitor,
{
@@ -638,6 +699,7 @@ pub trait Visitor {
}
/// `visit_byte_buf` deserializes a `Vec<u8>` into a `Value`.
#[cfg(any(feature = "std", feature = "collections"))]
fn visit_byte_buf<E>(&mut self, v: Vec<u8>) -> Result<Self::Value, E>
where E: Error,
{
+72 -4
View File
@@ -1,5 +1,6 @@
//! This module supports deserializing from primitives with the `ValueDeserializer` trait.
#[cfg(feature = "std")]
use std::collections::{
BTreeMap,
BTreeSet,
@@ -10,11 +11,31 @@ use std::collections::{
hash_map,
hash_set,
};
use std::hash::Hash;
use std::error;
use std::fmt;
#[cfg(feature = "std")]
use std::vec;
use std::marker::PhantomData;
#[cfg(all(feature = "collections", not(feature = "std")))]
use collections::{
BTreeMap,
BTreeSet,
Vec,
String,
btree_map,
btree_set,
vec,
};
#[cfg(all(feature = "nightly", feature = "collections"))]
use collections::borrow::ToOwned;
use core::hash::Hash;
#[cfg(feature = "std")]
use std::error;
#[cfg(not(feature = "std"))]
use error;
use core::fmt;
use core::marker::PhantomData;
use de;
use bytes;
@@ -25,7 +46,11 @@ use bytes;
#[derive(Clone, Debug, PartialEq)]
pub enum Error {
/// The value had some custom error.
#[cfg(any(feature = "std", feature = "collections"))]
Custom(String),
/// The value had some custom error.
#[cfg(all(not(feature = "std"), not(feature = "collections")))]
Custom(&'static str),
/// The value had an incorrect type.
InvalidType(de::Type),
@@ -34,29 +59,60 @@ pub enum Error {
InvalidLength(usize),
/// The value is invalid and cannot be deserialized.
#[cfg(any(feature = "std", feature = "collections"))]
InvalidValue(String),
/// The value is invalid and cannot be deserialized.
#[cfg(all(not(feature = "std"), not(feature = "collections")))]
InvalidValue(&'static str),
/// EOF while deserializing a value.
EndOfStream,
/// Unknown variant in enum.
#[cfg(any(feature = "std", feature = "collections"))]
UnknownVariant(String),
/// Unknown variant in enum.
#[cfg(all(not(feature = "std"), not(feature = "collections")))]
UnknownVariant(&'static str),
/// Unknown field in struct.
#[cfg(any(feature = "std", feature = "collections"))]
UnknownField(String),
/// Unknown field in struct.
#[cfg(all(not(feature = "std"), not(feature = "collections")))]
UnknownField(&'static str),
/// Struct is missing a field.
MissingField(&'static str),
}
impl de::Error for Error {
#[cfg(any(feature = "std", feature = "collections"))]
fn custom<T: Into<String>>(msg: T) -> Self { Error::Custom(msg.into()) }
#[cfg(all(not(feature = "std"), not(feature = "collections")))]
fn custom<T: Into<&'static str>>(msg: T) -> Self { Error::Custom(msg.into()) }
fn end_of_stream() -> Self { Error::EndOfStream }
fn invalid_type(ty: de::Type) -> Self { Error::InvalidType(ty) }
#[cfg(any(feature = "std", feature = "collections"))]
fn invalid_value(msg: &str) -> Self { Error::InvalidValue(msg.to_owned()) }
#[cfg(all(not(feature = "std"), not(feature = "collections")))]
fn invalid_value(msg: &str) -> Self { Error::InvalidValue("invalid value") }
fn invalid_length(len: usize) -> Self { Error::InvalidLength(len) }
#[cfg(any(feature = "std", feature = "collections"))]
fn unknown_variant(variant: &str) -> Self { Error::UnknownVariant(String::from(variant)) }
#[cfg(any(feature = "std", feature = "collections"))]
fn unknown_field(field: &str) -> Self { Error::UnknownField(String::from(field)) }
#[cfg(all(not(feature = "std"), not(feature = "collections")))]
fn unknown_variant(variant: &str) -> Self { Error::UnknownVariant("unknown variant") }
#[cfg(all(not(feature = "std"), not(feature = "collections")))]
fn unknown_field(field: &str) -> Self { Error::UnknownField("unknown field") }
fn missing_field(field: &'static str) -> Self { Error::MissingField(field) }
}
@@ -238,8 +294,10 @@ impl<'a, E> de::VariantVisitor for StrDeserializer<'a, E>
///////////////////////////////////////////////////////////////////////////////
/// A helper deserializer that deserializes a `String`.
#[cfg(any(feature = "std", feature = "collections"))]
pub struct StringDeserializer<E>(Option<String>, PhantomData<E>);
#[cfg(any(feature = "std", feature = "collections"))]
impl<E> ValueDeserializer<E> for String
where E: de::Error,
{
@@ -250,6 +308,7 @@ impl<E> ValueDeserializer<E> for String
}
}
#[cfg(any(feature = "std", feature = "collections"))]
impl<E> de::Deserializer for StringDeserializer<E>
where E: de::Error,
{
@@ -274,6 +333,7 @@ impl<E> de::Deserializer for StringDeserializer<E>
}
}
#[cfg(any(feature = "std", feature = "collections"))]
impl<'a, E> de::VariantVisitor for StringDeserializer<E>
where E: de::Error,
{
@@ -361,6 +421,7 @@ impl<I, T, E> de::SeqVisitor for SeqDeserializer<I, E>
///////////////////////////////////////////////////////////////////////////////
#[cfg(any(feature = "std", feature = "collections"))]
impl<T, E> ValueDeserializer<E> for Vec<T>
where T: ValueDeserializer<E>,
E: de::Error,
@@ -373,6 +434,7 @@ impl<T, E> ValueDeserializer<E> for Vec<T>
}
}
#[cfg(any(feature = "std", feature = "collections"))]
impl<T, E> ValueDeserializer<E> for BTreeSet<T>
where T: ValueDeserializer<E> + Eq + Ord,
E: de::Error,
@@ -385,6 +447,7 @@ impl<T, E> ValueDeserializer<E> for BTreeSet<T>
}
}
#[cfg(feature = "std")]
impl<T, E> ValueDeserializer<E> for HashSet<T>
where T: ValueDeserializer<E> + Eq + Hash,
E: de::Error,
@@ -527,6 +590,7 @@ impl<I, K, V, E> de::MapVisitor for MapDeserializer<I, K, V, E>
///////////////////////////////////////////////////////////////////////////////
#[cfg(any(feature = "std", feature = "collections"))]
impl<K, V, E> ValueDeserializer<E> for BTreeMap<K, V>
where K: ValueDeserializer<E> + Eq + Ord,
V: ValueDeserializer<E>,
@@ -540,6 +604,7 @@ impl<K, V, E> ValueDeserializer<E> for BTreeMap<K, V>
}
}
#[cfg(feature = "std")]
impl<K, V, E> ValueDeserializer<E> for HashMap<K, V>
where K: ValueDeserializer<E> + Eq + Hash,
V: ValueDeserializer<E>,
@@ -618,6 +683,7 @@ impl<'a, E> de::Deserializer for BytesDeserializer<'a, E>
///////////////////////////////////////////////////////////////////////////////
#[cfg(any(feature = "std", feature = "collections"))]
impl<E> ValueDeserializer<E> for bytes::ByteBuf
where E: de::Error,
{
@@ -629,8 +695,10 @@ impl<E> ValueDeserializer<E> for bytes::ByteBuf
}
/// A helper deserializer that deserializes a `Vec<u8>`.
#[cfg(any(feature = "std", feature = "collections"))]
pub struct ByteBufDeserializer<E>(Option<Vec<u8>>, PhantomData<E>);
#[cfg(any(feature = "std", feature = "collections"))]
impl<E> de::Deserializer for ByteBufDeserializer<E>
where E: de::Error,
{
+44
View File
@@ -0,0 +1,44 @@
//! A stand-in for `std::error`
use core::any::TypeId;
use core::fmt::{Debug, Display};
/// A stand-in for `std::error::Error`, which requires no allocation.
#[cfg(feature = "nightly")]
pub trait Error: Debug + Display + ::core::marker::Reflect {
/// A short description of the error.
///
/// The description should not contain newlines or sentence-ending
/// punctuation, to facilitate embedding in larger user-facing
/// strings.
fn description(&self) -> &str;
/// The lower-level cause of this error, if any.
fn cause(&self) -> Option<&Error> { None }
/// Get the `TypeId` of `self`
#[doc(hidden)]
fn type_id(&self) -> TypeId where Self: 'static {
TypeId::of::<Self>()
}
}
/// A stand-in for `std::error::Error`, which requires no allocation.
#[cfg(not(feature = "nightly"))]
pub trait Error: Debug + Display {
/// A short description of the error.
///
/// The description should not contain newlines or sentence-ending
/// punctuation, to facilitate embedding in larger user-facing
/// strings.
fn description(&self) -> &str;
/// The lower-level cause of this error, if any.
fn cause(&self) -> Option<&Error> { None }
/// Stubbed! Returns type_id of `()`
#[doc(hidden)]
fn type_id(&self) -> TypeId where Self: 'static {
TypeId::of::<()>()
}
}
+28 -5
View File
@@ -10,23 +10,46 @@
//! [github repository](https://github.com/serde-rs/serde)
#![doc(html_root_url="https://serde-rs.github.io/serde/serde")]
#![cfg_attr(feature = "nightly", feature(collections, enumset, nonzero, plugin, step_trait,
zero_one))]
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(feature = "nightly", feature(reflect_marker, unicode, nonzero, plugin, step_trait, zero_one))]
#![cfg_attr(feature = "alloc", feature(alloc))]
#![cfg_attr(feature = "collections", feature(collections, enumset))]
#![cfg_attr(feature = "nightly-testing", plugin(clippy))]
#![cfg_attr(feature = "nightly-testing", allow(linkedlist))]
#![cfg_attr(any(not(feature = "std"), feature = "nightly"), allow(unused_variables, unused_imports, unused_features, dead_code))]
#![deny(missing_docs)]
#[cfg(feature = "nightly")]
#[cfg(all(feature = "nightly", feature = "collections"))]
extern crate collections;
#[cfg(feature = "nightly")]
extern crate core;
#[cfg(all(feature = "nightly", feature = "alloc"))]
extern crate alloc;
#[cfg(feature = "std")]
mod core {
pub use std::{ops, hash, fmt, cmp, marker, mem, i8, i16, i32, i64, u8, u16, u32, u64, isize,
usize, f32, f64, char, str, num, slice, iter};
#[cfg(feature = "nightly")]
extern crate core;
#[cfg(feature = "nightly")]
pub use self::core::nonzero;
}
pub use ser::{Serialize, Serializer};
pub use de::{Deserialize, Deserializer, Error};
#[cfg(not(feature = "std"))]
macro_rules! format {
($s:expr, $($rest:tt)*) => ($s)
}
pub mod bytes;
pub mod de;
#[cfg(feature = "std")]
pub mod iter;
pub mod ser;
#[cfg(not(feature = "std"))]
pub mod error;
mod utils;
+130 -10
View File
@@ -1,6 +1,11 @@
//! Implementations for all of Rust's builtin types.
#[cfg(feature = "std")]
use std::borrow::Cow;
#[cfg(all(feature = "collections", not(feature = "std")))]
use collections::borrow::Cow;
#[cfg(feature = "std")]
use std::collections::{
BinaryHeap,
BTreeMap,
@@ -10,20 +15,47 @@ use std::collections::{
HashSet,
VecDeque,
};
#[cfg(feature = "nightly")]
#[cfg(all(feature = "collections", not(feature = "std")))]
use collections::{
BinaryHeap,
BTreeMap,
BTreeSet,
LinkedList,
VecDeque,
String,
Vec,
};
#[cfg(all(feature = "nightly", feature = "collections"))]
use collections::enum_set::{CLike, EnumSet};
use std::hash::Hash;
#[cfg(all(feature = "nightly", feature = "collections"))]
use collections::borrow::ToOwned;
use core::hash::{Hash, BuildHasher};
#[cfg(feature = "nightly")]
use std::iter;
use core::iter;
#[cfg(feature = "std")]
use std::net;
#[cfg(feature = "nightly")]
use std::num;
use core::num;
#[cfg(feature = "nightly")]
use std::ops;
use core::ops;
#[cfg(feature = "std")]
use std::path;
#[cfg(feature = "std")]
use std::rc::Rc;
#[cfg(all(feature = "alloc", not(feature = "std")))]
use alloc::rc::Rc;
#[cfg(feature = "std")]
use std::sync::Arc;
use std::marker::PhantomData;
#[cfg(all(feature = "alloc", not(feature = "std")))]
use alloc::arc::Arc;
#[cfg(all(feature = "alloc", not(feature = "std")))]
use alloc::boxed::Box;
use core::marker::PhantomData;
#[cfg(feature = "nightly")]
use core::nonzero::{NonZero, Zeroable};
@@ -77,6 +109,7 @@ impl Serialize for str {
}
}
#[cfg(any(feature = "std", feature = "collections"))]
impl Serialize for String {
#[inline]
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
@@ -260,6 +293,7 @@ array_impls!(32);
///////////////////////////////////////////////////////////////////////////////
#[cfg(any(feature = "std", feature = "collections"))]
impl<T> Serialize for BinaryHeap<T>
where T: Serialize + Ord
{
@@ -271,6 +305,7 @@ impl<T> Serialize for BinaryHeap<T>
}
}
#[cfg(any(feature = "std", feature = "collections"))]
impl<T> Serialize for BTreeSet<T>
where T: Serialize + Ord,
{
@@ -282,7 +317,7 @@ impl<T> Serialize for BTreeSet<T>
}
}
#[cfg(feature = "nightly")]
#[cfg(all(feature = "nightly", feature = "collections"))]
impl<T> Serialize for EnumSet<T>
where T: Serialize + CLike
{
@@ -294,8 +329,10 @@ impl<T> Serialize for EnumSet<T>
}
}
impl<T> Serialize for HashSet<T>
#[cfg(feature = "std")]
impl<T, H> Serialize for HashSet<T, H>
where T: Serialize + Eq + Hash,
H: BuildHasher,
{
#[inline]
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
@@ -305,6 +342,7 @@ impl<T> Serialize for HashSet<T>
}
}
#[cfg(any(feature = "std", feature = "collections"))]
impl<T> Serialize for LinkedList<T>
where T: Serialize,
{
@@ -330,6 +368,7 @@ impl<A> Serialize for ops::Range<A>
}
}
#[cfg(any(feature = "std", feature = "collections"))]
impl<T> Serialize for Vec<T> where T: Serialize {
#[inline]
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
@@ -339,6 +378,7 @@ impl<T> Serialize for Vec<T> where T: Serialize {
}
}
#[cfg(any(feature = "std", feature = "collections"))]
impl<T> Serialize for VecDeque<T> where T: Serialize {
#[inline]
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
@@ -528,6 +568,72 @@ tuple_impls! {
10 => 10,
11 => 11,
}
TupleVisitor13 (13, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) {
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,
}
TupleVisitor14 (14, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) {
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,
}
TupleVisitor15 (15, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) {
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,
}
TupleVisitor16 (16, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15) {
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,
}
}
///////////////////////////////////////////////////////////////////////////////
@@ -598,6 +704,7 @@ impl<K, V, I> MapVisitor for MapIteratorVisitor<I>
///////////////////////////////////////////////////////////////////////////////
#[cfg(any(feature = "std", feature = "collections"))]
impl<K, V> Serialize for BTreeMap<K, V>
where K: Serialize + Ord,
V: Serialize,
@@ -610,9 +717,11 @@ impl<K, V> Serialize for BTreeMap<K, V>
}
}
impl<K, V> Serialize for HashMap<K, V>
#[cfg(feature = "std")]
impl<K, V, H> Serialize for HashMap<K, V, H>
where K: Serialize + Eq + Hash,
V: Serialize,
H: BuildHasher,
{
#[inline]
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
@@ -642,6 +751,7 @@ impl<'a, T: ?Sized> Serialize for &'a mut T where T: Serialize {
}
}
#[cfg(any(feature = "std", feature = "alloc"))]
impl<T: ?Sized> Serialize for Box<T> where T: Serialize {
#[inline]
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
@@ -651,6 +761,7 @@ impl<T: ?Sized> Serialize for Box<T> where T: Serialize {
}
}
#[cfg(any(feature = "std", feature = "alloc"))]
impl<T> Serialize for Rc<T> where T: Serialize, {
#[inline]
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
@@ -660,6 +771,7 @@ impl<T> Serialize for Rc<T> where T: Serialize, {
}
}
#[cfg(any(feature = "std", feature = "alloc"))]
impl<T> Serialize for Arc<T> where T: Serialize, {
#[inline]
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
@@ -669,6 +781,7 @@ impl<T> Serialize for Arc<T> where T: Serialize, {
}
}
#[cfg(any(feature = "std", feature = "collections"))]
impl<'a, T: ?Sized> Serialize for Cow<'a, T> where T: Serialize + ToOwned, {
#[inline]
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
@@ -695,7 +808,7 @@ impl<T, E> Serialize for Result<T, E> where T: Serialize, E: Serialize {
///////////////////////////////////////////////////////////////////////////////
#[cfg(feature = "nightly")]
#[cfg(all(feature = "std", feature = "nightly"))]
impl Serialize for net::IpAddr {
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
where S: Serializer,
@@ -707,6 +820,7 @@ impl Serialize for net::IpAddr {
}
}
#[cfg(feature = "std")]
impl Serialize for net::Ipv4Addr {
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
where S: Serializer,
@@ -715,6 +829,7 @@ impl Serialize for net::Ipv4Addr {
}
}
#[cfg(feature = "std")]
impl Serialize for net::Ipv6Addr {
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
where S: Serializer,
@@ -725,6 +840,7 @@ impl Serialize for net::Ipv6Addr {
///////////////////////////////////////////////////////////////////////////////
#[cfg(feature = "std")]
impl Serialize for net::SocketAddr {
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
where S: Serializer,
@@ -736,6 +852,7 @@ impl Serialize for net::SocketAddr {
}
}
#[cfg(feature = "std")]
impl Serialize for net::SocketAddrV4 {
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
where S: Serializer,
@@ -744,6 +861,7 @@ impl Serialize for net::SocketAddrV4 {
}
}
#[cfg(feature = "std")]
impl Serialize for net::SocketAddrV6 {
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
where S: Serializer,
@@ -754,6 +872,7 @@ impl Serialize for net::SocketAddrV6 {
///////////////////////////////////////////////////////////////////////////////
#[cfg(feature = "std")]
impl Serialize for path::Path {
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
where S: Serializer,
@@ -765,6 +884,7 @@ impl Serialize for path::Path {
}
}
#[cfg(feature = "std")]
impl Serialize for path::PathBuf {
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
where S: Serializer,
+12 -3
View File
@@ -1,6 +1,12 @@
//! Generic serialization framework.
#[cfg(feature = "std")]
use std::error;
#[cfg(not(feature = "std"))]
use error;
#[cfg(all(feature = "collections", not(feature = "std")))]
use collections::String;
pub mod impls;
@@ -10,8 +16,13 @@ pub mod impls;
/// `Serializer` error.
pub trait Error: Sized + error::Error {
/// Raised when there is general error when deserializing a type.
#[cfg(any(feature = "std", feature = "collections"))]
fn custom<T: Into<String>>(msg: T) -> Self;
/// Raised when there is general error when deserializing a type.
#[cfg(all(not(feature = "std"), not(feature = "collections")))]
fn custom<T: Into<&'static str>>(msg: T) -> Self;
/// Raised when a `Serialize` was passed an incorrect value.
fn invalid_value(msg: &str) -> Self {
Error::custom(format!("invalid value: {}", msg))
@@ -115,9 +126,7 @@ pub trait Serializer {
/// single character.
#[inline]
fn serialize_char(&mut self, v: char) -> Result<(), Self::Error> {
// FIXME: this allocation is required in order to be compatible with stable rust, which
// doesn't support encoding a `char` into a stack buffer.
self.serialize_str(&v.to_string())
self.serialize_str(::utils::encode_utf8(v).as_str())
}
/// Serializes a `&str`.
+72
View File
@@ -0,0 +1,72 @@
//! Private utility functions
const TAG_CONT: u8 = 0b1000_0000;
const TAG_TWO_B: u8 = 0b1100_0000;
const TAG_THREE_B: u8 = 0b1110_0000;
const TAG_FOUR_B: u8 = 0b1111_0000;
const MAX_ONE_B: u32 = 0x80;
const MAX_TWO_B: u32 = 0x800;
const MAX_THREE_B: u32 = 0x10000;
#[inline]
pub fn encode_utf8(c: char) -> EncodeUtf8 {
let code = c as u32;
let mut buf = [0; 4];
let pos = if code < MAX_ONE_B {
buf[3] = code as u8;
3
} else if code < MAX_TWO_B {
buf[2] = (code >> 6 & 0x1F) as u8 | TAG_TWO_B;
buf[3] = (code & 0x3F) as u8 | TAG_CONT;
2
} else if code < MAX_THREE_B {
buf[1] = (code >> 12 & 0x0F) as u8 | TAG_THREE_B;
buf[2] = (code >> 6 & 0x3F) as u8 | TAG_CONT;
buf[3] = (code & 0x3F) as u8 | TAG_CONT;
1
} else {
buf[0] = (code >> 18 & 0x07) as u8 | TAG_FOUR_B;
buf[1] = (code >> 12 & 0x3F) as u8 | TAG_CONT;
buf[2] = (code >> 6 & 0x3F) as u8 | TAG_CONT;
buf[3] = (code & 0x3F) as u8 | TAG_CONT;
0
};
EncodeUtf8 { buf: buf, pos: pos }
}
pub struct EncodeUtf8 {
buf: [u8; 4],
pos: usize,
}
impl EncodeUtf8 {
// FIXME: use this from_utf8_unchecked, since we know it can never fail
pub fn as_str(&self) -> &str {
::core::str::from_utf8(&self.buf[self.pos..]).unwrap()
}
}
#[allow(non_upper_case_globals)]
const Pattern_White_Space_table: &'static [(char, char)] = &[
('\u{9}', '\u{d}'), ('\u{20}', '\u{20}'), ('\u{85}', '\u{85}'), ('\u{200e}', '\u{200f}'),
('\u{2028}', '\u{2029}')
];
fn bsearch_range_table(c: char, r: &'static [(char, char)]) -> bool {
use core::cmp::Ordering::{Equal, Less, Greater};
r.binary_search_by(|&(lo, hi)| {
if c < lo {
Greater
} else if hi < c {
Less
} else {
Equal
}
})
.is_ok()
}
#[allow(non_snake_case)]
pub fn Pattern_White_Space(c: char) -> bool {
bsearch_range_table(c, Pattern_White_Space_table)
}
+18 -9
View File
@@ -1,6 +1,6 @@
[package]
name = "serde_codegen"
version = "0.7.4"
version = "0.7.15"
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>"]
license = "MIT/Apache-2.0"
description = "Macros to auto-generate implementations for the serde framework"
@@ -8,21 +8,30 @@ repository = "https://github.com/serde-rs/serde"
documentation = "https://github.com/serde-rs/serde"
keywords = ["serde", "serialization"]
build = "build.rs"
include = ["Cargo.toml", "build.rs", "src/**/*.rs", "src/lib.rs.in"]
[features]
default = ["with-syntex"]
nightly = ["quasi_macros"]
nightly-testing = ["clippy"]
with-syntex = ["quasi/with-syntex", "quasi_codegen", "quasi_codegen/with-syntex", "syntex", "syntex_syntax"]
with-syntex = [
"quasi/with-syntex",
"quasi_codegen",
"quasi_codegen/with-syntex",
"serde_codegen_internals/with-syntex",
"syntex",
"syntex_syntax",
]
[build-dependencies]
quasi_codegen = { version = "^0.10.0", optional = true }
syntex = { version = "^0.31.0", optional = true }
quasi_codegen = { version = "^0.16.0", optional = true }
syntex = { version = "^0.39.0", optional = true }
[dependencies]
aster = { version = "^0.16.0", default-features = false }
aster = { version = "^0.22.0", default-features = false }
clippy = { version = "^0.*", optional = true }
quasi = { version = "^0.10.0", default-features = false }
quasi_macros = { version = "^0.10.0", optional = true }
syntex = { version = "^0.32.0", optional = true }
syntex_syntax = { version = "^0.32.0", optional = true }
quasi = { version = "^0.16.0", default-features = false }
quasi_macros = { version = "^0.16.0", optional = true }
serde_codegen_internals = { version = "^0.4.0", default-features = false }
syntex = { version = "^0.39.0", optional = true }
syntex_syntax = { version = "^0.39.0", optional = true }
+1 -4
View File
@@ -1,6 +1,5 @@
#[cfg(feature = "with-syntex")]
mod inner {
extern crate syntex;
extern crate quasi_codegen;
use std::env;
@@ -8,13 +7,11 @@ mod inner {
pub fn main() {
let out_dir = env::var_os("OUT_DIR").unwrap();
let mut registry = syntex::Registry::new();
quasi_codegen::register(&mut registry);
let src = Path::new("src/lib.rs.in");
let dst = Path::new(&out_dir).join("lib.rs");
registry.expand("", &src, &dst).unwrap();
quasi_codegen::expand(&src, &dst).unwrap();
}
}
-647
View File
@@ -1,647 +0,0 @@
use std::rc::Rc;
use syntax::ast::{self, TokenTree};
use syntax::attr;
use syntax::codemap::Span;
use syntax::ext::base::ExtCtxt;
use syntax::fold::Folder;
use syntax::parse::parser::PathStyle;
use syntax::parse::token::{self, InternedString};
use syntax::parse;
use syntax::print::pprust::{lit_to_string, meta_item_to_string};
use syntax::ptr::P;
use aster::AstBuilder;
use error::Error;
#[derive(Debug)]
pub struct Name {
ident: ast::Ident,
serialize_name: Option<InternedString>,
deserialize_name: Option<InternedString>,
}
impl Name {
fn new(ident: ast::Ident) -> Self {
Name {
ident: ident,
serialize_name: None,
deserialize_name: None,
}
}
/// Return the container name for the container when serializing.
pub fn serialize_name(&self) -> InternedString {
match self.serialize_name {
Some(ref name) => name.clone(),
None => self.ident.name.as_str(),
}
}
/// Return the container name expression for the container when deserializing.
pub fn serialize_name_expr(&self) -> P<ast::Expr> {
AstBuilder::new().expr().str(self.serialize_name())
}
/// Return the container name for the container when deserializing.
pub fn deserialize_name(&self) -> InternedString {
match self.deserialize_name {
Some(ref name) => name.clone(),
None => self.ident.name.as_str(),
}
}
/// Return the container name expression for the container when deserializing.
pub fn deserialize_name_expr(&self) -> P<ast::Expr> {
AstBuilder::new().expr().str(self.deserialize_name())
}
}
/// Represents container (e.g. struct) attribute information
#[derive(Debug)]
pub struct ContainerAttrs {
name: Name,
deny_unknown_fields: bool,
}
impl ContainerAttrs {
/// Extract out the `#[serde(...)]` attributes from an item.
pub fn from_item(cx: &ExtCtxt, item: &ast::Item) -> Result<Self, Error> {
let mut container_attrs = ContainerAttrs {
name: Name::new(item.ident),
deny_unknown_fields: false,
};
for meta_items in item.attrs().iter().filter_map(get_serde_meta_items) {
for meta_item in meta_items {
match meta_item.node {
// Parse `#[serde(rename="foo")]`
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"rename" => {
let s = try!(get_str_from_lit(cx, name, lit));
container_attrs.name.serialize_name = Some(s.clone());
container_attrs.name.deserialize_name = Some(s);
}
// Parse `#[serde(rename(serialize="foo", deserialize="bar"))]`
ast::MetaItemKind::List(ref name, ref meta_items) if name == &"rename" => {
let (ser_name, de_name) = try!(get_renames(cx, meta_items));
container_attrs.name.serialize_name = ser_name;
container_attrs.name.deserialize_name = de_name;
}
// Parse `#[serde(deny_unknown_fields)]`
ast::MetaItemKind::Word(ref name) if name == &"deny_unknown_fields" => {
container_attrs.deny_unknown_fields = true;
}
_ => {
cx.span_err(
meta_item.span,
&format!("unknown serde container attribute `{}`",
meta_item_to_string(meta_item)));
return Err(Error);
}
}
}
}
Ok(container_attrs)
}
pub fn name(&self) -> &Name {
&self.name
}
pub fn deny_unknown_fields(&self) -> bool {
self.deny_unknown_fields
}
}
/// Represents variant attribute information
#[derive(Debug)]
pub struct VariantAttrs {
name: Name,
}
impl VariantAttrs {
pub fn from_variant(cx: &ExtCtxt, variant: &ast::Variant) -> Result<Self, Error> {
let mut variant_attrs = VariantAttrs {
name: Name::new(variant.node.name),
};
for meta_items in variant.node.attrs.iter().filter_map(get_serde_meta_items) {
for meta_item in meta_items {
match meta_item.node {
// Parse `#[serde(rename="foo")]`
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"rename" => {
let s = try!(get_str_from_lit(cx, name, lit));
variant_attrs.name.serialize_name = Some(s.clone());
variant_attrs.name.deserialize_name = Some(s);
}
// Parse `#[serde(rename(serialize="foo", deserialize="bar"))]`
ast::MetaItemKind::List(ref name, ref meta_items) if name == &"rename" => {
let (ser_name, de_name) = try!(get_renames(cx, meta_items));
variant_attrs.name.serialize_name = ser_name;
variant_attrs.name.deserialize_name = de_name;
}
_ => {
cx.span_err(
meta_item.span,
&format!("unknown serde variant attribute `{}`",
meta_item_to_string(meta_item)));
return Err(Error);
}
}
}
}
Ok(variant_attrs)
}
pub fn name(&self) -> &Name {
&self.name
}
}
/// Represents field attribute information
#[derive(Debug)]
pub struct FieldAttrs {
name: Name,
skip_serializing_field: bool,
skip_deserializing_field: bool,
skip_serializing_field_if: Option<P<ast::Expr>>,
default_expr_if_missing: Option<P<ast::Expr>>,
serialize_with: Option<P<ast::Expr>>,
deserialize_with: P<ast::Expr>,
}
impl FieldAttrs {
/// Extract out the `#[serde(...)]` attributes from a struct field.
pub fn from_field(cx: &ExtCtxt,
container_ty: &P<ast::Ty>,
generics: &ast::Generics,
field: &ast::StructField,
is_enum: bool) -> Result<Self, Error> {
let builder = AstBuilder::new();
let field_ident = match field.ident {
Some(ident) => ident,
None => { cx.span_bug(field.span, "struct field has no name?") }
};
let identity = quote_expr!(cx, |x| x);
let mut field_attrs = FieldAttrs {
name: Name::new(field_ident),
skip_serializing_field: false,
skip_deserializing_field: false,
skip_serializing_field_if: None,
default_expr_if_missing: None,
serialize_with: None,
deserialize_with: identity,
};
for meta_items in field.attrs.iter().filter_map(get_serde_meta_items) {
for meta_item in meta_items {
match meta_item.node {
// Parse `#[serde(rename="foo")]`
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"rename" => {
let s = try!(get_str_from_lit(cx, name, lit));
field_attrs.name.serialize_name = Some(s.clone());
field_attrs.name.deserialize_name = Some(s);
}
// Parse `#[serde(rename(serialize="foo", deserialize="bar"))]`
ast::MetaItemKind::List(ref name, ref meta_items) if name == &"rename" => {
let (ser_name, de_name) = try!(get_renames(cx, meta_items));
field_attrs.name.serialize_name = ser_name;
field_attrs.name.deserialize_name = de_name;
}
// Parse `#[serde(default)]`
ast::MetaItemKind::Word(ref name) if name == &"default" => {
let default_expr = builder.expr().default();
field_attrs.default_expr_if_missing = Some(default_expr);
}
// Parse `#[serde(default="...")]`
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"default" => {
let wrapped_expr = wrap_default(
try!(parse_lit_into_path(cx, name, lit)),
);
field_attrs.default_expr_if_missing = Some(wrapped_expr);
}
// Parse `#[serde(skip_serializing)]`
ast::MetaItemKind::Word(ref name) if name == &"skip_serializing" => {
field_attrs.skip_serializing_field = true;
}
// Parse `#[serde(skip_deserializing)]`
ast::MetaItemKind::Word(ref name) if name == &"skip_deserializing" => {
field_attrs.skip_deserializing_field = true;
// Initialize field to Default::default() unless a different
// default is specified by `#[serde(default="...")]`
if field_attrs.default_expr_if_missing.is_none() {
let default_expr = builder.expr().default();
field_attrs.default_expr_if_missing = Some(default_expr);
}
}
// Parse `#[serde(skip_serializing_if="...")]`
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"skip_serializing_if" => {
let expr = wrap_skip_serializing(
field_ident,
try!(parse_lit_into_path(cx, name, lit)),
is_enum,
);
field_attrs.skip_serializing_field_if = Some(expr);
}
// Parse `#[serde(serialize_with="...")]`
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"serialize_with" => {
let expr = wrap_serialize_with(
cx,
container_ty,
generics,
field_ident,
try!(parse_lit_into_path(cx, name, lit)),
is_enum,
);
field_attrs.serialize_with = Some(expr);
}
// Parse `#[serde(deserialize_with="...")]`
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"deserialize_with" => {
let expr = wrap_deserialize_with(
cx,
&field.ty,
generics,
try!(parse_lit_into_path(cx, name, lit)),
);
field_attrs.deserialize_with = expr;
}
_ => {
cx.span_err(
meta_item.span,
&format!("unknown serde field attribute `{}`",
meta_item_to_string(meta_item)));
return Err(Error);
}
}
}
}
Ok(field_attrs)
}
pub fn name(&self) -> &Name {
&self.name
}
/// Predicate for using a field's default value
pub fn expr_is_missing(&self) -> P<ast::Expr> {
match self.default_expr_if_missing {
Some(ref expr) => expr.clone(),
None => {
let name = self.name.deserialize_name_expr();
AstBuilder::new().expr()
.try()
.method_call("missing_field").id("visitor")
.with_arg(name)
.build()
}
}
}
/// Predicate for ignoring a field when serializing a value
pub fn skip_serializing_field(&self) -> bool {
self.skip_serializing_field
}
pub fn skip_deserializing_field(&self) -> bool {
self.skip_deserializing_field
}
pub fn skip_serializing_field_if(&self) -> Option<&P<ast::Expr>> {
self.skip_serializing_field_if.as_ref()
}
pub fn serialize_with(&self) -> Option<&P<ast::Expr>> {
self.serialize_with.as_ref()
}
pub fn deserialize_with(&self) -> &P<ast::Expr> {
&self.deserialize_with
}
}
/// Extract out the `#[serde(...)]` attributes from a struct field.
pub fn get_struct_field_attrs(cx: &ExtCtxt,
container_ty: &P<ast::Ty>,
generics: &ast::Generics,
fields: &[ast::StructField],
is_enum: bool) -> Result<Vec<FieldAttrs>, Error> {
fields.iter()
.map(|field| FieldAttrs::from_field(cx, container_ty, generics, field, is_enum))
.collect()
}
fn get_renames(cx: &ExtCtxt,
items: &[P<ast::MetaItem>],
)-> Result<(Option<InternedString>, Option<InternedString>), Error> {
let mut ser_name = None;
let mut de_name = None;
for item in items {
match item.node {
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"serialize" => {
let s = try!(get_str_from_lit(cx, name, lit));
ser_name = Some(s);
}
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"deserialize" => {
let s = try!(get_str_from_lit(cx, name, lit));
de_name = Some(s);
}
_ => {
cx.span_err(
item.span,
&format!("unknown rename attribute `{}`",
meta_item_to_string(item)));
return Err(Error);
}
}
}
Ok((ser_name, de_name))
}
pub fn get_serde_meta_items(attr: &ast::Attribute) -> Option<&[P<ast::MetaItem>]> {
match attr.node.value.node {
ast::MetaItemKind::List(ref name, ref items) if name == &"serde" => {
attr::mark_used(&attr);
Some(items)
}
_ => None
}
}
/// This syntax folder rewrites tokens to say their spans are coming from a macro context.
struct Respanner<'a, 'b: 'a> {
cx: &'a ExtCtxt<'b>,
}
impl<'a, 'b> Folder for Respanner<'a, 'b> {
fn fold_tt(&mut self, tt: &TokenTree) -> TokenTree {
match *tt {
TokenTree::Token(span, ref tok) => {
TokenTree::Token(
self.new_span(span),
self.fold_token(tok.clone())
)
}
TokenTree::Delimited(span, ref delimed) => {
TokenTree::Delimited(
self.new_span(span),
Rc::new(ast::Delimited {
delim: delimed.delim,
open_span: delimed.open_span,
tts: self.fold_tts(&delimed.tts),
close_span: delimed.close_span,
})
)
}
TokenTree::Sequence(span, ref seq) => {
TokenTree::Sequence(
self.new_span(span),
Rc::new(ast::SequenceRepetition {
tts: self.fold_tts(&seq.tts),
separator: seq.separator.clone().map(|tok| self.fold_token(tok)),
..**seq
})
)
}
}
}
fn new_span(&mut self, span: Span) -> Span {
Span {
lo: span.lo,
hi: span.hi,
expn_id: self.cx.backtrace(),
}
}
}
fn get_str_from_lit(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result<InternedString, Error> {
match lit.node {
ast::LitKind::Str(ref s, _) => Ok(s.clone()),
_ => {
cx.span_err(
lit.span,
&format!("serde annotation `{}` must be a string, not `{}`",
name,
lit_to_string(lit)));
return Err(Error);
}
}
}
fn parse_lit_into_path(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result<ast::Path, Error> {
let source = try!(get_str_from_lit(cx, name, lit));
// If we just parse the string into an expression, any syntax errors in the source will only
// have spans that point inside the string, and not back to the attribute. So to have better
// error reporting, we'll first parse the string into a token tree. Then we'll update those
// spans to say they're coming from a macro context that originally came from the attribute,
// and then finally parse them into an expression.
let tts = panictry!(parse::parse_tts_from_source_str(
format!("<serde {} expansion>", name),
(*source).to_owned(),
cx.cfg(),
cx.parse_sess()));
// Respan the spans to say they are all coming from this macro.
let tts = Respanner { cx: cx }.fold_tts(&tts);
let mut parser = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(), tts);
let path = match parser.parse_path(PathStyle::Type) {
Ok(path) => path,
Err(mut e) => {
e.emit();
return Err(Error);
}
};
// Make sure to error out if there are trailing characters in the stream.
match parser.expect(&token::Eof) {
Ok(()) => { }
Err(mut e) => {
e.emit();
return Err(Error);
}
}
Ok(path)
}
/// This function wraps the expression in `#[serde(default="...")]` in a function to prevent it
/// from accessing the internal `Deserialize` state.
fn wrap_default(path: ast::Path) -> P<ast::Expr> {
AstBuilder::new().expr().call()
.build_path(path)
.build()
}
/// This function wraps the expression in `#[serde(skip_serializing_if="...")]` in a trait to
/// prevent it from accessing the internal `Serialize` state.
fn wrap_skip_serializing(field_ident: ast::Ident,
path: ast::Path,
is_enum: bool) -> P<ast::Expr> {
let builder = AstBuilder::new();
let expr = builder.expr()
.field(field_ident)
.field("value")
.self_();
let expr = if is_enum {
expr
} else {
builder.expr().ref_().build(expr)
};
builder.expr().call()
.build_path(path)
.arg().build(expr)
.build()
}
/// This function wraps the expression in `#[serde(serialize_with="...")]` in a trait to
/// prevent it from accessing the internal `Serialize` state.
fn wrap_serialize_with(cx: &ExtCtxt,
container_ty: &P<ast::Ty>,
generics: &ast::Generics,
field_ident: ast::Ident,
path: ast::Path,
is_enum: bool) -> P<ast::Expr> {
let builder = AstBuilder::new();
let expr = builder.expr()
.field(field_ident)
.self_();
let expr = if is_enum {
expr
} else {
builder.expr().ref_().build(expr)
};
let expr = builder.expr().call()
.build_path(path)
.arg().build(expr)
.arg()
.id("serializer")
.build();
let where_clause = &generics.where_clause;
quote_expr!(cx, {
trait __SerdeSerializeWith {
fn __serde_serialize_with<S>(&self, serializer: &mut S) -> Result<(), S::Error>
where S: _serde::ser::Serializer;
}
impl<'a, T> __SerdeSerializeWith for &'a T
where T: 'a + __SerdeSerializeWith,
{
fn __serde_serialize_with<S>(&self, serializer: &mut S) -> Result<(), S::Error>
where S: _serde::ser::Serializer
{
(**self).__serde_serialize_with(serializer)
}
}
impl $generics __SerdeSerializeWith for $container_ty $where_clause {
fn __serde_serialize_with<S>(&self, serializer: &mut S) -> Result<(), S::Error>
where S: _serde::ser::Serializer
{
$expr
}
}
struct __SerdeSerializeWithStruct<'a, T: 'a> {
value: &'a T,
}
impl<'a, T> _serde::ser::Serialize for __SerdeSerializeWithStruct<'a, T>
where T: 'a + __SerdeSerializeWith
{
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
where S: _serde::ser::Serializer
{
self.value.__serde_serialize_with(serializer)
}
}
__SerdeSerializeWithStruct {
value: &self.value,
}
})
}
/// This function wraps the expression in `#[serde(deserialize_with="...")]` in a trait to prevent
/// it from accessing the internal `Deserialize` state.
fn wrap_deserialize_with(cx: &ExtCtxt,
field_ty: &P<ast::Ty>,
generics: &ast::Generics,
path: ast::Path) -> P<ast::Expr> {
// Quasi-quoting doesn't do a great job of expanding generics into paths, so manually build it.
let ty_path = AstBuilder::new().path()
.segment("__SerdeDeserializeWithStruct")
.with_generics(generics.clone())
.build()
.build();
let where_clause = &generics.where_clause;
quote_expr!(cx, ({
struct __SerdeDeserializeWithStruct $generics $where_clause {
value: $field_ty,
}
impl $generics _serde::de::Deserialize for $ty_path $where_clause {
fn deserialize<D>(deserializer: &mut D) -> ::std::result::Result<Self, D::Error>
where D: _serde::de::Deserializer
{
let value = try!($path(deserializer));
Ok(__SerdeDeserializeWithStruct { value: value })
}
}
|visit: $ty_path| visit.value
}))
}
+72 -69
View File
@@ -1,12 +1,12 @@
use std::collections::HashSet;
use aster::AstBuilder;
use syntax::ast;
use syntax::ext::base::ExtCtxt;
use syntax::ptr::P;
use syntax::visit;
use internals::ast::Item;
use internals::attr;
// Remove the default from every type parameter because in the generated impls
// they look like associated types: "error: associated type bindings are not
// allowed here".
@@ -21,22 +21,47 @@ pub fn without_defaults(generics: &ast::Generics) -> ast::Generics {
}
}
pub fn with_bound(
cx: &ExtCtxt,
pub fn with_where_predicates(
builder: &AstBuilder,
item: &ast::Item,
generics: &ast::Generics,
filter: &Fn(&ast::StructField) -> bool,
bound: &ast::Path,
predicates: &[ast::WherePredicate],
) -> ast::Generics {
builder.from_generics(generics.clone())
.with_predicates(predicates.to_vec())
.build()
}
pub fn with_where_predicates_from_fields<F>(
builder: &AstBuilder,
item: &Item,
generics: &ast::Generics,
from_field: F,
) -> ast::Generics
where F: Fn(&attr::Field) -> Option<&[ast::WherePredicate]>,
{
builder.from_generics(generics.clone())
.with_predicates(
all_variants(cx, item).iter()
.flat_map(|variant_data| all_struct_fields(variant_data))
.filter(|field| filter(field))
item.body.all_fields()
.flat_map(|field| from_field(&field.attrs))
.flat_map(|predicates| predicates.to_vec()))
.build()
}
pub fn with_bound<F>(
builder: &AstBuilder,
item: &Item,
generics: &ast::Generics,
filter: F,
bound: &ast::Path,
) -> ast::Generics
where F: Fn(&attr::Field) -> bool,
{
builder.from_generics(generics.clone())
.with_predicates(
item.body.all_fields()
.filter(|&field| filter(&field.attrs))
.map(|field| &field.ty)
// TODO this filter can be removed later, see comment on function
.filter(|ty| contains_generic(ty, generics))
.filter(|ty| !contains_recursion(ty, item.ident))
.map(|ty| strip_reference(ty))
.map(|ty| builder.where_predicate()
// the type that is being bounded e.g. T
@@ -47,70 +72,48 @@ pub fn with_bound(
.build()
}
fn all_variants<'a>(cx: &ExtCtxt, item: &'a ast::Item) -> Vec<&'a ast::VariantData> {
match item.node {
ast::ItemKind::Struct(ref variant_data, _) => {
vec![variant_data]
}
ast::ItemKind::Enum(ref enum_def, _) => {
enum_def.variants.iter()
.map(|variant| &variant.node.data)
.collect()
}
_ => {
cx.span_bug(item.span, "expected Item to be Struct or Enum");
}
}
}
fn all_struct_fields(variant_data: &ast::VariantData) -> &[ast::StructField] {
match *variant_data {
ast::VariantData::Struct(ref fields, _) |
ast::VariantData::Tuple(ref fields, _) => {
fields
}
ast::VariantData::Unit(_) => {
&[]
}
}
}
// Rust <1.7 enforces that `where` clauses involve generic type parameters. The
// corresponding compiler error is E0193. It is no longer enforced in Rust >=1.7
// so this filtering can be removed in the future when we stop supporting <1.7.
// We do not attempt to generate any bounds based on field types that are
// directly recursive, as in:
//
// E0193 means we must not generate a `where` clause like `i32: Serialize`
// because even though i32 implements Serialize, i32 is not a generic type
// parameter. Clauses like `T: Serialize` and `Option<T>: Serialize` are okay.
// This function decides whether a given type references any of the generic type
// parameters in the input `Generics`.
fn contains_generic(ty: &ast::Ty, generics: &ast::Generics) -> bool {
struct FindGeneric<'a> {
generic_names: &'a HashSet<ast::Name>,
found_generic: bool,
// struct Test<D> {
// next: Box<Test<D>>,
// }
//
// This does not catch field types that are mutually recursive with some other
// type. For those, we require bounds to be specified by a `bound` attribute if
// the inferred ones are not correct.
//
// struct Test<D> {
// #[serde(bound="D: Serialize + Deserialize")]
// next: Box<Other<D>>,
// }
// struct Other<D> {
// #[serde(bound="D: Serialize + Deserialize")]
// next: Box<Test<D>>,
// }
fn contains_recursion(ty: &ast::Ty, ident: ast::Ident) -> bool {
struct FindRecursion {
ident: ast::Ident,
found_recursion: bool,
}
impl<'a, 'v> visit::Visitor<'v> for FindGeneric<'a> {
fn visit_path(&mut self, path: &'v ast::Path, _id: ast::NodeId) {
impl visit::Visitor for FindRecursion {
fn visit_path(&mut self, path: &ast::Path, _id: ast::NodeId) {
if !path.global
&& path.segments.len() == 1
&& self.generic_names.contains(&path.segments[0].identifier.name) {
self.found_generic = true;
&& path.segments[0].identifier == self.ident {
self.found_recursion = true;
} else {
visit::walk_path(self, path);
}
}
}
let generic_names: HashSet<_> = generics.ty_params.iter()
.map(|ty_param| ty_param.ident.name)
.collect();
let mut visitor = FindGeneric {
generic_names: &generic_names,
found_generic: false,
let mut visitor = FindRecursion {
ident: ident,
found_recursion: false,
};
visit::walk_ty(&mut visitor, ty);
visitor.found_generic
visitor.found_recursion
}
// This is required to handle types that use both a reference and a value of
@@ -139,9 +142,9 @@ fn contains_generic(ty: &ast::Ty, generics: &ast::Generics) -> bool {
//
// impl<'a, T> Serialize for Test<'a, T>
// where T: Serialize { ... }
fn strip_reference(ty: &P<ast::Ty>) -> &P<ast::Ty> {
match ty.node {
ast::TyKind::Rptr(_, ref mut_ty) => &mut_ty.ty,
_ => ty
fn strip_reference(mut ty: &P<ast::Ty>) -> &P<ast::Ty> {
while let ast::TyKind::Rptr(_, ref mut_ty) = ty.node {
ty = &mut_ty.ty;
}
ty
}
+552 -555
View File
File diff suppressed because it is too large Load Diff
-2
View File
@@ -1,2 +0,0 @@
/// Error returned if failed to parse attribute.
pub struct Error;
+14
View File
@@ -7,6 +7,7 @@
extern crate aster;
extern crate quasi;
extern crate serde_codegen_internals as internals;
#[cfg(feature = "with-syntex")]
extern crate syntex;
@@ -22,6 +23,9 @@ extern crate syntax;
#[cfg(not(feature = "with-syntex"))]
extern crate rustc_plugin;
#[cfg(feature = "with-syntex")]
use std::path::Path;
#[cfg(not(feature = "with-syntex"))]
use syntax::feature_gate::AttributeType;
@@ -31,6 +35,16 @@ include!(concat!(env!("OUT_DIR"), "/lib.rs"));
#[cfg(not(feature = "with-syntex"))]
include!("lib.rs.in");
#[cfg(feature = "with-syntex")]
pub fn expand<S, D>(src: S, dst: D) -> Result<(), syntex::Error>
where S: AsRef<Path>,
D: AsRef<Path>,
{
let mut registry = syntex::Registry::new();
register(&mut registry);
registry.expand("", src.as_ref(), dst.as_ref())
}
#[cfg(feature = "with-syntex")]
pub fn register(reg: &mut syntex::Registry) {
use syntax::{ast, fold};
+1 -2
View File
@@ -1,5 +1,4 @@
mod attr;
mod bound;
mod de;
mod error;
mod ser;
mod span;
+314 -267
View File
@@ -1,18 +1,14 @@
use aster;
use syntax::ast::{
Ident,
MetaItem,
Item,
};
use syntax::ast;
use syntax::ast::{self, Ident, MetaItem};
use syntax::codemap::Span;
use syntax::ext::base::{Annotatable, ExtCtxt};
use syntax::ptr::P;
use attr;
use bound;
use error::Error;
use span;
use internals::ast::{Body, Field, Item, Style, Variant};
use internals::{attr, Error};
pub fn expand_derive_serialize(
cx: &mut ExtCtxt,
@@ -31,52 +27,43 @@ pub fn expand_derive_serialize(
}
};
let builder = aster::AstBuilder::new().span(span);
let impl_item = match serialize_item(cx, &builder, &item) {
let item = match Item::from_ast(cx, item) {
Ok(item) => item,
Err(Error) => {
// An error occured, but it should have been reported already.
Err(Error::UnexpectedItemKind) => {
cx.span_err(item.span,
"`#[derive(Serialize)]` may only be applied to structs and enums");
return;
}
};
push(Annotatable::Item(impl_item))
let builder = aster::AstBuilder::new().span(span);
let impl_item = serialize_item(cx, &builder, &item);
push(span::record_expansion(cx, impl_item, "Serialize"))
}
fn serialize_item(
cx: &ExtCtxt,
builder: &aster::AstBuilder,
item: &Item,
) -> Result<P<ast::Item>, Error> {
let generics = match item.node {
ast::ItemKind::Struct(_, ref generics) => generics,
ast::ItemKind::Enum(_, ref generics) => generics,
_ => {
cx.span_err(
item.span,
"`#[derive(Serialize)]` may only be applied to structs and enums");
return Err(Error);
}
};
let impl_generics = build_impl_generics(cx, builder, item, generics);
) -> P<ast::Item> {
let impl_generics = build_impl_generics(builder, &item);
let ty = builder.ty().path()
.segment(item.ident).with_generics(impl_generics.clone()).build()
.build();
let body = try!(serialize_body(cx,
&builder,
&item,
&impl_generics,
ty.clone()));
let body = serialize_body(cx,
builder,
&item,
&impl_generics,
ty.clone());
let where_clause = &impl_generics.where_clause;
let dummy_const = builder.id(format!("_IMPL_SERIALIZE_FOR_{}", item.ident));
Ok(quote_item!(cx,
quote_item!(cx,
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
const $dummy_const: () = {
extern crate serde as _serde;
@@ -89,42 +76,41 @@ fn serialize_item(
}
}
};
).unwrap())
).unwrap()
}
// All the generics in the input, plus a bound `T: Serialize` for each generic
// field type that will be serialized by us.
fn build_impl_generics(
cx: &ExtCtxt,
builder: &aster::AstBuilder,
item: &Item,
generics: &ast::Generics,
) -> ast::Generics {
let generics = bound::without_defaults(generics);
let generics = bound::with_bound(cx, builder, item, &generics,
&serialized_by_us,
&builder.path().ids(&["_serde", "ser", "Serialize"]).build());
generics
let generics = bound::without_defaults(item.generics);
let generics = bound::with_where_predicates_from_fields(
builder, item, &generics,
|attrs| attrs.ser_bound());
match item.attrs.ser_bound() {
Some(predicates) => {
bound::with_where_predicates(builder, &generics, predicates)
}
None => {
bound::with_bound(builder, item, &generics,
needs_serialize_bound,
&builder.path().ids(&["_serde", "ser", "Serialize"]).build())
}
}
}
// Fields with a `skip_serializing` or `serialize_with` attribute are not
// serialized by us. All other fields may need a `T: Serialize` bound where T is
// the type of the field.
fn serialized_by_us(field: &ast::StructField) -> bool {
for meta_items in field.attrs.iter().filter_map(attr::get_serde_meta_items) {
for meta_item in meta_items {
match meta_item.node {
ast::MetaItemKind::Word(ref name) if name == &"skip_serializing" => {
return false
}
ast::MetaItemKind::NameValue(ref name, _) if name == &"serialize_with" => {
return false
}
_ => {}
}
}
}
true
// serialized by us so we do not generate a bound. Fields with a `bound`
// attribute specify their own bound so we do not generate one. All other fields
// may need a `T: Serialize` bound where T is the type of the field.
fn needs_serialize_bound(attrs: &attr::Field) -> bool {
!attrs.skip_serializing()
&& attrs.serialize_with().is_none()
&& attrs.ser_bound().is_none()
}
fn serialize_body(
@@ -133,112 +119,93 @@ fn serialize_body(
item: &Item,
impl_generics: &ast::Generics,
ty: P<ast::Ty>,
) -> Result<P<ast::Expr>, Error> {
let container_attrs = try!(attr::ContainerAttrs::from_item(cx, item));
match item.node {
ast::ItemKind::Struct(ref variant_data, _) => {
serialize_item_struct(
cx,
builder,
impl_generics,
ty,
item.span,
variant_data,
&container_attrs,
)
}
ast::ItemKind::Enum(ref enum_def, _) => {
) -> P<ast::Expr> {
match item.body {
Body::Enum(ref variants) => {
serialize_item_enum(
cx,
builder,
item.ident,
impl_generics,
ty,
enum_def,
&container_attrs,
)
variants,
&item.attrs)
}
_ => {
cx.span_bug(item.span,
"expected ItemStruct or ItemEnum in #[derive(Serialize)]");
}
}
}
fn serialize_item_struct(
cx: &ExtCtxt,
builder: &aster::AstBuilder,
impl_generics: &ast::Generics,
ty: P<ast::Ty>,
span: Span,
variant_data: &ast::VariantData,
container_attrs: &attr::ContainerAttrs,
) -> Result<P<ast::Expr>, Error> {
match *variant_data {
ast::VariantData::Unit(_) => {
serialize_unit_struct(
cx,
container_attrs,
)
}
ast::VariantData::Tuple(ref fields, _) if fields.len() == 1 => {
serialize_newtype_struct(
cx,
container_attrs,
)
}
ast::VariantData::Tuple(ref fields, _) => {
if fields.iter().any(|field| field.ident.is_some()) {
cx.span_bug(span, "tuple struct has named fields")
}
serialize_tuple_struct(
cx,
&builder,
impl_generics,
ty,
fields.len(),
container_attrs,
)
}
ast::VariantData::Struct(ref fields, _) => {
Body::Struct(Style::Struct, ref fields) => {
if fields.iter().any(|field| field.ident.is_none()) {
cx.span_bug(span, "struct has unnamed fields")
cx.span_bug(item.span, "struct has unnamed fields")
}
serialize_struct(
cx,
&builder,
builder,
impl_generics,
ty,
fields,
container_attrs,
)
&item.attrs)
}
Body::Struct(Style::Tuple, ref fields) => {
if fields.iter().any(|field| field.ident.is_some()) {
cx.span_bug(item.span, "tuple struct has named fields")
}
serialize_tuple_struct(
cx,
builder,
impl_generics,
ty,
fields,
&item.attrs)
}
Body::Struct(Style::Newtype, ref fields) => {
serialize_newtype_struct(
cx,
builder,
impl_generics,
ty,
&fields[0],
&item.attrs)
}
Body::Struct(Style::Unit, _) => {
serialize_unit_struct(
cx,
builder,
&item.attrs)
}
}
}
fn serialize_unit_struct(
cx: &ExtCtxt,
container_attrs: &attr::ContainerAttrs,
) -> Result<P<ast::Expr>, Error> {
let type_name = container_attrs.name().serialize_name_expr();
builder: &aster::AstBuilder,
item_attrs: &attr::Item,
) -> P<ast::Expr> {
let type_name = name_expr(builder, item_attrs.name());
Ok(quote_expr!(cx,
quote_expr!(cx,
_serializer.serialize_unit_struct($type_name)
))
)
}
fn serialize_newtype_struct(
cx: &ExtCtxt,
container_attrs: &attr::ContainerAttrs,
) -> Result<P<ast::Expr>, Error> {
let type_name = container_attrs.name().serialize_name_expr();
builder: &aster::AstBuilder,
impl_generics: &ast::Generics,
item_ty: P<ast::Ty>,
field: &Field,
item_attrs: &attr::Item,
) -> P<ast::Expr> {
let type_name = name_expr(builder, item_attrs.name());
Ok(quote_expr!(cx,
_serializer.serialize_newtype_struct($type_name, &self.0)
))
let mut field_expr = quote_expr!(cx, &self.0);
if let Some(path) = field.attrs.serialize_with() {
field_expr = wrap_serialize_with(cx, builder,
&item_ty, impl_generics, &field.ty, path, field_expr);
}
quote_expr!(cx,
_serializer.serialize_newtype_struct($type_name, $field_expr)
)
}
fn serialize_tuple_struct(
@@ -246,9 +213,9 @@ fn serialize_tuple_struct(
builder: &aster::AstBuilder,
impl_generics: &ast::Generics,
ty: P<ast::Ty>,
fields: usize,
container_attrs: &attr::ContainerAttrs,
) -> Result<P<ast::Expr>, Error> {
fields: &[Field],
item_attrs: &attr::Item,
) -> P<ast::Expr> {
let (visitor_struct, visitor_impl) = serialize_tuple_struct_visitor(
cx,
builder,
@@ -260,11 +227,12 @@ fn serialize_tuple_struct(
builder.id("serialize_tuple_struct_elt"),
fields,
impl_generics,
false,
);
let type_name = container_attrs.name().serialize_name_expr();
let type_name = name_expr(builder, item_attrs.name());
Ok(quote_expr!(cx, {
quote_expr!(cx, {
$visitor_struct
$visitor_impl
_serializer.serialize_tuple_struct($type_name, Visitor {
@@ -272,7 +240,7 @@ fn serialize_tuple_struct(
state: 0,
_structure_ty: ::std::marker::PhantomData::<&$ty>,
})
}))
})
}
fn serialize_struct(
@@ -280,10 +248,10 @@ fn serialize_struct(
builder: &aster::AstBuilder,
impl_generics: &ast::Generics,
ty: P<ast::Ty>,
fields: &[ast::StructField],
container_attrs: &attr::ContainerAttrs,
) -> Result<P<ast::Expr>, Error> {
let (visitor_struct, visitor_impl) = try!(serialize_struct_visitor(
fields: &[Field],
item_attrs: &attr::Item,
) -> P<ast::Expr> {
let (visitor_struct, visitor_impl) = serialize_struct_visitor(
cx,
builder,
ty.clone(),
@@ -295,11 +263,11 @@ fn serialize_struct(
fields,
impl_generics,
false,
));
);
let type_name = container_attrs.name().serialize_name_expr();
let type_name = name_expr(builder, item_attrs.name());
Ok(quote_expr!(cx, {
quote_expr!(cx, {
$visitor_struct
$visitor_impl
_serializer.serialize_struct($type_name, Visitor {
@@ -307,7 +275,7 @@ fn serialize_struct(
state: 0,
_structure_ty: ::std::marker::PhantomData::<&$ty>,
})
}))
})
}
fn serialize_item_enum(
@@ -316,11 +284,11 @@ fn serialize_item_enum(
type_ident: Ident,
impl_generics: &ast::Generics,
ty: P<ast::Ty>,
enum_def: &ast::EnumDef,
container_attrs: &attr::ContainerAttrs,
) -> Result<P<ast::Expr>, Error> {
let arms: Vec<_> = try!(
enum_def.variants.iter()
variants: &[Variant],
item_attrs: &attr::Item,
) -> P<ast::Expr> {
let arms: Vec<_> =
variants.iter()
.enumerate()
.map(|(variant_index, variant)| {
serialize_variant(
@@ -331,17 +299,16 @@ fn serialize_item_enum(
ty.clone(),
variant,
variant_index,
container_attrs,
item_attrs,
)
})
.collect()
);
.collect();
Ok(quote_expr!(cx,
quote_expr!(cx,
match *self {
$arms
}
))
)
}
fn serialize_variant(
@@ -350,24 +317,19 @@ fn serialize_variant(
type_ident: Ident,
generics: &ast::Generics,
ty: P<ast::Ty>,
variant: &ast::Variant,
variant: &Variant,
variant_index: usize,
container_attrs: &attr::ContainerAttrs,
) -> Result<ast::Arm, Error> {
let type_name = container_attrs.name().serialize_name_expr();
item_attrs: &attr::Item,
) -> ast::Arm {
let type_name = name_expr(builder, item_attrs.name());
let variant_ident = variant.node.name;
let variant_attrs = try!(attr::VariantAttrs::from_variant(cx, variant));
let variant_name = variant_attrs.name().serialize_name_expr();
let variant_ident = variant.ident;
let variant_name = name_expr(builder, variant.attrs.name());
match variant.node.data {
ast::VariantData::Unit(_) => {
let pat = builder.pat().path()
.id(type_ident).id(variant_ident)
.build();
Ok(quote_arm!(cx,
$pat => {
match variant.style {
Style::Unit => {
quote_arm!(cx,
$type_ident::$variant_ident => {
_serde::ser::Serializer::serialize_unit_variant(
_serializer,
$type_name,
@@ -375,30 +337,26 @@ fn serialize_variant(
$variant_name,
)
}
))
)
},
ast::VariantData::Tuple(ref fields, _) if fields.len() == 1 => {
let field = builder.id("__simple_value");
let field = builder.pat().ref_id(field);
let pat = builder.pat().enum_()
.id(type_ident).id(variant_ident).build()
.with_pats(Some(field).into_iter())
.build();
Style::Newtype => {
let expr = serialize_newtype_variant(
cx,
builder,
type_name,
variant_index,
variant_name,
ty,
generics,
&variant.fields[0],
);
Ok(quote_arm!(cx,
$pat => {
_serde::ser::Serializer::serialize_newtype_variant(
_serializer,
$type_name,
$variant_index,
$variant_name,
__simple_value,
)
}
))
quote_arm!(cx,
$type_ident::$variant_ident(ref __simple_value) => { $expr }
)
},
ast::VariantData::Tuple(ref fields, _) => {
let field_names: Vec<ast::Ident> = (0 .. fields.len())
Style::Tuple => {
let field_names: Vec<ast::Ident> = (0 .. variant.fields.len())
.map(|i| builder.id(format!("__field{}", i)))
.collect();
@@ -418,16 +376,16 @@ fn serialize_variant(
variant_name,
generics,
ty,
fields,
&variant.fields,
field_names,
);
Ok(quote_arm!(cx,
quote_arm!(cx,
$pat => { $expr }
))
)
}
ast::VariantData::Struct(ref fields, _) => {
let field_names: Vec<_> = (0 .. fields.len())
Style::Struct => {
let field_names: Vec<_> = (0 .. variant.fields.len())
.map(|i| builder.id(format!("__field{}", i)))
.collect();
@@ -435,7 +393,7 @@ fn serialize_variant(
.id(type_ident).id(variant_ident).build()
.with_pats(
field_names.iter()
.zip(fields.iter())
.zip(variant.fields.iter())
.map(|(id, field)| {
let name = match field.ident {
Some(name) => name,
@@ -449,25 +407,52 @@ fn serialize_variant(
)
.build();
let expr = try!(serialize_struct_variant(
let expr = serialize_struct_variant(
cx,
builder,
variant_index,
variant_name,
generics,
ty,
fields,
&variant.fields,
field_names,
container_attrs,
));
item_attrs,
);
Ok(quote_arm!(cx,
quote_arm!(cx,
$pat => { $expr }
))
)
}
}
}
fn serialize_newtype_variant(
cx: &ExtCtxt,
builder: &aster::AstBuilder,
type_name: P<ast::Expr>,
variant_index: usize,
variant_name: P<ast::Expr>,
item_ty: P<ast::Ty>,
generics: &ast::Generics,
field: &Field,
) -> P<ast::Expr> {
let mut field_expr = quote_expr!(cx, __simple_value);
if let Some(path) = field.attrs.serialize_with() {
field_expr = wrap_serialize_with(cx, builder,
&item_ty, generics, &field.ty, path, field_expr);
}
quote_expr!(cx,
_serde::ser::Serializer::serialize_newtype_variant(
_serializer,
$type_name,
$variant_index,
$variant_name,
$field_expr,
)
)
}
fn serialize_tuple_variant(
cx: &ExtCtxt,
builder: &aster::AstBuilder,
@@ -476,7 +461,7 @@ fn serialize_tuple_variant(
variant_name: P<ast::Expr>,
generics: &ast::Generics,
structure_ty: P<ast::Ty>,
fields: &[ast::StructField],
fields: &[Field],
field_names: Vec<Ident>,
) -> P<ast::Expr> {
let variant_ty = builder.ty().tuple()
@@ -496,8 +481,9 @@ fn serialize_tuple_variant(
structure_ty.clone(),
variant_ty,
builder.id("serialize_tuple_variant_elt"),
fields.len(),
fields,
generics,
true,
);
let value_expr = builder.expr().tuple()
@@ -526,10 +512,10 @@ fn serialize_struct_variant(
variant_name: P<ast::Expr>,
generics: &ast::Generics,
ty: P<ast::Ty>,
fields: &[ast::StructField],
fields: &[Field],
field_names: Vec<Ident>,
container_attrs: &attr::ContainerAttrs,
) -> Result<P<ast::Expr>, Error> {
item_attrs: &attr::Item,
) -> P<ast::Expr> {
let variant_generics = builder.generics()
.with(generics.clone())
.add_lifetime_bound("'__serde_variant")
@@ -541,7 +527,6 @@ fn serialize_struct_variant(
.with_fields(
fields.iter().map(|field| {
builder.struct_field(field.ident.expect("struct has unnamed fields"))
.with_attrs(field.attrs.iter().cloned())
.ty()
.ref_()
.lifetime("'__serde_variant")
@@ -578,7 +563,7 @@ fn serialize_struct_variant(
.build()
.build();
let (visitor_struct, visitor_impl) = try!(serialize_struct_visitor(
let (visitor_struct, visitor_impl) = serialize_struct_visitor(
cx,
builder,
variant_ty.clone(),
@@ -587,16 +572,16 @@ fn serialize_struct_variant(
fields,
&variant_generics,
true,
));
);
let container_name = container_attrs.name().serialize_name_expr();
let item_name = name_expr(builder, item_attrs.name());
Ok(quote_expr!(cx, {
quote_expr!(cx, {
$variant_struct
$visitor_struct
$visitor_impl
_serializer.serialize_struct_variant(
$container_name,
$item_name,
$variant_index,
$variant_name,
Visitor {
@@ -605,7 +590,7 @@ fn serialize_struct_variant(
_structure_ty: ::std::marker::PhantomData,
},
)
}))
})
}
fn serialize_tuple_struct_visitor(
@@ -614,20 +599,31 @@ fn serialize_tuple_struct_visitor(
structure_ty: P<ast::Ty>,
variant_ty: P<ast::Ty>,
serializer_method: ast::Ident,
fields: usize,
generics: &ast::Generics
fields: &[Field],
generics: &ast::Generics,
is_enum: bool,
) -> (P<ast::Item>, P<ast::Item>) {
let arms: Vec<ast::Arm> = (0 .. fields)
.map(|i| {
let expr = builder.expr().method_call(serializer_method)
.id("_serializer")
.arg().ref_().tup_field(i).field("value").self_()
.build();
let arms: Vec<_> = fields.iter()
.enumerate()
.map(|(i, field)| {
let mut field_expr = builder.expr().tup_field(i).field("value").self_();
if !is_enum {
field_expr = quote_expr!(cx, &$field_expr);
}
let continue_if_skip = field.attrs.skip_serializing_if()
.map(|path| quote_stmt!(cx, if $path($field_expr) { continue }));
if let Some(path) = field.attrs.serialize_with() {
field_expr = wrap_serialize_with(cx, builder,
&structure_ty, generics, &field.ty, path, field_expr);
}
quote_arm!(cx,
$i => {
self.state += 1;
Ok(Some(try!($expr)))
$continue_if_skip
Ok(Some(try!(_serializer.$serializer_method($field_expr))))
}
)
})
@@ -644,6 +640,8 @@ fn serialize_tuple_struct_visitor(
.strip_bounds()
.build();
let nfields = fields.len();
(
quote_item!(cx,
struct Visitor $visitor_impl_generics $where_clause {
@@ -658,8 +656,8 @@ fn serialize_tuple_struct_visitor(
for Visitor $visitor_generics
$where_clause {
#[inline]
fn visit<S>(&mut self, _serializer: &mut S) -> ::std::result::Result<Option<()>, S::Error>
where S: _serde::ser::Serializer
fn visit<__S>(&mut self, _serializer: &mut __S) -> ::std::result::Result<Option<()>, __S::Error>
where __S: _serde::ser::Serializer
{
match self.state {
$arms
@@ -669,7 +667,7 @@ fn serialize_tuple_struct_visitor(
#[inline]
fn len(&self) -> Option<usize> {
Some($fields)
Some($nfields)
}
}
).unwrap(),
@@ -682,32 +680,29 @@ fn serialize_struct_visitor(
structure_ty: P<ast::Ty>,
variant_ty: P<ast::Ty>,
serializer_method: ast::Ident,
fields: &[ast::StructField],
fields: &[Field],
generics: &ast::Generics,
is_enum: bool,
) -> Result<(P<ast::Item>, P<ast::Item>), Error> {
let field_attrs = try!(
attr::get_struct_field_attrs(cx, &structure_ty, generics, fields, is_enum)
);
let arms: Vec<ast::Arm> = fields.iter().zip(field_attrs.iter())
.filter(|&(_, ref field_attr)| !field_attr.skip_serializing_field())
) -> (P<ast::Item>, P<ast::Item>) {
let arms: Vec<ast::Arm> = fields.iter()
.filter(|&field| !field.attrs.skip_serializing())
.enumerate()
.map(|(i, (ref field, ref field_attr))| {
let name = field.ident.expect("struct has unnamed field");
.map(|(i, field)| {
let ident = field.ident.expect("struct has unnamed field");
let mut field_expr = quote_expr!(cx, self.value.$ident);
if !is_enum {
field_expr = quote_expr!(cx, &$field_expr);
}
let key_expr = field_attr.name().serialize_name_expr();
let key_expr = name_expr(builder, field.attrs.name());
let stmt = if let Some(expr) = field_attr.skip_serializing_field_if() {
Some(quote_stmt!(cx, if $expr { continue; }))
} else {
None
};
let continue_if_skip = field.attrs.skip_serializing_if()
.map(|path| quote_stmt!(cx, if $path($field_expr) { continue }));
let field_expr = match field_attr.serialize_with() {
Some(expr) => expr.clone(),
None => quote_expr!(cx, &self.value.$name),
};
if let Some(path) = field.attrs.serialize_with() {
field_expr = wrap_serialize_with(cx, builder,
&structure_ty, generics, &field.ty, path, field_expr)
}
let expr = quote_expr!(cx,
_serializer.$serializer_method($key_expr, $field_expr)
@@ -716,7 +711,7 @@ fn serialize_struct_visitor(
quote_arm!(cx,
$i => {
self.state += 1;
$stmt
$continue_if_skip
return Ok(Some(try!($expr)));
}
)
@@ -734,21 +729,23 @@ fn serialize_struct_visitor(
.strip_bounds()
.build();
let len = field_attrs.iter()
.filter(|field_attr| !field_attr.skip_serializing_field())
.map(|field_attr| {
match field_attr.skip_serializing_field_if() {
Some(expr) => {
quote_expr!(cx, if $expr { 0 } else { 1 })
}
None => {
quote_expr!(cx, 1)
}
let len = fields.iter()
.filter(|&field| !field.attrs.skip_serializing())
.map(|field| {
let ident = field.ident.expect("struct has unnamed fields");
let mut field_expr = quote_expr!(cx, self.value.$ident);
if !is_enum {
field_expr = quote_expr!(cx, &$field_expr);
}
match field.attrs.skip_serializing_if() {
Some(path) => quote_expr!(cx, if $path($field_expr) { 0 } else { 1 }),
None => quote_expr!(cx, 1),
}
})
.fold(quote_expr!(cx, 0), |sum, expr| quote_expr!(cx, $sum + $expr));
Ok((
(
quote_item!(cx,
struct Visitor $visitor_impl_generics $where_clause {
state: usize,
@@ -763,8 +760,8 @@ fn serialize_struct_visitor(
for Visitor $visitor_generics
$where_clause {
#[inline]
fn visit<S>(&mut self, _serializer: &mut S) -> ::std::result::Result<Option<()>, S::Error>
where S: _serde::ser::Serializer,
fn visit<__S>(&mut self, _serializer: &mut __S) -> ::std::result::Result<Option<()>, __S::Error>
where __S: _serde::ser::Serializer,
{
loop {
match self.state {
@@ -780,5 +777,55 @@ fn serialize_struct_visitor(
}
}
).unwrap(),
))
)
}
fn wrap_serialize_with(
cx: &ExtCtxt,
builder: &aster::AstBuilder,
item_ty: &P<ast::Ty>,
generics: &ast::Generics,
field_ty: &P<ast::Ty>,
path: &ast::Path,
value: P<ast::Expr>,
) -> P<ast::Expr> {
let where_clause = &generics.where_clause;
let wrapper_generics = builder.from_generics(generics.clone())
.add_lifetime_bound("'__a")
.lifetime_name("'__a")
.build();
let wrapper_ty = builder.path()
.segment("__SerializeWith")
.with_generics(wrapper_generics.clone())
.build()
.build();
quote_expr!(cx, {
struct __SerializeWith $wrapper_generics $where_clause {
value: &'__a $field_ty,
phantom: ::std::marker::PhantomData<$item_ty>,
}
impl $wrapper_generics _serde::ser::Serialize for $wrapper_ty $where_clause {
fn serialize<__S>(&self, __s: &mut __S) -> Result<(), __S::Error>
where __S: _serde::ser::Serializer
{
$path(self.value, __s)
}
}
__SerializeWith {
value: $value,
phantom: ::std::marker::PhantomData::<$item_ty>,
}
})
}
fn name_expr(
builder: &aster::AstBuilder,
name: &attr::Name,
) -> P<ast::Expr> {
builder.expr().str(name.serialize_name())
}
+43
View File
@@ -0,0 +1,43 @@
use syntax::ast;
use syntax::codemap::{self, ExpnId, Span};
use syntax::ext::base::{Annotatable, ExtCtxt};
use syntax::fold::{self, Folder};
use syntax::parse::token::intern;
use syntax::ptr::P;
pub fn record_expansion(
cx: &ExtCtxt,
item: P<ast::Item>,
derive: &str,
) -> Annotatable {
let info = codemap::ExpnInfo {
call_site: codemap::DUMMY_SP,
callee: codemap::NameAndSpan {
format: codemap::MacroAttribute(intern(&format!("derive({})", derive))),
span: None,
allow_internal_unstable: false,
},
};
let expn_id = cx.codemap().record_expansion(info);
let mut respanner = Respanner { expn_id: expn_id };
let item = item.map(|item| respanner.fold_item_simple(item));
Annotatable::Item(item)
}
struct Respanner {
expn_id: ExpnId,
}
impl Folder for Respanner {
fn new_span(&mut self, span: Span) -> Span {
Span {
expn_id: self.expn_id,
.. span
}
}
fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
fold::noop_fold_mac(mac, self)
}
}
+20
View File
@@ -0,0 +1,20 @@
[package]
name = "serde_codegen_internals"
version = "0.4.0"
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>"]
license = "MIT/Apache-2.0"
description = "AST representation used by Serde codegen. Unstable."
repository = "https://github.com/serde-rs/serde"
documentation = "https://github.com/serde-rs/serde"
keywords = ["serde", "serialization"]
include = ["Cargo.toml", "src/**/*.rs"]
[features]
default = ["with-syntex"]
nightly-testing = ["clippy"]
with-syntex = ["syntex_syntax", "syntex_errors"]
[dependencies]
clippy = { version = "^0.*", optional = true }
syntex_syntax = { version = "^0.39.0", optional = true }
syntex_errors = { version = "^0.39.0", optional = true }
+142
View File
@@ -0,0 +1,142 @@
use syntax::ast;
use syntax::codemap;
use syntax::ext::base::ExtCtxt;
use syntax::ptr::P;
use attr;
use Error;
pub struct Item<'a> {
pub ident: ast::Ident,
pub span: codemap::Span,
pub attrs: attr::Item,
pub body: Body<'a>,
pub generics: &'a ast::Generics,
}
pub enum Body<'a> {
Enum(Vec<Variant<'a>>),
Struct(Style, Vec<Field<'a>>),
}
pub struct Variant<'a> {
pub ident: ast::Ident,
pub span: codemap::Span,
pub attrs: attr::Variant,
pub style: Style,
pub fields: Vec<Field<'a>>,
}
pub struct Field<'a> {
pub ident: Option<ast::Ident>,
pub span: codemap::Span,
pub attrs: attr::Field,
pub ty: &'a P<ast::Ty>,
}
pub enum Style {
Struct,
Tuple,
Newtype,
Unit,
}
impl<'a> Item<'a> {
pub fn from_ast(
cx: &ExtCtxt,
item: &'a ast::Item,
) -> Result<Item<'a>, Error> {
let attrs = attr::Item::from_ast(cx, item);
let (body, generics) = match item.node {
ast::ItemKind::Enum(ref enum_def, ref generics) => {
let variants = enum_from_ast(cx, enum_def);
(Body::Enum(variants), generics)
}
ast::ItemKind::Struct(ref variant_data, ref generics) => {
let (style, fields) = struct_from_ast(cx, variant_data);
(Body::Struct(style, fields), generics)
}
_ => {
return Err(Error::UnexpectedItemKind);
}
};
Ok(Item {
ident: item.ident,
span: item.span,
attrs: attrs,
body: body,
generics: generics,
})
}
}
impl<'a> Body<'a> {
pub fn all_fields(&'a self) -> Box<Iterator<Item=&'a Field<'a>> + 'a> {
match *self {
Body::Enum(ref variants) => {
Box::new(variants.iter()
.flat_map(|variant| variant.fields.iter()))
}
Body::Struct(_, ref fields) => {
Box::new(fields.iter())
}
}
}
}
fn enum_from_ast<'a>(
cx: &ExtCtxt,
enum_def: &'a ast::EnumDef,
) -> Vec<Variant<'a>> {
enum_def.variants.iter()
.map(|variant| {
let (style, fields) = struct_from_ast(cx, &variant.node.data);
Variant {
ident: variant.node.name,
span: variant.span,
attrs: attr::Variant::from_ast(cx, variant),
style: style,
fields: fields,
}
})
.collect()
}
fn struct_from_ast<'a>(
cx: &ExtCtxt,
variant_data: &'a ast::VariantData,
) -> (Style, Vec<Field<'a>>) {
match *variant_data {
ast::VariantData::Struct(ref fields, _) => {
(Style::Struct, fields_from_ast(cx, fields))
}
ast::VariantData::Tuple(ref fields, _) if fields.len() == 1 => {
(Style::Newtype, fields_from_ast(cx, fields))
}
ast::VariantData::Tuple(ref fields, _) => {
(Style::Tuple, fields_from_ast(cx, fields))
}
ast::VariantData::Unit(_) => {
(Style::Unit, Vec::new())
}
}
}
fn fields_from_ast<'a>(
cx: &ExtCtxt,
fields: &'a [ast::StructField],
) -> Vec<Field<'a>> {
fields.iter()
.enumerate()
.map(|(i, field)| {
Field {
ident: field.ident,
span: field.span,
attrs: attr::Field::from_ast(cx, i, field),
ty: &field.ty,
}
})
.collect()
}
+638
View File
@@ -0,0 +1,638 @@
use std::rc::Rc;
use syntax::ast;
use syntax::attr::{self, HasAttrs};
use syntax::codemap::{Span, Spanned, respan};
use syntax::ext::base::ExtCtxt;
use syntax::fold::Folder;
use syntax::parse::parser::{Parser, PathStyle};
use syntax::parse::token::{self, InternedString};
use syntax::parse;
use syntax::print::pprust::{lit_to_string, meta_item_to_string};
use syntax::ptr::P;
use syntax::tokenstream::{self, TokenTree};
// This module handles parsing of `#[serde(...)]` attributes. The entrypoints
// are `attr::Item::from_ast`, `attr::Variant::from_ast`, and
// `attr::Field::from_ast`. Each returns an instance of the corresponding
// struct. Note that none of them return a Result. Unrecognized, malformed, or
// duplicated attributes result in a span_err but otherwise are ignored. The
// user will see errors simultaneously for all bad attributes in the crate
// rather than just the first.
struct Attr<'a, 'b: 'a, T> {
cx: &'a ExtCtxt<'b>,
name: &'static str,
value: Option<Spanned<T>>,
}
impl<'a, 'b, T> Attr<'a, 'b, T> {
fn none(cx: &'a ExtCtxt<'b>, name: &'static str) -> Self {
Attr {
cx: cx,
name: name,
value: None,
}
}
fn set(&mut self, span: Span, t: T) {
if let Some(Spanned { span: prev_span, .. }) = self.value {
let mut err = self.cx.struct_span_err(
span,
&format!("duplicate serde attribute `{}`", self.name));
err.span_help(prev_span, "previously set here");
err.emit();
} else {
self.value = Some(respan(span, t));
}
}
fn set_opt(&mut self, v: Option<Spanned<T>>) {
if let Some(v) = v {
self.set(v.span, v.node);
}
}
fn set_if_none(&mut self, span: Span, t: T) {
if self.value.is_none() {
self.value = Some(respan(span, t));
}
}
fn get(self) -> Option<T> {
self.value.map(|spanned| spanned.node)
}
fn get_spanned(self) -> Option<Spanned<T>> {
self.value
}
}
struct BoolAttr<'a, 'b: 'a>(Attr<'a, 'b, ()>);
impl<'a, 'b> BoolAttr<'a, 'b> {
fn none(cx: &'a ExtCtxt<'b>, name: &'static str) -> Self {
BoolAttr(Attr::none(cx, name))
}
fn set_true(&mut self, span: Span) {
self.0.set(span, ());
}
fn get(&self) -> bool {
self.0.value.is_some()
}
}
#[derive(Debug)]
pub struct Name {
serialize: InternedString,
deserialize: InternedString,
}
impl Name {
/// Return the container name for the container when serializing.
pub fn serialize_name(&self) -> InternedString {
self.serialize.clone()
}
/// Return the container name for the container when deserializing.
pub fn deserialize_name(&self) -> InternedString {
self.deserialize.clone()
}
}
/// Represents container (e.g. struct) attribute information
#[derive(Debug)]
pub struct Item {
name: Name,
deny_unknown_fields: bool,
ser_bound: Option<Vec<ast::WherePredicate>>,
de_bound: Option<Vec<ast::WherePredicate>>,
}
impl Item {
/// Extract out the `#[serde(...)]` attributes from an item.
pub fn from_ast(cx: &ExtCtxt, item: &ast::Item) -> Self {
let mut ser_name = Attr::none(cx, "rename");
let mut de_name = Attr::none(cx, "rename");
let mut deny_unknown_fields = BoolAttr::none(cx, "deny_unknown_fields");
let mut ser_bound = Attr::none(cx, "bound");
let mut de_bound = Attr::none(cx, "bound");
let ident = item.ident.name.as_str();
for meta_items in item.attrs().iter().filter_map(get_serde_meta_items) {
for meta_item in meta_items {
let span = meta_item.span;
match meta_item.node {
// Parse `#[serde(rename="foo")]`
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"rename" => {
if let Ok(s) = get_str_from_lit(cx, name, lit) {
ser_name.set(span, s.clone());
de_name.set(span, s);
}
}
// Parse `#[serde(rename(serialize="foo", deserialize="bar"))]`
ast::MetaItemKind::List(ref name, ref meta_items) if name == &"rename" => {
if let Ok((ser, de)) = get_renames(cx, meta_items) {
ser_name.set_opt(ser);
de_name.set_opt(de);
}
}
// Parse `#[serde(deny_unknown_fields)]`
ast::MetaItemKind::Word(ref name) if name == &"deny_unknown_fields" => {
deny_unknown_fields.set_true(span);
}
// Parse `#[serde(bound="D: Serialize")]`
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"bound" => {
if let Ok(where_predicates) = parse_lit_into_where(cx, name, lit) {
ser_bound.set(span, where_predicates.clone());
de_bound.set(span, where_predicates);
}
}
// Parse `#[serde(bound(serialize="D: Serialize", deserialize="D: Deserialize"))]`
ast::MetaItemKind::List(ref name, ref meta_items) if name == &"bound" => {
if let Ok((ser, de)) = get_where_predicates(cx, meta_items) {
ser_bound.set_opt(ser);
de_bound.set_opt(de);
}
}
_ => {
cx.span_err(
meta_item.span,
&format!("unknown serde container attribute `{}`",
meta_item_to_string(meta_item)));
}
}
}
}
Item {
name: Name {
serialize: ser_name.get().unwrap_or(ident.clone()),
deserialize: de_name.get().unwrap_or(ident),
},
deny_unknown_fields: deny_unknown_fields.get(),
ser_bound: ser_bound.get(),
de_bound: de_bound.get(),
}
}
pub fn name(&self) -> &Name {
&self.name
}
pub fn deny_unknown_fields(&self) -> bool {
self.deny_unknown_fields
}
pub fn ser_bound(&self) -> Option<&[ast::WherePredicate]> {
self.ser_bound.as_ref().map(|vec| &vec[..])
}
pub fn de_bound(&self) -> Option<&[ast::WherePredicate]> {
self.de_bound.as_ref().map(|vec| &vec[..])
}
}
/// Represents variant attribute information
#[derive(Debug)]
pub struct Variant {
name: Name,
}
impl Variant {
pub fn from_ast(cx: &ExtCtxt, variant: &ast::Variant) -> Self {
let mut ser_name = Attr::none(cx, "rename");
let mut de_name = Attr::none(cx, "rename");
let ident = variant.node.name.name.as_str();
for meta_items in variant.node.attrs.iter().filter_map(get_serde_meta_items) {
for meta_item in meta_items {
let span = meta_item.span;
match meta_item.node {
// Parse `#[serde(rename="foo")]`
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"rename" => {
if let Ok(s) = get_str_from_lit(cx, name, lit) {
ser_name.set(span, s.clone());
de_name.set(span, s);
}
}
// Parse `#[serde(rename(serialize="foo", deserialize="bar"))]`
ast::MetaItemKind::List(ref name, ref meta_items) if name == &"rename" => {
if let Ok((ser, de)) = get_renames(cx, meta_items) {
ser_name.set_opt(ser);
de_name.set_opt(de);
}
}
_ => {
cx.span_err(
meta_item.span,
&format!("unknown serde variant attribute `{}`",
meta_item_to_string(meta_item)));
}
}
}
}
Variant {
name: Name {
serialize: ser_name.get().unwrap_or(ident.clone()),
deserialize: de_name.get().unwrap_or(ident),
},
}
}
pub fn name(&self) -> &Name {
&self.name
}
}
/// Represents field attribute information
#[derive(Debug)]
pub struct Field {
name: Name,
skip_serializing: bool,
skip_deserializing: bool,
skip_serializing_if: Option<ast::Path>,
default: FieldDefault,
serialize_with: Option<ast::Path>,
deserialize_with: Option<ast::Path>,
ser_bound: Option<Vec<ast::WherePredicate>>,
de_bound: Option<Vec<ast::WherePredicate>>,
}
/// Represents the default to use for a field when deserializing.
#[derive(Debug, PartialEq)]
pub enum FieldDefault {
/// Field must always be specified because it does not have a default.
None,
/// The default is given by `std::default::Default::default()`.
Default,
/// The default is given by this function.
Path(ast::Path),
}
impl Field {
/// Extract out the `#[serde(...)]` attributes from a struct field.
pub fn from_ast(cx: &ExtCtxt,
index: usize,
field: &ast::StructField) -> Self {
let mut ser_name = Attr::none(cx, "rename");
let mut de_name = Attr::none(cx, "rename");
let mut skip_serializing = BoolAttr::none(cx, "skip_serializing");
let mut skip_deserializing = BoolAttr::none(cx, "skip_deserializing");
let mut skip_serializing_if = Attr::none(cx, "skip_serializing_if");
let mut default = Attr::none(cx, "default");
let mut serialize_with = Attr::none(cx, "serialize_with");
let mut deserialize_with = Attr::none(cx, "deserialize_with");
let mut ser_bound = Attr::none(cx, "bound");
let mut de_bound = Attr::none(cx, "bound");
let ident = match field.ident {
Some(ident) => ident.name.as_str(),
None => token::intern_and_get_ident(&index.to_string()),
};
for meta_items in field.attrs.iter().filter_map(get_serde_meta_items) {
for meta_item in meta_items {
let span = meta_item.span;
match meta_item.node {
// Parse `#[serde(rename="foo")]`
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"rename" => {
if let Ok(s) = get_str_from_lit(cx, name, lit) {
ser_name.set(span, s.clone());
de_name.set(span, s);
}
}
// Parse `#[serde(rename(serialize="foo", deserialize="bar"))]`
ast::MetaItemKind::List(ref name, ref meta_items) if name == &"rename" => {
if let Ok((ser, de)) = get_renames(cx, meta_items) {
ser_name.set_opt(ser);
de_name.set_opt(de);
}
}
// Parse `#[serde(default)]`
ast::MetaItemKind::Word(ref name) if name == &"default" => {
default.set(span, FieldDefault::Default);
}
// Parse `#[serde(default="...")]`
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"default" => {
if let Ok(path) = parse_lit_into_path(cx, name, lit) {
default.set(span, FieldDefault::Path(path));
}
}
// Parse `#[serde(skip_serializing)]`
ast::MetaItemKind::Word(ref name) if name == &"skip_serializing" => {
skip_serializing.set_true(span);
}
// Parse `#[serde(skip_deserializing)]`
ast::MetaItemKind::Word(ref name) if name == &"skip_deserializing" => {
skip_deserializing.set_true(span);
}
// Parse `#[serde(skip_serializing_if="...")]`
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"skip_serializing_if" => {
if let Ok(path) = parse_lit_into_path(cx, name, lit) {
skip_serializing_if.set(span, path);
}
}
// Parse `#[serde(serialize_with="...")]`
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"serialize_with" => {
if let Ok(path) = parse_lit_into_path(cx, name, lit) {
serialize_with.set(span, path);
}
}
// Parse `#[serde(deserialize_with="...")]`
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"deserialize_with" => {
if let Ok(path) = parse_lit_into_path(cx, name, lit) {
deserialize_with.set(span, path);
}
}
// Parse `#[serde(bound="D: Serialize")]`
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"bound" => {
if let Ok(where_predicates) = parse_lit_into_where(cx, name, lit) {
ser_bound.set(span, where_predicates.clone());
de_bound.set(span, where_predicates);
}
}
// Parse `#[serde(bound(serialize="D: Serialize", deserialize="D: Deserialize"))]`
ast::MetaItemKind::List(ref name, ref meta_items) if name == &"bound" => {
if let Ok((ser, de)) = get_where_predicates(cx, meta_items) {
ser_bound.set_opt(ser);
de_bound.set_opt(de);
}
}
_ => {
cx.span_err(
meta_item.span,
&format!("unknown serde field attribute `{}`",
meta_item_to_string(meta_item)));
}
}
}
}
// Is skip_deserializing, initialize the field to Default::default()
// unless a different default is specified by `#[serde(default="...")]`
if let Some(Spanned { span, .. }) = skip_deserializing.0.value {
default.set_if_none(span, FieldDefault::Default);
}
Field {
name: Name {
serialize: ser_name.get().unwrap_or(ident.clone()),
deserialize: de_name.get().unwrap_or(ident),
},
skip_serializing: skip_serializing.get(),
skip_deserializing: skip_deserializing.get(),
skip_serializing_if: skip_serializing_if.get(),
default: default.get().unwrap_or(FieldDefault::None),
serialize_with: serialize_with.get(),
deserialize_with: deserialize_with.get(),
ser_bound: ser_bound.get(),
de_bound: de_bound.get(),
}
}
pub fn name(&self) -> &Name {
&self.name
}
pub fn skip_serializing(&self) -> bool {
self.skip_serializing
}
pub fn skip_deserializing(&self) -> bool {
self.skip_deserializing
}
pub fn skip_serializing_if(&self) -> Option<&ast::Path> {
self.skip_serializing_if.as_ref()
}
pub fn default(&self) -> &FieldDefault {
&self.default
}
pub fn serialize_with(&self) -> Option<&ast::Path> {
self.serialize_with.as_ref()
}
pub fn deserialize_with(&self) -> Option<&ast::Path> {
self.deserialize_with.as_ref()
}
pub fn ser_bound(&self) -> Option<&[ast::WherePredicate]> {
self.ser_bound.as_ref().map(|vec| &vec[..])
}
pub fn de_bound(&self) -> Option<&[ast::WherePredicate]> {
self.de_bound.as_ref().map(|vec| &vec[..])
}
}
fn get_ser_and_de<T, F>(
cx: &ExtCtxt,
attribute: &'static str,
items: &[P<ast::MetaItem>],
f: F
) -> Result<(Option<Spanned<T>>, Option<Spanned<T>>), ()>
where F: Fn(&ExtCtxt, &str, &ast::Lit) -> Result<T, ()>,
{
let mut ser_item = Attr::none(cx, attribute);
let mut de_item = Attr::none(cx, attribute);
for item in items {
match item.node {
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"serialize" => {
if let Ok(v) = f(cx, name, lit) {
ser_item.set(item.span, v);
}
}
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"deserialize" => {
if let Ok(v) = f(cx, name, lit) {
de_item.set(item.span, v);
}
}
_ => {
cx.span_err(
item.span,
&format!("unknown {} attribute `{}`",
attribute,
meta_item_to_string(item)));
return Err(());
}
}
}
Ok((ser_item.get_spanned(), de_item.get_spanned()))
}
fn get_renames(
cx: &ExtCtxt,
items: &[P<ast::MetaItem>],
) -> Result<(Option<Spanned<InternedString>>, Option<Spanned<InternedString>>), ()> {
get_ser_and_de(cx, "rename", items, get_str_from_lit)
}
fn get_where_predicates(
cx: &ExtCtxt,
items: &[P<ast::MetaItem>],
) -> Result<(Option<Spanned<Vec<ast::WherePredicate>>>, Option<Spanned<Vec<ast::WherePredicate>>>), ()> {
get_ser_and_de(cx, "bound", items, parse_lit_into_where)
}
pub fn get_serde_meta_items(attr: &ast::Attribute) -> Option<&[P<ast::MetaItem>]> {
match attr.node.value.node {
ast::MetaItemKind::List(ref name, ref items) if name == &"serde" => {
attr::mark_used(&attr);
Some(items)
}
_ => None
}
}
/// This syntax folder rewrites tokens to say their spans are coming from a macro context.
struct Respanner<'a, 'b: 'a> {
cx: &'a ExtCtxt<'b>,
}
impl<'a, 'b> Folder for Respanner<'a, 'b> {
fn fold_tt(&mut self, tt: &TokenTree) -> TokenTree {
match *tt {
TokenTree::Token(span, ref tok) => {
TokenTree::Token(
self.new_span(span),
self.fold_token(tok.clone())
)
}
TokenTree::Delimited(span, ref delimed) => {
TokenTree::Delimited(
self.new_span(span),
Rc::new(tokenstream::Delimited {
delim: delimed.delim,
open_span: delimed.open_span,
tts: self.fold_tts(&delimed.tts),
close_span: delimed.close_span,
})
)
}
TokenTree::Sequence(span, ref seq) => {
TokenTree::Sequence(
self.new_span(span),
Rc::new(tokenstream::SequenceRepetition {
tts: self.fold_tts(&seq.tts),
separator: seq.separator.clone().map(|tok| self.fold_token(tok)),
..**seq
})
)
}
}
}
fn new_span(&mut self, span: Span) -> Span {
Span {
lo: span.lo,
hi: span.hi,
expn_id: self.cx.backtrace(),
}
}
}
fn get_str_from_lit(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result<InternedString, ()> {
match lit.node {
ast::LitKind::Str(ref s, _) => Ok(s.clone()),
_ => {
cx.span_err(
lit.span,
&format!("serde annotation `{}` must be a string, not `{}`",
name,
lit_to_string(lit)));
return Err(());
}
}
}
// If we just parse a string literal from an attibute, any syntax errors in the
// source will only have spans that point inside the string and not back to the
// attribute. So to have better error reporting, we'll first parse the string
// into a token tree. Then we'll update those spans to say they're coming from a
// macro context that originally came from the attribnute, and then finally
// parse them into an expression or where-clause.
fn parse_string_via_tts<T, F>(cx: &ExtCtxt, name: &str, string: String, action: F) -> Result<T, ()>
where F: for<'a> Fn(&'a mut Parser) -> parse::PResult<'a, T>,
{
let tts = panictry!(parse::parse_tts_from_source_str(
format!("<serde {} expansion>", name),
string,
cx.cfg(),
cx.parse_sess()));
// Respan the spans to say they are all coming from this macro.
let tts = Respanner { cx: cx }.fold_tts(&tts);
let mut parser = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(), tts);
let path = match action(&mut parser) {
Ok(path) => path,
Err(mut e) => {
e.emit();
return Err(());
}
};
// Make sure to error out if there are trailing characters in the stream.
match parser.expect(&token::Eof) {
Ok(()) => { }
Err(mut e) => {
e.emit();
return Err(());
}
}
Ok(path)
}
fn parse_lit_into_path(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result<ast::Path, ()> {
let string = try!(get_str_from_lit(cx, name, lit)).to_string();
parse_string_via_tts(cx, name, string, |parser| {
parser.parse_path(PathStyle::Type)
})
}
fn parse_lit_into_where(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result<Vec<ast::WherePredicate>, ()> {
let string = try!(get_str_from_lit(cx, name, lit));
if string.is_empty() {
return Ok(Vec::new());
}
let where_string = format!("where {}", string);
parse_string_via_tts(cx, name, where_string, |parser| {
let where_clause = try!(parser.parse_where_clause());
Ok(where_clause.predicates)
})
}
+19
View File
@@ -0,0 +1,19 @@
use std::error;
use std::fmt;
#[derive(Debug)]
pub enum Error {
UnexpectedItemKind,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "expected a struct or enum")
}
}
impl error::Error for Error {
fn description(&self) -> &str {
"expected a struct or enum"
}
}
+21
View File
@@ -0,0 +1,21 @@
#![cfg_attr(feature = "nightly-testing", plugin(clippy))]
#![cfg_attr(feature = "nightly-testing", feature(plugin))]
#![cfg_attr(not(feature = "with-syntex"), feature(rustc_private, plugin))]
#[cfg(feature = "with-syntex")]
#[macro_use]
extern crate syntex_syntax as syntax;
#[cfg(feature = "with-syntex")]
extern crate syntex_errors as errors;
#[cfg(not(feature = "with-syntex"))]
#[macro_use]
extern crate syntax;
#[cfg(not(feature = "with-syntex"))]
extern crate rustc_errors as errors;
pub mod ast;
pub mod attr;
mod error;
pub use error::Error;
+8 -4
View File
@@ -1,12 +1,13 @@
[package]
name = "serde_macros"
version = "0.7.4"
version = "0.7.15"
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>"]
license = "MIT/Apache-2.0"
description = "Macros to auto-generate implementations for the serde framework"
repository = "https://github.com/serde-rs/serde"
documentation = "https://github.com/serde-rs/serde"
keywords = ["serde", "serialization"]
include = ["Cargo.toml", "src/**/*.rs"]
[lib]
name = "serde_macros"
@@ -17,12 +18,15 @@ nightly-testing = ["clippy", "serde/nightly-testing", "serde_codegen/nightly-tes
[dependencies]
clippy = { version = "^0.*", optional = true }
serde_codegen = { version = "^0.7.4", path = "../serde_codegen", default-features = false, features = ["nightly"] }
serde_codegen = { version = "^0.7.15", default-features = false, features = ["nightly"] }
[dev-dependencies]
compiletest_rs = "^0.1.1"
clippy = "^0.0.79"
compiletest_rs = "^0.2.0"
fnv = "1.0"
rustc-serialize = "^0.3.16"
serde = { version = "^0.7.4", path = "../serde" }
serde = "0.7.15"
serde_test = "0.7.15"
[[test]]
name = "test"
+1 -1
View File
@@ -6,4 +6,4 @@ extern crate rustc_serialize;
extern crate serde;
extern crate test;
include!("../../serde_tests/benches/bench.rs.in");
include!("../../testing/benches/bench.rs.in");
@@ -0,0 +1,32 @@
#![feature(custom_attribute, custom_derive, plugin)]
#![plugin(serde_macros)]
#[derive(Serialize)]
struct S {
#[serde(rename(serialize="x"))]
#[serde(rename(serialize="y"))] //~ ERROR: duplicate serde attribute `rename`
a: (),
#[serde(rename(serialize="x"))]
#[serde(rename="y")] //~ ERROR: duplicate serde attribute `rename`
b: (),
#[serde(rename(serialize="x"))]
#[serde(rename(deserialize="y"))] // ok
c: (),
#[serde(rename="x")]
#[serde(rename(deserialize="y"))] //~ ERROR: duplicate serde attribute `rename`
d: (),
#[serde(rename(serialize="x", serialize="y"))] //~ ERROR: duplicate serde attribute `rename`
e: (),
#[serde(rename="x", serialize="y")] //~ ERROR: unknown serde field attribute `serialize = "y"`
f: (),
#[serde(rename(serialize="x"), rename(serialize="y"))] //~ ERROR: duplicate serde attribute `rename`
g: (),
}
fn main() {}
@@ -0,0 +1,9 @@
#![feature(custom_attribute, custom_derive, plugin)]
#![plugin(serde_macros)]
#[derive(Serialize, Deserialize)]
struct Test<'a> {
s: &'a str, //~ ERROR: Serde does not support deserializing fields of type &str
}
fn main() {}
+6 -1
View File
@@ -20,6 +20,11 @@ fn run_mode(mode: &'static str) {
}
#[test]
fn compile_test() {
fn compile_fail() {
run_mode("compile-fail");
}
#[test]
fn run_pass() {
run_mode("run-pass");
}
@@ -0,0 +1,12 @@
#![feature(custom_derive, plugin)]
#![plugin(serde_macros, clippy)]
#![deny(identity_op)]
// The derived implementation uses 0+1 to add up the number of fields
// serialized, which Clippy warns about. If the expansion info is registered
// correctly, the Clippy lint is not triggered.
#[derive(Serialize)]
struct A { b: u8 }
fn main() {}
+1 -1
View File
@@ -4,6 +4,6 @@
extern crate serde;
extern crate test;
include!("../../serde_tests/tests/test.rs.in");
include!("../../testing/tests/test.rs.in");
mod compile_tests;
+14
View File
@@ -0,0 +1,14 @@
[package]
name = "serde_test"
version = "0.7.15"
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>"]
license = "MIT/Apache-2.0"
description = "Token De/Serializer for testing De/Serialize implementations"
repository = "https://github.com/serde-rs/serde"
documentation = "https://serde-rs.github.io/serde/serde/"
readme = "../README.md"
keywords = ["serde", "serialization"]
include = ["Cargo.toml", "src/**/*.rs"]
[dependencies]
serde = "0.7.15"
+54
View File
@@ -0,0 +1,54 @@
use serde::{Serialize, Deserialize};
use de::Deserializer;
use error::Error;
use ser::Serializer;
use token::Token;
use std::fmt::Debug;
pub fn assert_tokens<T>(value: &T, tokens: &[Token<'static>])
where T: Serialize + Deserialize + PartialEq + Debug,
{
assert_ser_tokens(value, tokens);
assert_de_tokens(value, tokens);
}
pub fn assert_ser_tokens<T>(value: &T, tokens: &[Token])
where T: Serialize,
{
let mut ser = Serializer::new(tokens.iter());
assert_eq!(Serialize::serialize(value, &mut ser), Ok(()));
assert_eq!(ser.next_token(), None);
}
/// Expect an error serializing `T`.
pub fn assert_ser_tokens_error<T>(value: &T, tokens: &[Token], error: Error)
where T: Serialize + PartialEq + Debug,
{
let mut ser = Serializer::new(tokens.iter());
let v: Result<(), Error> = Serialize::serialize(value, &mut ser);
assert_eq!(v.as_ref(), Err(&error));
assert_eq!(ser.next_token(), None);
}
pub fn assert_de_tokens<T>(value: &T, tokens: &[Token<'static>])
where T: Deserialize + PartialEq + Debug,
{
let mut de = Deserializer::new(tokens.to_vec().into_iter());
let v: Result<T, Error> = Deserialize::deserialize(&mut de);
assert_eq!(v.as_ref(), Ok(value));
assert_eq!(de.next_token(), None);
}
/// Expect an error deserializing tokens into a `T`.
pub fn assert_de_tokens_error<T>(tokens: &[Token<'static>], error: Error)
where T: Deserialize + PartialEq + Debug,
{
let mut de = Deserializer::new(tokens.to_vec().into_iter());
let v: Result<T, Error> = Deserialize::deserialize(&mut de);
assert_eq!(v, Err(error));
// There may be one token left if a peek caused the error
de.next_token();
assert_eq!(de.next_token(), None);
}
File diff suppressed because it is too large Load Diff
+87
View File
@@ -0,0 +1,87 @@
use std::{error, fmt};
use serde::{ser, de};
use token::Token;
#[derive(Clone, PartialEq, Debug)]
pub enum Error {
// Shared
Custom(String),
InvalidValue(String),
// De
EndOfStream,
InvalidType(de::Type),
InvalidLength(usize),
UnknownVariant(String),
UnknownField(String),
MissingField(&'static str),
DuplicateField(&'static str),
InvalidName(&'static str),
UnexpectedToken(Token<'static>),
}
impl ser::Error for Error {
fn custom<T: Into<String>>(msg: T) -> Error {
Error::Custom(msg.into())
}
fn invalid_value(msg: &str) -> Error {
Error::InvalidValue(msg.to_owned())
}
}
impl de::Error for Error {
fn custom<T: Into<String>>(msg: T) -> Error {
Error::Custom(msg.into())
}
fn end_of_stream() -> Error {
Error::EndOfStream
}
fn invalid_type(ty: de::Type) -> Error {
Error::InvalidType(ty)
}
fn invalid_value(msg: &str) -> Error {
Error::InvalidValue(msg.to_owned())
}
fn invalid_length(len: usize) -> Error {
Error::InvalidLength(len)
}
fn unknown_variant(variant: &str) -> Error {
Error::UnknownVariant(variant.to_owned())
}
fn unknown_field(field: &str) -> Error {
Error::UnknownField(field.to_owned())
}
fn missing_field(field: &'static str) -> Error {
Error::MissingField(field)
}
fn duplicate_field(field: &'static str) -> Error {
Error::DuplicateField(field)
}
}
impl fmt::Display for Error {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
formatter.write_str(format!("{:?}", self).as_ref())
}
}
impl error::Error for Error {
fn description(&self) -> &str {
"Serde Error"
}
fn cause(&self) -> Option<&error::Error> {
None
}
}
+22
View File
@@ -0,0 +1,22 @@
extern crate serde;
mod assert;
pub use assert::{
assert_tokens,
assert_ser_tokens,
assert_ser_tokens_error,
assert_de_tokens,
assert_de_tokens_error,
};
mod ser;
pub use ser::Serializer;
mod de;
pub use de::Deserializer;
mod token;
pub use token::Token;
mod error;
pub use error::Error;
+350
View File
@@ -0,0 +1,350 @@
use std::marker::PhantomData;
use serde::ser::{
self,
MapVisitor,
SeqVisitor,
Serialize,
};
use error::Error;
use token::Token;
pub struct Serializer<'a, I>
where I: Iterator<Item=&'a Token<'a>>,
{
tokens: I,
phantom: PhantomData<&'a Token<'a>>,
}
impl<'a, I> Serializer<'a, I>
where I: Iterator<Item=&'a Token<'a>>,
{
pub fn new(tokens: I) -> Serializer<'a, I> {
Serializer {
tokens: tokens,
phantom: PhantomData,
}
}
pub fn next_token(&mut self) -> Option<&'a Token<'a>> {
self.tokens.next()
}
fn visit_seq<V>(&mut self, mut visitor: V) -> Result<(), Error>
where V: SeqVisitor
{
while let Some(()) = try!(visitor.visit(self)) { }
assert_eq!(self.tokens.next(), Some(&Token::SeqEnd));
Ok(())
}
fn visit_map<V>(&mut self, mut visitor: V) -> Result<(), Error>
where V: MapVisitor
{
while let Some(()) = try!(visitor.visit(self)) { }
assert_eq!(self.tokens.next(), Some(&Token::MapEnd));
Ok(())
}
}
impl<'a, I> ser::Serializer for Serializer<'a, I>
where I: Iterator<Item=&'a Token<'a>>,
{
type Error = Error;
fn serialize_unit(&mut self) -> Result<(), Error> {
assert_eq!(self.tokens.next(), Some(&Token::Unit));
Ok(())
}
fn serialize_newtype_variant<T>(&mut self,
name: &str,
_variant_index: usize,
variant: &str,
value: T) -> Result<(), Error>
where T: Serialize,
{
assert_eq!(self.tokens.next(), Some(&Token::EnumNewType(name, variant)));
value.serialize(self)
}
fn serialize_unit_struct(&mut self, name: &str) -> Result<(), Error> {
assert_eq!(self.tokens.next(), Some(&Token::UnitStruct(name)));
Ok(())
}
fn serialize_unit_variant(&mut self,
name: &str,
_variant_index: usize,
variant: &str) -> Result<(), Error> {
assert_eq!(self.tokens.next(), Some(&Token::EnumUnit(name, variant)));
Ok(())
}
fn serialize_bool(&mut self, v: bool) -> Result<(), Error> {
assert_eq!(self.tokens.next(), Some(&Token::Bool(v)));
Ok(())
}
fn serialize_isize(&mut self, v: isize) -> Result<(), Error> {
assert_eq!(self.tokens.next(), Some(&Token::Isize(v)));
Ok(())
}
fn serialize_i8(&mut self, v: i8) -> Result<(), Error> {
assert_eq!(self.tokens.next(), Some(&Token::I8(v)));
Ok(())
}
fn serialize_i16(&mut self, v: i16) -> Result<(), Error> {
assert_eq!(self.tokens.next(), Some(&Token::I16(v)));
Ok(())
}
fn serialize_i32(&mut self, v: i32) -> Result<(), Error> {
assert_eq!(self.tokens.next(), Some(&Token::I32(v)));
Ok(())
}
fn serialize_i64(&mut self, v: i64) -> Result<(), Error> {
assert_eq!(self.tokens.next(), Some(&Token::I64(v)));
Ok(())
}
fn serialize_usize(&mut self, v: usize) -> Result<(), Error> {
assert_eq!(self.tokens.next(), Some(&Token::Usize(v)));
Ok(())
}
fn serialize_u8(&mut self, v: u8) -> Result<(), Error> {
assert_eq!(self.tokens.next(), Some(&Token::U8(v)));
Ok(())
}
fn serialize_u16(&mut self, v: u16) -> Result<(), Error> {
assert_eq!(self.tokens.next(), Some(&Token::U16(v)));
Ok(())
}
fn serialize_u32(&mut self, v: u32) -> Result<(), Error> {
assert_eq!(self.tokens.next(), Some(&Token::U32(v)));
Ok(())
}
fn serialize_u64(&mut self, v: u64) -> Result<(), Error> {
assert_eq!(self.tokens.next(), Some(&Token::U64(v)));
Ok(())
}
fn serialize_f32(&mut self, v: f32) -> Result<(), Error> {
assert_eq!(self.tokens.next(), Some(&Token::F32(v)));
Ok(())
}
fn serialize_f64(&mut self, v: f64) -> Result<(), Error> {
assert_eq!(self.tokens.next(), Some(&Token::F64(v)));
Ok(())
}
fn serialize_char(&mut self, v: char) -> Result<(), Error> {
assert_eq!(self.tokens.next(), Some(&Token::Char(v)));
Ok(())
}
fn serialize_str(&mut self, v: &str) -> Result<(), Error> {
assert_eq!(self.tokens.next(), Some(&Token::Str(v)));
Ok(())
}
fn serialize_none(&mut self) -> Result<(), Error> {
assert_eq!(self.tokens.next(), Some(&Token::Option(false)));
Ok(())
}
fn serialize_some<V>(&mut self, value: V) -> Result<(), Error>
where V: Serialize,
{
assert_eq!(self.tokens.next(), Some(&Token::Option(true)));
value.serialize(self)
}
fn serialize_seq<V>(&mut self, visitor: V) -> Result<(), Error>
where V: SeqVisitor
{
let len = visitor.len();
assert_eq!(self.tokens.next(), Some(&Token::SeqStart(len)));
self.visit_seq(visitor)
}
fn serialize_fixed_size_array<V>(&mut self, visitor: V) -> Result<(), Error>
where V: SeqVisitor
{
let len = visitor.len().expect("arrays must have a length");
assert_eq!(self.tokens.next(), Some(&Token::SeqArrayStart(len)));
self.visit_seq(visitor)
}
fn serialize_seq_elt<T>(&mut self, value: T) -> Result<(), Error>
where T: Serialize
{
assert_eq!(self.tokens.next(), Some(&Token::SeqSep));
value.serialize(self)
}
fn serialize_tuple<V>(&mut self, mut visitor: V) -> Result<(), Error>
where V: SeqVisitor
{
let len = visitor.len().expect("arrays must have a length");
assert_eq!(self.tokens.next(), Some(&Token::TupleStart(len)));
while let Some(()) = try!(visitor.visit(self)) { }
assert_eq!(self.tokens.next(), Some(&Token::TupleEnd));
Ok(())
}
fn serialize_tuple_elt<T>(&mut self, value: T) -> Result<(), Error>
where T: Serialize
{
assert_eq!(self.tokens.next(), Some(&Token::TupleSep));
value.serialize(self)
}
fn serialize_newtype_struct<T>(&mut self,
name: &'static str,
value: T) -> Result<(), Error>
where T: Serialize,
{
assert_eq!(self.tokens.next(), Some(&Token::StructNewType(name)));
value.serialize(self)
}
fn serialize_tuple_struct<V>(&mut self, name: &str, mut visitor: V) -> Result<(), Error>
where V: SeqVisitor
{
let len = visitor.len();
assert_eq!(self.tokens.next(), Some(&Token::TupleStructStart(name, len)));
while let Some(()) = try!(visitor.visit(self)) { }
assert_eq!(self.tokens.next(), Some(&Token::TupleStructEnd));
Ok(())
}
fn serialize_tuple_struct_elt<T>(&mut self, value: T) -> Result<(), Error>
where T: Serialize,
{
assert_eq!(self.tokens.next(), Some(&Token::TupleStructSep));
value.serialize(self)
}
fn serialize_tuple_variant<V>(&mut self,
name: &str,
_variant_index: usize,
variant: &str,
mut visitor: V) -> Result<(), Error>
where V: SeqVisitor
{
let len = visitor.len();
assert_eq!(self.tokens.next(), Some(&Token::EnumSeqStart(name, variant, len)));
while let Some(()) = try!(visitor.visit(self)) { }
assert_eq!(self.tokens.next(), Some(&Token::EnumSeqEnd));
Ok(())
}
fn serialize_tuple_variant_elt<T>(&mut self, value: T) -> Result<(), Error>
where T: Serialize,
{
assert_eq!(self.tokens.next(), Some(&Token::EnumSeqSep));
value.serialize(self)
}
fn serialize_map<V>(&mut self, visitor: V) -> Result<(), Error>
where V: MapVisitor
{
let len = visitor.len();
assert_eq!(self.tokens.next(), Some(&Token::MapStart(len)));
self.visit_map(visitor)
}
fn serialize_map_elt<K, V>(&mut self, key: K, value: V) -> Result<(), Error>
where K: Serialize,
V: Serialize,
{
assert_eq!(self.tokens.next(), Some(&Token::MapSep));
try!(key.serialize(self));
value.serialize(self)
}
fn serialize_struct<V>(&mut self, name: &str, mut visitor: V) -> Result<(), Error>
where V: MapVisitor
{
let len = visitor.len();
assert_eq!(self.tokens.next(), Some(&Token::StructStart(name, len)));
while let Some(()) = try!(visitor.visit(self)) { }
assert_eq!(self.tokens.next(), Some(&Token::StructEnd));
Ok(())
}
fn serialize_struct_elt<T>(&mut self, key: &'static str, value: T) -> Result<(), Error>
where T: Serialize,
{
assert_eq!(self.tokens.next(), Some(&Token::StructSep));
try!(key.serialize(self));
value.serialize(self)
}
fn serialize_struct_variant<V>(&mut self,
name: &str,
_variant_index: usize,
variant: &str,
mut visitor: V) -> Result<(), Error>
where V: MapVisitor
{
let len = visitor.len();
assert_eq!(self.tokens.next(), Some(&Token::EnumMapStart(name, variant, len)));
while let Some(()) = try!(visitor.visit(self)) { }
assert_eq!(self.tokens.next(), Some(&Token::EnumMapEnd));
Ok(())
}
fn serialize_struct_variant_elt<T>(&mut self, key: &'static str, value: T) -> Result<(), Error>
where T: Serialize,
{
assert_eq!(self.tokens.next(), Some(&Token::EnumMapSep));
try!(key.serialize(self));
value.serialize(self)
}
}
+60
View File
@@ -0,0 +1,60 @@
#[derive(Clone, PartialEq, Debug)]
pub enum Token<'a> {
Bool(bool),
Isize(isize),
I8(i8),
I16(i16),
I32(i32),
I64(i64),
Usize(usize),
U8(u8),
U16(u16),
U32(u32),
U64(u64),
F32(f32),
F64(f64),
Char(char),
Str(&'a str),
String(String),
Bytes(&'a [u8]),
Option(bool),
Unit,
UnitStruct(&'a str),
StructNewType(&'a str),
EnumStart(&'a str),
EnumUnit(&'a str, &'a str),
EnumNewType(&'a str, &'a str),
SeqStart(Option<usize>),
SeqArrayStart(usize),
SeqSep,
SeqEnd,
TupleStart(usize),
TupleSep,
TupleEnd,
TupleStructStart(&'a str, Option<usize>),
TupleStructSep,
TupleStructEnd,
MapStart(Option<usize>),
MapSep,
MapEnd,
StructStart(&'a str, Option<usize>),
StructSep,
StructEnd,
EnumSeqStart(&'a str, &'a str, Option<usize>),
EnumSeqSep,
EnumSeqEnd,
EnumMapStart(&'a str, &'a str, Option<usize>),
EnumMapSep,
EnumMapEnd,
}
@@ -1,6 +1,6 @@
[package]
name = "serde_tests"
version = "0.7.1"
name = "serde_testing"
version = "0.7.15"
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>"]
license = "MIT/Apache-2.0"
description = "A generic serialization/deserialization framework"
@@ -14,14 +14,13 @@ build = "build.rs"
nightly-testing = ["clippy", "serde/nightly-testing", "serde_codegen/nightly-testing"]
[build-dependencies]
syntex = { version = "^0.32.0" }
syntex_syntax = { version = "^0.32.0" }
serde_codegen = { version = "*", path = "../serde_codegen", features = ["with-syntex"] }
serde_codegen = { version = "*", features = ["with-syntex"] }
[dev-dependencies]
fnv = "1.0"
rustc-serialize = "^0.3.16"
serde = { version = "*", path = "../serde" }
syntex = "^0.32.0"
serde = "*"
serde_test = "*"
[dependencies]
clippy = { version = "^0.*", optional = true }
@@ -1,9 +1,9 @@
use test::Bencher;
use std::error;
use std::fmt;
use rustc_serialize::{Decoder, Decodable};
use rustc_serialize::Decodable;
use serde;
use serde::de::{Deserializer, Deserialize};
use serde::de::Deserialize;
//////////////////////////////////////////////////////////////////////////////
@@ -3,10 +3,10 @@ use test::Bencher;
use std::fmt;
use std::error;
use rustc_serialize::{Decoder, Decodable};
use rustc_serialize::Decodable;
use serde;
use serde::de::{Deserializer, Deserialize};
use serde::de::Deserialize;
//////////////////////////////////////////////////////////////////////////////
+1 -6
View File
@@ -1,4 +1,3 @@
extern crate syntex;
extern crate serde_codegen;
use std::env;
@@ -13,10 +12,6 @@ fn main() {
] {
let src = Path::new(src);
let dst = Path::new(&out_dir).join(dst);
let mut registry = syntex::Registry::new();
serde_codegen::register(&mut registry);
registry.expand("", &src, &dst).unwrap();
serde_codegen::expand(&src, &dst).unwrap();
}
}
@@ -5,7 +5,7 @@ macro_rules! declare_ser_tests {
#[test]
fn $name() {
$(
::token::assert_ser_tokens(&$value, $tokens);
assert_ser_tokens(&$value, $tokens);
)+
}
)+
@@ -62,6 +62,14 @@ macro_rules! hashset {
$(set.insert($value);)+
set
}
};
($hasher:ident @ $($value:expr),+) => {
{
use std::hash::BuildHasherDefault;
let mut set = HashSet::with_hasher(BuildHasherDefault::<$hasher>::default());
$(set.insert($value);)+
set
}
}
}
@@ -75,5 +83,13 @@ macro_rules! hashmap {
$(map.insert($key, $value);)+
map
}
};
($hasher:ident @ $($key:expr => $value:expr),+) => {
{
use std::hash::BuildHasherDefault;
let mut map = HashMap::with_hasher(BuildHasherDefault::<$hasher>::default());
$(map.insert($key, $value);)+
map
}
}
}
@@ -1,10 +1,9 @@
#[macro_use]
mod macros;
mod token;
mod test_annotations;
mod test_bytes;
mod test_de;
mod test_gen;
mod test_macros;
mod test_ser;
@@ -1,7 +1,8 @@
extern crate serde;
use self::serde::{Serialize, Serializer, Deserialize, Deserializer};
use token::{
extern crate serde_test;
use self::serde_test::{
Error,
Token,
assert_tokens,
@@ -80,7 +81,7 @@ struct DefaultStruct<A, B, C, D, E>
fn test_default_struct() {
assert_de_tokens(
&DefaultStruct { a1: 1, a2: 2, a3: 3, a4: 0, a5: 123 },
vec![
&[
Token::StructStart("DefaultStruct", Some(3)),
Token::StructSep,
@@ -109,7 +110,7 @@ fn test_default_struct() {
assert_de_tokens(
&DefaultStruct { a1: 1, a2: 0, a3: 123, a4: 0, a5: 123 },
vec![
&[
Token::StructStart("DefaultStruct", Some(1)),
Token::StructSep,
@@ -143,7 +144,7 @@ enum DefaultEnum<A, B, C, D, E>
fn test_default_enum() {
assert_de_tokens(
&DefaultEnum::Struct { a1: 1, a2: 2, a3: 3, a4: 0, a5: 123 },
vec![
&[
Token::EnumMapStart("DefaultEnum", "Struct", Some(5)),
Token::EnumMapSep,
@@ -172,7 +173,7 @@ fn test_default_enum() {
assert_de_tokens(
&DefaultEnum::Struct { a1: 1, a2: 0, a3: 123, a4: 0, a5: 123 },
vec![
&[
Token::EnumMapStart("DefaultEnum", "Struct", Some(5)),
Token::EnumMapSep,
@@ -206,7 +207,7 @@ struct ContainsNoStdDefault<A: MyDefault> {
fn test_no_std_default() {
assert_de_tokens(
&ContainsNoStdDefault { a: NoStdDefault(123) },
vec![
&[
Token::StructStart("ContainsNoStdDefault", Some(1)),
Token::StructEnd,
]
@@ -214,7 +215,7 @@ fn test_no_std_default() {
assert_de_tokens(
&ContainsNoStdDefault { a: NoStdDefault(8) },
vec![
&[
Token::StructStart("ContainsNoStdDefault", Some(1)),
Token::StructSep,
@@ -237,6 +238,14 @@ impl Default for NotDeserializeStruct {
}
}
impl DeserializeWith for NotDeserializeStruct {
fn deserialize_with<D>(_: &mut D) -> Result<Self, D::Error>
where D: Deserializer
{
panic!()
}
}
// Does not implement Deserialize.
#[derive(Debug, PartialEq)]
enum NotDeserializeEnum { Trouble }
@@ -248,13 +257,15 @@ impl MyDefault for NotDeserializeEnum {
}
#[derive(Debug, PartialEq, Deserialize)]
struct ContainsNotDeserialize<A, B, C: MyDefault> {
struct ContainsNotDeserialize<A, B, C: DeserializeWith, E: MyDefault> {
#[serde(skip_deserializing)]
a: A,
#[serde(skip_deserializing, default)]
b: B,
#[serde(skip_deserializing, default="MyDefault::my_default")]
#[serde(deserialize_with="DeserializeWith::deserialize_with", default)]
c: C,
#[serde(skip_deserializing, default="MyDefault::my_default")]
e: E,
}
// Tests that a struct field does not need to implement Deserialize if it is
@@ -266,9 +277,10 @@ fn test_elt_not_deserialize() {
&ContainsNotDeserialize {
a: NotDeserializeStruct(123),
b: NotDeserializeStruct(123),
c: NotDeserializeEnum::Trouble,
c: NotDeserializeStruct(123),
e: NotDeserializeEnum::Trouble,
},
vec![
&[
Token::StructStart("ContainsNotDeserialize", Some(3)),
Token::StructEnd,
]
@@ -286,7 +298,7 @@ fn test_ignore_unknown() {
// 'Default' allows unknown. Basic smoke test of ignore...
assert_de_tokens(
&DefaultStruct { a1: 1, a2: 2, a3: 3, a4: 0, a5: 123 },
vec![
&[
Token::StructStart("DefaultStruct", Some(5)),
Token::StructSep,
@@ -321,7 +333,7 @@ fn test_ignore_unknown() {
);
assert_de_tokens_error::<DenyUnknown>(
vec![
&[
Token::StructStart("DenyUnknown", Some(2)),
Token::StructSep,
@@ -330,11 +342,8 @@ fn test_ignore_unknown() {
Token::StructSep,
Token::Str("whoops"),
Token::I32(2),
Token::StructEnd,
],
Error::UnknownFieldError("whoops".to_owned())
Error::UnknownField("whoops".to_owned())
);
}
@@ -358,7 +367,7 @@ struct RenameStructSerializeDeserialize {
fn test_rename_struct() {
assert_tokens(
&RenameStruct { a1: 1, a2: 2 },
vec![
&[
Token::StructStart("Superhero", Some(2)),
Token::StructSep,
@@ -392,7 +401,7 @@ fn test_rename_struct() {
assert_de_tokens(
&RenameStructSerializeDeserialize { a1: 1, a2: 2 },
vec![
&[
Token::StructStart("SuperheroDe", Some(2)),
Token::StructSep,
@@ -430,7 +439,8 @@ enum RenameEnumSerializeDeserialize<A> {
#[serde(rename(serialize="dick_grayson", deserialize="jason_todd"))]
Robin {
a: i8,
#[serde(rename(serialize="c", deserialize="d"))]
#[serde(rename(serialize="c"))]
#[serde(rename(deserialize="d"))]
b: A,
},
}
@@ -439,14 +449,14 @@ enum RenameEnumSerializeDeserialize<A> {
fn test_rename_enum() {
assert_tokens(
&RenameEnum::Batman,
vec![
&[
Token::EnumUnit("Superhero", "bruce_wayne"),
]
);
assert_tokens(
&RenameEnum::Superman(0),
vec![
&[
Token::EnumNewType("Superhero", "clark_kent"),
Token::I8(0),
]
@@ -454,7 +464,7 @@ fn test_rename_enum() {
assert_tokens(
&RenameEnum::WonderWoman(0, 1),
vec![
&[
Token::EnumSeqStart("Superhero", "diana_prince", Some(2)),
Token::EnumSeqSep,
@@ -469,7 +479,7 @@ fn test_rename_enum() {
assert_tokens(
&RenameEnum::Flash { a: 1 },
vec![
&[
Token::EnumMapStart("Superhero", "barry_allan", Some(1)),
Token::EnumMapSep,
@@ -505,7 +515,7 @@ fn test_rename_enum() {
a: 0,
b: String::new(),
},
vec![
&[
Token::EnumMapStart("SuperheroDe", "jason_todd", Some(2)),
Token::EnumMapSep,
@@ -795,7 +805,7 @@ fn test_deserialize_with_struct() {
a: 1,
b: 2,
},
vec![
&[
Token::StructStart("DeserializeWithStruct", Some(2)),
Token::StructSep,
@@ -815,7 +825,7 @@ fn test_deserialize_with_struct() {
a: 1,
b: 123,
},
vec![
&[
Token::StructStart("DeserializeWithStruct", Some(2)),
Token::StructSep,
@@ -847,7 +857,7 @@ fn test_deserialize_with_enum() {
a: 1,
b: 2,
},
vec![
&[
Token::EnumMapStart("DeserializeWithEnum", "Struct", Some(2)),
Token::EnumMapSep,
@@ -867,7 +877,7 @@ fn test_deserialize_with_enum() {
a: 1,
b: 123,
},
vec![
&[
Token::EnumMapStart("DeserializeWithEnum", "Struct", Some(2)),
Token::EnumMapSep,
@@ -886,7 +896,7 @@ fn test_deserialize_with_enum() {
#[test]
fn test_missing_renamed_field_struct() {
assert_de_tokens_error::<RenameStruct>(
vec![
&[
Token::StructStart("Superhero", Some(2)),
Token::StructSep,
@@ -895,11 +905,11 @@ fn test_missing_renamed_field_struct() {
Token::StructEnd,
],
Error::MissingFieldError("a3"),
Error::MissingField("a3"),
);
assert_de_tokens_error::<RenameStructSerializeDeserialize>(
vec![
&[
Token::StructStart("SuperheroDe", Some(2)),
Token::StructSep,
@@ -908,23 +918,23 @@ fn test_missing_renamed_field_struct() {
Token::StructEnd,
],
Error::MissingFieldError("a5"),
Error::MissingField("a5"),
);
}
#[test]
fn test_missing_renamed_field_enum() {
assert_de_tokens_error::<RenameEnum>(
vec![
&[
Token::EnumMapStart("Superhero", "barry_allan", Some(1)),
Token::EnumMapEnd,
],
Error::MissingFieldError("b"),
Error::MissingField("b"),
);
assert_de_tokens_error::<RenameEnumSerializeDeserialize<i8>>(
vec![
&[
Token::EnumMapStart("SuperheroDe", "jason_todd", Some(2)),
Token::EnumMapSep,
@@ -933,6 +943,34 @@ fn test_missing_renamed_field_enum() {
Token::EnumMapEnd,
],
Error::MissingFieldError("d"),
Error::MissingField("d"),
);
}
#[derive(Debug, PartialEq, Deserialize)]
enum InvalidLengthEnum {
A(i32, i32, i32),
B(#[serde(skip_deserializing)] i32, i32, i32),
}
#[test]
fn test_invalid_length_enum() {
assert_de_tokens_error::<InvalidLengthEnum>(
&[
Token::EnumSeqStart("InvalidLengthEnum", "A", Some(3)),
Token::EnumSeqSep,
Token::I32(1),
Token::EnumSeqEnd,
],
Error::InvalidLength(1),
);
assert_de_tokens_error::<InvalidLengthEnum>(
&[
Token::EnumSeqStart("InvalidLengthEnum", "B", Some(3)),
Token::EnumSeqSep,
Token::I32(1),
Token::EnumSeqEnd,
],
Error::InvalidLength(1),
);
}
@@ -3,13 +3,16 @@ use std::net;
use std::path::PathBuf;
extern crate serde;
use self::serde::de::{Deserializer, Visitor};
use self::serde::Deserialize;
use token::{
extern crate fnv;
use self::fnv::FnvHasher;
extern crate serde_test;
use self::serde_test::{
Error,
Token,
assert_de_tokens,
assert_de_tokens_ignore,
assert_de_tokens_error,
};
@@ -73,107 +76,147 @@ macro_rules! declare_error_tests {
}
}
fn assert_de_tokens_ignore(ignorable_tokens: &[Token<'static>]) {
#[derive(PartialEq, Debug, Deserialize)]
struct IgnoreBase {
a: i32,
}
let expected = IgnoreBase{a: 1};
// Embed the tokens to be ignored in the normal token
// stream for an IgnoreBase type
let concated_tokens : Vec<Token<'static>> = vec![
Token::MapStart(Some(2)),
Token::MapSep,
Token::Str("a"),
Token::I32(1),
Token::MapSep,
Token::Str("ignored")
]
.into_iter()
.chain(ignorable_tokens.to_vec().into_iter())
.chain(vec![
Token::MapEnd,
].into_iter())
.collect();
let mut de = serde_test::Deserializer::new(concated_tokens.into_iter());
let v: Result<IgnoreBase, Error> = Deserialize::deserialize(&mut de);
// We run this test on every token stream for convenience, but
// some token streams don't make sense embedded as a map value,
// so we ignore those. SyntaxError is the real sign of trouble.
if let Err(Error::UnexpectedToken(_)) = v {
return;
}
assert_eq!(v.as_ref(), Ok(&expected));
assert_eq!(de.next_token(), None);
}
//////////////////////////////////////////////////////////////////////////
declare_tests! {
test_bool {
true => vec![Token::Bool(true)],
false => vec![Token::Bool(false)],
true => &[Token::Bool(true)],
false => &[Token::Bool(false)],
}
test_isize {
0isize => vec![Token::Isize(0)],
0isize => vec![Token::I8(0)],
0isize => vec![Token::I16(0)],
0isize => vec![Token::I32(0)],
0isize => vec![Token::I64(0)],
0isize => vec![Token::Usize(0)],
0isize => vec![Token::U8(0)],
0isize => vec![Token::U16(0)],
0isize => vec![Token::U32(0)],
0isize => vec![Token::U64(0)],
0isize => vec![Token::F32(0.)],
0isize => vec![Token::F64(0.)],
0isize => &[Token::Isize(0)],
0isize => &[Token::I8(0)],
0isize => &[Token::I16(0)],
0isize => &[Token::I32(0)],
0isize => &[Token::I64(0)],
0isize => &[Token::Usize(0)],
0isize => &[Token::U8(0)],
0isize => &[Token::U16(0)],
0isize => &[Token::U32(0)],
0isize => &[Token::U64(0)],
0isize => &[Token::F32(0.)],
0isize => &[Token::F64(0.)],
}
test_ints {
0isize => vec![Token::Isize(0)],
0i8 => vec![Token::I8(0)],
0i16 => vec![Token::I16(0)],
0i32 => vec![Token::I32(0)],
0i64 => vec![Token::I64(0)],
0isize => &[Token::Isize(0)],
0i8 => &[Token::I8(0)],
0i16 => &[Token::I16(0)],
0i32 => &[Token::I32(0)],
0i64 => &[Token::I64(0)],
}
test_uints {
0usize => vec![Token::Usize(0)],
0u8 => vec![Token::U8(0)],
0u16 => vec![Token::U16(0)],
0u32 => vec![Token::U32(0)],
0u64 => vec![Token::U64(0)],
0usize => &[Token::Usize(0)],
0u8 => &[Token::U8(0)],
0u16 => &[Token::U16(0)],
0u32 => &[Token::U32(0)],
0u64 => &[Token::U64(0)],
}
test_floats {
0f32 => vec![Token::F32(0.)],
0f64 => vec![Token::F64(0.)],
0f32 => &[Token::F32(0.)],
0f64 => &[Token::F64(0.)],
}
test_char {
'a' => vec![Token::Char('a')],
'a' => vec![Token::Str("a")],
'a' => vec![Token::String("a".to_owned())],
'a' => &[Token::Char('a')],
'a' => &[Token::Str("a")],
'a' => &[Token::String("a".to_owned())],
}
test_string {
"abc".to_owned() => vec![Token::Str("abc")],
"abc".to_owned() => vec![Token::String("abc".to_owned())],
"a".to_owned() => vec![Token::Char('a')],
"abc".to_owned() => &[Token::Str("abc")],
"abc".to_owned() => &[Token::String("abc".to_owned())],
"a".to_owned() => &[Token::Char('a')],
}
test_option {
None::<i32> => vec![Token::Unit],
None::<i32> => vec![Token::Option(false)],
Some(1) => vec![Token::I32(1)],
Some(1) => vec![
None::<i32> => &[Token::Unit],
None::<i32> => &[Token::Option(false)],
Some(1) => &[Token::I32(1)],
Some(1) => &[
Token::Option(true),
Token::I32(1),
],
}
test_result {
Ok::<i32, i32>(0) => vec![
Ok::<i32, i32>(0) => &[
Token::EnumStart("Result"),
Token::Str("Ok"),
Token::I32(0),
],
Err::<i32, i32>(1) => vec![
Err::<i32, i32>(1) => &[
Token::EnumStart("Result"),
Token::Str("Err"),
Token::I32(1),
],
}
test_unit {
() => vec![Token::Unit],
() => vec![
() => &[Token::Unit],
() => &[
Token::SeqStart(Some(0)),
Token::SeqEnd,
],
() => vec![
() => &[
Token::SeqStart(None),
Token::SeqEnd,
],
() => vec![
() => &[
Token::TupleStructStart("Anything", Some(0)),
Token::SeqEnd,
],
}
test_unit_struct {
UnitStruct => vec![Token::Unit],
UnitStruct => vec![
UnitStruct => &[Token::Unit],
UnitStruct => &[
Token::UnitStruct("UnitStruct"),
],
UnitStruct => vec![
UnitStruct => &[
Token::SeqStart(Some(0)),
Token::SeqEnd,
],
UnitStruct => vec![
UnitStruct => &[
Token::SeqStart(None),
Token::SeqEnd,
],
}
test_tuple_struct {
TupleStruct(1, 2, 3) => vec![
TupleStruct(1, 2, 3) => &[
Token::SeqStart(Some(3)),
Token::SeqSep,
Token::I32(1),
@@ -185,7 +228,7 @@ declare_tests! {
Token::I32(3),
Token::SeqEnd,
],
TupleStruct(1, 2, 3) => vec![
TupleStruct(1, 2, 3) => &[
Token::SeqStart(None),
Token::SeqSep,
Token::I32(1),
@@ -197,7 +240,7 @@ declare_tests! {
Token::I32(3),
Token::SeqEnd,
],
TupleStruct(1, 2, 3) => vec![
TupleStruct(1, 2, 3) => &[
Token::TupleStructStart("TupleStruct", Some(3)),
Token::TupleStructSep,
Token::I32(1),
@@ -209,7 +252,7 @@ declare_tests! {
Token::I32(3),
Token::TupleStructEnd,
],
TupleStruct(1, 2, 3) => vec![
TupleStruct(1, 2, 3) => &[
Token::TupleStructStart("TupleStruct", None),
Token::TupleStructSep,
Token::I32(1),
@@ -223,14 +266,14 @@ declare_tests! {
],
}
test_btreeset {
BTreeSet::<isize>::new() => vec![
BTreeSet::<isize>::new() => &[
Token::Unit,
],
BTreeSet::<isize>::new() => vec![
BTreeSet::<isize>::new() => &[
Token::SeqStart(Some(0)),
Token::SeqEnd,
],
btreeset![btreeset![], btreeset![1], btreeset![2, 3]] => vec![
btreeset![btreeset![], btreeset![1], btreeset![2, 3]] => &[
Token::SeqStart(Some(3)),
Token::SeqSep,
Token::SeqStart(Some(0)),
@@ -252,23 +295,23 @@ declare_tests! {
Token::SeqEnd,
Token::SeqEnd,
],
BTreeSet::<isize>::new() => vec![
BTreeSet::<isize>::new() => &[
Token::UnitStruct("Anything"),
],
BTreeSet::<isize>::new() => vec![
BTreeSet::<isize>::new() => &[
Token::TupleStructStart("Anything", Some(0)),
Token::SeqEnd,
],
}
test_hashset {
HashSet::<isize>::new() => vec![
HashSet::<isize>::new() => &[
Token::Unit,
],
HashSet::<isize>::new() => vec![
HashSet::<isize>::new() => &[
Token::SeqStart(Some(0)),
Token::SeqEnd,
],
hashset![1, 2, 3] => vec![
hashset![1, 2, 3] => &[
Token::SeqStart(Some(3)),
Token::SeqSep,
Token::I32(1),
@@ -280,23 +323,35 @@ declare_tests! {
Token::I32(3),
Token::SeqEnd,
],
HashSet::<isize>::new() => vec![
HashSet::<isize>::new() => &[
Token::UnitStruct("Anything"),
],
HashSet::<isize>::new() => vec![
HashSet::<isize>::new() => &[
Token::TupleStructStart("Anything", Some(0)),
Token::SeqEnd,
],
hashset![FnvHasher @ 1, 2, 3] => &[
Token::SeqStart(Some(3)),
Token::SeqSep,
Token::I32(1),
Token::SeqSep,
Token::I32(2),
Token::SeqSep,
Token::I32(3),
Token::SeqEnd,
],
}
test_vec {
Vec::<isize>::new() => vec![
Vec::<isize>::new() => &[
Token::Unit,
],
Vec::<isize>::new() => vec![
Vec::<isize>::new() => &[
Token::SeqStart(Some(0)),
Token::SeqEnd,
],
vec![vec![], vec![1], vec![2, 3]] => vec![
vec![vec![], vec![1], vec![2, 3]] => &[
Token::SeqStart(Some(3)),
Token::SeqSep,
Token::SeqStart(Some(0)),
@@ -318,27 +373,27 @@ declare_tests! {
Token::SeqEnd,
Token::SeqEnd,
],
Vec::<isize>::new() => vec![
Vec::<isize>::new() => &[
Token::UnitStruct("Anything"),
],
Vec::<isize>::new() => vec![
Vec::<isize>::new() => &[
Token::TupleStructStart("Anything", Some(0)),
Token::SeqEnd,
],
}
test_array {
[0; 0] => vec![
[0; 0] => &[
Token::Unit,
],
[0; 0] => vec![
[0; 0] => &[
Token::SeqStart(Some(0)),
Token::SeqEnd,
],
[0; 0] => vec![
[0; 0] => &[
Token::SeqArrayStart(0),
Token::SeqEnd,
],
([0; 0], [1], [2, 3]) => vec![
([0; 0], [1], [2, 3]) => &[
Token::SeqStart(Some(3)),
Token::SeqSep,
Token::SeqStart(Some(0)),
@@ -360,7 +415,7 @@ declare_tests! {
Token::SeqEnd,
Token::SeqEnd,
],
([0; 0], [1], [2, 3]) => vec![
([0; 0], [1], [2, 3]) => &[
Token::SeqArrayStart(3),
Token::SeqSep,
Token::SeqArrayStart(0),
@@ -382,22 +437,22 @@ declare_tests! {
Token::SeqEnd,
Token::SeqEnd,
],
[0; 0] => vec![
[0; 0] => &[
Token::UnitStruct("Anything"),
],
[0; 0] => vec![
[0; 0] => &[
Token::TupleStructStart("Anything", Some(0)),
Token::SeqEnd,
],
}
test_tuple {
(1,) => vec![
(1,) => &[
Token::SeqStart(Some(1)),
Token::SeqSep,
Token::I32(1),
Token::SeqEnd,
],
(1, 2, 3) => vec![
(1, 2, 3) => &[
Token::SeqStart(Some(3)),
Token::SeqSep,
Token::I32(1),
@@ -409,13 +464,13 @@ declare_tests! {
Token::I32(3),
Token::SeqEnd,
],
(1,) => vec![
(1,) => &[
Token::TupleStart(1),
Token::TupleSep,
Token::I32(1),
Token::TupleEnd,
],
(1, 2, 3) => vec![
(1, 2, 3) => &[
Token::TupleStart(3),
Token::TupleSep,
Token::I32(1),
@@ -429,21 +484,21 @@ declare_tests! {
],
}
test_btreemap {
BTreeMap::<isize, isize>::new() => vec![
BTreeMap::<isize, isize>::new() => &[
Token::Unit,
],
BTreeMap::<isize, isize>::new() => vec![
BTreeMap::<isize, isize>::new() => &[
Token::MapStart(Some(0)),
Token::MapEnd,
],
btreemap![1 => 2] => vec![
btreemap![1 => 2] => &[
Token::MapStart(Some(1)),
Token::MapSep,
Token::I32(1),
Token::I32(2),
Token::MapEnd,
],
btreemap![1 => 2, 3 => 4] => vec![
btreemap![1 => 2, 3 => 4] => &[
Token::MapStart(Some(2)),
Token::MapSep,
Token::I32(1),
@@ -454,7 +509,7 @@ declare_tests! {
Token::I32(4),
Token::MapEnd,
],
btreemap![1 => btreemap![], 2 => btreemap![3 => 4, 5 => 6]] => vec![
btreemap![1 => btreemap![], 2 => btreemap![3 => 4, 5 => 6]] => &[
Token::MapStart(Some(2)),
Token::MapSep,
Token::I32(1),
@@ -474,30 +529,30 @@ declare_tests! {
Token::MapEnd,
Token::MapEnd,
],
BTreeMap::<isize, isize>::new() => vec![
BTreeMap::<isize, isize>::new() => &[
Token::UnitStruct("Anything"),
],
BTreeMap::<isize, isize>::new() => vec![
BTreeMap::<isize, isize>::new() => &[
Token::StructStart("Anything", Some(0)),
Token::MapEnd,
],
}
test_hashmap {
HashMap::<isize, isize>::new() => vec![
HashMap::<isize, isize>::new() => &[
Token::Unit,
],
HashMap::<isize, isize>::new() => vec![
HashMap::<isize, isize>::new() => &[
Token::MapStart(Some(0)),
Token::MapEnd,
],
hashmap![1 => 2] => vec![
hashmap![1 => 2] => &[
Token::MapStart(Some(1)),
Token::MapSep,
Token::I32(1),
Token::I32(2),
Token::MapEnd,
],
hashmap![1 => 2, 3 => 4] => vec![
hashmap![1 => 2, 3 => 4] => &[
Token::MapStart(Some(2)),
Token::MapSep,
Token::I32(1),
@@ -508,7 +563,7 @@ declare_tests! {
Token::I32(4),
Token::MapEnd,
],
hashmap![1 => hashmap![], 2 => hashmap![3 => 4, 5 => 6]] => vec![
hashmap![1 => hashmap![], 2 => hashmap![3 => 4, 5 => 6]] => &[
Token::MapStart(Some(2)),
Token::MapSep,
Token::I32(1),
@@ -528,16 +583,27 @@ declare_tests! {
Token::MapEnd,
Token::MapEnd,
],
HashMap::<isize, isize>::new() => vec![
HashMap::<isize, isize>::new() => &[
Token::UnitStruct("Anything"),
],
HashMap::<isize, isize>::new() => vec![
HashMap::<isize, isize>::new() => &[
Token::StructStart("Anything", Some(0)),
Token::MapEnd,
],
hashmap![FnvHasher @ 1 => 2, 3 => 4] => &[
Token::MapStart(Some(2)),
Token::MapSep,
Token::I32(1),
Token::I32(2),
Token::MapSep,
Token::I32(3),
Token::I32(4),
Token::MapEnd,
],
}
test_struct {
Struct { a: 1, b: 2, c: 0 } => vec![
Struct { a: 1, b: 2, c: 0 } => &[
Token::MapStart(Some(3)),
Token::MapSep,
Token::Str("a"),
@@ -548,7 +614,7 @@ declare_tests! {
Token::I32(2),
Token::MapEnd,
],
Struct { a: 1, b: 2, c: 0 } => vec![
Struct { a: 1, b: 2, c: 0 } => &[
Token::StructStart("Struct", Some(3)),
Token::StructSep,
Token::Str("a"),
@@ -559,7 +625,7 @@ declare_tests! {
Token::I32(2),
Token::StructEnd,
],
Struct { a: 1, b: 2, c: 0 } => vec![
Struct { a: 1, b: 2, c: 0 } => &[
Token::SeqStart(Some(3)),
Token::SeqSep,
Token::I32(1),
@@ -570,7 +636,7 @@ declare_tests! {
],
}
test_struct_with_skip {
Struct { a: 1, b: 2, c: 0 } => vec![
Struct { a: 1, b: 2, c: 0 } => &[
Token::MapStart(Some(3)),
Token::MapSep,
Token::Str("a"),
@@ -589,7 +655,7 @@ declare_tests! {
Token::I32(4),
Token::MapEnd,
],
Struct { a: 1, b: 2, c: 0 } => vec![
Struct { a: 1, b: 2, c: 0 } => &[
Token::StructStart("Struct", Some(3)),
Token::StructSep,
Token::Str("a"),
@@ -610,18 +676,18 @@ declare_tests! {
],
}
test_enum_unit {
Enum::Unit => vec![
Enum::Unit => &[
Token::EnumUnit("Enum", "Unit"),
],
}
test_enum_simple {
Enum::Simple(1) => vec![
Enum::Simple(1) => &[
Token::EnumNewType("Enum", "Simple"),
Token::I32(1),
],
}
test_enum_seq {
Enum::Seq(1, 2, 3) => vec![
Enum::Seq(1, 2, 3) => &[
Token::EnumSeqStart("Enum", "Seq", Some(3)),
Token::EnumSeqSep,
Token::I32(1),
@@ -635,7 +701,7 @@ declare_tests! {
],
}
test_enum_map {
Enum::Map { a: 1, b: 2, c: 3 } => vec![
Enum::Map { a: 1, b: 2, c: 3 } => &[
Token::EnumMapStart("Enum", "Map", Some(3)),
Token::EnumMapSep,
Token::Str("a"),
@@ -652,24 +718,24 @@ declare_tests! {
],
}
test_enum_unit_usize {
Enum::Unit => vec![
Enum::Unit => &[
Token::EnumStart("Enum"),
Token::Usize(0),
Token::Unit,
],
}
test_enum_unit_bytes {
Enum::Unit => vec![
Enum::Unit => &[
Token::EnumStart("Enum"),
Token::Bytes(b"Unit"),
Token::Unit,
],
}
test_box {
Box::new(0i32) => vec![Token::I32(0)],
Box::new(0i32) => &[Token::I32(0)],
}
test_boxed_slice {
Box::new([0, 1, 2]) => vec![
Box::new([0, 1, 2]) => &[
Token::SeqStart(Some(3)),
Token::SeqSep,
Token::I32(0),
@@ -681,18 +747,18 @@ declare_tests! {
],
}
test_net_ipv4addr {
"1.2.3.4".parse::<net::Ipv4Addr>().unwrap() => vec![Token::Str("1.2.3.4")],
"1.2.3.4".parse::<net::Ipv4Addr>().unwrap() => &[Token::Str("1.2.3.4")],
}
test_net_ipv6addr {
"::1".parse::<net::Ipv6Addr>().unwrap() => vec![Token::Str("::1")],
"::1".parse::<net::Ipv6Addr>().unwrap() => &[Token::Str("::1")],
}
test_net_socketaddr {
"1.2.3.4:1234".parse::<net::SocketAddr>().unwrap() => vec![Token::Str("1.2.3.4:1234")],
"1.2.3.4:1234".parse::<net::SocketAddrV4>().unwrap() => vec![Token::Str("1.2.3.4:1234")],
"[::1]:1234".parse::<net::SocketAddrV6>().unwrap() => vec![Token::Str("[::1]:1234")],
"1.2.3.4:1234".parse::<net::SocketAddr>().unwrap() => &[Token::Str("1.2.3.4:1234")],
"1.2.3.4:1234".parse::<net::SocketAddrV4>().unwrap() => &[Token::Str("1.2.3.4:1234")],
"[::1]:1234".parse::<net::SocketAddrV6>().unwrap() => &[Token::Str("[::1]:1234")],
}
test_path_buf {
PathBuf::from("/usr/local/lib") => vec![
PathBuf::from("/usr/local/lib") => &[
Token::String("/usr/local/lib".to_owned()),
],
}
@@ -703,26 +769,48 @@ declare_tests! {
fn test_net_ipaddr() {
assert_de_tokens(
"1.2.3.4".parse::<net::IpAddr>().unwrap(),
vec![Token::Str("1.2.3.4")],
&[Token::Str("1.2.3.4")],
);
}
declare_error_tests! {
test_unknown_variant<Enum> {
vec![
&[
Token::EnumUnit("Enum", "Foo"),
],
Error::UnknownVariantError("Foo".to_owned()),
Error::UnknownVariant("Foo".to_owned()),
}
test_struct_seq_too_long<Struct> {
vec![
&[
Token::SeqStart(Some(4)),
Token::SeqSep, Token::I32(1),
Token::SeqSep, Token::I32(2),
Token::SeqSep, Token::I32(3),
Token::SeqSep, Token::I32(4),
Token::SeqEnd,
],
Error::UnexpectedToken(Token::SeqSep),
}
test_duplicate_field_struct<Struct> {
&[
Token::MapStart(Some(3)),
Token::MapSep,
Token::Str("a"),
Token::I32(1),
Token::MapSep,
Token::Str("a"),
],
Error::DuplicateField("a"),
}
test_duplicate_field_enum<Enum> {
&[
Token::EnumMapStart("Enum", "Map", Some(3)),
Token::EnumMapSep,
Token::Str("a"),
Token::I32(1),
Token::EnumMapSep,
Token::Str("a"),
],
Error::DuplicateField("a"),
}
}
+134
View File
@@ -0,0 +1,134 @@
// These just test that serde_codegen is able to produce code that compiles
// successfully when there are a variety of generics and non-(de)serializable
// types involved.
extern crate serde;
use self::serde::ser::{Serialize, Serializer};
use self::serde::de::{Deserialize, Deserializer};
//////////////////////////////////////////////////////////////////////////
#[derive(Serialize, Deserialize)]
struct With<T> {
t: T,
#[serde(serialize_with="ser_x", deserialize_with="de_x")]
x: X,
}
#[derive(Serialize, Deserialize)]
struct WithRef<'a, T: 'a> {
#[serde(skip_deserializing)]
t: Option<&'a T>,
#[serde(serialize_with="ser_x", deserialize_with="de_x")]
x: X,
}
#[derive(Serialize, Deserialize)]
struct Bounds<T: Serialize + Deserialize> {
t: T,
option: Option<T>,
boxed: Box<T>,
option_boxed: Option<Box<T>>,
}
#[derive(Serialize, Deserialize)]
struct NoBounds<T> {
t: T,
option: Option<T>,
boxed: Box<T>,
option_boxed: Option<Box<T>>,
}
#[derive(Serialize, Deserialize)]
enum EnumWith<T> {
Unit,
Newtype(
#[serde(serialize_with="ser_x", deserialize_with="de_x")]
X),
Tuple(
T,
#[serde(serialize_with="ser_x", deserialize_with="de_x")]
X),
Struct {
t: T,
#[serde(serialize_with="ser_x", deserialize_with="de_x")]
x: X },
}
#[derive(Serialize)]
struct MultipleRef<'a, 'b, 'c, T> where T: 'c, 'c: 'b, 'b: 'a {
t: T,
rrrt: &'a &'b &'c T,
}
#[derive(Serialize, Deserialize)]
struct Newtype(
#[serde(serialize_with="ser_x", deserialize_with="de_x")]
X
);
#[derive(Serialize, Deserialize)]
struct Tuple<T>(
T,
#[serde(serialize_with="ser_x", deserialize_with="de_x")]
X,
);
#[derive(Serialize, Deserialize)]
enum TreeNode<D> {
Split {
left: Box<TreeNode<D>>,
right: Box<TreeNode<D>>,
},
Leaf {
data: D,
},
}
#[derive(Serialize, Deserialize)]
struct ListNode<D> {
data: D,
next: Box<ListNode<D>>,
}
#[derive(Serialize, Deserialize)]
#[serde(bound="D: SerializeWith + DeserializeWith")]
struct WithTraits1<D, E> {
#[serde(serialize_with="SerializeWith::serialize_with",
deserialize_with="DeserializeWith::deserialize_with")]
d: D,
#[serde(serialize_with="SerializeWith::serialize_with",
deserialize_with="DeserializeWith::deserialize_with",
bound="E: SerializeWith + DeserializeWith")]
e: E,
}
#[derive(Serialize, Deserialize)]
#[serde(bound(serialize="D: SerializeWith",
deserialize="D: DeserializeWith"))]
struct WithTraits2<D, E> {
#[serde(serialize_with="SerializeWith::serialize_with",
deserialize_with="DeserializeWith::deserialize_with")]
d: D,
#[serde(serialize_with="SerializeWith::serialize_with",
bound(serialize="E: SerializeWith"))]
#[serde(deserialize_with="DeserializeWith::deserialize_with",
bound(deserialize="E: DeserializeWith"))]
e: E,
}
//////////////////////////////////////////////////////////////////////////
trait SerializeWith {
fn serialize_with<S: Serializer>(_: &Self, _: &mut S) -> Result<(), S::Error>;
}
trait DeserializeWith: Sized {
fn deserialize_with<D: Deserializer>(_: &mut D) -> Result<Self, D::Error>;
}
// Implements neither Serialize nor Deserialize
struct X;
fn ser_x<S: Serializer>(_: &X, _: &mut S) -> Result<(), S::Error> { panic!() }
fn de_x<D: Deserializer>(_: &mut D) -> Result<X, D::Error> { panic!() }
@@ -1,11 +1,12 @@
use std::marker::PhantomData;
use token::{Token, assert_tokens, assert_ser_tokens, assert_de_tokens};
extern crate serde_test;
use self::serde_test::{
Token,
assert_tokens,
assert_ser_tokens,
assert_de_tokens,
};
/*
trait Trait {
type Type;
}
*/
use std::marker::PhantomData;
// That tests that the derived Serialize implementation doesn't trigger
// any warning about `serializer` not being used, in case of empty enums.
@@ -161,7 +162,7 @@ struct DefaultTyParam<T: AssociatedType<X=i32> = i32> {
fn test_named_unit() {
assert_tokens(
&NamedUnit,
vec![Token::UnitStruct("NamedUnit")]
&[Token::UnitStruct("NamedUnit")]
);
}
@@ -192,7 +193,7 @@ fn test_ser_named_tuple() {
fn test_de_named_tuple() {
assert_de_tokens(
&DeNamedTuple(5, 6, 7),
vec![
&[
Token::SeqStart(Some(3)),
Token::SeqSep,
Token::I32(5),
@@ -209,7 +210,7 @@ fn test_de_named_tuple() {
assert_de_tokens(
&DeNamedTuple(5, 6, 7),
vec![
&[
Token::TupleStructStart("DeNamedTuple", Some(3)),
Token::TupleStructSep,
Token::I32(5),
@@ -265,7 +266,7 @@ fn test_de_named_map() {
b: 6,
c: 7,
},
vec![
&[
Token::StructStart("DeNamedMap", Some(3)),
Token::StructSep,
@@ -379,7 +380,7 @@ fn test_ser_enum_map() {
fn test_de_enum_unit() {
assert_tokens(
&DeEnum::Unit::<u32, u32, u32>,
vec![
&[
Token::EnumUnit("DeEnum", "Unit"),
],
);
@@ -403,7 +404,7 @@ fn test_de_enum_seq() {
e,
//f,
),
vec![
&[
Token::EnumSeqStart("DeEnum", "Seq", Some(4)),
Token::EnumSeqSep,
@@ -441,7 +442,7 @@ fn test_de_enum_map() {
e: e,
//f: f,
},
vec![
&[
Token::EnumMapStart("DeEnum", "Map", Some(4)),
Token::EnumMapSep,
@@ -516,7 +517,7 @@ fn test_lifetimes() {
fn test_generic_struct() {
assert_tokens(
&GenericStruct { x: 5u32 },
vec![
&[
Token::StructStart("GenericStruct", Some(1)),
Token::StructSep,
@@ -532,7 +533,7 @@ fn test_generic_struct() {
fn test_generic_newtype_struct() {
assert_tokens(
&GenericNewTypeStruct(5u32),
vec![
&[
Token::StructNewType("GenericNewTypeStruct"),
Token::U32(5),
]
@@ -543,7 +544,7 @@ fn test_generic_newtype_struct() {
fn test_generic_tuple_struct() {
assert_tokens(
&GenericTupleStruct(5u32, 6u32),
vec![
&[
Token::TupleStructStart("GenericTupleStruct", Some(2)),
Token::TupleStructSep,
@@ -561,7 +562,7 @@ fn test_generic_tuple_struct() {
fn test_generic_enum_unit() {
assert_tokens(
&GenericEnum::Unit::<u32, u32>,
vec![
&[
Token::EnumUnit("GenericEnum", "Unit"),
]
);
@@ -571,7 +572,7 @@ fn test_generic_enum_unit() {
fn test_generic_enum_newtype() {
assert_tokens(
&GenericEnum::NewType::<u32, u32>(5),
vec![
&[
Token::EnumNewType("GenericEnum", "NewType"),
Token::U32(5),
]
@@ -582,7 +583,7 @@ fn test_generic_enum_newtype() {
fn test_generic_enum_seq() {
assert_tokens(
&GenericEnum::Seq::<u32, u32>(5, 6),
vec![
&[
Token::EnumSeqStart("GenericEnum", "Seq", Some(2)),
Token::EnumSeqSep,
@@ -600,7 +601,7 @@ fn test_generic_enum_seq() {
fn test_generic_enum_map() {
assert_tokens(
&GenericEnum::Map::<u32, u32> { x: 5, y: 6 },
vec![
&[
Token::EnumMapStart("GenericEnum", "Map", Some(2)),
Token::EnumMapSep,
@@ -620,7 +621,7 @@ fn test_generic_enum_map() {
fn test_default_ty_param() {
assert_tokens(
&DefaultTyParam::<i32> { phantom: PhantomData },
vec![
&[
Token::StructStart("DefaultTyParam", Some(1)),
Token::StructSep,
@@ -1,9 +1,18 @@
use std::collections::BTreeMap;
use std::collections::{BTreeMap, HashMap, HashSet};
use std::net;
use std::path::{Path, PathBuf};
use std::str;
use token::{self, Token};
extern crate serde_test;
use self::serde_test::{
Error,
Token,
assert_ser_tokens,
assert_ser_tokens_error,
};
extern crate fnv;
use self::fnv::FnvHasher;
//////////////////////////////////////////////////////////////////////////
@@ -144,6 +153,24 @@ declare_ser_tests! {
Token::SeqEnd,
],
}
test_hashset {
HashSet::<isize>::new() => &[
Token::SeqStart(Some(0)),
Token::SeqEnd,
],
hashset![1] => &[
Token::SeqStart(Some(1)),
Token::SeqSep,
Token::I32(1),
Token::SeqEnd,
],
hashset![FnvHasher @ 1] => &[
Token::SeqStart(Some(1)),
Token::SeqSep,
Token::I32(1),
Token::SeqEnd,
],
}
test_tuple {
(1,) => &[
Token::TupleStart(1),
@@ -204,6 +231,26 @@ declare_ser_tests! {
Token::MapEnd,
],
}
test_hashmap {
HashMap::<isize, isize>::new() => &[
Token::MapStart(Some(0)),
Token::MapEnd,
],
hashmap![1 => 2] => &[
Token::MapStart(Some(1)),
Token::MapSep,
Token::I32(1),
Token::I32(2),
Token::MapEnd,
],
hashmap![FnvHasher @ 1 => 2] => &[
Token::MapStart(Some(1)),
Token::MapSep,
Token::I32(1),
Token::I32(2),
Token::MapEnd,
],
}
test_unit_struct {
UnitStruct => &[Token::UnitStruct("UnitStruct")],
}
@@ -314,16 +361,16 @@ fn test_cannot_serialize_paths() {
let path = unsafe {
str::from_utf8_unchecked(b"Hello \xF0\x90\x80World")
};
token::assert_ser_tokens_error(
assert_ser_tokens_error(
&Path::new(path),
&[Token::Str("Hello World")],
token::Error::InvalidValue("Path contains invalid UTF-8 characters".to_owned()));
&[],
Error::InvalidValue("Path contains invalid UTF-8 characters".to_owned()));
let mut path_buf = PathBuf::new();
path_buf.push(path);
token::assert_ser_tokens_error(
assert_ser_tokens_error(
&path_buf,
&[Token::Str("Hello World")],
token::Error::InvalidValue("Path contains invalid UTF-8 characters".to_owned()));
&[],
Error::InvalidValue("Path contains invalid UTF-8 characters".to_owned()));
}