зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1511811 - Revendor rust dependencies.
--HG-- rename : third_party/rust/darling_core/src/macros.rs => third_party/rust/darling_core/src/macros_private.rs
This commit is contained in:
Родитель
e648c4dee9
Коммит
5b5bf2110c
|
@ -181,7 +181,7 @@ dependencies = [
|
|||
name = "bench-collections-gtest"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
@ -677,32 +677,33 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.4.0"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"darling_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"darling_macro 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"darling_core 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"darling_macro 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.4.0"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ident_case 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.15.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.4.0"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"darling_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"darling_core 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.15.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -895,7 +896,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.5"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
|
@ -1102,7 +1103,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
dependencies = [
|
||||
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bytes 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"http 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"indexmap 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1125,7 +1126,7 @@ version = "0.1.10"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bytes 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
@ -1432,9 +1433,9 @@ dependencies = [
|
|||
name = "malloc_size_of_derive"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"synstructure 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.15.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2408,10 +2409,11 @@ dependencies = [
|
|||
name = "style_derive"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"darling 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"synstructure 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"darling 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.15.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2479,17 +2481,6 @@ dependencies = [
|
|||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.10.1"
|
||||
|
@ -3182,9 +3173,9 @@ dependencies = [
|
|||
"checksum cubeb-backend 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fdcac95519416d9ec814db2dc40e6293e7da25b906023d93f48b87f0587ab138"
|
||||
"checksum cubeb-core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "37f7b20f757a4e4b6aa28863236551bff77682dc6db192eba15af615492b5445"
|
||||
"checksum cubeb-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "653b9e245d35dbe2a2da7c4586275cee75ff656ddeb02d4a73b4afdfa6d67502"
|
||||
"checksum darling 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2a78af487e4eb8f4421a1770687b328af6bb4494ca93435210678c6eea875c11"
|
||||
"checksum darling_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b315f49c7b6db3708bca6e6913c194581a44ec619b7a39e131d4dd63733a3698"
|
||||
"checksum darling_macro 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eb69a38fdeaeaf3db712e1df170de67ee9dfc24fb88ca3e9d21e703ec25a4d8e"
|
||||
"checksum darling 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7f000e7b03a0083a30e1f10b1428a530849c21e72b338fa76869b5dbc4b045bf"
|
||||
"checksum darling_core 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "86bc5ce438f4b703755d12f59bbf0a16c642766d4534e922db47569dbdd0b998"
|
||||
"checksum darling_macro 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9973050ba46be2a2935a7b316147f41a808ac604b8f0fef6eba77fd47a89daeb"
|
||||
"checksum devd-rs 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e7c9ac481c38baf400d3b732e4a06850dfaa491d1b6379a249d9d40d14c2434c"
|
||||
"checksum diff 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "3c2b69f912779fbb121ceb775d74d51e915af17aaebc38d28a592843a2dd0a3a"
|
||||
"checksum digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "03b072242a8cbaf9c145665af9d250c59af3b958f83ed6824e13533cf76d5b90"
|
||||
|
@ -3205,7 +3196,7 @@ dependencies = [
|
|||
"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
|
||||
"checksum fixedbitset 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "85cb8fec437468d86dc7c83ca7cfc933341d561873275f22dd5eedefa63a6478"
|
||||
"checksum flate2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9fac2277e84e5e858483756647a9d0aa8d9a2b7cba517fd84325a0aaa69a0909"
|
||||
"checksum fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc484842f1e2884faf56f529f960cc12ad8c71ce96cc7abba0a067c98fee344"
|
||||
"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
|
||||
"checksum foreign-types 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5ebc04f19019fff1f2d627b5581574ead502f80c48c88900575a46e0840fe5d0"
|
||||
"checksum freetype 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b659e75b7a7338fe75afd7f909fc2b71937845cffb6ebe54ba2e50f13d8e903d"
|
||||
"checksum fs2 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"
|
||||
|
@ -3346,7 +3337,6 @@ dependencies = [
|
|||
"checksum syn 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4e4b5274d4a0a3d2749d5c158dc64d3403e60554dc61194648787ada5212473d"
|
||||
"checksum syn 0.15.7 (registry+https://github.com/rust-lang/crates.io-index)" = "455a6ec9b368f8c479b0ae5494d13b22dc00990d2f00d68c9dc6a2dc4f17f210"
|
||||
"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015"
|
||||
"checksum synstructure 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "98cad891cd238c98e1f0aec9f7c0f620aa696e4e5f7daba56ac67b5e86a6b049"
|
||||
"checksum target-lexicon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4af5e2227f0b887d591d3724b796a96eff04226104d872f5b3883fcd427d64b9"
|
||||
"checksum tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "87974a6f5c1dfb344d733055601650059a3363de2a6104819293baff662132d6"
|
||||
"checksum term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fa63644f74ce96fbeb9b794f66aff2a52d601cbd5e80f4b97123e3899f4570f1"
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"CHANGELOG.md":"d7322023ffc58d041c542f8de0f43167f34ba4fdacc9a5014308d97055f7b729","Cargo.toml":"c1d8f7f99c24eb373e5aefaf3c678eea57d72552fdbb3547872b62b0d28aa07f","LICENSE":"8ea93490d74a5a1b1af3ff71d786271b3f1e5f0bea79ac16e02ec533cef040d6","README.md":"7e05868f02bae698ee3193b86e86f25faa4dbc63661062e1d3f7bff590cfb313","examples/consume_fields.rs":"f32d3873b61e22b1ded500571ec7120420b4825ee7f807d07409e3a257407add","examples/fallible_read.rs":"4e2d558f8a77c4fffa79bde5a6c48df3dbc932822e7bc7cf0a903d1ea38b8d6f","publish.sh":"42795a155247c69402f8c4c917c9874a06dfde5a7606c8b59fc4e9ccd34233dd","src/lib.rs":"c13e19cd0363784d9ec3605bafcaf74648594fb419162143c4ecc3308a8ec695","tests/accrue_errors.rs":"7a995118cfa75ac90accf9a35e17b07a00e8528c8ccc5dd8700ba7b4c59393c1","tests/custom_bound.rs":"cca7c557ac0a2efd9554d31f0df9a98c4f6f01b9f211107846732cc1fe9f7856","tests/enums_newtype.rs":"328ebbbb4aa540f06f13158dff22cf9d98d45dba2331f75e4aa169d348397b76","tests/enums_struct.rs":"560a8bfdea9eca7f8c2024bc8740e71ef1a0681cea36b098ceadba520fad8640","tests/enums_unit.rs":"5e9458af9d695706e181b6622dcbc8b80b9eae33dcc1f15da7eecfd3e7037777","tests/error.rs":"69d12e969555fc1d72990d7596b54adcb469da95f1f257d0273f31bc9c855db5","tests/from_variant.rs":"af60c9dec64e80e2ac3beafb942d8edc1100a1342bb97378e6a35f878dd1fb50","tests/generics.rs":"3d884d65cb6b57d4bc4b3f7c39f321b2df3cd339fa15db5b66dc7c97ef84df17","tests/happy_path.rs":"5143dbf33b59fcab94be61affefddf485857f1a5cb3d45d5583463423d417cdf","tests/multiple.rs":"20e1c5110449db46df68c5a4cdb6e0c4c0e9a6b47451fe73f1523a1cf730196d","tests/newtype.rs":"a8709857e2067bc01b388a11230db5764c9e5fe2341c98d6c819adc01472b988","tests/skip.rs":"e34034c6b5fae80c8cf2caa762a41ab3d971f8af50f1022e68ba299218477892","tests/split_declaration.rs":"d55219ec0dce001ccd1975f0b4fbe0f5e8c5792a1ddf2de5a210d380bc6761e0","tests/supports.rs":"1131c2afd42e20e4a39f922758cbb8d7c5a0167ae41f9cd1cd14b40db055cd10"},"package":"2a78af487e4eb8f4421a1770687b328af6bb4494ca93435210678c6eea875c11"}
|
||||
{"files":{"CHANGELOG.md":"06091817afa317ff86efb9aee3fa8d165358150a92c6369e4ab2958765c14af8","Cargo.toml":"82b023db972c6554f198cc926eaca456f945d6ac2cdc4df65cdeb5b186a1ced0","LICENSE":"8ea93490d74a5a1b1af3ff71d786271b3f1e5f0bea79ac16e02ec533cef040d6","README.md":"f70d772dd28cd14fc817c71d343b07ec8bfbe02dfe8d3e999a0185c6ef8731ec","examples/automatic_bounds.rs":"2716da31a5beed954da77866779ef77600686688a8890fefa7e23db628358104","examples/consume_fields.rs":"abcdd6a919a95a5c295e8d362db0367ab171fe3c8a80b23d83701765cd902d2d","examples/fallible_read.rs":"e06d5c045d3a47c0e00d3c77ed3aa1a1d5173d2c3f462dc1967379db3094af4c","examples/supports_struct.rs":"f2d84a4377e555ed7535e563c9593bd6a398a63b42f7da72381eea2fccec229f","publish.sh":"42795a155247c69402f8c4c917c9874a06dfde5a7606c8b59fc4e9ccd34233dd","src/lib.rs":"07da20edb52c4aba30f2b680b28ca96cf995859566b0db3b1d2e0ee967249a18","src/macros_public.rs":"3ed7eb99f309d9cd600d3a09ff4dcf5cc5d787fb49e8e5ead6bb00e31e5e6793","tests/accrue_errors.rs":"f873acf2a13dba88f508c3eb118d14e36515bf94b7ee914ddec2ae1eb55e381f","tests/computed_bound.rs":"2313da96a250b5948ca06bf86cb7158b55a59eba75334aa8ba27a46b28ede6b5","tests/custom_bound.rs":"4dd3e8fd76734d8f262e857773f53a820229ad6e10fe6fbbbe1f636c7da9eab0","tests/defaults.rs":"b544be90d18be26305a757e8468bf8735356889b59c167294d118e32ee7f82ea","tests/enums_newtype.rs":"723c761eab356f577e804a5e07f5efa244f70fb7525d20fceb1bc3852c8d742e","tests/enums_struct.rs":"e0097d7f947cf9e4612c9450c55ea5075c7f6fcbbf8cac7176449d92eebc9343","tests/enums_unit.rs":"8c075d5897b60b58cb71d6306e7fdd80851f5a147ae3010ba70ea100007364a3","tests/error.rs":"f989a43fad47d61d75da2bc78af6c51bac549f39f14f87a804eea226308ece32","tests/from_generics.rs":"79230ad21e8482cf6e6ceb0c74303bc0cdf77fbb951f46a6ba1006ecf1248fd5","tests/from_type_param.rs":"4369737a9d4c21cfa8595a9159cd5d98d02af3445d8b4085654ad4ce8e8bba3f","tests/from_type_param_default.rs":"a5ee72f22540004e755445c37dd4c425ef12beef496896d608695bae964afe7f","tests/from_variant.rs":"2e804326302a62b979eae963e88f0a2cdb6a21ee9667407c361d178f8c0aadba","tests/generics.rs":"3c0f43b39a8f319b8318e409d3836c7235892ae4f50f5986ea80f69b80a4080b","tests/happy_path.rs":"13cf6cd5f87920d90762fd08e45d9f0bb378563e1c92cf5ffb3ba78decdc4969","tests/multiple.rs":"b76172c396a89414b1669eec2bf854c79a7215fd3b6977123b81591ac6761670","tests/newtype.rs":"c45842fd764174a6a8177851e15445d344ab93bf2124b5f77213780d1306adea","tests/skip.rs":"fd936a5eff92c0a49b0a900e6fcda6b7a10a83dd4096ae2000201ccb3f66a738","tests/split_declaration.rs":"b191ed446bf7b8fdf1d41059354adb4b155523507f37b0d36aed3347617a2492","tests/supports.rs":"acb53d4d810dcd3e46763db13234e4fb42e425cfde2516d9bf5d7b690be72dcc"},"package":"7f000e7b03a0083a30e1f10b1428a530849c21e72b338fa76869b5dbc4b045bf"}
|
|
@ -1,7 +1,37 @@
|
|||
# Changelog
|
||||
|
||||
## Unreleased Features
|
||||
_None_
|
||||
## v0.8.0
|
||||
- Update dependency on `syn` to 0.15 [#44](https://github.com/darling/pull/44). Thanks to @hcpl
|
||||
|
||||
## v0.7.0 (July 24, 2018)
|
||||
- Update dependencies on `syn` and `proc-macro2`
|
||||
- Add `util::IdentString`, which acts as an Ident or its string equivalent
|
||||
|
||||
## v0.6.3 (May 22, 2018)
|
||||
- Add support for `Uses*` traits in where predicates
|
||||
|
||||
## v0.6.2 (May 22, 2018)
|
||||
- Add `usage` module for tracking type param and lifetime usage in generic declarations
|
||||
- Add `UsesTypeParams` and `CollectsTypeParams` traits [#37](https://github.com/darling/issues/37)
|
||||
- Add `UsesLifetimes` and `CollectLifetimes` traits [#41](https://github.com/darling/pull/41)
|
||||
- Don't add `FromMeta` bounds to type parameters only used by skipped fields [#40](https://github.com/darling/pull/40)
|
||||
|
||||
## v0.6.1 (May 17, 2018)
|
||||
- Fix an issue where the `syn` update broke shape validation [#36](https://github.com/TedDriggs/darling/issues/36)
|
||||
|
||||
## v0.6.0 (May 15, 2018)
|
||||
|
||||
### Breaking Changes
|
||||
- Renamed `FromMetaItem` to `FromMeta`, and renamed `from_meta_item` method to `from_meta`
|
||||
- Added dedicated `derive(FromMetaItem)` which panics and redirects users to `FromMeta`
|
||||
|
||||
## v0.5.0 (May 10, 2018)
|
||||
- Add `ast::Generics` and `ast::GenericParam` to work with generics in a manner similar to `ast::Data`
|
||||
- Add `ast::GenericParamExt` to support alternate representations of generic parameters
|
||||
- Add `util::WithOriginal` to get a parsed representation and syn's own struct for a syntax block
|
||||
- Add `FromGenerics` and `FromGenericParam` traits (without derive support)
|
||||
- Change generated code for `generics` magic field to invoke `FromGenerics` trait during parsing
|
||||
- Add `FromTypeParam` trait [#30](https://github.com/TedDriggs/darling/pull/30). Thanks to @upsuper
|
||||
|
||||
## v0.4.0 (April 5, 2018)
|
||||
- Update dependencies on `proc-macro`, `quote`, and `syn` [#26](https://github.com/TedDriggs/darling/pull/26). Thanks to @hcpl
|
||||
|
@ -36,4 +66,4 @@ _None_
|
|||
- Added support for returning multiple errors from parsing [#5](https://github.com/TedDriggs/darling/pull/5)
|
||||
- Derived impls no longer return on first error [#5](https://github.com/TedDriggs/darling/pull/5)
|
||||
- Removed default types for `V` and `F` from `ast::Body`
|
||||
- Enum variants are automatically converted to snake_case [#12](https://github.com/TedDriggs/darling/pull/12)
|
||||
- Enum variants are automatically converted to snake_case [#12](https://github.com/TedDriggs/darling/pull/12)
|
||||
|
|
|
@ -12,22 +12,25 @@
|
|||
|
||||
[package]
|
||||
name = "darling"
|
||||
version = "0.4.0"
|
||||
version = "0.8.0"
|
||||
authors = ["Ted Driggs <ted.driggs@outlook.com>"]
|
||||
description = "A proc-macro library for reading attributes into structs when\nimplementing custom derives.\n"
|
||||
documentation = "https://docs.rs/darling/0.4.0"
|
||||
documentation = "https://docs.rs/darling/0.8.0"
|
||||
readme = "README.md"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/TedDriggs/darling"
|
||||
[dependencies.darling_core]
|
||||
version = "=0.4.0"
|
||||
version = "=0.8.0"
|
||||
|
||||
[dependencies.darling_macro]
|
||||
version = "=0.4.0"
|
||||
version = "=0.8.0"
|
||||
[dev-dependencies.proc-macro2]
|
||||
version = "0.4"
|
||||
|
||||
[dev-dependencies.quote]
|
||||
version = "0.5"
|
||||
version = "0.6"
|
||||
|
||||
[dev-dependencies.syn]
|
||||
version = "0.13"
|
||||
version = "0.15"
|
||||
[badges.travis-ci]
|
||||
repository = "TedDriggs/darling"
|
||||
|
|
|
@ -9,7 +9,7 @@ Darling
|
|||
# Usage
|
||||
`darling` provides a set of traits which can be derived or manually implemented.
|
||||
|
||||
1. `FromMetaItem` is used to extract values from a meta-item in an attribute. Implementations are likely reusable for many libraries, much like `FromStr` or `serde::Deserialize`. Trait implementations are provided for primitives, some std types, and some `syn` types.
|
||||
1. `FromMeta` is used to extract values from a meta-item in an attribute. Implementations are likely reusable for many libraries, much like `FromStr` or `serde::Deserialize`. Trait implementations are provided for primitives, some std types, and some `syn` types.
|
||||
1. `FromDeriveInput` is implemented or derived by each proc-macro crate which depends on `darling`. This is the root for input parsing; it gets access to the identity, generics, and visibility of the target type, and can specify which attribute names should be parsed or forwarded from the input AST.
|
||||
1. `FromField` is implemented or derived by each proc-macro crate which depends on `darling`. Structs deriving this trait will get access to the identity (if it exists), type, and visibility of the field.
|
||||
|
||||
|
@ -20,7 +20,7 @@ Darling
|
|||
extern crate darling;
|
||||
extern crate syn;
|
||||
|
||||
#[derive(Default, FromMetaItem)]
|
||||
#[derive(Default, FromMeta)]
|
||||
#[darling(default)]
|
||||
pub struct Lorem {
|
||||
#[darling(rename = "sit")]
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
#[macro_use]
|
||||
extern crate darling;
|
||||
|
||||
extern crate syn;
|
||||
|
||||
use darling::FromDeriveInput;
|
||||
|
||||
#[derive(FromMeta, PartialEq, Eq, Debug)]
|
||||
enum Volume {
|
||||
Whisper,
|
||||
Talk,
|
||||
Shout,
|
||||
}
|
||||
|
||||
/// A more complex example showing the ability to skip at a field or struct
|
||||
/// level while still tracking which type parameters need to be bounded.
|
||||
/// This can be seen by expanding this example using `cargo expand`.
|
||||
#[derive(FromMeta)]
|
||||
#[allow(dead_code)]
|
||||
enum Emphasis<T> {
|
||||
Constant(Volume),
|
||||
Variable(darling::util::IdentList),
|
||||
#[darling(skip)]
|
||||
PerPhoneme(Option<T>),
|
||||
Strided {
|
||||
#[darling(skip)]
|
||||
step: Vec<T>,
|
||||
#[darling(multiple)]
|
||||
volume: Vec<Volume>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(FromDeriveInput)]
|
||||
#[darling(attributes(speak))]
|
||||
struct SpeakingOptions<T, U> {
|
||||
max_volume: U,
|
||||
#[darling(skip, default)]
|
||||
additional_data: Vec<T>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Phoneme {
|
||||
#[allow(dead_code)]
|
||||
first: String,
|
||||
}
|
||||
|
||||
// This is probably the holy grail for `darling`'s own internal use-case:
|
||||
// Auto-apply `Default` bound to skipped *field* types in `where` clause.
|
||||
impl<T, U> Default for SpeakingOptions<T, U>
|
||||
where
|
||||
Vec<T>: Default,
|
||||
U: Default,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
max_volume: Default::default(),
|
||||
additional_data: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let derive_input = syn::parse_str(
|
||||
r#"
|
||||
#[derive(Speak)]
|
||||
#[speak(max_volume = "shout")]
|
||||
enum HtmlElement {
|
||||
Div(String)
|
||||
}
|
||||
"#,
|
||||
).unwrap();
|
||||
|
||||
let parsed: SpeakingOptions<Phoneme, Volume> =
|
||||
FromDeriveInput::from_derive_input(&derive_input).unwrap();
|
||||
assert_eq!(parsed.max_volume, Volume::Shout);
|
||||
assert_eq!(parsed.additional_data.len(), 0);
|
||||
}
|
|
@ -2,19 +2,20 @@
|
|||
|
||||
#[macro_use]
|
||||
extern crate darling;
|
||||
|
||||
extern crate proc_macro2;
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
extern crate syn;
|
||||
|
||||
use darling::ast;
|
||||
use darling::FromDeriveInput;
|
||||
use quote::{Tokens, ToTokens};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::ToTokens;
|
||||
use syn::parse_str;
|
||||
|
||||
/// A speaking volume. Deriving `FromMeta` will cause this to be usable
|
||||
/// as a string value for a meta-item key.
|
||||
#[derive(Debug, Clone, Copy, FromMetaItem)]
|
||||
#[derive(Debug, Clone, Copy, FromMeta)]
|
||||
#[darling(default)]
|
||||
enum Volume {
|
||||
Normal,
|
||||
|
@ -28,7 +29,7 @@ impl Default for Volume {
|
|||
}
|
||||
}
|
||||
|
||||
/// Support parsing from a full derive input. Unlike FromMetaItem, this isn't
|
||||
/// Support parsing from a full derive input. Unlike FromMeta, this isn't
|
||||
/// composable; each darling-dependent crate should have its own struct to handle
|
||||
/// when its trait is derived.
|
||||
#[derive(Debug, FromDeriveInput)]
|
||||
|
@ -54,7 +55,7 @@ struct MyInputReceiver {
|
|||
}
|
||||
|
||||
impl ToTokens for MyInputReceiver {
|
||||
fn to_tokens(&self, tokens: &mut Tokens) {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let MyInputReceiver {
|
||||
ref ident,
|
||||
ref generics,
|
||||
|
@ -78,11 +79,10 @@ impl ToTokens for MyInputReceiver {
|
|||
// unfortunately.
|
||||
format!(
|
||||
"{} = {{}}",
|
||||
f.ident.as_ref().map(|v| format!("{}", v)).unwrap_or_else(
|
||||
|| {
|
||||
format!("{}", i)
|
||||
},
|
||||
)
|
||||
f.ident
|
||||
.as_ref()
|
||||
.map(|v| format!("{}", v))
|
||||
.unwrap_or_else(|| format!("{}", i))
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
|
@ -97,7 +97,10 @@ impl ToTokens for MyInputReceiver {
|
|||
|
||||
// This works with named or indexed fields, so we'll fall back to the index so we can
|
||||
// write the output as a key-value pair.
|
||||
let field_ident = f.ident.as_ref().map(|v| quote!(#v)).unwrap_or_else(|| quote!(#i));
|
||||
let field_ident = f.ident
|
||||
.as_ref()
|
||||
.map(|v| quote!(#v))
|
||||
.unwrap_or_else(|| quote!(#i));
|
||||
|
||||
match field_volume {
|
||||
Volume::Normal => quote!(self.#field_ident),
|
||||
|
@ -111,7 +114,7 @@ impl ToTokens for MyInputReceiver {
|
|||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
tokens.append_all(quote! {
|
||||
tokens.extend(quote! {
|
||||
impl #imp Speak for #ident #ty #wher {
|
||||
fn speak(&self, writer: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
write!(#fmt_string, #(#field_list),*)
|
||||
|
@ -152,7 +155,8 @@ pub struct Foo {
|
|||
let receiver = MyInputReceiver::from_derive_input(&parsed).unwrap();
|
||||
let tokens = quote!(#receiver);
|
||||
|
||||
println!(r#"
|
||||
println!(
|
||||
r#"
|
||||
INPUT:
|
||||
|
||||
{}
|
||||
|
@ -164,5 +168,7 @@ PARSED AS:
|
|||
EMITS:
|
||||
|
||||
{}
|
||||
"#, input, receiver, tokens);
|
||||
"#,
|
||||
input, receiver, tokens
|
||||
);
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ extern crate darling;
|
|||
|
||||
extern crate syn;
|
||||
|
||||
use darling::{FromDeriveInput, FromMetaItem};
|
||||
use darling::{FromDeriveInput, FromMeta};
|
||||
use syn::parse_str;
|
||||
|
||||
#[derive(Debug, FromDeriveInput)]
|
||||
|
@ -36,14 +36,18 @@ impl MyInputReceiver {
|
|||
/// skipped fields or to perform corrections that don't lend themselves to being
|
||||
/// done elsewhere.
|
||||
fn autocorrect(self) -> Self {
|
||||
let Self { name, frequency, amplitude } = self;
|
||||
let Self {
|
||||
name,
|
||||
frequency,
|
||||
amplitude,
|
||||
} = self;
|
||||
|
||||
// Amplitude doesn't have a sign, so if we received a negative number then
|
||||
// we'll go ahead and make it positive.
|
||||
let amplitude = match amplitude {
|
||||
Ok(amp) => amp,
|
||||
Err(mi) => {
|
||||
let val: i64 = if let Ok(v) = FromMetaItem::from_meta_item(&mi) {
|
||||
let val: i64 = if let Ok(v) = FromMeta::from_meta(&mi) {
|
||||
v
|
||||
} else {
|
||||
panic!(format!("amplitude should have been an integer"))
|
||||
|
@ -69,7 +73,8 @@ pub struct Foo;"#;
|
|||
let parsed = parse_str(input).unwrap();
|
||||
let receiver = MyInputReceiver::from_derive_input(&parsed).unwrap();
|
||||
|
||||
println!(r#"
|
||||
println!(
|
||||
r#"
|
||||
INPUT:
|
||||
|
||||
{}
|
||||
|
@ -77,5 +82,7 @@ INPUT:
|
|||
PARSED AS:
|
||||
|
||||
{:?}
|
||||
"#, input, receiver);
|
||||
"#,
|
||||
input, receiver
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
#[macro_use]
|
||||
extern crate darling;
|
||||
|
||||
extern crate syn;
|
||||
|
||||
use darling::{ast, util, FromDeriveInput};
|
||||
use syn::{Ident, Type};
|
||||
|
||||
#[derive(Debug, FromField)]
|
||||
#[darling(attributes(lorem))]
|
||||
pub struct LoremField {
|
||||
ident: Option<Ident>,
|
||||
ty: Type,
|
||||
#[darling(default)]
|
||||
skip: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, FromDeriveInput)]
|
||||
#[darling(attributes(lorem), supports(struct_named))]
|
||||
pub struct Lorem {
|
||||
ident: Ident,
|
||||
data: ast::Data<util::Ignored, LoremField>,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let good_input = r#"#[derive(Lorem)]
|
||||
pub struct Foo {
|
||||
#[lorem(skip)]
|
||||
bar: bool,
|
||||
|
||||
baz: i64,
|
||||
}"#;
|
||||
|
||||
let bad_input = r#"#[derive(Lorem)]
|
||||
pub struct BadFoo(String, u32);"#;
|
||||
|
||||
let parsed = syn::parse_str(good_input).unwrap();
|
||||
let receiver = Lorem::from_derive_input(&parsed).unwrap();
|
||||
let wrong_shape_parsed = syn::parse_str(bad_input).unwrap();
|
||||
let wrong_shape = Lorem::from_derive_input(&wrong_shape_parsed)
|
||||
.expect_err("Shape was wrong");
|
||||
|
||||
println!(
|
||||
r#"
|
||||
INPUT:
|
||||
|
||||
{}
|
||||
|
||||
PARSED AS:
|
||||
|
||||
{:?}
|
||||
|
||||
BAD INPUT:
|
||||
|
||||
{}
|
||||
|
||||
PRODUCED ERROR:
|
||||
|
||||
{}
|
||||
"#,
|
||||
good_input, receiver, bad_input, wrong_shape
|
||||
);
|
||||
}
|
|
@ -4,12 +4,12 @@
|
|||
//!
|
||||
//! ## Design
|
||||
//! Darling takes considerable design inspiration from [`serde`]. A data structure that can be
|
||||
//! read from any attribute implements `FromMetaItem` (or has an implementation automatically
|
||||
//! generated using `derive`). Any crate can provide `FromMetaItem` implementations, even one not
|
||||
//! read from any attribute implements `FromMeta` (or has an implementation automatically
|
||||
//! generated using `derive`). Any crate can provide `FromMeta` implementations, even one not
|
||||
//! specifically geared towards proc-macro authors.
|
||||
//!
|
||||
//! Proc-macro crates should provide their own structs which implement or derive `FromDeriveInput` and
|
||||
//! `FromField` to gather settings relevant to their operation.
|
||||
//! Proc-macro crates should provide their own structs which implement or derive `FromDeriveInput`,
|
||||
//! `FromField`, `FromVariant`, `FromGenerics`, _et alia_ to gather settings relevant to their operation.
|
||||
//!
|
||||
//! ## Attributes
|
||||
//! There are a number of attributes that `darling` exposes to enable finer-grained control over the code
|
||||
|
@ -24,10 +24,10 @@
|
|||
//! `Default::default()` for their value, but you can override that with an explicit default or a value from the type-level default.
|
||||
//!
|
||||
//! ## Forwarded Fields
|
||||
//! The traits `FromDeriveInput` and `FromField` support forwarding fields from the input AST directly
|
||||
//! to the derived struct. These fields are matched up by identifier **before** `rename` attribute values are
|
||||
//! considered. The deriving struct is responsible for making sure the types of fields it does declare match this
|
||||
//! table.
|
||||
//! All derivable traits except `FromMeta` support forwarding some fields from the input AST to the derived struct.
|
||||
//! These fields are matched up by identifier **before** `rename` attribute values are considered,
|
||||
//! allowing you to use their names for your own properties.
|
||||
//! The deriving struct is responsible for making sure the types of fields it chooses to declare are compatible with this table.
|
||||
//!
|
||||
//! A deriving struct is free to include or exclude any of the fields below.
|
||||
//!
|
||||
|
@ -36,8 +36,8 @@
|
|||
//! |---|---|---|
|
||||
//! |`ident`|`syn::Ident`|The identifier of the passed-in type|
|
||||
//! |`vis`|`syn::Visibility`|The visibility of the passed-in type|
|
||||
//! |`generics`|`syn::Generics`|The generics of the passed-in type|
|
||||
//! |`body`|`darling::ast::Data`|The body of the passed-in type|
|
||||
//! |`generics`|`T: darling::FromGenerics`|The generics of the passed-in type. This can be `syn::Generics`, `darling::ast::Generics`, or any compatible type.|
|
||||
//! |`data`|`darling::ast::Data`|The body of the passed-in type|
|
||||
//! |`attrs`|`Vec<syn::Attribute>`|The forwarded attributes from the passed in type. These are controlled using the `forward_attrs` attribute.|
|
||||
//!
|
||||
//! ### `FromField`
|
||||
|
@ -47,6 +47,14 @@
|
|||
//! |`vis`|`syn::Visibility`|The visibility of the passed-in field|
|
||||
//! |`ty`|`syn::Type`|The type of the passed-in field|
|
||||
//! |`attrs`|`Vec<syn::Attribute>`|The forwarded attributes from the passed in field. These are controlled using the `forward_attrs` attribute.|
|
||||
//!
|
||||
//! ### `FromTypeParam`
|
||||
//! |Field name|Type|Meaning|
|
||||
//! |---|---|---|
|
||||
//! |`ident`|`syn::Ident`|The identifier of the passed-in type param|
|
||||
//! |`bounds`|`Vec<syn::TypeParamBound>`|The bounds applied to the type param|
|
||||
//! |`default`|`Option<syn::Type>`|The default type of the parameter, if one exists|
|
||||
//! |`attrs`|`Vec<syn::Attribute>`|The forwarded attributes from the passed in type param. These are controlled using the `forward_attrs` attribute.|
|
||||
|
||||
extern crate core;
|
||||
extern crate darling_core;
|
||||
|
@ -59,22 +67,32 @@ extern crate darling_macro;
|
|||
pub use darling_macro::*;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use darling_core::{FromMetaItem, FromDeriveInput, FromField, FromVariant};
|
||||
pub use darling_core::{FromDeriveInput, FromField, FromGenericParam, FromGenerics, FromMeta,
|
||||
FromTypeParam, FromVariant};
|
||||
|
||||
#[doc(inline)]
|
||||
pub use darling_core::{Result, Error};
|
||||
pub use darling_core::{Error, Result};
|
||||
|
||||
#[doc(inline)]
|
||||
pub use darling_core::{ast, error, util};
|
||||
pub use darling_core::{ast, error, usage, util};
|
||||
|
||||
// XXX exported so that `ExtractAttribute::extractor` can convert a path into tokens.
|
||||
// This is likely to change in the future, so only generated code should depend on this export.
|
||||
#[doc(hidden)]
|
||||
pub use darling_core::ToTokens;
|
||||
|
||||
/// Core/std trait re-exports. This should help produce generated code which doesn't
|
||||
/// depend on `std` unnecessarily, and avoids problems caused by aliasing `std` or any
|
||||
/// of the referenced types.
|
||||
#[doc(hidden)]
|
||||
pub mod export {
|
||||
pub use ::core::convert::From;
|
||||
pub use ::core::option::Option::{self, Some, None};
|
||||
pub use ::core::result::Result::{self, Ok, Err};
|
||||
pub use ::core::default::Default;
|
||||
pub use ::std::vec::Vec;
|
||||
pub mod export {
|
||||
pub use core::convert::From;
|
||||
pub use core::default::Default;
|
||||
pub use core::option::Option::{self, None, Some};
|
||||
pub use core::result::Result::{self, Err, Ok};
|
||||
pub use std::vec::Vec;
|
||||
pub use std::string::ToString;
|
||||
}
|
||||
|
||||
#[macro_use]
|
||||
mod macros_public;
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
//! Macros that should be exported from both `darling_core` and `darling`.
|
||||
//! Note that these are **sym-linked** into the main code, and so cannot declare on items that are exported differently
|
||||
//! in `darling_core` vs. `darling`.
|
||||
|
||||
/// Generator for `UsesTypeParam` impls that unions the used type parameters of the selected fields.
|
||||
///
|
||||
/// # Usage
|
||||
/// The macro takes the type implementing the trait as the first argument, then a comma-separated list of
|
||||
/// fields for the rest of its arguments.
|
||||
///
|
||||
/// The type of each passed-in field must implement `UsesTypeParams`, or the resulting code won't compile.
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate syn;
|
||||
/// #
|
||||
/// # #[macro_use]
|
||||
/// # extern crate darling_core;
|
||||
/// #
|
||||
/// struct MyField {
|
||||
/// ty: syn::Type,
|
||||
/// }
|
||||
///
|
||||
/// uses_type_params!(MyField, ty);
|
||||
///
|
||||
/// fn main() {
|
||||
/// // no test run
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// `darling` cannot derive this trait automatically, as it doesn't know which information extracted from
|
||||
/// proc-macro input is meant to constitute "using" the type parameter, but crate consumers should
|
||||
/// implement it by hand or using the macro.
|
||||
#[macro_export]
|
||||
macro_rules! uses_type_params {
|
||||
($impl_type:ty, $accessor:ident) => {
|
||||
impl $crate::usage::UsesTypeParams for $impl_type {
|
||||
fn uses_type_params<'gen>(
|
||||
&self,
|
||||
options: &$crate::usage::Options,
|
||||
type_set: &'gen $crate::usage::IdentSet
|
||||
) -> $crate::usage::IdentRefSet<'gen> {
|
||||
self.$accessor.uses_type_params(options, type_set)
|
||||
}
|
||||
}
|
||||
};
|
||||
($impl_type:ty, $first:ident, $($field:ident),+) => {
|
||||
impl $crate::usage::UsesTypeParams for $impl_type {
|
||||
fn uses_type_params<'gen>(
|
||||
&self,
|
||||
options: &$crate::usage::Options,
|
||||
type_set: &'gen $crate::usage::IdentSet
|
||||
) -> $crate::usage::IdentRefSet<'gen> {
|
||||
let mut hits = self.$first.uses_type_params(options, type_set);
|
||||
$(
|
||||
hits.extend(self.$field.uses_type_params(options, type_set));
|
||||
)*
|
||||
hits
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Generator for `UsesLifetimes` impls that unions the used lifetimes of the selected fields.
|
||||
///
|
||||
/// # Usage
|
||||
/// The macro takes the type implementing the trait as the first argument, then a comma-separated list of
|
||||
/// fields for the rest of its arguments.
|
||||
///
|
||||
/// The type of each passed-in field must implement `UsesLifetimes`, or the resulting code won't compile.
|
||||
#[macro_export]
|
||||
macro_rules! uses_lifetimes {
|
||||
($impl_type:ty, $accessor:ident) => {
|
||||
impl $crate::usage::UsesLifetimes for $impl_type {
|
||||
fn uses_lifetimes<'gen>(
|
||||
&self,
|
||||
options: &$crate::usage::Options,
|
||||
type_set: &'gen $crate::usage::LifetimeSet
|
||||
) -> $crate::usage::LifetimeRefSet<'gen> {
|
||||
self.$accessor.uses_lifetimes(options, type_set)
|
||||
}
|
||||
}
|
||||
};
|
||||
($impl_type:ty, $first:ident, $($field:ident),+) => {
|
||||
impl $crate::usage::UsesLifetimes for $impl_type {
|
||||
fn uses_lifetimes<'gen>(
|
||||
&self,
|
||||
options: &$crate::usage::Options,
|
||||
type_set: &'gen $crate::usage::LifetimeSet
|
||||
) -> $crate::usage::LifetimeRefSet<'gen> {
|
||||
let mut hits = self.$first.uses_lifetimes(options, type_set);
|
||||
$(
|
||||
hits.extend(self.$field.uses_lifetimes(options, type_set));
|
||||
)*
|
||||
hits
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
|
@ -16,9 +16,9 @@ struct Lorem {
|
|||
data: ast::Data<(), LoremField>,
|
||||
}
|
||||
|
||||
#[derive(Debug, FromMetaItem)]
|
||||
#[derive(Debug, FromMeta)]
|
||||
struct Dolor {
|
||||
sit: bool
|
||||
sit: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, FromField)]
|
||||
|
@ -30,12 +30,14 @@ struct LoremField {
|
|||
|
||||
#[test]
|
||||
fn bad_type_and_missing_fields() {
|
||||
let input = syn::parse_str(r#"
|
||||
let input = syn::parse_str(
|
||||
r#"
|
||||
#[accrue(ipsum = true, dolor(amet = "Hi"))]
|
||||
pub struct NonConforming {
|
||||
foo: ()
|
||||
}
|
||||
"#).unwrap();
|
||||
"#,
|
||||
).unwrap();
|
||||
|
||||
let s_result: ::darling::Error = Lorem::from_derive_input(&input).unwrap_err();
|
||||
//assert_eq!(3, s_result.len());
|
||||
|
@ -46,44 +48,45 @@ fn bad_type_and_missing_fields() {
|
|||
|
||||
#[test]
|
||||
fn body_only_issues() {
|
||||
let input = syn::parse_str(r#"
|
||||
let input = syn::parse_str(
|
||||
r#"
|
||||
#[accrue(ipsum = "Hello", dolor(sit))]
|
||||
pub struct NonConforming {
|
||||
foo: (),
|
||||
bar: bool,
|
||||
}
|
||||
"#).unwrap();
|
||||
"#,
|
||||
).unwrap();
|
||||
|
||||
let s_err: ::darling::Error = Lorem::from_derive_input(&input).unwrap_err();
|
||||
println!("{:?}", s_err);
|
||||
assert_eq!(2, s_err.len());
|
||||
}
|
||||
|
||||
#[derive(Debug, FromMetaItem)]
|
||||
#[derive(Debug, FromMeta)]
|
||||
enum Week {
|
||||
Monday,
|
||||
Tuesday {
|
||||
morning: bool,
|
||||
afternoon: String,
|
||||
},
|
||||
Tuesday { morning: bool, afternoon: String },
|
||||
Wednesday(Dolor),
|
||||
}
|
||||
|
||||
#[derive(Debug, FromDeriveInput)]
|
||||
#[darling(attributes(accrue))]
|
||||
struct Month {
|
||||
schedule: Week
|
||||
schedule: Week,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_in_enum_fields() {
|
||||
let input = syn::parse_str(r#"
|
||||
let input = syn::parse_str(
|
||||
r#"
|
||||
#[accrue(schedule(tuesday(morning = "yes")))]
|
||||
pub struct NonConforming {
|
||||
foo: (),
|
||||
bar: bool,
|
||||
}
|
||||
"#).unwrap();
|
||||
"#,
|
||||
).unwrap();
|
||||
|
||||
let s_err: ::darling::Error = Month::from_derive_input(&input).unwrap_err();
|
||||
assert_eq!(2, s_err.len());
|
||||
|
@ -94,13 +97,15 @@ fn error_in_enum_fields() {
|
|||
|
||||
#[test]
|
||||
fn error_in_newtype_variant() {
|
||||
let input = syn::parse_str(r#"
|
||||
let input = syn::parse_str(
|
||||
r#"
|
||||
#[accrue(schedule(wednesday(sit = "yes")))]
|
||||
pub struct NonConforming {
|
||||
foo: (),
|
||||
bar: bool,
|
||||
}
|
||||
"#).unwrap();
|
||||
"#,
|
||||
).unwrap();
|
||||
|
||||
let s_err: ::darling::Error = Month::from_derive_input(&input).unwrap_err();
|
||||
assert_eq!(1, s_err.len());
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
#[macro_use]
|
||||
extern crate darling;
|
||||
extern crate syn;
|
||||
|
||||
use darling::FromDeriveInput;
|
||||
|
||||
fn parse<T: FromDeriveInput>(src: &str) -> T {
|
||||
let ast = syn::parse_str(src).unwrap();
|
||||
FromDeriveInput::from_derive_input(&ast).unwrap()
|
||||
}
|
||||
|
||||
#[derive(FromMeta, PartialEq, Eq, Debug)]
|
||||
enum Volume {
|
||||
Whisper,
|
||||
Talk,
|
||||
Shout,
|
||||
}
|
||||
|
||||
#[derive(FromDeriveInput)]
|
||||
#[darling(attributes(speak))]
|
||||
struct SpeakingOptions<T: Default, U> {
|
||||
max_volume: U,
|
||||
#[darling(skip)]
|
||||
#[allow(dead_code)]
|
||||
additional_data: T,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Phoneme {
|
||||
#[allow(dead_code)]
|
||||
first: String,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn skipped_field() {
|
||||
let parsed: SpeakingOptions<Phoneme, Volume> = parse(
|
||||
r#"
|
||||
#[derive(Speak)]
|
||||
#[speak(max_volume = "shout")]
|
||||
enum HtmlElement {
|
||||
Div(String)
|
||||
}
|
||||
"#,
|
||||
);
|
||||
assert_eq!(parsed.max_volume, Volume::Shout);
|
||||
}
|
|
@ -4,8 +4,8 @@ extern crate syn;
|
|||
|
||||
use std::ops::Add;
|
||||
|
||||
#[derive(Debug, Clone, FromMetaItem)]
|
||||
#[darling(bound = "T: FromMetaItem + Add")]
|
||||
#[derive(Debug, Clone, FromMeta)]
|
||||
#[darling(bound = "T: FromMeta + Add")]
|
||||
struct Wrapper<T>(pub T);
|
||||
|
||||
impl<T: Add> Add for Wrapper<T> {
|
||||
|
@ -16,7 +16,7 @@ impl<T: Add> Add for Wrapper<T> {
|
|||
}
|
||||
|
||||
#[derive(Debug, FromDeriveInput)]
|
||||
#[darling(attributes(hello), bound = "Wrapper<T>: Add, T: FromMetaItem")]
|
||||
#[darling(attributes(hello), bound = "Wrapper<T>: Add, T: FromMeta")]
|
||||
struct Foo<T> {
|
||||
lorem: Wrapper<T>,
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
#[macro_use]
|
||||
extern crate darling;
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
#[macro_use]
|
||||
extern crate syn;
|
||||
|
||||
use darling::FromDeriveInput;
|
||||
|
||||
mod foo {
|
||||
pub mod bar {
|
||||
pub fn init() -> String {
|
||||
String::from("hello")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromDeriveInput)]
|
||||
#[darling(attributes(speak))]
|
||||
pub struct SpeakerOpts {
|
||||
#[darling(default="foo::bar::init")]
|
||||
first_word: String,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn path_default() {
|
||||
let speaker: SpeakerOpts = FromDeriveInput::from_derive_input(&parse_quote! {
|
||||
struct Foo;
|
||||
}).expect("Unit struct with no attrs should parse");
|
||||
|
||||
assert_eq!(speaker.first_word, "hello");
|
||||
}
|
|
@ -5,15 +5,15 @@ use darling::FromDeriveInput;
|
|||
|
||||
extern crate syn;
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq, FromMetaItem)]
|
||||
#[derive(Debug, Default, PartialEq, Eq, FromMeta)]
|
||||
#[darling(default)]
|
||||
pub struct Amet {
|
||||
hello: bool,
|
||||
world: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, FromMetaItem)]
|
||||
#[darling(rename_all="snake_case")]
|
||||
#[derive(Debug, PartialEq, Eq, FromMeta)]
|
||||
#[darling(rename_all = "snake_case")]
|
||||
pub enum Lorem {
|
||||
Ipsum(bool),
|
||||
Dolor(String),
|
||||
|
@ -34,10 +34,12 @@ impl PartialEq<Lorem> for Holder {
|
|||
|
||||
#[test]
|
||||
fn bool_word() {
|
||||
let di = syn::parse_str(r#"
|
||||
let di = syn::parse_str(
|
||||
r#"
|
||||
#[hello(lorem(ipsum))]
|
||||
pub struct Bar;
|
||||
"#).unwrap();
|
||||
"#,
|
||||
).unwrap();
|
||||
|
||||
let pr = Holder::from_derive_input(&di).unwrap();
|
||||
assert_eq!(pr, Lorem::Ipsum(true));
|
||||
|
@ -45,10 +47,12 @@ fn bool_word() {
|
|||
|
||||
#[test]
|
||||
fn bool_literal() {
|
||||
let di = syn::parse_str(r#"
|
||||
let di = syn::parse_str(
|
||||
r#"
|
||||
#[hello(lorem(ipsum = false))]
|
||||
pub struct Bar;
|
||||
"#).unwrap();
|
||||
"#,
|
||||
).unwrap();
|
||||
|
||||
let pr = Holder::from_derive_input(&di).unwrap();
|
||||
assert_eq!(pr, Lorem::Ipsum(false));
|
||||
|
@ -56,10 +60,12 @@ fn bool_literal() {
|
|||
|
||||
#[test]
|
||||
fn string_literal() {
|
||||
let di = syn::parse_str(r#"
|
||||
let di = syn::parse_str(
|
||||
r#"
|
||||
#[hello(lorem(dolor = "Hello"))]
|
||||
pub struct Bar;
|
||||
"#).unwrap();
|
||||
"#,
|
||||
).unwrap();
|
||||
|
||||
let pr = Holder::from_derive_input(&di).unwrap();
|
||||
assert_eq!(pr, Lorem::Dolor("Hello".to_string()));
|
||||
|
@ -67,25 +73,32 @@ fn string_literal() {
|
|||
|
||||
#[test]
|
||||
fn struct_nested() {
|
||||
let di = syn::parse_str(r#"
|
||||
let di = syn::parse_str(
|
||||
r#"
|
||||
#[hello(lorem(sit(world = "Hello", hello = false)))]
|
||||
pub struct Bar;
|
||||
"#).unwrap();
|
||||
"#,
|
||||
).unwrap();
|
||||
|
||||
let pr = Holder::from_derive_input(&di).unwrap();
|
||||
assert_eq!(pr, Lorem::Sit(Amet {
|
||||
hello: false,
|
||||
world: "Hello".to_string(),
|
||||
}));
|
||||
assert_eq!(
|
||||
pr,
|
||||
Lorem::Sit(Amet {
|
||||
hello: false,
|
||||
world: "Hello".to_string(),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn format_mismatch() {
|
||||
let di = syn::parse_str(r#"
|
||||
let di = syn::parse_str(
|
||||
r#"
|
||||
#[hello(lorem(dolor(world = "Hello", hello = false)))]
|
||||
pub struct Bar;
|
||||
"#).unwrap();
|
||||
"#,
|
||||
).unwrap();
|
||||
|
||||
Holder::from_derive_input(&di).unwrap();
|
||||
}
|
||||
|
|
|
@ -4,20 +4,13 @@
|
|||
extern crate darling;
|
||||
extern crate syn;
|
||||
|
||||
#[derive(Debug, FromMetaItem)]
|
||||
#[darling(rename_all="snake_case")]
|
||||
#[derive(Debug, FromMeta)]
|
||||
#[darling(rename_all = "snake_case")]
|
||||
enum Message {
|
||||
Hello {
|
||||
user: String,
|
||||
silent: bool
|
||||
},
|
||||
Hello { user: String, silent: bool },
|
||||
Ping,
|
||||
Goodbye {
|
||||
user: String,
|
||||
}
|
||||
Goodbye { user: String },
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expansion() {
|
||||
|
||||
}
|
||||
fn expansion() {}
|
||||
|
|
|
@ -4,15 +4,13 @@
|
|||
extern crate darling;
|
||||
extern crate syn;
|
||||
|
||||
#[derive(Debug, FromMetaItem)]
|
||||
#[darling(rename_all="snake_case")]
|
||||
#[derive(Debug, FromMeta)]
|
||||
#[darling(rename_all = "snake_case")]
|
||||
enum Pattern {
|
||||
Owned,
|
||||
Immutable,
|
||||
Mutable
|
||||
Mutable,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expansion() {
|
||||
|
||||
}
|
||||
fn expansion() {}
|
||||
|
|
|
@ -5,7 +5,7 @@ extern crate syn;
|
|||
|
||||
use darling::FromDeriveInput;
|
||||
|
||||
#[derive(Debug, FromMetaItem)]
|
||||
#[derive(Debug, FromMeta)]
|
||||
struct Dolor {
|
||||
#[darling(rename = "amet")]
|
||||
sit: bool,
|
||||
|
@ -33,20 +33,24 @@ impl From<syn::Ident> for Lorem {
|
|||
|
||||
#[test]
|
||||
fn parsing_fail() {
|
||||
let di = syn::parse_str(r#"
|
||||
let di = syn::parse_str(
|
||||
r#"
|
||||
#[hello(ipsum(amet = "yes", world = false))]
|
||||
pub struct Foo;
|
||||
"#).unwrap();
|
||||
"#,
|
||||
).unwrap();
|
||||
|
||||
println!("{}", Lorem::from_derive_input(&di).unwrap_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_field() {
|
||||
let di = syn::parse_str(r#"
|
||||
let di = syn::parse_str(
|
||||
r#"
|
||||
#[hello(ipsum(amet = true))]
|
||||
pub struct Foo;
|
||||
"#).unwrap();
|
||||
"#,
|
||||
).unwrap();
|
||||
|
||||
println!("{}", Lorem::from_derive_input(&di).unwrap_err());
|
||||
}
|
||||
|
|
|
@ -0,0 +1,186 @@
|
|||
//! Tests for `FromGenerics`, and - indirectly - `FromGenericParam`.
|
||||
//! These tests assume `FromTypeParam` is working and only look at whether the wrappers for magic
|
||||
//! fields are working as expected.
|
||||
|
||||
#[macro_use]
|
||||
extern crate darling;
|
||||
extern crate syn;
|
||||
|
||||
use darling::ast::{self, GenericParamExt};
|
||||
use darling::util::{Ignored, WithOriginal};
|
||||
use darling::{FromDeriveInput, Result};
|
||||
|
||||
#[derive(FromDeriveInput)]
|
||||
#[darling(attributes(lorem))]
|
||||
struct MyReceiver {
|
||||
pub ident: syn::Ident,
|
||||
pub generics: ast::Generics<ast::GenericParam<MyTypeParam>>,
|
||||
}
|
||||
|
||||
#[derive(FromTypeParam)]
|
||||
#[darling(attributes(lorem))]
|
||||
struct MyTypeParam {
|
||||
pub ident: syn::Ident,
|
||||
#[darling(default)]
|
||||
pub foo: bool,
|
||||
#[darling(default)]
|
||||
pub bar: Option<String>,
|
||||
}
|
||||
|
||||
fn fdi<T: FromDeriveInput>(src: &str) -> Result<T> {
|
||||
FromDeriveInput::from_derive_input(&syn::parse_str(src).expect("Source parses"))
|
||||
}
|
||||
|
||||
/// Verify that `ast::Generics` is populated correctly when there is no generics declaration
|
||||
#[test]
|
||||
fn no_generics() {
|
||||
let rec: MyReceiver = fdi("struct Baz;").expect("Input is well-formed");
|
||||
assert!(rec.generics.where_clause.is_none());
|
||||
assert_eq!(rec.generics.params.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expand_some() {
|
||||
let rec: MyReceiver = fdi(r#"
|
||||
struct Baz<
|
||||
'a,
|
||||
#[lorem(foo)] T,
|
||||
#[lorem(bar = "x")] U: Eq + ?Sized
|
||||
>(&'a T, U);
|
||||
"#)
|
||||
.expect("Input is well-formed");
|
||||
assert!(rec.generics.where_clause.is_none());
|
||||
|
||||
// Make sure we've preserved the lifetime def, though we don't do anything with it.
|
||||
assert!(rec.generics.params[0].as_lifetime_def().is_some());
|
||||
|
||||
let mut ty_param_iter = rec.generics.type_params();
|
||||
|
||||
let first = ty_param_iter
|
||||
.next()
|
||||
.expect("type_params should not be empty");
|
||||
assert!(first.bar.is_none());
|
||||
assert!(first.foo);
|
||||
assert_eq!(first.ident, "T");
|
||||
|
||||
let second = ty_param_iter
|
||||
.next()
|
||||
.expect("type_params should have a second value");
|
||||
assert_eq!(
|
||||
second
|
||||
.bar
|
||||
.as_ref()
|
||||
.expect("Second type param should set bar"),
|
||||
"x"
|
||||
);
|
||||
assert_eq!(second.foo, false);
|
||||
assert_eq!(second.ident, "U");
|
||||
}
|
||||
|
||||
/// Verify ≤0.4.1 behavior - where `generics` had to be `syn::Generics` - keeps working.
|
||||
#[test]
|
||||
fn passthrough() {
|
||||
#[derive(FromDeriveInput)]
|
||||
struct PassthroughReceiver {
|
||||
pub generics: syn::Generics,
|
||||
}
|
||||
|
||||
let rec: PassthroughReceiver = fdi(r#"
|
||||
struct Baz<
|
||||
'a,
|
||||
#[lorem(foo)] T,
|
||||
#[lorem(bar = "x")] U: Eq + ?Sized
|
||||
>(&'a T, U);
|
||||
"#)
|
||||
.expect("Input is well-formed");
|
||||
|
||||
let mut type_param_iter = rec.generics.type_params();
|
||||
assert!(type_param_iter.next().is_some());
|
||||
}
|
||||
|
||||
/// Verify that `where_clause` is passed through when it exists.
|
||||
/// As of 0.4.1, there is no `FromWhereClause` trait, so other types aren't supported
|
||||
/// for that field.
|
||||
#[test]
|
||||
fn where_clause() {
|
||||
let rec: MyReceiver = fdi(r#"
|
||||
struct Baz<
|
||||
'a,
|
||||
#[lorem(foo)] T,
|
||||
#[lorem(bar = "x")] U: Eq + ?Sized
|
||||
>(&'a T, U) where T: Into<String>;
|
||||
"#)
|
||||
.expect("Input is well-formed");
|
||||
|
||||
assert!(rec.generics.where_clause.is_some());
|
||||
}
|
||||
|
||||
/// Test that `WithOriginal` works for generics.
|
||||
#[test]
|
||||
fn with_original() {
|
||||
#[derive(FromDeriveInput)]
|
||||
struct WorigReceiver {
|
||||
generics: WithOriginal<ast::Generics<ast::GenericParam<MyTypeParam>>, syn::Generics>,
|
||||
}
|
||||
|
||||
let rec: WorigReceiver = fdi(r#"
|
||||
struct Baz<
|
||||
'a,
|
||||
#[lorem(foo)] T,
|
||||
#[lorem(bar = "x")] U: Eq + ?Sized
|
||||
>(&'a T, U) where T: Into<String>;
|
||||
"#)
|
||||
.expect("Input is well-formed");
|
||||
|
||||
// Make sure we haven't lost anything in the conversion
|
||||
assert_eq!(rec.generics.parsed.params.len(), 3);
|
||||
assert_eq!(
|
||||
rec.generics
|
||||
.original
|
||||
.params
|
||||
.iter()
|
||||
.collect::<Vec<_>>()
|
||||
.len(),
|
||||
3
|
||||
);
|
||||
|
||||
let parsed_t: &MyTypeParam = rec.generics.parsed.params[1]
|
||||
.as_type_param()
|
||||
.expect("Second argument should be type param");
|
||||
|
||||
// Make sure the first type param in each case is T
|
||||
assert_eq!(parsed_t.ident, "T");
|
||||
assert_eq!(
|
||||
rec.generics
|
||||
.original
|
||||
.type_params()
|
||||
.next()
|
||||
.expect("First type param should exist")
|
||||
.ident,
|
||||
"T"
|
||||
);
|
||||
|
||||
// Make sure we actually parsed the first type param
|
||||
assert!(parsed_t.foo);
|
||||
assert!(parsed_t.bar.is_none());
|
||||
}
|
||||
|
||||
/// Make sure generics can be ignored
|
||||
#[test]
|
||||
fn ignored() {
|
||||
#[derive(FromDeriveInput)]
|
||||
struct IgnoredReceiver {
|
||||
generics: Ignored,
|
||||
}
|
||||
|
||||
let rec: IgnoredReceiver = fdi(r#"
|
||||
struct Baz<
|
||||
'a,
|
||||
#[lorem(foo)] T,
|
||||
#[lorem(bar = "x")] U: Eq + ?Sized
|
||||
>(&'a T, U) where T: Into<String>;
|
||||
"#)
|
||||
.expect("Input is well-formed");
|
||||
|
||||
assert_eq!(Ignored, rec.generics);
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
#[macro_use]
|
||||
extern crate darling;
|
||||
extern crate syn;
|
||||
|
||||
use darling::FromTypeParam;
|
||||
use syn::{DeriveInput, GenericParam, Ident, TypeParam};
|
||||
|
||||
#[darling(attributes(lorem), from_ident)]
|
||||
#[derive(FromTypeParam)]
|
||||
struct Lorem {
|
||||
ident: Ident,
|
||||
bounds: Vec<syn::TypeParamBound>,
|
||||
foo: bool,
|
||||
bar: Option<String>,
|
||||
}
|
||||
|
||||
impl From<Ident> for Lorem {
|
||||
fn from(ident: Ident) -> Self {
|
||||
Lorem {
|
||||
ident,
|
||||
foo: false,
|
||||
bar: None,
|
||||
bounds: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_type(param: &GenericParam) -> &TypeParam {
|
||||
match *param {
|
||||
GenericParam::Type(ref ty) => ty,
|
||||
_ => unreachable!("Not a type param"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expand_many() {
|
||||
let di: DeriveInput = syn::parse_str(
|
||||
r#"
|
||||
struct Baz<
|
||||
#[lorem(foo)] T,
|
||||
#[lorem(bar = "x")] U: Eq + ?Sized
|
||||
>(T, U);
|
||||
"#,
|
||||
).unwrap();
|
||||
let params = di.generics.params;
|
||||
|
||||
{
|
||||
let ty = extract_type(¶ms[0]);
|
||||
let lorem = Lorem::from_type_param(ty).unwrap();
|
||||
assert_eq!(lorem.ident, "T");
|
||||
assert_eq!(lorem.foo, true);
|
||||
assert_eq!(lorem.bar, None);
|
||||
}
|
||||
|
||||
{
|
||||
let ty = extract_type(¶ms[1]);
|
||||
let lorem = Lorem::from_type_param(ty).unwrap();
|
||||
assert_eq!(lorem.ident, "U");
|
||||
assert_eq!(lorem.foo, false);
|
||||
assert_eq!(lorem.bar, Some("x".to_string()));
|
||||
assert_eq!(lorem.bounds.len(), 2);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
#[macro_use]
|
||||
extern crate darling;
|
||||
extern crate syn;
|
||||
|
||||
use darling::FromTypeParam;
|
||||
use syn::{DeriveInput, GenericParam, TypeParam};
|
||||
|
||||
#[darling(attributes(lorem), default)]
|
||||
#[derive(Default, FromTypeParam)]
|
||||
struct Lorem {
|
||||
foo: bool,
|
||||
bar: Option<String>,
|
||||
default: Option<syn::Type>,
|
||||
}
|
||||
|
||||
fn extract_type(param: &GenericParam) -> &TypeParam {
|
||||
match *param {
|
||||
GenericParam::Type(ref ty) => ty,
|
||||
_ => unreachable!("Not a type param"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expand_many() {
|
||||
let di: DeriveInput = syn::parse_str(
|
||||
r#"
|
||||
struct Baz<
|
||||
#[lorem(foo)] T,
|
||||
#[lorem(bar = "x")] U: Eq + ?Sized,
|
||||
#[lorem(foo = false)] V = (),
|
||||
>(T, U, V);
|
||||
"#,
|
||||
).unwrap();
|
||||
let params = di.generics.params;
|
||||
|
||||
{
|
||||
let ty = extract_type(¶ms[0]);
|
||||
let lorem = Lorem::from_type_param(ty).unwrap();
|
||||
assert_eq!(lorem.foo, true);
|
||||
assert_eq!(lorem.bar, None);
|
||||
}
|
||||
|
||||
{
|
||||
let ty = extract_type(¶ms[1]);
|
||||
let lorem = Lorem::from_type_param(ty).unwrap();
|
||||
assert_eq!(lorem.foo, false);
|
||||
assert_eq!(lorem.bar, Some("x".to_string()));
|
||||
assert!(lorem.default.is_none());
|
||||
}
|
||||
|
||||
{
|
||||
let ty = extract_type(¶ms[2]);
|
||||
let lorem = Lorem::from_type_param(ty).unwrap();
|
||||
assert_eq!(lorem.foo, false);
|
||||
assert_eq!(lorem.bar, None);
|
||||
assert!(lorem.default.is_some());
|
||||
}
|
||||
}
|
|
@ -24,6 +24,4 @@ impl From<syn::Ident> for Lorem {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn expansion() {
|
||||
|
||||
}
|
||||
fn expansion() {}
|
||||
|
|
|
@ -4,7 +4,7 @@ extern crate syn;
|
|||
|
||||
use darling::FromDeriveInput;
|
||||
|
||||
#[derive(Debug, Clone, FromMetaItem)]
|
||||
#[derive(Debug, Clone, FromMeta)]
|
||||
struct Wrapper<T>(pub T);
|
||||
|
||||
#[derive(Debug, FromDeriveInput)]
|
||||
|
@ -15,11 +15,12 @@ struct Foo<T> {
|
|||
|
||||
#[test]
|
||||
fn expansion() {
|
||||
let di = syn::parse_str(r#"
|
||||
let di = syn::parse_str(
|
||||
r#"
|
||||
#[hello(lorem = "Hello")]
|
||||
pub struct Foo;
|
||||
"#)
|
||||
.unwrap();
|
||||
"#,
|
||||
).unwrap();
|
||||
|
||||
let _parsed = Foo::<String>::from_derive_input(&di).unwrap();
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ extern crate quote;
|
|||
|
||||
use darling::FromDeriveInput;
|
||||
|
||||
#[derive(Default, FromMetaItem, PartialEq, Debug)]
|
||||
#[derive(Default, FromMeta, PartialEq, Debug)]
|
||||
#[darling(default)]
|
||||
struct Lorem {
|
||||
ipsum: bool,
|
||||
|
@ -21,7 +21,7 @@ struct Core {
|
|||
ident: syn::Ident,
|
||||
vis: syn::Visibility,
|
||||
generics: syn::Generics,
|
||||
lorem: Lorem
|
||||
lorem: Lorem,
|
||||
}
|
||||
|
||||
#[derive(FromDeriveInput, PartialEq, Debug)]
|
||||
|
@ -34,37 +34,47 @@ struct TraitCore {
|
|||
|
||||
#[test]
|
||||
fn simple() {
|
||||
let di = syn::parse_str(r#"
|
||||
let di = syn::parse_str(
|
||||
r#"
|
||||
#[derive(Foo)]
|
||||
#[darling_demo(lorem(ipsum))]
|
||||
pub struct Bar;
|
||||
"#).unwrap();
|
||||
"#,
|
||||
).unwrap();
|
||||
|
||||
assert_eq!(Core::from_derive_input(&di).unwrap(), Core {
|
||||
ident: syn::Ident::from("Bar"),
|
||||
vis: parse_quote!(pub),
|
||||
generics: Default::default(),
|
||||
lorem: Lorem {
|
||||
ipsum: true,
|
||||
dolor: None,
|
||||
assert_eq!(
|
||||
Core::from_derive_input(&di).unwrap(),
|
||||
Core {
|
||||
ident: parse_quote!(Bar),
|
||||
vis: parse_quote!(pub),
|
||||
generics: Default::default(),
|
||||
lorem: Lorem {
|
||||
ipsum: true,
|
||||
dolor: None,
|
||||
},
|
||||
}
|
||||
});
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trait_type() {
|
||||
let di = syn::parse_str(r#"
|
||||
let di = syn::parse_str(
|
||||
r#"
|
||||
#[derive(Foo)]
|
||||
#[darling_demo(lorem(dolor = "hello"))]
|
||||
pub struct Bar;
|
||||
"#).unwrap();
|
||||
"#,
|
||||
).unwrap();
|
||||
|
||||
assert_eq!(TraitCore::from_derive_input(&di).unwrap(), TraitCore {
|
||||
ident: syn::Ident::from("Bar"),
|
||||
generics: Default::default(),
|
||||
lorem: Lorem {
|
||||
ipsum: false,
|
||||
dolor: Some("hello".to_owned()),
|
||||
assert_eq!(
|
||||
TraitCore::from_derive_input(&di).unwrap(),
|
||||
TraitCore {
|
||||
ident: parse_quote!(Bar),
|
||||
generics: Default::default(),
|
||||
lorem: Lorem {
|
||||
ipsum: false,
|
||||
dolor: Some("hello".to_owned()),
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ struct Lorem {
|
|||
ipsum: Ipsum,
|
||||
}
|
||||
|
||||
#[derive(FromMetaItem)]
|
||||
#[derive(FromMeta)]
|
||||
struct Ipsum {
|
||||
#[darling(multiple)]
|
||||
dolor: Vec<String>,
|
||||
|
@ -20,11 +20,16 @@ struct Ipsum {
|
|||
|
||||
#[test]
|
||||
fn expand_many() {
|
||||
let di = syn::parse_str(r#"
|
||||
let di = syn::parse_str(
|
||||
r#"
|
||||
#[hello(ipsum(dolor = "Hello", dolor = "World"))]
|
||||
pub struct Baz;
|
||||
"#).unwrap();
|
||||
"#,
|
||||
).unwrap();
|
||||
|
||||
let lorem: Lorem = Lorem::from_derive_input(&di).unwrap();
|
||||
assert_eq!(lorem.ipsum.dolor, vec!["Hello".to_string(), "World".to_string()]);
|
||||
assert_eq!(
|
||||
lorem.ipsum.dolor,
|
||||
vec!["Hello".to_string(), "World".to_string()]
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! A newtype struct should be able to derive `FromMetaItem` if its member implements it.
|
||||
//! A newtype struct should be able to derive `FromMeta` if its member implements it.
|
||||
|
||||
#[macro_use]
|
||||
extern crate darling;
|
||||
|
@ -7,22 +7,24 @@ extern crate syn;
|
|||
|
||||
use darling::FromDeriveInput;
|
||||
|
||||
#[derive(Debug, FromMetaItem, PartialEq, Eq)]
|
||||
#[derive(Debug, FromMeta, PartialEq, Eq)]
|
||||
struct Lorem(bool);
|
||||
|
||||
#[derive(Debug, FromDeriveInput)]
|
||||
#[darling(attributes(newtype))]
|
||||
struct DemoContainer {
|
||||
lorem: Lorem
|
||||
lorem: Lorem,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generated() {
|
||||
let di = syn::parse_str(r#"
|
||||
let di = syn::parse_str(
|
||||
r#"
|
||||
#[derive(Baz)]
|
||||
#[newtype(lorem = false)]
|
||||
pub struct Foo;
|
||||
"#).unwrap();
|
||||
"#,
|
||||
).unwrap();
|
||||
|
||||
let c = DemoContainer::from_derive_input(&di).unwrap();
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ pub struct Lorem {
|
|||
}
|
||||
|
||||
/// Verify variant-level and field-level skip work correctly for enums.
|
||||
#[derive(Debug, FromMetaItem)]
|
||||
#[derive(Debug, FromMeta)]
|
||||
pub enum Sit {
|
||||
Amet(bool),
|
||||
|
||||
|
@ -28,19 +28,24 @@ pub enum Sit {
|
|||
Bar {
|
||||
hello: bool,
|
||||
#[darling(skip)]
|
||||
world: u8
|
||||
}
|
||||
world: u8,
|
||||
},
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_skipped_field_not_required() {
|
||||
let di = syn::parse_str(r#"
|
||||
let di = syn::parse_str(
|
||||
r#"
|
||||
#[skip_test(ipsum = "Hello")]
|
||||
struct Baz;
|
||||
"#).unwrap();
|
||||
"#,
|
||||
).unwrap();
|
||||
|
||||
assert_eq!(Lorem::from_derive_input(&di).unwrap(), Lorem {
|
||||
ipsum: "Hello".to_string(),
|
||||
dolor: 0,
|
||||
});
|
||||
assert_eq!(
|
||||
Lorem::from_derive_input(&di).unwrap(),
|
||||
Lorem {
|
||||
ipsum: "Hello".to_string(),
|
||||
dolor: 0,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -18,44 +18,62 @@ struct Lorem {
|
|||
|
||||
#[test]
|
||||
fn split_attributes_accrue_to_instance() {
|
||||
let di = syn::parse_str(r#"
|
||||
let di = syn::parse_str(
|
||||
r#"
|
||||
#[split(foo = "Hello")]
|
||||
#[split(bar)]
|
||||
pub struct Foo;
|
||||
"#).unwrap();
|
||||
"#,
|
||||
).unwrap();
|
||||
|
||||
let parsed = Lorem::from_derive_input(&di).unwrap();
|
||||
assert_eq!(parsed, Lorem {
|
||||
foo: "Hello".to_string(),
|
||||
bar: true,
|
||||
});
|
||||
assert_eq!(
|
||||
parsed,
|
||||
Lorem {
|
||||
foo: "Hello".to_string(),
|
||||
bar: true,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn duplicates_across_split_attrs_error() {
|
||||
let di = syn::parse_str(r#"
|
||||
let di = syn::parse_str(
|
||||
r#"
|
||||
#[split(foo = "Hello")]
|
||||
#[split(foo = "World", bar)]
|
||||
pub struct Foo;
|
||||
"#).unwrap();
|
||||
"#,
|
||||
).unwrap();
|
||||
|
||||
let pr = Lorem::from_derive_input(&di);
|
||||
assert_eq!(pr.unwrap_err().to_string(), Error::duplicate_field("foo").to_string());
|
||||
assert_eq!(
|
||||
pr.unwrap_err().to_string(),
|
||||
Error::duplicate_field("foo").to_string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_errors_accrue_to_instance() {
|
||||
let di = syn::parse_str(r#"
|
||||
let di = syn::parse_str(
|
||||
r#"
|
||||
#[split(foo = "Hello")]
|
||||
#[split(foo = "World")]
|
||||
pub struct Foo;
|
||||
"#).unwrap();
|
||||
"#,
|
||||
).unwrap();
|
||||
|
||||
let pr = Lorem::from_derive_input(&di);
|
||||
let err: Error = pr.unwrap_err();
|
||||
assert_eq!(2, err.len());
|
||||
let mut errs = err.into_iter();
|
||||
assert_eq!(errs.next().unwrap().to_string(), Error::duplicate_field("foo").to_string());
|
||||
assert_eq!(errs.next().unwrap().to_string(), Error::missing_field("bar").to_string());
|
||||
assert_eq!(
|
||||
errs.next().unwrap().to_string(),
|
||||
Error::duplicate_field("foo").to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
errs.next().unwrap().to_string(),
|
||||
Error::missing_field("bar").to_string()
|
||||
);
|
||||
assert!(errs.next().is_none());
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ extern crate syn;
|
|||
use darling::ast;
|
||||
use darling::FromDeriveInput;
|
||||
|
||||
#[derive(Debug,FromDeriveInput)]
|
||||
#[derive(Debug, FromDeriveInput)]
|
||||
#[darling(attributes(from_variants), supports(enum_any))]
|
||||
pub struct Container {
|
||||
data: ast::Data<Variant, ()>,
|
||||
|
@ -18,28 +18,71 @@ pub struct Variant {
|
|||
skip: Option<bool>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expansion() {
|
||||
let di = syn::parse_str(r#"
|
||||
#[derive(Debug, FromDeriveInput)]
|
||||
#[darling(attributes(from_struct), supports(struct_named))]
|
||||
pub struct StructContainer {
|
||||
data: ast::Data<(), syn::Field>,
|
||||
}
|
||||
|
||||
mod source {
|
||||
use syn::{self, DeriveInput};
|
||||
|
||||
pub fn newtype_enum() -> DeriveInput {
|
||||
syn::parse_str(
|
||||
r#"
|
||||
enum Hello {
|
||||
World(bool),
|
||||
String(String),
|
||||
}
|
||||
"#).unwrap();
|
||||
"#,
|
||||
).unwrap()
|
||||
}
|
||||
|
||||
Container::from_derive_input(&di).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unsupported_shape() {
|
||||
let di = syn::parse_str(r#"
|
||||
pub fn named_field_enum() -> DeriveInput {
|
||||
syn::parse_str(
|
||||
r#"
|
||||
enum Hello {
|
||||
Foo(u16),
|
||||
World {
|
||||
name: String
|
||||
},
|
||||
}
|
||||
"#).unwrap();
|
||||
"#,
|
||||
).unwrap()
|
||||
}
|
||||
|
||||
Container::from_derive_input(&di).unwrap_err();
|
||||
pub fn named_struct() -> DeriveInput {
|
||||
syn::parse_str(
|
||||
r#"
|
||||
struct Hello {
|
||||
world: bool,
|
||||
}
|
||||
"#,
|
||||
).unwrap()
|
||||
}
|
||||
|
||||
pub fn tuple_struct() -> DeriveInput {
|
||||
syn::parse_str("struct Hello(String, bool);").unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn enum_newtype_or_unit() {
|
||||
// Should pass
|
||||
Container::from_derive_input(&source::newtype_enum()).unwrap();
|
||||
|
||||
// Should error
|
||||
Container::from_derive_input(&source::named_field_enum()).unwrap_err();
|
||||
Container::from_derive_input(&source::named_struct()).unwrap_err();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn struct_named() {
|
||||
// Should pass
|
||||
StructContainer::from_derive_input(&source::named_struct()).unwrap();
|
||||
|
||||
// Should fail
|
||||
StructContainer::from_derive_input(&source::tuple_struct()).unwrap_err();
|
||||
StructContainer::from_derive_input(&source::named_field_enum()).unwrap_err();
|
||||
StructContainer::from_derive_input(&source::newtype_enum()).unwrap_err();
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"06561e115990be3f278ed12704d0eb575c971242cbdfe8ebb32a8132280e054e","src/ast.rs":"2538b41e2a579c0a5a49e02f911120ffff39d48dfc0d80570a5fcfe95c971794","src/codegen/default_expr.rs":"28d750fb5ed3a6344007bf545c48e4d9a15c175209903d4009efc0de257adf2e","src/codegen/error.rs":"2a1bde9a20c664f26c6a0017e35ddf82885a31b8be42a628ea5549013b1eab44","src/codegen/field.rs":"ad8355c7bb87269c7dcc1d27695b0f8de410b546625d33d5a219fbadf85f8230","src/codegen/fmi_impl.rs":"89a66b24d7527989dd90ca71d9409fd8cdcf3a659fa1a670448032a4b384e83c","src/codegen/from_derive_impl.rs":"36507c9eddd354a50f96cd28e737c914be494c83ae61202b533524a9d90a2ca9","src/codegen/from_field.rs":"586866442f6628fd055f139b018a8c5c13e3aea20954ec741517aa9ab731c163","src/codegen/from_variant_impl.rs":"d42ecd82d3159aa7ee89ed81ed355c927dea9df2a298cf1db0c486699b77eac2","src/codegen/mod.rs":"46cdb1b4a76eb2e56f01e2c9e2879aed9b1c21ecbed42575a2eeccabf446a27a","src/codegen/outer_from_impl.rs":"2314c1594bd63e682ebd4a4b4954b2b9f16aa50b1422c05568bce97ae29f9727","src/codegen/trait_impl.rs":"715ce9dcb82d855e9dd8f2a70599bc3c5328acde70c92b7db5bd4c276598a7d0","src/codegen/variant.rs":"294045aefcfcb54e8b9536d6d91251a46115271869578b3212ae36ae76883b18","src/codegen/variant_data.rs":"efdee90d7e9acce39672024502ceb0616bc162d11e59d255fcbcd23f00f80806","src/error.rs":"55f33c960123799e1ccb27676d780751c0b62d1f52ccb9a2ac69cea4acfe55db","src/from_derive_input.rs":"ea55cc1b2bc17495d36fb616f7cd7aa78e4b74ea7c8554eed9d392ee853700c3","src/from_field.rs":"b42c2fc037aebdd55f195d722ba20075d3e095f03e89890a0d37d406d6419d87","src/from_meta_item.rs":"996ccec9dca998ff41f65bb346e5cc75952af5d61339c6951bebdbf8db1212c5","src/from_variant.rs":"2baeb09c8a95ff606d9d5ca8992f10bbad9c4925590332af1a8b5bdae54ebae8","src/lib.rs":"58b910cecc1f1962c2d6059db384f065099547c34631d9ddcc35099db8e16405","src/macros.rs":"ef249cd9ca593aac423b4242df1c39c31610438da094c21562d74a7e5823c700","src/options/core.rs":"689067ee0901714e53caeef5d5634c4bc02f52ff06e3ff286410eecaca665734","src/options/forward_attrs.rs":"35a83a4ae695872413d964d9050e35a0075c8386c286d291b1ecf1779f9ba8a3","src/options/from_derive.rs":"502e18c3d9f90d7a4cebc8c6b60181ab6068958a0ba2e70fe645528dee34b231","src/options/from_field.rs":"7222be5e62026184169f12adb08403abc89d66c53e678f8d8b43afaeceed9e4f","src/options/from_meta_item.rs":"cbc2d747e9e35e0d68b26c9f1592914bb4924cac01a6cdaf9137f643a72b551a","src/options/from_variant.rs":"6f8538da3fb61e614552839ee32bc479d33b5227d7f9d9b357d8d05146b96dac","src/options/input_field.rs":"6d43c2907694c4187e9f182f7945fc769ce210cde8eb1b4a336dea2a7fce3710","src/options/input_variant.rs":"2fc064fb87a73542a012a31aa5fd9702cf58b52a1bf37dabbfa7fb2e758ff9cc","src/options/mod.rs":"ceefde4d1dba9b5f3822f667c34a6eb963e5a899973475456bfe7939177f0e19","src/options/outer_from.rs":"3125aad9f8c033727fd3ef4ef1e1881c77fa52463f78342c408bf135e8533037","src/options/shape.rs":"118af560da80a46d6e3f8980c3d9b4863319d224a8b2985520901bfea0eba531","src/util/ident_list.rs":"11b5008380ace89d5745cdd83b73a2841c5382f05d3a8942ba998a7e4d6abb31","src/util/ignored.rs":"66e2e3201e17e8fffe2f249a4327b8178a20304624a47c0149fe8dd5e05d187c","src/util/mod.rs":"0c9ee0ba8ec03ca654fd298bd0d82588f224e3743227e6cba2beba4ab2f4dee4","src/util/over_ride.rs":"f63637ff73b3f377a4b1c38714a0f108b98ff40a96dd3ffbebb1e4ecc7523813"},"package":"b315f49c7b6db3708bca6e6913c194581a44ec619b7a39e131d4dd63733a3698"}
|
||||
{"files":{"Cargo.toml":"f7061460abe53907c290c7975f49077a6c698e4acc9034d3e904c63a224dbd21","src/ast/data.rs":"c95e469d870f7918e77707da0e9e5e945e205e640bf34637355ef863b6efb28f","src/ast/generics.rs":"3bdfe1abdbee64f5aaa9b189dc5c58e222568f196ed206e9d6d91ea5f29673e7","src/ast/mod.rs":"58a58eeb1db7682e5994732cc8eb04088f6ca662805460ef443cf089e5c24e2c","src/codegen/attr_extractor.rs":"d13a3ba16f3a93f4a540421bb9df54a8f74cc6ee0e9e769b78b6c8045f285408","src/codegen/default_expr.rs":"60f23b77ddf80da70ec92fd0d8cb5a97143535df85eafa0e47943ac797ec46d2","src/codegen/error.rs":"50d0a1a2a392560ca10d3eb622ba745612761bf18846404742bd30caf2436228","src/codegen/field.rs":"d8b20dd1dda2c5591f5dcd4a2e5b8442ffae3daa23378323a81a682637080ea1","src/codegen/from_derive_impl.rs":"2a1e331d205db9334218b800760afcb64539fa2b25b0b2b014586b49b5b25882","src/codegen/from_field.rs":"af6f848ed7c6d2274e15d6307b011a1b8bcba66172ef68b0e665c9b4ae85157d","src/codegen/from_meta_impl.rs":"a937808b081e5d8bd16020fea8da0461e7e45872516cc0ee9e8a8cc957858a76","src/codegen/from_type_param.rs":"48493227f4a81fe1a533bd958b89207d56b1c70f754a2a28fb0e9eb9114a9aeb","src/codegen/from_variant_impl.rs":"3d9b10d60e3eb969946bbd210ce93a43375b9c8d3ffa827c7e87ab9235e2c3f3","src/codegen/mod.rs":"2dc3ee7f18177b2ef9c27c4f0cabc5bbd49f970abdba28a0539d81c70d81b6e2","src/codegen/outer_from_impl.rs":"059c3c05e4d6c7ba3876d23855ccbffa9e6158e7af7f6dc619b4d9770587f161","src/codegen/trait_impl.rs":"0e479f9e5b7253a3da2f77fd0bd63ea44a08a27dba2bd7dd992f05dc40b1d2a2","src/codegen/variant.rs":"3bc37ceb0e3dbf7eb02d7b66178adb3b165a24e92afa3295c012cf2f091d7328","src/codegen/variant_data.rs":"2467651663bfbb0ab35370b421d456948b053c26d0a23581a6c91ae7789b2db3","src/error.rs":"77436dc9a30172e94c434bdb61e3620ce37b2d64848a4fa19daba6e9c9329c95","src/from_derive_input.rs":"b2a04fefa4e14684061c819387ea0384297c67c7f50d5d0959aa69ab19075e76","src/from_field.rs":"f667924422ab4ab8d8b16ebfd42f61e74864cfaa80e96b49de5d1e1287e3d1d3","src/from_generic_param.rs":"49c5a8335eb0a9010998c5600c95c89440f7febe03265afb625d818dbc883d92","src/from_generics.rs":"7285b0fd213507e87c1da413cd1bc9eeada485d4a8f9d19630848d02a4d41c4a","src/from_meta.rs":"daa3d9a82b2f659147572f7d0e7fdef38139583e61e8319986098a4a73106453","src/from_type_param.rs":"9b611f276871002ade78146266fde3b6f9101b360082681a2fa2dafc16431f84","src/from_variant.rs":"70a23b5b4cb93c7e5a498fe9d16420b9b93bd69c1376d7703bc7cefd9417d159","src/lib.rs":"ea8f2d28793e4c0e367c4905e55e405417fbee9eb7ef0577f883b91aee1b5a57","src/macros_private.rs":"ef249cd9ca593aac423b4242df1c39c31610438da094c21562d74a7e5823c700","src/macros_public.rs":"3ed7eb99f309d9cd600d3a09ff4dcf5cc5d787fb49e8e5ead6bb00e31e5e6793","src/options/core.rs":"2dfb29a85a68d5ed6dccc46fe521c46ea8483bcd5d73cf21dbf21a7163ec318a","src/options/forward_attrs.rs":"89d8294a2204d6c32e5f58b797c23f4b035a8ff424bcf1b1c7db973ada89a4bc","src/options/from_derive.rs":"c8e41d2779e570dadd76c28b9cc9dd9cee57d8ef027766b4b5f39460a29df5d1","src/options/from_field.rs":"0b06590092a5c13d5c8eedf4e2d0518d869206813f828a06c65605d6cb8decc4","src/options/from_meta.rs":"ef268f78480f1d3df4e56cbc630aa4013ab5128cbb4471c4e85e9ff5a3980e7b","src/options/from_type_param.rs":"80d1fa60bac5bb31d0895e321faf1b8af99e407d9594028a01704b424be1dd4c","src/options/from_variant.rs":"99d4b60e48f8feef9c1fc2414ac0a420e66551c14e40c4a718668cca8e1f8cb5","src/options/input_field.rs":"08a5c584ccbb96ca11d2e58572ee654c269672f81cd76f9e2f29c3c23577c998","src/options/input_variant.rs":"29c39072f250dae92acf362185f81ea3ceac210e556f861e6cc0c87b043764dd","src/options/mod.rs":"30aefb531716954bad2b484c321baa333ef1d7d07ca55a0430df26bf4c1afb15","src/options/outer_from.rs":"e60196f2b9ea3ae014eaf3444607c6cf2a227c8d8aa4a5d64f59454602a0a5e6","src/options/shape.rs":"f6c110134954c62377e81d17e3fe623a9750ca7eb99eab853add114180f55904","src/usage/generics_ext.rs":"340774fe43033680e6b494c76dd4f558ada7ca4f09e91f9e4d076f125a729fc2","src/usage/ident_set.rs":"30edb2f0a599284967e3c6b579da31e5f9b15f3dd67bc9a82d6335eb44133df0","src/usage/lifetimes.rs":"19a63490660eb416bbccd4be9c060079a630dd15d5fd23c39e3c6c8c5498e369","src/usage/mod.rs":"bcf0ffb2257aed3b7b2cdad91c0136743384f39ff8f61f25c195c73b8fcdf144","src/usage/options.rs":"0491c995aad0d55783b24cce8a4e40f0f4435988c54ce2ded34763ac9b199fcf","src/usage/type_params.rs":"5aecf7f89c4b0623010e4717f5b32388b06d15e8511fde7b72f1d72e88fd3cb0","src/util/ident_list.rs":"fb6314436435547c111539213b209abd8d2593c28331560b84468a03b52d7c44","src/util/ident_string.rs":"06441e502d3bad342e292e42d083aff9070735a93d45f204aee5ef5f0144fdd0","src/util/ignored.rs":"7c979fbef880498ff64efda7396e759d80bf77fb1c7df0313bed8e533a16c0e0","src/util/mod.rs":"027793775e7079ea425f45b9bdad8b8fa309defcee02bf8ee6b6c556ec016342","src/util/over_ride.rs":"e90b3aeb41dd3364753f3ee6f8c6bbd85bf51d51569bf048396ec6e7735c569f","src/util/with_original.rs":"a545b38ba9d624fdc939eb844f001b7fc4102717b1d3683b4cbd0aae00fa7ef2"},"package":"86bc5ce438f4b703755d12f59bbf0a16c642766d4534e922db47569dbdd0b998"}
|
|
@ -12,22 +12,25 @@
|
|||
|
||||
[package]
|
||||
name = "darling_core"
|
||||
version = "0.4.0"
|
||||
version = "0.8.0"
|
||||
authors = ["Ted Driggs <ted.driggs@outlook.com>"]
|
||||
description = "Helper crate for proc-macro library for reading attributes into structs when\nimplementing custom derives. Use https://crates.io/crates/darling in your code.\n"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/TedDriggs/darling"
|
||||
[dependencies.fnv]
|
||||
version = "1.0.6"
|
||||
|
||||
[dependencies.ident_case]
|
||||
version = "1.0.0"
|
||||
|
||||
[dependencies.proc-macro2]
|
||||
version = "0.3"
|
||||
version = "0.4.2"
|
||||
|
||||
[dependencies.quote]
|
||||
version = "0.5"
|
||||
version = "0.6"
|
||||
|
||||
[dependencies.syn]
|
||||
version = "0.13"
|
||||
version = "0.15"
|
||||
features = ["extra-traits"]
|
||||
|
||||
[features]
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
//! Utility types for working with the AST.
|
||||
use std::slice;
|
||||
|
||||
use syn;
|
||||
|
||||
use usage::{
|
||||
self, IdentRefSet, IdentSet, LifetimeRefSet, LifetimeSet, UsesLifetimes, UsesTypeParams,
|
||||
};
|
||||
use {Error, FromField, FromVariant, Result};
|
||||
|
||||
/// A struct or enum body.
|
||||
/// A struct or enum body.
|
||||
///
|
||||
/// `V` is the type which receives any encountered variants, and `F` receives struct fields.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
@ -13,7 +16,7 @@ pub enum Data<V, F> {
|
|||
Struct(Fields<F>),
|
||||
}
|
||||
|
||||
#[deprecated(since="0.3", note="this has been renamed to Data")]
|
||||
#[deprecated(since = "0.3", note = "this has been renamed to Data")]
|
||||
pub type Body<V, F> = Data<V, F>;
|
||||
|
||||
impl<V, F> Data<V, F> {
|
||||
|
@ -36,7 +39,8 @@ impl<V, F> Data<V, F> {
|
|||
|
||||
/// Applies a function `V -> U` on enum variants, if this is an enum.
|
||||
pub fn map_enum_variants<T, U>(self, map: T) -> Data<U, F>
|
||||
where T: FnMut(V) -> U
|
||||
where
|
||||
T: FnMut(V) -> U,
|
||||
{
|
||||
match self {
|
||||
Data::Enum(v) => Data::Enum(v.into_iter().map(map).collect()),
|
||||
|
@ -46,7 +50,8 @@ impl<V, F> Data<V, F> {
|
|||
|
||||
/// Applies a function `F -> U` on struct fields, if this is a struct.
|
||||
pub fn map_struct_fields<T, U>(self, map: T) -> Data<V, U>
|
||||
where T: FnMut(F) -> U
|
||||
where
|
||||
T: FnMut(F) -> U,
|
||||
{
|
||||
match self {
|
||||
Data::Enum(v) => Data::Enum(v),
|
||||
|
@ -56,7 +61,8 @@ impl<V, F> Data<V, F> {
|
|||
|
||||
/// Applies a function to the `Fields` if this is a struct.
|
||||
pub fn map_struct<T, U>(self, mut map: T) -> Data<V, U>
|
||||
where T: FnMut(Fields<F>) -> Fields<U>
|
||||
where
|
||||
T: FnMut(Fields<F>) -> Fields<U>,
|
||||
{
|
||||
match self {
|
||||
Data::Enum(v) => Data::Enum(v),
|
||||
|
@ -101,10 +107,14 @@ impl<V: FromVariant, F: FromField> Data<V, F> {
|
|||
syn::Data::Enum(ref data) => {
|
||||
let mut items = Vec::with_capacity(data.variants.len());
|
||||
let mut errors = Vec::new();
|
||||
for v_result in data.variants.clone().into_iter().map(|v| FromVariant::from_variant(&v)) {
|
||||
for v_result in data.variants
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|v| FromVariant::from_variant(&v))
|
||||
{
|
||||
match v_result {
|
||||
Ok(val) => items.push(val),
|
||||
Err(err) => errors.push(err)
|
||||
Err(err) => errors.push(err),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,13 +130,39 @@ impl<V: FromVariant, F: FromField> Data<V, F> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<V: UsesTypeParams, F: UsesTypeParams> UsesTypeParams for Data<V, F> {
|
||||
fn uses_type_params<'a>(
|
||||
&self,
|
||||
options: &usage::Options,
|
||||
type_set: &'a IdentSet,
|
||||
) -> IdentRefSet<'a> {
|
||||
match *self {
|
||||
Data::Struct(ref v) => v.uses_type_params(options, type_set),
|
||||
Data::Enum(ref v) => v.uses_type_params(options, type_set),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: UsesLifetimes, F: UsesLifetimes> UsesLifetimes for Data<V, F> {
|
||||
fn uses_lifetimes<'a>(
|
||||
&self,
|
||||
options: &usage::Options,
|
||||
lifetimes: &'a LifetimeSet,
|
||||
) -> LifetimeRefSet<'a> {
|
||||
match *self {
|
||||
Data::Struct(ref v) => v.uses_lifetimes(options, lifetimes),
|
||||
Data::Enum(ref v) => v.uses_lifetimes(options, lifetimes),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Fields<T> {
|
||||
pub style: Style,
|
||||
pub fields: Vec<T>,
|
||||
}
|
||||
|
||||
#[deprecated(since="0.3", note="this has been renamed to Fields")]
|
||||
#[deprecated(since = "0.3", note = "this has been renamed to Fields")]
|
||||
pub type VariantData<T> = Fields<T>;
|
||||
|
||||
impl<T> Fields<T> {
|
||||
|
@ -167,12 +203,19 @@ impl<T> Fields<T> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn map<F, U>(self, map: F) -> Fields<U> where F: FnMut(T) -> U {
|
||||
pub fn map<F, U>(self, map: F) -> Fields<U>
|
||||
where
|
||||
F: FnMut(T) -> U,
|
||||
{
|
||||
Fields {
|
||||
style: self.style,
|
||||
fields: self.fields.into_iter().map(map).collect()
|
||||
fields: self.fields.into_iter().map(map).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> slice::Iter<T> {
|
||||
self.fields.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: FromField> Fields<F> {
|
||||
|
@ -187,10 +230,10 @@ impl<F: FromField> Fields<F> {
|
|||
match f_result {
|
||||
Ok(val) => items.push(val),
|
||||
Err(err) => errors.push(if let Some(ref ident) = field.ident {
|
||||
err.at(ident.as_ref())
|
||||
err.at(ident)
|
||||
} else {
|
||||
err
|
||||
})
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -205,10 +248,10 @@ impl<F: FromField> Fields<F> {
|
|||
match f_result {
|
||||
Ok(val) => items.push(val),
|
||||
Err(err) => errors.push(if let Some(ref ident) = field.ident {
|
||||
err.at(ident.as_ref())
|
||||
err.at(ident)
|
||||
} else {
|
||||
err
|
||||
})
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -217,7 +260,6 @@ impl<F: FromField> Fields<F> {
|
|||
syn::Fields::Unit => (vec![], vec![]),
|
||||
};
|
||||
|
||||
|
||||
if !errors.is_empty() {
|
||||
Err(Error::multiple(errors))
|
||||
} else {
|
||||
|
@ -244,6 +286,26 @@ impl<T, U: Into<Vec<T>>> From<(Style, U)> for Fields<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: UsesTypeParams> UsesTypeParams for Fields<T> {
|
||||
fn uses_type_params<'a>(
|
||||
&self,
|
||||
options: &usage::Options,
|
||||
type_set: &'a IdentSet,
|
||||
) -> IdentRefSet<'a> {
|
||||
self.fields.uses_type_params(options, type_set)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UsesLifetimes> UsesLifetimes for Fields<T> {
|
||||
fn uses_lifetimes<'a>(
|
||||
&self,
|
||||
options: &usage::Options,
|
||||
lifetimes: &'a LifetimeSet,
|
||||
) -> LifetimeRefSet<'a> {
|
||||
self.fields.uses_lifetimes(options, lifetimes)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Style {
|
||||
Tuple,
|
|
@ -0,0 +1,194 @@
|
|||
//! Types for working with generics
|
||||
|
||||
use std::iter::Iterator;
|
||||
use std::slice::Iter;
|
||||
|
||||
use syn;
|
||||
|
||||
use {FromGenericParam, FromGenerics, FromTypeParam, Result};
|
||||
|
||||
/// Extension trait for `GenericParam` to support getting values by variant.
|
||||
///
|
||||
/// # Usage
|
||||
/// `darling::ast::Generics` needs a way to test its params array in order to iterate over type params.
|
||||
/// Rather than require callers to use `darling::ast::GenericParam` in all cases, this trait makes that
|
||||
/// polymorphic.
|
||||
pub trait GenericParamExt {
|
||||
/// The type this GenericParam uses to represent type params and their bounds
|
||||
type TypeParam;
|
||||
type LifetimeDef;
|
||||
type ConstParam;
|
||||
|
||||
/// If this GenericParam is a type param, get the underlying value.
|
||||
fn as_type_param(&self) -> Option<&Self::TypeParam> {
|
||||
None
|
||||
}
|
||||
|
||||
/// If this GenericParam is a lifetime, get the underlying value.
|
||||
fn as_lifetime_def(&self) -> Option<&Self::LifetimeDef> {
|
||||
None
|
||||
}
|
||||
|
||||
/// If this GenericParam is a const param, get the underlying value.
|
||||
fn as_const_param(&self) -> Option<&Self::ConstParam> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl GenericParamExt for syn::GenericParam {
|
||||
type TypeParam = syn::TypeParam;
|
||||
type LifetimeDef = syn::LifetimeDef;
|
||||
type ConstParam = syn::ConstParam;
|
||||
|
||||
fn as_type_param(&self) -> Option<&Self::TypeParam> {
|
||||
if let syn::GenericParam::Type(ref val) = *self {
|
||||
Some(val)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn as_lifetime_def(&self) -> Option<&Self::LifetimeDef> {
|
||||
if let syn::GenericParam::Lifetime(ref val) = *self {
|
||||
Some(val)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn as_const_param(&self) -> Option<&Self::ConstParam> {
|
||||
if let syn::GenericParam::Const(ref val) = *self {
|
||||
Some(val)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GenericParamExt for syn::TypeParam {
|
||||
type TypeParam = syn::TypeParam;
|
||||
type LifetimeDef = ();
|
||||
type ConstParam = ();
|
||||
|
||||
fn as_type_param(&self) -> Option<&Self::TypeParam> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// A mirror of `syn::GenericParam` which is generic over all its contents.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum GenericParam<T = syn::TypeParam, L = syn::LifetimeDef, C = syn::ConstParam> {
|
||||
Type(T),
|
||||
Lifetime(L),
|
||||
Const(C),
|
||||
}
|
||||
|
||||
impl<T: FromTypeParam> FromTypeParam for GenericParam<T> {
|
||||
fn from_type_param(type_param: &syn::TypeParam) -> Result<Self> {
|
||||
Ok(GenericParam::Type(FromTypeParam::from_type_param(
|
||||
type_param,
|
||||
)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FromTypeParam> FromGenericParam for GenericParam<T> {
|
||||
fn from_generic_param(param: &syn::GenericParam) -> Result<Self> {
|
||||
Ok(match *param {
|
||||
syn::GenericParam::Type(ref ty) => {
|
||||
GenericParam::Type(FromTypeParam::from_type_param(ty)?)
|
||||
}
|
||||
syn::GenericParam::Lifetime(ref val) => GenericParam::Lifetime(val.clone()),
|
||||
syn::GenericParam::Const(ref val) => GenericParam::Const(val.clone()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, L, C> GenericParamExt for GenericParam<T, L, C> {
|
||||
type TypeParam = T;
|
||||
type LifetimeDef = L;
|
||||
type ConstParam = C;
|
||||
|
||||
fn as_type_param(&self) -> Option<&T> {
|
||||
if let GenericParam::Type(ref val) = *self {
|
||||
Some(val)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn as_lifetime_def(&self) -> Option<&L> {
|
||||
if let GenericParam::Lifetime(ref val) = *self {
|
||||
Some(val)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn as_const_param(&self) -> Option<&C> {
|
||||
if let GenericParam::Const(ref val) = *self {
|
||||
Some(val)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A mirror of the `syn::Generics` type which can contain arbitrary representations
|
||||
/// of params and where clauses.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Generics<P, W = syn::WhereClause> {
|
||||
pub params: Vec<P>,
|
||||
pub where_clause: Option<W>,
|
||||
}
|
||||
|
||||
impl<P, W> Generics<P, W> {
|
||||
pub fn type_params<'a>(&'a self) -> TypeParams<'a, P> {
|
||||
TypeParams(self.params.iter())
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: FromGenericParam> FromGenerics for Generics<P> {
|
||||
fn from_generics(generics: &syn::Generics) -> Result<Self> {
|
||||
Ok(Generics {
|
||||
params: generics
|
||||
.params
|
||||
.iter()
|
||||
.map(FromGenericParam::from_generic_param)
|
||||
.collect::<Result<Vec<P>>>()?,
|
||||
where_clause: generics.where_clause.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TypeParams<'a, P: 'a>(Iter<'a, P>);
|
||||
|
||||
impl<'a, P: GenericParamExt> Iterator for TypeParams<'a, P> {
|
||||
type Item = &'a <P as GenericParamExt>::TypeParam;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let next = self.0.next();
|
||||
match next {
|
||||
None => None,
|
||||
Some(v) => match v.as_type_param() {
|
||||
Some(val) => Some(val),
|
||||
None => self.next(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use syn;
|
||||
|
||||
use super::{GenericParam, Generics};
|
||||
use FromGenerics;
|
||||
|
||||
#[test]
|
||||
fn generics() {
|
||||
let g: syn::Generics = parse_quote!(<T>);
|
||||
let deified: Generics<GenericParam<syn::Ident>> = FromGenerics::from_generics(&g).unwrap();
|
||||
assert!(deified.params.len() == 1);
|
||||
assert!(deified.where_clause.is_none());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
//! Utility types for working with the AST.
|
||||
|
||||
mod data;
|
||||
mod generics;
|
||||
|
||||
pub use self::data::*;
|
||||
pub use self::generics::{GenericParam, GenericParamExt, Generics};
|
|
@ -0,0 +1,109 @@
|
|||
use proc_macro2::TokenStream;
|
||||
|
||||
use options::ForwardAttrs;
|
||||
use util::IdentList;
|
||||
|
||||
/// Infrastructure for generating an attribute extractor.
|
||||
pub trait ExtractAttribute {
|
||||
/// A set of mutable declarations for all members of the implementing type.
|
||||
fn local_declarations(&self) -> TokenStream;
|
||||
|
||||
/// A set of immutable declarations for all members of the implementing type.
|
||||
/// This is used in the case where a deriving struct handles no attributes and therefore can
|
||||
/// never change its default state.
|
||||
fn immutable_declarations(&self) -> TokenStream;
|
||||
|
||||
/// Gets the list of attribute names that should be parsed by the extractor.
|
||||
fn attr_names(&self) -> &IdentList;
|
||||
|
||||
fn forwarded_attrs(&self) -> Option<&ForwardAttrs>;
|
||||
|
||||
/// Gets the name used by the generated impl to return to the `syn` item passed as input.
|
||||
fn param_name(&self) -> TokenStream;
|
||||
|
||||
/// Gets the core from-meta-item loop that should be used on matching attributes.
|
||||
fn core_loop(&self) -> TokenStream;
|
||||
|
||||
fn declarations(&self) -> TokenStream {
|
||||
if !self.attr_names().is_empty() {
|
||||
self.local_declarations()
|
||||
} else {
|
||||
self.immutable_declarations()
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates the main extraction loop.
|
||||
fn extractor(&self) -> TokenStream {
|
||||
let declarations = self.declarations();
|
||||
|
||||
let will_parse_any = !self.attr_names().is_empty();
|
||||
let will_fwd_any = self.forwarded_attrs()
|
||||
.map(|fa| !fa.is_empty())
|
||||
.unwrap_or_default();
|
||||
|
||||
if !(will_parse_any || will_fwd_any) {
|
||||
return quote! {
|
||||
#declarations
|
||||
};
|
||||
}
|
||||
|
||||
let input = self.param_name();
|
||||
|
||||
// The block for parsing attributes whose names have been claimed by the target
|
||||
// struct. If no attributes were claimed, this is a pass-through.
|
||||
let parse_handled = if will_parse_any {
|
||||
let attr_names = self.attr_names().to_strings();
|
||||
let core_loop = self.core_loop();
|
||||
quote!(
|
||||
#(#attr_names)|* => {
|
||||
if let Some(::syn::Meta::List(ref __data)) = __attr.interpret_meta() {
|
||||
let __items = &__data.nested;
|
||||
|
||||
#core_loop
|
||||
} else {
|
||||
// darling currently only supports list-style
|
||||
continue
|
||||
}
|
||||
}
|
||||
)
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
|
||||
// Specifies the behavior for unhandled attributes. They will either be silently ignored or
|
||||
// forwarded to the inner struct for later analysis.
|
||||
let forward_unhandled = if will_fwd_any {
|
||||
forwards_to_local(self.forwarded_attrs().unwrap())
|
||||
} else {
|
||||
quote!(_ => continue)
|
||||
};
|
||||
|
||||
quote!(
|
||||
#declarations
|
||||
use ::darling::ToTokens;
|
||||
let mut __fwd_attrs: ::darling::export::Vec<::syn::Attribute> = vec![];
|
||||
|
||||
for __attr in &#input.attrs {
|
||||
// Filter attributes based on name
|
||||
match ::darling::export::ToString::to_string(&__attr.path.clone().into_token_stream()).as_str() {
|
||||
#parse_handled
|
||||
#forward_unhandled
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn forwards_to_local(behavior: &ForwardAttrs) -> TokenStream {
|
||||
let push_command = quote!(__fwd_attrs.push(__attr.clone()));
|
||||
match *behavior {
|
||||
ForwardAttrs::All => quote!(_ => #push_command),
|
||||
ForwardAttrs::Only(ref idents) => {
|
||||
let names = idents.to_strings();
|
||||
quote!(
|
||||
#(#names)|* => #push_command,
|
||||
_ => continue,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
use quote::{Tokens, ToTokens};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{TokenStreamExt, ToTokens};
|
||||
use syn::{Ident, Path};
|
||||
|
||||
/// This will be in scope during struct initialization after option parsing.
|
||||
|
@ -21,12 +22,12 @@ impl<'a> DefaultExpression<'a> {
|
|||
}
|
||||
|
||||
impl<'a> ToTokens for DefaultExpression<'a> {
|
||||
fn to_tokens(&self, tokens: &mut Tokens) {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
tokens.append_all(match *self {
|
||||
DefaultExpression::Inherit(ident) => {
|
||||
let dsn = Ident::new(DEFAULT_STRUCT_NAME, ::proc_macro2::Span::call_site());
|
||||
quote!(#dsn.#ident)
|
||||
},
|
||||
}
|
||||
DefaultExpression::Explicit(path) => quote!(#path()),
|
||||
DefaultExpression::Trait => quote!(::darling::export::Default::default()),
|
||||
});
|
||||
|
@ -37,7 +38,7 @@ impl<'a> ToTokens for DefaultExpression<'a> {
|
|||
pub struct DefaultDeclaration<'a>(&'a DefaultExpression<'a>);
|
||||
|
||||
impl<'a> ToTokens for DefaultDeclaration<'a> {
|
||||
fn to_tokens(&self, tokens: &mut Tokens) {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let name = Ident::new(DEFAULT_STRUCT_NAME, ::proc_macro2::Span::call_site());
|
||||
let expr = self.0;
|
||||
tokens.append_all(quote!(let #name: Self = #expr;));
|
||||
|
|
|
@ -1,20 +1,19 @@
|
|||
use quote::{Tokens, ToTokens};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{TokenStreamExt, ToTokens};
|
||||
|
||||
/// Declares the local variable into which errors will be accumulated.
|
||||
pub struct ErrorDeclaration {
|
||||
__hidden: ()
|
||||
__hidden: (),
|
||||
}
|
||||
|
||||
impl ErrorDeclaration {
|
||||
pub fn new() -> Self {
|
||||
ErrorDeclaration {
|
||||
__hidden: ()
|
||||
}
|
||||
ErrorDeclaration { __hidden: () }
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for ErrorDeclaration {
|
||||
fn to_tokens(&self, tokens: &mut Tokens) {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
tokens.append_all(quote! {
|
||||
let mut __errors = Vec::new();
|
||||
})
|
||||
|
@ -24,14 +23,14 @@ impl ToTokens for ErrorDeclaration {
|
|||
/// Returns early if attribute or body parsing has caused any errors.
|
||||
pub struct ErrorCheck<'a> {
|
||||
location: Option<&'a str>,
|
||||
__hidden: ()
|
||||
__hidden: (),
|
||||
}
|
||||
|
||||
impl<'a> ErrorCheck<'a> {
|
||||
pub fn new() -> Self {
|
||||
ErrorCheck {
|
||||
location: None,
|
||||
__hidden: ()
|
||||
__hidden: (),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,7 +43,7 @@ impl<'a> ErrorCheck<'a> {
|
|||
}
|
||||
|
||||
impl<'a> ToTokens for ErrorCheck<'a> {
|
||||
fn to_tokens(&self, tokens: &mut Tokens) {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let at_call = if let Some(ref s) = self.location {
|
||||
quote!(.at(#s))
|
||||
} else {
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
use quote::{Tokens, ToTokens};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{TokenStreamExt, ToTokens};
|
||||
use syn::{Ident, Path, Type};
|
||||
|
||||
use codegen::DefaultExpression;
|
||||
use usage::{self, IdentRefSet, IdentSet, UsesTypeParams};
|
||||
|
||||
/// Properties needed to generate code for a field in all the contexts
|
||||
/// where one may appear.
|
||||
|
@ -9,7 +11,7 @@ use codegen::DefaultExpression;
|
|||
pub struct Field<'a> {
|
||||
/// The name presented to the user of the library. This will appear
|
||||
/// in error messages and will be looked when parsing names.
|
||||
pub name_in_attr: &'a str,
|
||||
pub name_in_attr: String,
|
||||
|
||||
/// The name presented to the author of the library. This will appear
|
||||
/// in the setters or temporary variables which contain the values.
|
||||
|
@ -42,6 +44,16 @@ impl<'a> Field<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> UsesTypeParams for Field<'a> {
|
||||
fn uses_type_params<'b>(
|
||||
&self,
|
||||
options: &usage::Options,
|
||||
type_set: &'b IdentSet,
|
||||
) -> IdentRefSet<'b> {
|
||||
self.ty.uses_type_params(options, type_set)
|
||||
}
|
||||
}
|
||||
|
||||
/// An individual field during variable declaration in the generated parsing method.
|
||||
pub struct Declaration<'a>(&'a Field<'a>, bool);
|
||||
|
||||
|
@ -53,7 +65,7 @@ impl<'a> Declaration<'a> {
|
|||
}
|
||||
|
||||
impl<'a> ToTokens for Declaration<'a> {
|
||||
fn to_tokens(&self, tokens: &mut Tokens) {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let field: &Field = self.0;
|
||||
let ident = field.ident;
|
||||
let ty = field.ty;
|
||||
|
@ -73,10 +85,10 @@ impl<'a> ToTokens for Declaration<'a> {
|
|||
pub struct MatchArm<'a>(&'a Field<'a>);
|
||||
|
||||
impl<'a> ToTokens for MatchArm<'a> {
|
||||
fn to_tokens(&self, tokens: &mut Tokens) {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let field: &Field = self.0;
|
||||
if !field.skip {
|
||||
let name_str = field.name_in_attr;
|
||||
let name_str = &field.name_in_attr;
|
||||
let ident = field.ident;
|
||||
let with_path = &field.with_path;
|
||||
|
||||
|
@ -140,7 +152,7 @@ impl<'a> ToTokens for MatchArm<'a> {
|
|||
pub struct Initializer<'a>(&'a Field<'a>);
|
||||
|
||||
impl<'a> ToTokens for Initializer<'a> {
|
||||
fn to_tokens(&self, tokens: &mut Tokens) {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let field: &Field = self.0;
|
||||
let ident = field.ident;
|
||||
tokens.append_all(if field.multiple {
|
||||
|
@ -170,10 +182,10 @@ impl<'a> ToTokens for Initializer<'a> {
|
|||
pub struct CheckMissing<'a>(&'a Field<'a>);
|
||||
|
||||
impl<'a> ToTokens for CheckMissing<'a> {
|
||||
fn to_tokens(&self, tokens: &mut Tokens) {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
if !self.0.multiple && self.0.default_expression.is_none() {
|
||||
let ident = self.0.ident;
|
||||
let name_in_attr = self.0.name_in_attr;
|
||||
let name_in_attr = &self.0.name_in_attr;
|
||||
|
||||
tokens.append_all(quote! {
|
||||
if !#ident.0 {
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
use quote::{Tokens, ToTokens};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::ToTokens;
|
||||
use syn::{self, Ident};
|
||||
|
||||
use ast::Data;
|
||||
use codegen::{TraitImpl, ExtractAttribute, OuterFromImpl};
|
||||
use codegen::{ExtractAttribute, OuterFromImpl, TraitImpl};
|
||||
use options::{ForwardAttrs, Shape};
|
||||
use util::IdentList;
|
||||
|
||||
pub struct FromDeriveInputImpl<'a> {
|
||||
pub ident: Option<&'a Ident>,
|
||||
|
@ -12,45 +14,56 @@ pub struct FromDeriveInputImpl<'a> {
|
|||
pub attrs: Option<&'a Ident>,
|
||||
pub data: Option<&'a Ident>,
|
||||
pub base: TraitImpl<'a>,
|
||||
pub attr_names: Vec<&'a str>,
|
||||
pub attr_names: &'a IdentList,
|
||||
pub forward_attrs: Option<&'a ForwardAttrs>,
|
||||
pub from_ident: Option<bool>,
|
||||
pub from_ident: bool,
|
||||
pub supports: Option<&'a Shape>,
|
||||
}
|
||||
|
||||
impl<'a> ToTokens for FromDeriveInputImpl<'a> {
|
||||
fn to_tokens(&self, tokens: &mut Tokens) {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let ty_ident = self.base.ident;
|
||||
let input = self.param_name();
|
||||
let map = self.base.map_fn();
|
||||
|
||||
if let Data::Struct(ref data) = self.base.data {
|
||||
if data.is_newtype() {
|
||||
self.wrap(quote!{
|
||||
fn from_derive_input(#input: &::syn::DeriveInput) -> ::darling::Result<Self> {
|
||||
::darling::export::Ok(
|
||||
#ty_ident(::darling::FromDeriveInput::from_derive_input(#input)?)
|
||||
) #map
|
||||
}
|
||||
}, tokens);
|
||||
self.wrap(
|
||||
quote!{
|
||||
fn from_derive_input(#input: &::syn::DeriveInput) -> ::darling::Result<Self> {
|
||||
::darling::export::Ok(
|
||||
#ty_ident(::darling::FromDeriveInput::from_derive_input(#input)?)
|
||||
) #map
|
||||
}
|
||||
},
|
||||
tokens,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let passed_ident = self.ident.as_ref().map(|i| quote!(#i: #input.ident.clone(),));
|
||||
let passed_ident = self.ident
|
||||
.as_ref()
|
||||
.map(|i| quote!(#i: #input.ident.clone(),));
|
||||
let passed_vis = self.vis.as_ref().map(|i| quote!(#i: #input.vis.clone(),));
|
||||
let passed_generics = self.generics.as_ref().map(|i| quote!(#i: #input.generics.clone(),));
|
||||
let passed_generics = self.generics
|
||||
.as_ref()
|
||||
.map(|i| quote!(#i: ::darling::FromGenerics::from_generics(&#input.generics)?,));
|
||||
let passed_attrs = self.attrs.as_ref().map(|i| quote!(#i: __fwd_attrs,));
|
||||
let passed_body = self.data.as_ref().map(|i| quote!(#i: ::darling::ast::Data::try_from(&#input.data)?,));
|
||||
let passed_body = self.data
|
||||
.as_ref()
|
||||
.map(|i| quote!(#i: ::darling::ast::Data::try_from(&#input.data)?,));
|
||||
|
||||
let supports = self.supports.map(|i| quote!{
|
||||
#i
|
||||
__validate_body(&#input.data)?;
|
||||
let supports = self.supports.map(|i| {
|
||||
quote!{
|
||||
#i
|
||||
__validate_body(&#input.data)?;
|
||||
}
|
||||
});
|
||||
|
||||
let inits = self.base.initializers();
|
||||
let default = if let Some(true) = self.from_ident {
|
||||
let default = if self.from_ident {
|
||||
quote!(let __default: Self = ::darling::export::From::from(#input.ident.clone());)
|
||||
} else {
|
||||
self.base.fallback_decl()
|
||||
|
@ -62,55 +75,58 @@ impl<'a> ToTokens for FromDeriveInputImpl<'a> {
|
|||
let require_fields = self.base.require_fields();
|
||||
let check_errors = self.base.check_errors();
|
||||
|
||||
self.wrap(quote! {
|
||||
fn from_derive_input(#input: &::syn::DeriveInput) -> ::darling::Result<Self> {
|
||||
#declare_errors
|
||||
self.wrap(
|
||||
quote! {
|
||||
fn from_derive_input(#input: &::syn::DeriveInput) -> ::darling::Result<Self> {
|
||||
#declare_errors
|
||||
|
||||
#grab_attrs
|
||||
#grab_attrs
|
||||
|
||||
#supports
|
||||
#supports
|
||||
|
||||
#require_fields
|
||||
#require_fields
|
||||
|
||||
#check_errors
|
||||
#check_errors
|
||||
|
||||
#default
|
||||
#default
|
||||
|
||||
::darling::export::Ok(#ty_ident {
|
||||
#passed_ident
|
||||
#passed_generics
|
||||
#passed_vis
|
||||
#passed_attrs
|
||||
#passed_body
|
||||
#inits
|
||||
}) #map
|
||||
}
|
||||
}, tokens);
|
||||
::darling::export::Ok(#ty_ident {
|
||||
#passed_ident
|
||||
#passed_generics
|
||||
#passed_vis
|
||||
#passed_attrs
|
||||
#passed_body
|
||||
#inits
|
||||
}) #map
|
||||
}
|
||||
},
|
||||
tokens,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ExtractAttribute for FromDeriveInputImpl<'a> {
|
||||
fn attr_names(&self) -> &[&str] {
|
||||
self.attr_names.as_slice()
|
||||
fn attr_names(&self) -> &IdentList {
|
||||
&self.attr_names
|
||||
}
|
||||
|
||||
fn forwarded_attrs(&self) -> Option<&ForwardAttrs> {
|
||||
self.forward_attrs
|
||||
}
|
||||
|
||||
fn param_name(&self) -> Tokens {
|
||||
fn param_name(&self) -> TokenStream {
|
||||
quote!(__di)
|
||||
}
|
||||
|
||||
fn core_loop(&self) -> Tokens {
|
||||
fn core_loop(&self) -> TokenStream {
|
||||
self.base.core_loop()
|
||||
}
|
||||
|
||||
fn local_declarations(&self) -> Tokens {
|
||||
fn local_declarations(&self) -> TokenStream {
|
||||
self.base.local_declarations()
|
||||
}
|
||||
|
||||
fn immutable_declarations(&self) -> Tokens {
|
||||
fn immutable_declarations(&self) -> TokenStream {
|
||||
self.base.immutable_declarations()
|
||||
}
|
||||
}
|
||||
|
@ -121,7 +137,7 @@ impl<'a> OuterFromImpl<'a> for FromDeriveInputImpl<'a> {
|
|||
}
|
||||
|
||||
fn trait_bound(&self) -> syn::Path {
|
||||
path!(::darling::FromMetaItem)
|
||||
path!(::darling::FromMeta)
|
||||
}
|
||||
|
||||
fn base(&'a self) -> &'a TraitImpl<'a> {
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
use quote::{Tokens, ToTokens};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::ToTokens;
|
||||
use syn::{self, Ident};
|
||||
|
||||
use codegen::{TraitImpl, ExtractAttribute, OuterFromImpl};
|
||||
use codegen::{ExtractAttribute, OuterFromImpl, TraitImpl};
|
||||
use options::ForwardAttrs;
|
||||
use util::IdentList;
|
||||
|
||||
/// `impl FromField` generator. This is used for parsing an individual
|
||||
/// field and its attributes.
|
||||
|
@ -12,13 +14,13 @@ pub struct FromFieldImpl<'a> {
|
|||
pub ty: Option<&'a Ident>,
|
||||
pub attrs: Option<&'a Ident>,
|
||||
pub base: TraitImpl<'a>,
|
||||
pub attr_names: Vec<&'a str>,
|
||||
pub attr_names: &'a IdentList,
|
||||
pub forward_attrs: Option<&'a ForwardAttrs>,
|
||||
pub from_ident: bool,
|
||||
}
|
||||
|
||||
impl<'a> ToTokens for FromFieldImpl<'a> {
|
||||
fn to_tokens(&self, tokens: &mut Tokens) {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let input = self.param_name();
|
||||
|
||||
let error_declaration = self.base.declare_errors();
|
||||
|
@ -33,7 +35,9 @@ impl<'a> ToTokens for FromFieldImpl<'a> {
|
|||
self.base.fallback_decl()
|
||||
};
|
||||
|
||||
let passed_ident = self.ident.as_ref().map(|i| quote!(#i: #input.ident.clone(),));
|
||||
let passed_ident = self.ident
|
||||
.as_ref()
|
||||
.map(|i| quote!(#i: #input.ident.clone(),));
|
||||
let passed_vis = self.vis.as_ref().map(|i| quote!(#i: #input.vis.clone(),));
|
||||
let passed_ty = self.ty.as_ref().map(|i| quote!(#i: #input.ty.clone(),));
|
||||
let passed_attrs = self.attrs.as_ref().map(|i| quote!(#i: __fwd_attrs,));
|
||||
|
@ -42,53 +46,56 @@ impl<'a> ToTokens for FromFieldImpl<'a> {
|
|||
let grab_attrs = self.extractor();
|
||||
let map = self.base.map_fn();
|
||||
|
||||
self.wrap(quote!{
|
||||
fn from_field(#input: &::syn::Field) -> ::darling::Result<Self> {
|
||||
#error_declaration
|
||||
self.wrap(
|
||||
quote!{
|
||||
fn from_field(#input: &::syn::Field) -> ::darling::Result<Self> {
|
||||
#error_declaration
|
||||
|
||||
#grab_attrs
|
||||
#grab_attrs
|
||||
|
||||
#require_fields
|
||||
#require_fields
|
||||
|
||||
#error_check
|
||||
#error_check
|
||||
|
||||
#default
|
||||
#default
|
||||
|
||||
::darling::export::Ok(Self {
|
||||
#passed_ident
|
||||
#passed_ty
|
||||
#passed_vis
|
||||
#passed_attrs
|
||||
#initializers
|
||||
}) #map
|
||||
::darling::export::Ok(Self {
|
||||
#passed_ident
|
||||
#passed_ty
|
||||
#passed_vis
|
||||
#passed_attrs
|
||||
#initializers
|
||||
}) #map
|
||||
|
||||
}
|
||||
}, tokens);
|
||||
}
|
||||
},
|
||||
tokens,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ExtractAttribute for FromFieldImpl<'a> {
|
||||
fn attr_names(&self) -> &[&str] {
|
||||
self.attr_names.as_slice()
|
||||
fn attr_names(&self) -> &IdentList {
|
||||
&self.attr_names
|
||||
}
|
||||
|
||||
fn forwarded_attrs(&self) -> Option<&ForwardAttrs> {
|
||||
self.forward_attrs
|
||||
}
|
||||
|
||||
fn param_name(&self) -> Tokens {
|
||||
fn param_name(&self) -> TokenStream {
|
||||
quote!(__field)
|
||||
}
|
||||
|
||||
fn core_loop(&self) -> Tokens {
|
||||
fn core_loop(&self) -> TokenStream {
|
||||
self.base.core_loop()
|
||||
}
|
||||
|
||||
fn local_declarations(&self) -> Tokens {
|
||||
fn local_declarations(&self) -> TokenStream {
|
||||
self.base.local_declarations()
|
||||
}
|
||||
|
||||
fn immutable_declarations(&self) -> Tokens {
|
||||
fn immutable_declarations(&self) -> TokenStream {
|
||||
self.base.immutable_declarations()
|
||||
}
|
||||
}
|
||||
|
@ -99,10 +106,10 @@ impl<'a> OuterFromImpl<'a> for FromFieldImpl<'a> {
|
|||
}
|
||||
|
||||
fn trait_bound(&self) -> syn::Path {
|
||||
path!(::darling::FromMetaItem)
|
||||
path!(::darling::FromMeta)
|
||||
}
|
||||
|
||||
fn base(&'a self) -> &'a TraitImpl<'a> {
|
||||
&self.base
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
use quote::{Tokens, ToTokens};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::ToTokens;
|
||||
use syn;
|
||||
|
||||
use ast::{Data, Style, Fields};
|
||||
use codegen::{Field, TraitImpl, OuterFromImpl, Variant};
|
||||
use ast::{Data, Fields, Style};
|
||||
use codegen::{Field, OuterFromImpl, TraitImpl, Variant};
|
||||
|
||||
pub struct FmiImpl<'a> {
|
||||
pub struct FromMetaImpl<'a> {
|
||||
pub base: TraitImpl<'a>,
|
||||
}
|
||||
|
||||
impl<'a> ToTokens for FmiImpl<'a> {
|
||||
fn to_tokens(&self, tokens: &mut Tokens) {
|
||||
impl<'a> ToTokens for FromMetaImpl<'a> {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let base = &self.base;
|
||||
|
||||
let impl_block = match base.data {
|
||||
|
@ -24,15 +25,23 @@ impl<'a> ToTokens for FmiImpl<'a> {
|
|||
}
|
||||
|
||||
// Newtype structs proxy to the sole value they contain.
|
||||
Data::Struct(Fields { ref fields, style: Style::Tuple, .. }) if fields.len() == 1 => {
|
||||
Data::Struct(Fields {
|
||||
ref fields,
|
||||
style: Style::Tuple,
|
||||
..
|
||||
}) if fields.len() == 1 =>
|
||||
{
|
||||
let ty_ident = base.ident;
|
||||
quote!(
|
||||
fn from_meta_item(__item: &::syn::Meta) -> ::darling::Result<Self> {
|
||||
Ok(#ty_ident(::darling::FromMetaItem::from_meta_item(__item)?))
|
||||
fn from_meta(__item: &::syn::Meta) -> ::darling::Result<Self> {
|
||||
Ok(#ty_ident(::darling::FromMeta::from_meta(__item)?))
|
||||
}
|
||||
)
|
||||
}
|
||||
Data::Struct(Fields { style: Style::Tuple, .. }) => {
|
||||
Data::Struct(Fields {
|
||||
style: Style::Tuple,
|
||||
..
|
||||
}) => {
|
||||
panic!("Multi-field tuples are not supported");
|
||||
}
|
||||
Data::Struct(ref data) => {
|
||||
|
@ -45,7 +54,6 @@ impl<'a> ToTokens for FmiImpl<'a> {
|
|||
let default = base.fallback_decl();
|
||||
let map = base.map_fn();
|
||||
|
||||
|
||||
quote!(
|
||||
fn from_list(__items: &[::syn::NestedMeta]) -> ::darling::Result<Self> {
|
||||
|
||||
|
@ -79,7 +87,7 @@ impl<'a> ToTokens for FmiImpl<'a> {
|
|||
0 => ::darling::export::Err(::darling::Error::too_few_items(1)),
|
||||
1 => {
|
||||
if let ::syn::NestedMeta::Meta(ref __nested) = __outer[0] {
|
||||
match __nested.name().as_ref() {
|
||||
match __nested.name().to_string().as_ref() {
|
||||
#(#struct_arms)*
|
||||
__other => ::darling::export::Err(::darling::Error::unknown_value(__other))
|
||||
}
|
||||
|
@ -105,9 +113,9 @@ impl<'a> ToTokens for FmiImpl<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> OuterFromImpl<'a> for FmiImpl<'a> {
|
||||
impl<'a> OuterFromImpl<'a> for FromMetaImpl<'a> {
|
||||
fn trait_path(&self) -> syn::Path {
|
||||
path!(::darling::FromMetaItem)
|
||||
path!(::darling::FromMeta)
|
||||
}
|
||||
|
||||
fn base(&'a self) -> &'a TraitImpl<'a> {
|
|
@ -0,0 +1,114 @@
|
|||
use proc_macro2::TokenStream;
|
||||
use quote::ToTokens;
|
||||
use syn::{self, Ident};
|
||||
|
||||
use codegen::{ExtractAttribute, OuterFromImpl, TraitImpl};
|
||||
use options::ForwardAttrs;
|
||||
use util::IdentList;
|
||||
|
||||
pub struct FromTypeParamImpl<'a> {
|
||||
pub base: TraitImpl<'a>,
|
||||
pub ident: Option<&'a Ident>,
|
||||
pub attrs: Option<&'a Ident>,
|
||||
pub bounds: Option<&'a Ident>,
|
||||
pub default: Option<&'a Ident>,
|
||||
pub attr_names: &'a IdentList,
|
||||
pub forward_attrs: Option<&'a ForwardAttrs>,
|
||||
pub from_ident: bool,
|
||||
}
|
||||
|
||||
impl<'a> ToTokens for FromTypeParamImpl<'a> {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let input = self.param_name();
|
||||
|
||||
let error_declaration = self.base.declare_errors();
|
||||
let grab_attrs = self.extractor();
|
||||
let require_fields = self.base.require_fields();
|
||||
let error_check = self.base.check_errors();
|
||||
|
||||
let default = if self.from_ident {
|
||||
quote!(let __default: Self = ::darling::export::From::from(#input.ident.clone());)
|
||||
} else {
|
||||
self.base.fallback_decl()
|
||||
};
|
||||
|
||||
let passed_ident = self.ident
|
||||
.as_ref()
|
||||
.map(|i| quote!(#i: #input.ident.clone(),));
|
||||
let passed_attrs = self.attrs.as_ref().map(|i| quote!(#i: __fwd_attrs,));
|
||||
let passed_bounds = self.bounds
|
||||
.as_ref()
|
||||
.map(|i| quote!(#i: #input.bounds.clone().into_iter().collect::<Vec<_>>(),));
|
||||
let passed_default = self.default
|
||||
.as_ref()
|
||||
.map(|i| quote!(#i: #input.default.clone(),));
|
||||
let initializers = self.base.initializers();
|
||||
|
||||
let map = self.base.map_fn();
|
||||
|
||||
self.wrap(
|
||||
quote! {
|
||||
fn from_type_param(#input: &::syn::TypeParam) -> ::darling::Result<Self> {
|
||||
#error_declaration
|
||||
|
||||
#grab_attrs
|
||||
|
||||
#require_fields
|
||||
|
||||
#error_check
|
||||
|
||||
#default
|
||||
|
||||
::darling::export::Ok(Self {
|
||||
#passed_ident
|
||||
#passed_bounds
|
||||
#passed_default
|
||||
#passed_attrs
|
||||
#initializers
|
||||
}) #map
|
||||
}
|
||||
},
|
||||
tokens,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ExtractAttribute for FromTypeParamImpl<'a> {
|
||||
fn attr_names(&self) -> &IdentList {
|
||||
&self.attr_names
|
||||
}
|
||||
|
||||
fn forwarded_attrs(&self) -> Option<&ForwardAttrs> {
|
||||
self.forward_attrs
|
||||
}
|
||||
|
||||
fn param_name(&self) -> TokenStream {
|
||||
quote!(__type_param)
|
||||
}
|
||||
|
||||
fn core_loop(&self) -> TokenStream {
|
||||
self.base.core_loop()
|
||||
}
|
||||
|
||||
fn local_declarations(&self) -> TokenStream {
|
||||
self.base.local_declarations()
|
||||
}
|
||||
|
||||
fn immutable_declarations(&self) -> TokenStream {
|
||||
self.base.immutable_declarations()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> OuterFromImpl<'a> for FromTypeParamImpl<'a> {
|
||||
fn trait_path(&self) -> syn::Path {
|
||||
path!(::darling::FromTypeParam)
|
||||
}
|
||||
|
||||
fn trait_bound(&self) -> syn::Path {
|
||||
path!(::darling::FromMeta)
|
||||
}
|
||||
|
||||
fn base(&'a self) -> &'a TraitImpl<'a> {
|
||||
&self.base
|
||||
}
|
||||
}
|
|
@ -1,47 +1,56 @@
|
|||
use quote::{Tokens, ToTokens};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::ToTokens;
|
||||
use syn::{self, Ident};
|
||||
|
||||
use codegen::{ExtractAttribute, OuterFromImpl, TraitImpl};
|
||||
use options::{DataShape, ForwardAttrs};
|
||||
use util::IdentList;
|
||||
|
||||
pub struct FromVariantImpl<'a> {
|
||||
pub base: TraitImpl<'a>,
|
||||
pub ident: Option<&'a Ident>,
|
||||
pub fields: Option<&'a Ident>,
|
||||
pub attrs: Option<&'a Ident>,
|
||||
pub attr_names: Vec<&'a str>,
|
||||
pub attr_names: &'a IdentList,
|
||||
pub forward_attrs: Option<&'a ForwardAttrs>,
|
||||
pub from_ident: Option<bool>,
|
||||
pub from_ident: bool,
|
||||
pub supports: Option<&'a DataShape>,
|
||||
}
|
||||
|
||||
impl<'a> ToTokens for FromVariantImpl<'a> {
|
||||
fn to_tokens(&self, tokens: &mut Tokens) {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let input = self.param_name();
|
||||
let extractor = self.extractor();
|
||||
let passed_ident = self.ident.as_ref().map(|i| quote!(#i: #input.ident.clone(),));
|
||||
let passed_ident = self.ident
|
||||
.as_ref()
|
||||
.map(|i| quote!(#i: #input.ident.clone(),));
|
||||
let passed_attrs = self.attrs.as_ref().map(|i| quote!(#i: __fwd_attrs,));
|
||||
let passed_fields = self.fields.as_ref().map(|i| quote!(#i: ::darling::ast::Fields::try_from(&#input.fields)?,));
|
||||
let passed_fields = self.fields
|
||||
.as_ref()
|
||||
.map(|i| quote!(#i: ::darling::ast::Fields::try_from(&#input.fields)?,));
|
||||
|
||||
let inits = self.base.initializers();
|
||||
let map = self.base.map_fn();
|
||||
|
||||
let default = if let Some(true) = self.from_ident {
|
||||
let default = if self.from_ident {
|
||||
quote!(let __default: Self = ::darling::export::From::from(#input.ident.clone());)
|
||||
} else {
|
||||
self.base.fallback_decl()
|
||||
};
|
||||
|
||||
let supports = self.supports.map(|i| quote! {
|
||||
#i
|
||||
__validate_data(&#input.fields)?;
|
||||
let supports = self.supports.map(|i| {
|
||||
quote! {
|
||||
#i
|
||||
__validate_data(&#input.fields)?;
|
||||
}
|
||||
});
|
||||
|
||||
let error_declaration = self.base.declare_errors();
|
||||
let require_fields = self.base.require_fields();
|
||||
let error_check = self.base.check_errors();
|
||||
|
||||
self.wrap(quote!(
|
||||
self.wrap(
|
||||
quote!(
|
||||
fn from_variant(#input: &::syn::Variant) -> ::darling::Result<Self> {
|
||||
#error_declaration
|
||||
|
||||
|
@ -62,44 +71,45 @@ impl<'a> ToTokens for FromVariantImpl<'a> {
|
|||
#inits
|
||||
}) #map
|
||||
}
|
||||
), tokens);
|
||||
),
|
||||
tokens,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ExtractAttribute for FromVariantImpl<'a> {
|
||||
fn local_declarations(&self) -> Tokens {
|
||||
fn local_declarations(&self) -> TokenStream {
|
||||
self.base.local_declarations()
|
||||
}
|
||||
|
||||
fn immutable_declarations(&self) -> Tokens {
|
||||
fn immutable_declarations(&self) -> TokenStream {
|
||||
self.base.immutable_declarations()
|
||||
}
|
||||
|
||||
fn attr_names(&self) -> &[&str] {
|
||||
self.attr_names.as_slice()
|
||||
fn attr_names(&self) -> &IdentList {
|
||||
&self.attr_names
|
||||
}
|
||||
|
||||
fn forwarded_attrs(&self) -> Option<&ForwardAttrs> {
|
||||
self.forward_attrs
|
||||
}
|
||||
|
||||
fn param_name(&self) -> Tokens {
|
||||
fn param_name(&self) -> TokenStream {
|
||||
quote!(__variant)
|
||||
}
|
||||
|
||||
fn core_loop(&self) -> Tokens {
|
||||
fn core_loop(&self) -> TokenStream {
|
||||
self.base.core_loop()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<'a> OuterFromImpl<'a> for FromVariantImpl<'a> {
|
||||
fn trait_path(&self) -> syn::Path {
|
||||
path!(::darling::FromVariant)
|
||||
}
|
||||
|
||||
fn trait_bound(&self) -> syn::Path {
|
||||
path!(::darling::FromMetaItem)
|
||||
path!(::darling::FromMeta)
|
||||
}
|
||||
|
||||
fn base(&'a self) -> &'a TraitImpl<'a> {
|
||||
|
|
|
@ -1,124 +1,29 @@
|
|||
use quote::Tokens;
|
||||
|
||||
mod attr_extractor;
|
||||
mod default_expr;
|
||||
mod error;
|
||||
mod field;
|
||||
mod fmi_impl;
|
||||
mod from_meta_impl;
|
||||
mod from_derive_impl;
|
||||
mod from_field;
|
||||
mod from_type_param;
|
||||
mod from_variant_impl;
|
||||
mod outer_from_impl;
|
||||
mod trait_impl;
|
||||
mod variant;
|
||||
mod variant_data;
|
||||
|
||||
pub(in codegen) use self::attr_extractor::ExtractAttribute;
|
||||
pub use self::default_expr::DefaultExpression;
|
||||
pub use self::field::Field;
|
||||
pub use self::fmi_impl::FmiImpl;
|
||||
pub use self::from_meta_impl::FromMetaImpl;
|
||||
pub use self::from_derive_impl::FromDeriveInputImpl;
|
||||
pub use self::from_field::FromFieldImpl;
|
||||
pub use self::from_type_param::FromTypeParamImpl;
|
||||
pub use self::from_variant_impl::FromVariantImpl;
|
||||
pub use self::outer_from_impl::OuterFromImpl;
|
||||
pub use self::trait_impl::TraitImpl;
|
||||
pub use self::variant::Variant;
|
||||
pub use self::variant_data::FieldsGen;
|
||||
|
||||
use options::ForwardAttrs;
|
||||
|
||||
/// Infrastructure for generating an attribute extractor.
|
||||
pub trait ExtractAttribute {
|
||||
fn local_declarations(&self) -> Tokens;
|
||||
|
||||
fn immutable_declarations(&self) -> Tokens;
|
||||
|
||||
/// Gets the list of attribute names that should be parsed by the extractor.
|
||||
fn attr_names(&self) -> &[&str];
|
||||
|
||||
fn forwarded_attrs(&self) -> Option<&ForwardAttrs>;
|
||||
|
||||
/// Gets the name used by the generated impl to return to the `syn` item passed as input.
|
||||
fn param_name(&self) -> Tokens;
|
||||
|
||||
/// Gets the core from-meta-item loop that should be used on matching attributes.
|
||||
fn core_loop(&self) -> Tokens;
|
||||
|
||||
fn declarations(&self) -> Tokens {
|
||||
if !self.attr_names().is_empty() {
|
||||
self.local_declarations()
|
||||
} else {
|
||||
self.immutable_declarations()
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates the main extraction loop.
|
||||
fn extractor(&self) -> Tokens {
|
||||
let declarations = self.declarations();
|
||||
|
||||
let will_parse_any = !self.attr_names().is_empty();
|
||||
let will_fwd_any = self.forwarded_attrs().map(|fa| !fa.is_empty()).unwrap_or_default();
|
||||
|
||||
if !(will_parse_any || will_fwd_any) {
|
||||
return quote! {
|
||||
#declarations
|
||||
};
|
||||
}
|
||||
|
||||
let input = self.param_name();
|
||||
|
||||
// The block for parsing attributes whose names have been claimed by the target
|
||||
// struct. If no attributes were claimed, this is a pass-through.
|
||||
let parse_handled = if will_parse_any {
|
||||
let attr_names = self.attr_names();
|
||||
let core_loop = self.core_loop();
|
||||
quote!(
|
||||
#(#attr_names)|* => {
|
||||
if let Some(::syn::Meta::List(ref __data)) = __attr.interpret_meta() {
|
||||
let __items = &__data.nested;
|
||||
|
||||
#core_loop
|
||||
} else {
|
||||
// darling currently only supports list-style
|
||||
continue
|
||||
}
|
||||
}
|
||||
)
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
|
||||
// Specifies the behavior for unhandled attributes. They will either be silently ignored or
|
||||
// forwarded to the inner struct for later analysis.
|
||||
let forward_unhandled = if will_fwd_any {
|
||||
forwards_to_local(self.forwarded_attrs().unwrap())
|
||||
} else {
|
||||
quote!(_ => continue)
|
||||
};
|
||||
|
||||
quote!(
|
||||
#declarations
|
||||
let mut __fwd_attrs: ::darling::export::Vec<::syn::Attribute> = vec![];
|
||||
|
||||
for __attr in &#input.attrs {
|
||||
// Filter attributes based on name
|
||||
match __attr.path.segments.iter().map(|s| s.ident.as_ref()).collect::<Vec<&str>>().join("::").as_str() {
|
||||
#parse_handled
|
||||
#forward_unhandled
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn forwards_to_local(behavior: &ForwardAttrs) -> Tokens {
|
||||
let push_command = quote!(__fwd_attrs.push(__attr.clone()));
|
||||
match *behavior {
|
||||
ForwardAttrs::All => quote!(_ => #push_command),
|
||||
ForwardAttrs::Only(ref idents) => {
|
||||
let names = idents.as_strs();
|
||||
quote!(
|
||||
#(#names)|* => #push_command,
|
||||
_ => continue,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
use syn::{Generics, Path, TraitBound, TraitBoundModifier, TypeParamBound, GenericParam};
|
||||
use quote::{Tokens, ToTokens};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{TokenStreamExt, ToTokens};
|
||||
use syn::{GenericParam, Generics, Path, TraitBound, TraitBoundModifier, TypeParamBound};
|
||||
|
||||
use codegen::TraitImpl;
|
||||
use usage::IdentSet;
|
||||
|
||||
/// Wrapper for "outer From" traits, such as `FromDeriveInput`, `FromVariant`, and `FromField`.
|
||||
pub trait OuterFromImpl<'a> {
|
||||
|
@ -14,11 +16,14 @@ pub trait OuterFromImpl<'a> {
|
|||
self.trait_path()
|
||||
}
|
||||
|
||||
fn wrap<T: ToTokens>(&'a self, body: T, tokens: &mut Tokens) {
|
||||
fn wrap<T: ToTokens>(&'a self, body: T, tokens: &mut TokenStream) {
|
||||
let base = self.base();
|
||||
let trayt = self.trait_path();
|
||||
let ty_ident = base.ident;
|
||||
let generics = compute_impl_bounds(self.trait_bound(), base.generics.clone());
|
||||
// The type parameters used in non-skipped, non-magic fields.
|
||||
// These must impl `FromMeta` unless they have custom bounds.
|
||||
let used = base.used_type_params();
|
||||
let generics = compute_impl_bounds(self.trait_bound(), base.generics.clone(), &used);
|
||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||
|
||||
tokens.append_all(quote!(
|
||||
|
@ -31,21 +36,23 @@ pub trait OuterFromImpl<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn compute_impl_bounds(bound: Path, mut generics: Generics) -> Generics {
|
||||
fn compute_impl_bounds(bound: Path, mut generics: Generics, applies_to: &IdentSet) -> Generics {
|
||||
if generics.params.is_empty() {
|
||||
return generics;
|
||||
}
|
||||
|
||||
let added_bound = TypeParamBound::Trait(TraitBound {
|
||||
paren_token: None,
|
||||
modifier: TraitBoundModifier::None,
|
||||
lifetimes: None,
|
||||
path: bound,
|
||||
});
|
||||
paren_token: None,
|
||||
modifier: TraitBoundModifier::None,
|
||||
lifetimes: None,
|
||||
path: bound,
|
||||
});
|
||||
|
||||
for mut param in generics.params.iter_mut() {
|
||||
if let &mut GenericParam::Type(ref mut typ) = param {
|
||||
typ.bounds.push(added_bound.clone());
|
||||
if applies_to.contains(&typ.ident) {
|
||||
typ.bounds.push(added_bound.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
use quote::Tokens;
|
||||
use proc_macro2::TokenStream;
|
||||
use syn::{Generics, Ident, Path, WherePredicate};
|
||||
|
||||
use codegen::{DefaultExpression, Field, Variant, FieldsGen};
|
||||
use codegen::field;
|
||||
use ast::{Data, Fields};
|
||||
use codegen::error::{ErrorCheck, ErrorDeclaration};
|
||||
use ast::Data;
|
||||
use codegen::field;
|
||||
use codegen::{DefaultExpression, Field, FieldsGen, Variant};
|
||||
use usage::{CollectTypeParams, IdentSet, Purpose};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TraitImpl<'a> {
|
||||
|
@ -16,6 +17,64 @@ pub struct TraitImpl<'a> {
|
|||
pub bound: Option<&'a [WherePredicate]>,
|
||||
}
|
||||
|
||||
impl<'a> TraitImpl<'a> {
|
||||
/// Get all declared type parameters.
|
||||
pub fn declared_type_params(&self) -> IdentSet {
|
||||
self.generics
|
||||
.type_params()
|
||||
.map(|tp| tp.ident.clone())
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Get the type parameters which are used by non-skipped fields.
|
||||
pub fn used_type_params(&self) -> IdentSet {
|
||||
self.type_params_matching(|f| !f.skip, |v| !v.skip)
|
||||
}
|
||||
|
||||
/// Get the type parameters which are used by skipped fields.
|
||||
pub fn skipped_type_params(&self) -> IdentSet {
|
||||
self.type_params_matching(|f| f.skip, |v| v.skip)
|
||||
}
|
||||
|
||||
fn type_params_matching<'b, F, V>(&'b self, field_filter: F, variant_filter: V) -> IdentSet
|
||||
where
|
||||
F: Fn(&&Field) -> bool,
|
||||
V: Fn(&&Variant) -> bool,
|
||||
{
|
||||
let declared = self.declared_type_params();
|
||||
match self.data {
|
||||
Data::Struct(ref v) => self.type_params_in_fields(v, &field_filter, &declared),
|
||||
Data::Enum(ref v) => v.iter().filter(variant_filter).fold(
|
||||
Default::default(),
|
||||
|mut state, variant| {
|
||||
state.extend(self.type_params_in_fields(
|
||||
&variant.data,
|
||||
&field_filter,
|
||||
&declared,
|
||||
));
|
||||
state
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the type parameters of all fields in a set matching some filter
|
||||
fn type_params_in_fields<'b, F>(
|
||||
&'b self,
|
||||
fields: &'b Fields<Field<'a>>,
|
||||
field_filter: F,
|
||||
declared: &IdentSet,
|
||||
) -> IdentSet
|
||||
where
|
||||
F: Fn(&&'b Field) -> bool,
|
||||
{
|
||||
fields
|
||||
.iter()
|
||||
.filter(field_filter)
|
||||
.collect_type_params_cloned(&Purpose::BoundImpl.into(), declared)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TraitImpl<'a> {
|
||||
/// Gets the `let` declaration for errors accumulated during parsing.
|
||||
pub fn declare_errors(&self) -> ErrorDeclaration {
|
||||
|
@ -28,7 +87,7 @@ impl<'a> TraitImpl<'a> {
|
|||
}
|
||||
|
||||
/// Generate local variable declarations for all fields.
|
||||
pub(in codegen) fn local_declarations(&self) -> Tokens {
|
||||
pub(in codegen) fn local_declarations(&self) -> TokenStream {
|
||||
if let Data::Struct(ref vd) = self.data {
|
||||
let vdr = vd.as_ref().map(Field::as_declaration);
|
||||
let decls = vdr.fields.as_slice();
|
||||
|
@ -39,7 +98,7 @@ impl<'a> TraitImpl<'a> {
|
|||
}
|
||||
|
||||
/// Generate immutable variable declarations for all fields.
|
||||
pub(in codegen) fn immutable_declarations(&self) -> Tokens {
|
||||
pub(in codegen) fn immutable_declarations(&self) -> TokenStream {
|
||||
if let Data::Struct(ref vd) = self.data {
|
||||
let vdr = vd.as_ref().map(|f| field::Declaration::new(f, false));
|
||||
let decls = vdr.fields.as_slice();
|
||||
|
@ -49,17 +108,17 @@ impl<'a> TraitImpl<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(in codegen) fn map_fn(&self) -> Option<Tokens> {
|
||||
pub(in codegen) fn map_fn(&self) -> Option<TokenStream> {
|
||||
self.map.as_ref().map(|path| quote!(.map(#path)))
|
||||
}
|
||||
|
||||
/// Generate local variable declaration and initialization for instance from which missing fields will be taken.
|
||||
pub(in codegen) fn fallback_decl(&self) -> Tokens {
|
||||
pub(in codegen) fn fallback_decl(&self) -> TokenStream {
|
||||
let default = self.default.as_ref().map(DefaultExpression::as_declaration);
|
||||
quote!(#default)
|
||||
}
|
||||
|
||||
pub fn require_fields(&self) -> Tokens {
|
||||
pub fn require_fields(&self) -> TokenStream {
|
||||
if let Data::Struct(ref vd) = self.data {
|
||||
let check_nones = vd.as_ref().map(Field::as_presence_check);
|
||||
let checks = check_nones.fields.as_slice();
|
||||
|
@ -69,24 +128,20 @@ impl<'a> TraitImpl<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(in codegen) fn initializers(&self) -> Tokens {
|
||||
pub(in codegen) fn initializers(&self) -> TokenStream {
|
||||
let foo = match self.data {
|
||||
Data::Enum(_) => panic!("Core loop on enums isn't supported"),
|
||||
Data::Struct(ref data) => {
|
||||
FieldsGen(data)
|
||||
}
|
||||
Data::Struct(ref data) => FieldsGen(data),
|
||||
};
|
||||
|
||||
foo.initializers()
|
||||
}
|
||||
|
||||
/// Generate the loop which walks meta items looking for property matches.
|
||||
pub(in codegen) fn core_loop(&self) -> Tokens {
|
||||
pub(in codegen) fn core_loop(&self) -> TokenStream {
|
||||
let foo = match self.data {
|
||||
Data::Enum(_) => panic!("Core loop on enums isn't supported"),
|
||||
Data::Struct(ref data) => {
|
||||
FieldsGen(data)
|
||||
}
|
||||
Data::Struct(ref data) => FieldsGen(data),
|
||||
};
|
||||
|
||||
foo.core_loop()
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
use quote::{Tokens, ToTokens};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{TokenStreamExt, ToTokens};
|
||||
use syn::Ident;
|
||||
|
||||
use ast::Fields;
|
||||
use codegen::{Field, FieldsGen};
|
||||
use codegen::error::{ErrorCheck, ErrorDeclaration};
|
||||
use codegen::{Field, FieldsGen};
|
||||
use usage::{self, IdentRefSet, IdentSet, UsesTypeParams};
|
||||
|
||||
/// An enum variant.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Variant<'a> {
|
||||
/// The name which will appear in code passed to the `FromMetaItem` input.
|
||||
pub name_in_attr: &'a str,
|
||||
/// The name which will appear in code passed to the `FromMeta` input.
|
||||
pub name_in_attr: String,
|
||||
|
||||
/// The name of the variant which will be returned for a given `name_in_attr`.
|
||||
pub variant_ident: &'a Ident,
|
||||
|
@ -33,17 +35,27 @@ impl<'a> Variant<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> UsesTypeParams for Variant<'a> {
|
||||
fn uses_type_params<'b>(
|
||||
&self,
|
||||
options: &usage::Options,
|
||||
type_set: &'b IdentSet,
|
||||
) -> IdentRefSet<'b> {
|
||||
self.data.uses_type_params(options, type_set)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UnitMatchArm<'a>(&'a Variant<'a>);
|
||||
|
||||
impl<'a> ToTokens for UnitMatchArm<'a> {
|
||||
fn to_tokens(&self, tokens: &mut Tokens) {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let val: &Variant<'a> = self.0;
|
||||
|
||||
if val.skip {
|
||||
return;
|
||||
}
|
||||
|
||||
let name_in_attr = val.name_in_attr;
|
||||
let name_in_attr = &val.name_in_attr;
|
||||
|
||||
if val.data.is_unit() {
|
||||
let variant_ident = val.variant_ident;
|
||||
|
@ -63,14 +75,14 @@ impl<'a> ToTokens for UnitMatchArm<'a> {
|
|||
pub struct DataMatchArm<'a>(&'a Variant<'a>);
|
||||
|
||||
impl<'a> ToTokens for DataMatchArm<'a> {
|
||||
fn to_tokens(&self, tokens: &mut Tokens) {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let val: &Variant<'a> = self.0;
|
||||
|
||||
if val.skip {
|
||||
return;
|
||||
}
|
||||
|
||||
let name_in_attr = val.name_in_attr;
|
||||
let name_in_attr = &val.name_in_attr;
|
||||
let variant_ident = val.variant_ident;
|
||||
let ty_ident = val.ty_ident;
|
||||
|
||||
|
@ -82,12 +94,11 @@ impl<'a> ToTokens for DataMatchArm<'a> {
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
let vdg = FieldsGen(&val.data);
|
||||
|
||||
if val.data.is_struct() {
|
||||
let declare_errors = ErrorDeclaration::new();
|
||||
let check_errors = ErrorCheck::with_location(name_in_attr);
|
||||
let check_errors = ErrorCheck::with_location(&name_in_attr);
|
||||
let require_fields = vdg.require_fields();
|
||||
let decls = vdg.declarations();
|
||||
let core_loop = vdg.core_loop();
|
||||
|
@ -121,7 +132,7 @@ impl<'a> ToTokens for DataMatchArm<'a> {
|
|||
#name_in_attr => {
|
||||
::darling::export::Ok(
|
||||
#ty_ident::#variant_ident(
|
||||
::darling::FromMetaItem::from_meta_item(__nested)
|
||||
::darling::FromMeta::from_meta(__nested)
|
||||
.map_err(|e| e.at(#name_in_attr))?)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
use quote::Tokens;
|
||||
use proc_macro2::TokenStream;
|
||||
|
||||
use ast::Fields;
|
||||
use ast::Style;
|
||||
use codegen::field;
|
||||
use codegen::Field;
|
||||
use ast::Fields;
|
||||
|
||||
pub struct FieldsGen<'a>(pub &'a Fields<Field<'a>>);
|
||||
|
||||
impl<'a> FieldsGen<'a> {
|
||||
pub(in codegen) fn declarations(&self) -> Tokens {
|
||||
pub(in codegen) fn declarations(&self) -> TokenStream {
|
||||
match *self.0 {
|
||||
Fields { style: Style::Struct, ref fields } => {
|
||||
Fields {
|
||||
style: Style::Struct,
|
||||
ref fields,
|
||||
} => {
|
||||
let vdr = fields.into_iter().map(Field::as_declaration);
|
||||
quote!(#(#vdr)*)
|
||||
}
|
||||
|
@ -19,7 +22,7 @@ impl<'a> FieldsGen<'a> {
|
|||
}
|
||||
|
||||
/// Generate the loop which walks meta items looking for property matches.
|
||||
pub(in codegen) fn core_loop(&self) -> Tokens {
|
||||
pub(in codegen) fn core_loop(&self) -> TokenStream {
|
||||
let arms: Vec<field::MatchArm> = self.0.as_ref().map(Field::as_match).fields;
|
||||
|
||||
quote!(
|
||||
|
@ -35,17 +38,20 @@ impl<'a> FieldsGen<'a> {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn require_fields(&self) -> Tokens {
|
||||
pub fn require_fields(&self) -> TokenStream {
|
||||
match *self.0 {
|
||||
Fields { style: Style::Struct, ref fields } => {
|
||||
Fields {
|
||||
style: Style::Struct,
|
||||
ref fields,
|
||||
} => {
|
||||
let checks = fields.into_iter().map(Field::as_presence_check);
|
||||
quote!(#(#checks)*)
|
||||
}
|
||||
_ => panic!("FieldsGen doesn't support tuples for requirement checks")
|
||||
_ => panic!("FieldsGen doesn't support tuples for requirement checks"),
|
||||
}
|
||||
}
|
||||
|
||||
pub(in codegen) fn initializers(&self) -> Tokens {
|
||||
pub(in codegen) fn initializers(&self) -> TokenStream {
|
||||
let inits: Vec<_> = self.0.as_ref().map(Field::as_initializer).fields;
|
||||
|
||||
quote!(#(#inits),*)
|
||||
|
|
|
@ -51,6 +51,11 @@ impl Error {
|
|||
Error::new(ErrorKind::UnknownField(name.into()))
|
||||
}
|
||||
|
||||
/// Creates a new error for a struct or variant that does not adhere to the supported shape.
|
||||
pub fn unsupported_shape(shape: &str) -> Self {
|
||||
Error::new(ErrorKind::UnsupportedShape(shape.into()))
|
||||
}
|
||||
|
||||
pub fn unsupported_format(format: &str) -> Self {
|
||||
Error::new(ErrorKind::UnexpectedFormat(format.into()))
|
||||
}
|
||||
|
@ -84,7 +89,9 @@ impl Error {
|
|||
if errors.len() > 1 {
|
||||
Error::new(ErrorKind::Multiple(errors))
|
||||
} else if errors.len() == 1 {
|
||||
errors.pop().expect("Error array of length 1 has a first item")
|
||||
errors
|
||||
.pop()
|
||||
.expect("Error array of length 1 has a first item")
|
||||
} else {
|
||||
panic!("Can't deal with 0 errors")
|
||||
}
|
||||
|
@ -168,11 +175,11 @@ impl IntoIterator for Error {
|
|||
fn into_iter(self) -> IntoIter {
|
||||
if let ErrorKind::Multiple(errors) = self.kind {
|
||||
IntoIter {
|
||||
inner: IntoIterEnum::Multiple(errors.into_iter())
|
||||
inner: IntoIterEnum::Multiple(errors.into_iter()),
|
||||
}
|
||||
} else {
|
||||
IntoIter {
|
||||
inner: IntoIterEnum::Single(iter::once(self))
|
||||
inner: IntoIterEnum::Single(iter::once(self)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -196,7 +203,7 @@ impl Iterator for IntoIterEnum {
|
|||
|
||||
/// An iterator that moves out of an `Error`.
|
||||
pub struct IntoIter {
|
||||
inner: IntoIterEnum
|
||||
inner: IntoIterEnum,
|
||||
}
|
||||
|
||||
impl Iterator for IntoIter {
|
||||
|
@ -207,6 +214,7 @@ impl Iterator for IntoIter {
|
|||
}
|
||||
}
|
||||
|
||||
type DeriveInputShape = String;
|
||||
type FieldName = String;
|
||||
type MetaFormat = String;
|
||||
|
||||
|
@ -219,6 +227,7 @@ enum ErrorKind {
|
|||
Custom(String),
|
||||
DuplicateField(FieldName),
|
||||
MissingField(FieldName),
|
||||
UnsupportedShape(DeriveInputShape),
|
||||
UnknownField(FieldName),
|
||||
UnexpectedFormat(MetaFormat),
|
||||
UnexpectedType(String),
|
||||
|
@ -230,7 +239,7 @@ enum ErrorKind {
|
|||
|
||||
// TODO make this variant take `!` so it can't exist
|
||||
#[doc(hidden)]
|
||||
__NonExhaustive
|
||||
__NonExhaustive,
|
||||
}
|
||||
|
||||
impl ErrorKind {
|
||||
|
@ -242,6 +251,7 @@ impl ErrorKind {
|
|||
DuplicateField(_) => "Duplicate field",
|
||||
MissingField(_) => "Missing field",
|
||||
UnknownField(_) => "Unexpected field",
|
||||
UnsupportedShape(_) => "Unsupported shape",
|
||||
UnexpectedFormat(_) => "Unexpected meta-item format",
|
||||
UnexpectedType(_) => "Unexpected literal type",
|
||||
UnknownValue(_) => "Unknown literal value",
|
||||
|
@ -271,6 +281,7 @@ impl fmt::Display for ErrorKind {
|
|||
DuplicateField(ref field) => write!(f, "Duplicate field `{}`", field),
|
||||
MissingField(ref field) => write!(f, "Missing field `{}`", field),
|
||||
UnknownField(ref field) => write!(f, "Unexpected field `{}`", field),
|
||||
UnsupportedShape(ref shape) => write!(f, "Unsupported shape `{}`", shape),
|
||||
UnexpectedFormat(ref format) => write!(f, "Unexpected meta-item format `{}`", format),
|
||||
UnexpectedType(ref ty) => write!(f, "Unexpected literal type `{}`", ty),
|
||||
UnknownValue(ref val) => write!(f, "Unknown literal value `{}`", val),
|
||||
|
@ -291,7 +302,7 @@ impl fmt::Display for ErrorKind {
|
|||
}
|
||||
|
||||
write!(f, ")")
|
||||
},
|
||||
}
|
||||
__NonExhaustive => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
@ -311,8 +322,9 @@ mod tests {
|
|||
fn flatten_simple() {
|
||||
let err = Error::multiple(vec![
|
||||
Error::unknown_field("hello").at("world"),
|
||||
Error::missing_field("hell_no").at("world")
|
||||
]).at("foo").flatten();
|
||||
Error::missing_field("hell_no").at("world"),
|
||||
]).at("foo")
|
||||
.flatten();
|
||||
|
||||
assert!(err.location().is_empty());
|
||||
|
||||
|
@ -340,7 +352,7 @@ mod tests {
|
|||
fn len_multiple() {
|
||||
let err = Error::multiple(vec![
|
||||
Error::duplicate_field("hello"),
|
||||
Error::missing_field("hell_no")
|
||||
Error::missing_field("hell_no"),
|
||||
]);
|
||||
assert_eq!(2, err.len());
|
||||
}
|
||||
|
@ -352,10 +364,8 @@ mod tests {
|
|||
Error::multiple(vec![
|
||||
Error::duplicate_field("hi"),
|
||||
Error::missing_field("bye"),
|
||||
Error::multiple(vec![
|
||||
Error::duplicate_field("whatsup")
|
||||
])
|
||||
])
|
||||
Error::multiple(vec![Error::duplicate_field("whatsup")]),
|
||||
]),
|
||||
]);
|
||||
|
||||
assert_eq!(4, err.len());
|
||||
|
|
|
@ -23,4 +23,4 @@ impl FromDeriveInput for DeriveInput {
|
|||
fn from_derive_input(input: &DeriveInput) -> Result<Self> {
|
||||
Ok(input.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,4 +35,4 @@ impl FromField for Vec<syn::Attribute> {
|
|||
fn from_field(field: &Field) -> Result<Self> {
|
||||
Ok(field.attrs.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
use syn;
|
||||
|
||||
use Result;
|
||||
|
||||
/// Creates an instance by parsing a specific `syn::GenericParam`.
|
||||
/// This can be a type param, a lifetime, or a const param.
|
||||
pub trait FromGenericParam: Sized {
|
||||
fn from_generic_param(param: &syn::GenericParam) -> Result<Self>;
|
||||
}
|
||||
|
||||
impl FromGenericParam for () {
|
||||
fn from_generic_param(_param: &syn::GenericParam) -> Result<Self> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromGenericParam for syn::GenericParam {
|
||||
fn from_generic_param(param: &syn::GenericParam) -> Result<Self> {
|
||||
Ok(param.clone())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
use syn::Generics;
|
||||
|
||||
use Result;
|
||||
|
||||
/// Creates an instance by parsing an entire generics declaration, including the
|
||||
/// `where` clause.
|
||||
pub trait FromGenerics: Sized {
|
||||
fn from_generics(generics: &Generics) -> Result<Self>;
|
||||
}
|
||||
|
||||
impl FromGenerics for () {
|
||||
fn from_generics(_generics: &Generics) -> Result<Self> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromGenerics for Generics {
|
||||
fn from_generics(generics: &Generics) -> Result<Self> {
|
||||
Ok(generics.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FromGenerics> FromGenerics for Result<T> {
|
||||
fn from_generics(generics: &Generics) -> Result<Self> {
|
||||
Ok(FromGenerics::from_generics(generics))
|
||||
}
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
use std::cell::RefCell;
|
||||
use std::collections::hash_map::{Entry, HashMap};
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::Arc;
|
||||
|
||||
use ident_case;
|
||||
use syn::{self, Lit, Meta, NestedMeta};
|
||||
|
@ -11,7 +11,7 @@ use {Error, Result};
|
|||
|
||||
/// Create an instance from an item in an attribute declaration.
|
||||
///
|
||||
/// # Implementing `FromMetaItem`
|
||||
/// # Implementing `FromMeta`
|
||||
/// * Do not take a dependency on the `ident` of the passed-in meta item. The ident will be set by the field name of the containing struct.
|
||||
/// * Implement only the `from_*` methods that you intend to support. The default implementations will return useful errors.
|
||||
///
|
||||
|
@ -36,20 +36,26 @@ use {Error, Result};
|
|||
/// ## `Result<T, darling::Error>`
|
||||
/// * Allows for fallible parsing; will populate the target field with the result of the
|
||||
/// parse attempt.
|
||||
pub trait FromMetaItem: Sized {
|
||||
fn from_nested_meta_item(item: &NestedMeta) -> Result<Self> {
|
||||
pub trait FromMeta: Sized {
|
||||
fn from_nested_meta(item: &NestedMeta) -> Result<Self> {
|
||||
match *item {
|
||||
NestedMeta::Literal(ref lit) => Self::from_value(lit),
|
||||
NestedMeta::Meta(ref mi) => Self::from_meta_item(mi),
|
||||
NestedMeta::Meta(ref mi) => Self::from_meta(mi),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an instance from a `syn::Meta` by dispatching to the format-appropriate
|
||||
/// trait function. This generally should not be overridden by implementers.
|
||||
fn from_meta_item(item: &Meta) -> Result<Self> {
|
||||
fn from_meta(item: &Meta) -> Result<Self> {
|
||||
match *item {
|
||||
Meta::Word(_) => Self::from_word(),
|
||||
Meta::List(ref value) => Self::from_list(&value.nested.clone().into_iter().collect::<Vec<syn::NestedMeta>>()[..]),
|
||||
Meta::List(ref value) => Self::from_list(
|
||||
&value
|
||||
.nested
|
||||
.clone()
|
||||
.into_iter()
|
||||
.collect::<Vec<syn::NestedMeta>>()[..],
|
||||
),
|
||||
Meta::NameValue(ref value) => Self::from_value(&value.lit),
|
||||
}
|
||||
}
|
||||
|
@ -73,7 +79,7 @@ pub trait FromMetaItem: Sized {
|
|||
match *value {
|
||||
Lit::Bool(ref b) => Self::from_bool(b.value),
|
||||
Lit::Str(ref s) => Self::from_string(&s.value()),
|
||||
ref _other => Err(Error::unexpected_type("other"))
|
||||
ref _other => Err(Error::unexpected_type("other")),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -96,15 +102,15 @@ pub trait FromMetaItem: Sized {
|
|||
}
|
||||
}
|
||||
|
||||
// FromMetaItem impls for std and syn types.
|
||||
// FromMeta impls for std and syn types.
|
||||
|
||||
impl FromMetaItem for () {
|
||||
impl FromMeta for () {
|
||||
fn from_word() -> Result<Self> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromMetaItem for bool {
|
||||
impl FromMeta for bool {
|
||||
fn from_word() -> Result<Self> {
|
||||
Ok(true)
|
||||
}
|
||||
|
@ -114,181 +120,181 @@ impl FromMetaItem for bool {
|
|||
}
|
||||
|
||||
fn from_string(value: &str) -> Result<Self> {
|
||||
value.parse().or_else(|_| Err(Error::unknown_value(value)))
|
||||
value.parse().map_err(|_| Error::unknown_value(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromMetaItem for AtomicBool {
|
||||
fn from_meta_item(mi: &Meta) -> Result<Self> {
|
||||
Ok(AtomicBool::new(FromMetaItem::from_meta_item(mi)?))
|
||||
impl FromMeta for AtomicBool {
|
||||
fn from_meta(mi: &Meta) -> Result<Self> {
|
||||
FromMeta::from_meta(mi).map(AtomicBool::new)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromMetaItem for String {
|
||||
impl FromMeta for String {
|
||||
fn from_string(s: &str) -> Result<Self> {
|
||||
Ok(s.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromMetaItem for u8 {
|
||||
impl FromMeta for u8 {
|
||||
fn from_string(s: &str) -> Result<Self> {
|
||||
s.parse().or_else(|_| Err(Error::unknown_value(s)))
|
||||
s.parse().map_err(|_| Error::unknown_value(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromMetaItem for u16 {
|
||||
impl FromMeta for u16 {
|
||||
fn from_string(s: &str) -> Result<Self> {
|
||||
s.parse().or_else(|_| Err(Error::unknown_value(s)))
|
||||
s.parse().map_err(|_| Error::unknown_value(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromMetaItem for u32 {
|
||||
impl FromMeta for u32 {
|
||||
fn from_string(s: &str) -> Result<Self> {
|
||||
s.parse().or_else(|_| Err(Error::unknown_value(s)))
|
||||
s.parse().map_err(|_| Error::unknown_value(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromMetaItem for u64 {
|
||||
impl FromMeta for u64 {
|
||||
fn from_string(s: &str) -> Result<Self> {
|
||||
s.parse().or_else(|_| Err(Error::unknown_value(s)))
|
||||
s.parse().map_err(|_| Error::unknown_value(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromMetaItem for usize {
|
||||
impl FromMeta for usize {
|
||||
fn from_string(s: &str) -> Result<Self> {
|
||||
s.parse().or_else(|_| Err(Error::unknown_value(s)))
|
||||
s.parse().map_err(|_| Error::unknown_value(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromMetaItem for i8 {
|
||||
impl FromMeta for i8 {
|
||||
fn from_string(s: &str) -> Result<Self> {
|
||||
s.parse().or_else(|_| Err(Error::unknown_value(s)))
|
||||
s.parse().map_err(|_| Error::unknown_value(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromMetaItem for i16 {
|
||||
impl FromMeta for i16 {
|
||||
fn from_string(s: &str) -> Result<Self> {
|
||||
s.parse().or_else(|_| Err(Error::unknown_value(s)))
|
||||
s.parse().map_err(|_| Error::unknown_value(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromMetaItem for i32 {
|
||||
impl FromMeta for i32 {
|
||||
fn from_string(s: &str) -> Result<Self> {
|
||||
s.parse().or_else(|_| Err(Error::unknown_value(s)))
|
||||
s.parse().map_err(|_| Error::unknown_value(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromMetaItem for i64 {
|
||||
impl FromMeta for i64 {
|
||||
fn from_string(s: &str) -> Result<Self> {
|
||||
s.parse().or_else(|_| Err(Error::unknown_value(s)))
|
||||
s.parse().map_err(|_| Error::unknown_value(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromMetaItem for isize {
|
||||
impl FromMeta for isize {
|
||||
fn from_string(s: &str) -> Result<Self> {
|
||||
s.parse().or_else(|_| Err(Error::unknown_value(s)))
|
||||
s.parse().map_err(|_| Error::unknown_value(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromMetaItem for syn::Ident {
|
||||
impl FromMeta for syn::Ident {
|
||||
fn from_string(value: &str) -> Result<Self> {
|
||||
Ok(syn::Ident::new(value, ::proc_macro2::Span::call_site()))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromMetaItem for syn::Path {
|
||||
impl FromMeta for syn::Path {
|
||||
fn from_string(value: &str) -> Result<Self> {
|
||||
Ok(syn::parse_str::<syn::Path>(value).unwrap())
|
||||
syn::parse_str(value).map_err(|_| Error::unknown_value(value))
|
||||
}
|
||||
}
|
||||
/*
|
||||
impl FromMetaItem for syn::TypeParamBound {
|
||||
impl FromMeta for syn::TypeParamBound {
|
||||
fn from_string(value: &str) -> Result<Self> {
|
||||
Ok(syn::TypeParamBound::from(value))
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
impl FromMetaItem for syn::Meta {
|
||||
fn from_meta_item(value: &syn::Meta) -> Result<Self> {
|
||||
impl FromMeta for syn::Meta {
|
||||
fn from_meta(value: &syn::Meta) -> Result<Self> {
|
||||
Ok(value.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromMetaItem for syn::WhereClause {
|
||||
impl FromMeta for syn::WhereClause {
|
||||
fn from_string(value: &str) -> Result<Self> {
|
||||
let ret: syn::WhereClause = syn::parse_str(value).unwrap();
|
||||
Ok(ret)
|
||||
syn::parse_str(value).map_err(|_| Error::unknown_value(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromMetaItem for Vec<syn::WherePredicate> {
|
||||
impl FromMeta for Vec<syn::WherePredicate> {
|
||||
fn from_string(value: &str) -> Result<Self> {
|
||||
syn::WhereClause::from_string(&format!("where {}", value)).map(|c| c.predicates.into_iter().collect())
|
||||
syn::WhereClause::from_string(&format!("where {}", value))
|
||||
.map(|c| c.predicates.into_iter().collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromMetaItem for ident_case::RenameRule {
|
||||
impl FromMeta for ident_case::RenameRule {
|
||||
fn from_string(value: &str) -> Result<Self> {
|
||||
value.parse().or_else(|_| Err(Error::unknown_value(value)))
|
||||
value.parse().map_err(|_| Error::unknown_value(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FromMetaItem> FromMetaItem for Option<T> {
|
||||
fn from_meta_item(item: &Meta) -> Result<Self> {
|
||||
Ok(Some(FromMetaItem::from_meta_item(item)?))
|
||||
impl<T: FromMeta> FromMeta for Option<T> {
|
||||
fn from_meta(item: &Meta) -> Result<Self> {
|
||||
FromMeta::from_meta(item).map(Some)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FromMetaItem> FromMetaItem for Box<T> {
|
||||
fn from_meta_item(item: &Meta) -> Result<Self> {
|
||||
Ok(Box::new(FromMetaItem::from_meta_item(item)?))
|
||||
impl<T: FromMeta> FromMeta for Box<T> {
|
||||
fn from_meta(item: &Meta) -> Result<Self> {
|
||||
FromMeta::from_meta(item).map(Box::new)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FromMetaItem> FromMetaItem for Result<T> {
|
||||
fn from_meta_item(item: &Meta) -> Result<Self> {
|
||||
Ok(FromMetaItem::from_meta_item(item))
|
||||
impl<T: FromMeta> FromMeta for Result<T> {
|
||||
fn from_meta(item: &Meta) -> Result<Self> {
|
||||
Ok(FromMeta::from_meta(item))
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses the meta-item, and in case of error preserves a copy of the input for
|
||||
/// later analysis.
|
||||
impl<T: FromMetaItem> FromMetaItem for ::std::result::Result<T, Meta> {
|
||||
fn from_meta_item(item: &Meta) -> Result<Self> {
|
||||
T::from_meta_item(item).map(Ok).or_else(|_| Ok(Err(item.clone())))
|
||||
impl<T: FromMeta> FromMeta for ::std::result::Result<T, Meta> {
|
||||
fn from_meta(item: &Meta) -> Result<Self> {
|
||||
T::from_meta(item)
|
||||
.map(Ok)
|
||||
.or_else(|_| Ok(Err(item.clone())))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FromMetaItem> FromMetaItem for Rc<T> {
|
||||
fn from_meta_item(item: &Meta) -> Result<Self> {
|
||||
Ok(Rc::new(FromMetaItem::from_meta_item(item)?))
|
||||
impl<T: FromMeta> FromMeta for Rc<T> {
|
||||
fn from_meta(item: &Meta) -> Result<Self> {
|
||||
FromMeta::from_meta(item).map(Rc::new)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FromMetaItem> FromMetaItem for Arc<T> {
|
||||
fn from_meta_item(item: &Meta) -> Result<Self> {
|
||||
Ok(Arc::new(FromMetaItem::from_meta_item(item)?))
|
||||
impl<T: FromMeta> FromMeta for Arc<T> {
|
||||
fn from_meta(item: &Meta) -> Result<Self> {
|
||||
FromMeta::from_meta(item).map(Arc::new)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FromMetaItem> FromMetaItem for RefCell<T> {
|
||||
fn from_meta_item(item: &Meta) -> Result<Self> {
|
||||
Ok(RefCell::new(FromMetaItem::from_meta_item(item)?))
|
||||
impl<T: FromMeta> FromMeta for RefCell<T> {
|
||||
fn from_meta(item: &Meta) -> Result<Self> {
|
||||
FromMeta::from_meta(item).map(RefCell::new)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: FromMetaItem> FromMetaItem for HashMap<String, V> {
|
||||
impl<V: FromMeta> FromMeta for HashMap<String, V> {
|
||||
fn from_list(nested: &[syn::NestedMeta]) -> Result<Self> {
|
||||
let mut map = HashMap::with_capacity(nested.len());
|
||||
for item in nested {
|
||||
if let syn::NestedMeta::Meta(ref inner) = *item {
|
||||
match map.entry(inner.name().to_string()) {
|
||||
Entry::Occupied(_) => return Err(Error::duplicate_field(inner.name().as_ref())),
|
||||
Entry::Occupied(_) => return Err(Error::duplicate_field(&inner.name().to_string())),
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(
|
||||
FromMetaItem::from_meta_item(inner).map_err(|e| e.at(inner.name()))?
|
||||
);
|
||||
entry.insert(FromMeta::from_meta(inner).map_err(|e| e.at(inner.name()))?);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -298,65 +304,68 @@ impl<V: FromMetaItem> FromMetaItem for HashMap<String, V> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Tests for `FromMetaItem` implementations. Wherever the word `ignore` appears in test input,
|
||||
/// Tests for `FromMeta` implementations. Wherever the word `ignore` appears in test input,
|
||||
/// it should not be considered by the parsing.
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use proc_macro2::TokenStream;
|
||||
use syn;
|
||||
use quote::Tokens;
|
||||
|
||||
use {FromMetaItem, Result};
|
||||
use {FromMeta, Result};
|
||||
|
||||
/// parse a string as a syn::Meta instance.
|
||||
fn pmi(tokens: Tokens) -> ::std::result::Result<syn::Meta, String> {
|
||||
fn pm(tokens: TokenStream) -> ::std::result::Result<syn::Meta, String> {
|
||||
let attribute: syn::Attribute = parse_quote!(#[#tokens]);
|
||||
attribute.interpret_meta().ok_or("Unable to parse".into())
|
||||
}
|
||||
|
||||
fn fmi<T: FromMetaItem>(tokens: Tokens) -> T {
|
||||
FromMetaItem::from_meta_item(&pmi(tokens).expect("Tests should pass well-formed input"))
|
||||
fn fm<T: FromMeta>(tokens: TokenStream) -> T {
|
||||
FromMeta::from_meta(&pm(tokens).expect("Tests should pass well-formed input"))
|
||||
.expect("Tests should pass valid input")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unit_succeeds() {
|
||||
assert_eq!(fmi::<()>(quote!(ignore)), ());
|
||||
assert_eq!(fm::<()>(quote!(ignore)), ());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bool_succeeds() {
|
||||
// word format
|
||||
assert_eq!(fmi::<bool>(quote!(ignore)), true);
|
||||
assert_eq!(fm::<bool>(quote!(ignore)), true);
|
||||
|
||||
// bool literal
|
||||
assert_eq!(fmi::<bool>(quote!(ignore = true)), true);
|
||||
assert_eq!(fmi::<bool>(quote!(ignore = false)), false);
|
||||
assert_eq!(fm::<bool>(quote!(ignore = true)), true);
|
||||
assert_eq!(fm::<bool>(quote!(ignore = false)), false);
|
||||
|
||||
// string literals
|
||||
assert_eq!(fmi::<bool>(quote!(ignore = "true")), true);
|
||||
assert_eq!(fmi::<bool>(quote!(ignore = "false")), false);
|
||||
assert_eq!(fm::<bool>(quote!(ignore = "true")), true);
|
||||
assert_eq!(fm::<bool>(quote!(ignore = "false")), false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_succeeds() {
|
||||
// cooked form
|
||||
assert_eq!(&fmi::<String>(quote!(ignore = "world")), "world");
|
||||
assert_eq!(&fm::<String>(quote!(ignore = "world")), "world");
|
||||
|
||||
// raw form
|
||||
assert_eq!(&fmi::<String>(quote!(ignore = r#"world"#)), "world");
|
||||
assert_eq!(&fm::<String>(quote!(ignore = r#"world"#)), "world");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn number_succeeds() {
|
||||
assert_eq!(fmi::<u8>(quote!(ignore = "2")), 2u8);
|
||||
assert_eq!(fmi::<i16>(quote!(ignore="-25")), -25i16);
|
||||
assert_eq!(fm::<u8>(quote!(ignore = "2")), 2u8);
|
||||
assert_eq!(fm::<i16>(quote!(ignore = "-25")), -25i16);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn meta_item_succeeds() {
|
||||
fn meta_succeeds() {
|
||||
use syn::Meta;
|
||||
|
||||
assert_eq!(fmi::<Meta>(quote!(hello(world,today))), pmi(quote!(hello(world,today))).unwrap());
|
||||
assert_eq!(
|
||||
fm::<Meta>(quote!(hello(world, today))),
|
||||
pm(quote!(hello(world, today))).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -371,14 +380,17 @@ mod tests {
|
|||
c
|
||||
};
|
||||
|
||||
assert_eq!(fmi::<HashMap<String, bool>>(quote!(ignore(hello, world = false, there = "true"))), comparison);
|
||||
assert_eq!(
|
||||
fm::<HashMap<String, bool>>(quote!(ignore(hello, world = false, there = "true"))),
|
||||
comparison
|
||||
);
|
||||
}
|
||||
|
||||
/// Tests that fallible parsing will always produce an outer `Ok` (from `fmi`),
|
||||
/// Tests that fallible parsing will always produce an outer `Ok` (from `fm`),
|
||||
/// and will accurately preserve the inner contents.
|
||||
#[test]
|
||||
fn darling_result_succeeds() {
|
||||
fmi::<Result<()>>(quote!(ignore)).unwrap();
|
||||
fmi::<Result<()>>(quote!(ignore(world))).unwrap_err();
|
||||
fm::<Result<()>>(quote!(ignore)).unwrap();
|
||||
fm::<Result<()>>(quote!(ignore(world))).unwrap_err();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
use syn::{self, TypeParam};
|
||||
|
||||
use Result;
|
||||
|
||||
/// Creates an instance by parsing an individual type_param and its attributes.
|
||||
pub trait FromTypeParam: Sized {
|
||||
fn from_type_param(type_param: &TypeParam) -> Result<Self>;
|
||||
}
|
||||
|
||||
impl FromTypeParam for () {
|
||||
fn from_type_param(_: &TypeParam) -> Result<Self> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromTypeParam for TypeParam {
|
||||
fn from_type_param(type_param: &TypeParam) -> Result<Self> {
|
||||
Ok(type_param.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromTypeParam for Vec<syn::Attribute> {
|
||||
fn from_type_param(type_param: &TypeParam) -> Result<Self> {
|
||||
Ok(type_param.attrs.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromTypeParam for syn::Ident {
|
||||
fn from_type_param(type_param: &TypeParam) -> Result<Self> {
|
||||
Ok(type_param.ident.clone())
|
||||
}
|
||||
}
|
|
@ -30,4 +30,4 @@ impl FromVariant for Vec<syn::Attribute> {
|
|||
fn from_variant(variant: &Variant) -> Result<Self> {
|
||||
Ok(variant.attrs.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,28 +7,37 @@ extern crate quote;
|
|||
extern crate syn;
|
||||
extern crate proc_macro2;
|
||||
|
||||
extern crate fnv;
|
||||
extern crate ident_case;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
mod macros_private;
|
||||
#[macro_use]
|
||||
mod macros_public;
|
||||
|
||||
pub mod ast;
|
||||
pub mod codegen;
|
||||
pub mod error;
|
||||
mod from_field;
|
||||
mod from_derive_input;
|
||||
mod from_meta_item;
|
||||
mod from_field;
|
||||
mod from_generic_param;
|
||||
mod from_generics;
|
||||
mod from_meta;
|
||||
mod from_type_param;
|
||||
mod from_variant;
|
||||
pub mod options;
|
||||
pub mod usage;
|
||||
pub mod util;
|
||||
|
||||
pub use error::{Result, Error};
|
||||
pub use error::{Error, Result};
|
||||
pub use from_derive_input::FromDeriveInput;
|
||||
pub use from_field::FromField;
|
||||
pub use from_meta_item::{FromMetaItem};
|
||||
pub use from_generic_param::FromGenericParam;
|
||||
pub use from_generics::FromGenerics;
|
||||
pub use from_meta::FromMeta;
|
||||
pub use from_type_param::FromTypeParam;
|
||||
pub use from_variant::FromVariant;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
}
|
||||
// Re-export tokenizer
|
||||
#[doc(hidden)]
|
||||
pub use quote::ToTokens;
|
|
@ -0,0 +1,98 @@
|
|||
//! Macros that should be exported from both `darling_core` and `darling`.
|
||||
//! Note that these are **sym-linked** into the main code, and so cannot declare on items that are exported differently
|
||||
//! in `darling_core` vs. `darling`.
|
||||
|
||||
/// Generator for `UsesTypeParam` impls that unions the used type parameters of the selected fields.
|
||||
///
|
||||
/// # Usage
|
||||
/// The macro takes the type implementing the trait as the first argument, then a comma-separated list of
|
||||
/// fields for the rest of its arguments.
|
||||
///
|
||||
/// The type of each passed-in field must implement `UsesTypeParams`, or the resulting code won't compile.
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate syn;
|
||||
/// #
|
||||
/// # #[macro_use]
|
||||
/// # extern crate darling_core;
|
||||
/// #
|
||||
/// struct MyField {
|
||||
/// ty: syn::Type,
|
||||
/// }
|
||||
///
|
||||
/// uses_type_params!(MyField, ty);
|
||||
///
|
||||
/// fn main() {
|
||||
/// // no test run
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// `darling` cannot derive this trait automatically, as it doesn't know which information extracted from
|
||||
/// proc-macro input is meant to constitute "using" the type parameter, but crate consumers should
|
||||
/// implement it by hand or using the macro.
|
||||
#[macro_export]
|
||||
macro_rules! uses_type_params {
|
||||
($impl_type:ty, $accessor:ident) => {
|
||||
impl $crate::usage::UsesTypeParams for $impl_type {
|
||||
fn uses_type_params<'gen>(
|
||||
&self,
|
||||
options: &$crate::usage::Options,
|
||||
type_set: &'gen $crate::usage::IdentSet
|
||||
) -> $crate::usage::IdentRefSet<'gen> {
|
||||
self.$accessor.uses_type_params(options, type_set)
|
||||
}
|
||||
}
|
||||
};
|
||||
($impl_type:ty, $first:ident, $($field:ident),+) => {
|
||||
impl $crate::usage::UsesTypeParams for $impl_type {
|
||||
fn uses_type_params<'gen>(
|
||||
&self,
|
||||
options: &$crate::usage::Options,
|
||||
type_set: &'gen $crate::usage::IdentSet
|
||||
) -> $crate::usage::IdentRefSet<'gen> {
|
||||
let mut hits = self.$first.uses_type_params(options, type_set);
|
||||
$(
|
||||
hits.extend(self.$field.uses_type_params(options, type_set));
|
||||
)*
|
||||
hits
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Generator for `UsesLifetimes` impls that unions the used lifetimes of the selected fields.
|
||||
///
|
||||
/// # Usage
|
||||
/// The macro takes the type implementing the trait as the first argument, then a comma-separated list of
|
||||
/// fields for the rest of its arguments.
|
||||
///
|
||||
/// The type of each passed-in field must implement `UsesLifetimes`, or the resulting code won't compile.
|
||||
#[macro_export]
|
||||
macro_rules! uses_lifetimes {
|
||||
($impl_type:ty, $accessor:ident) => {
|
||||
impl $crate::usage::UsesLifetimes for $impl_type {
|
||||
fn uses_lifetimes<'gen>(
|
||||
&self,
|
||||
options: &$crate::usage::Options,
|
||||
type_set: &'gen $crate::usage::LifetimeSet
|
||||
) -> $crate::usage::LifetimeRefSet<'gen> {
|
||||
self.$accessor.uses_lifetimes(options, type_set)
|
||||
}
|
||||
}
|
||||
};
|
||||
($impl_type:ty, $first:ident, $($field:ident),+) => {
|
||||
impl $crate::usage::UsesLifetimes for $impl_type {
|
||||
fn uses_lifetimes<'gen>(
|
||||
&self,
|
||||
options: &$crate::usage::Options,
|
||||
type_set: &'gen $crate::usage::LifetimeSet
|
||||
) -> $crate::usage::LifetimeRefSet<'gen> {
|
||||
let mut hits = self.$first.uses_lifetimes(options, type_set);
|
||||
$(
|
||||
hits.extend(self.$field.uses_lifetimes(options, type_set));
|
||||
)*
|
||||
hits
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,12 +1,12 @@
|
|||
use ident_case::RenameRule;
|
||||
use syn;
|
||||
|
||||
use {Result, Error, FromMetaItem};
|
||||
use ast::{Data, Style, Fields};
|
||||
use ast::{Data, Fields, Style};
|
||||
use codegen;
|
||||
use options::{DefaultExpression, InputField, InputVariant, ParseAttribute, ParseData};
|
||||
use {Error, FromMeta, Result};
|
||||
|
||||
/// A struct or enum which should have `FromMetaItem` or `FromDeriveInput` implementations
|
||||
/// A struct or enum which should have `FromMeta` or `FromDeriveInput` implementations
|
||||
/// generated.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Core {
|
||||
|
@ -45,7 +45,7 @@ impl Core {
|
|||
default: Default::default(),
|
||||
// See https://github.com/TedDriggs/darling/issues/10: We default to snake_case
|
||||
// for enums to help authors produce more idiomatic APIs.
|
||||
rename_rule: if let syn::Data::Enum(_) = di.data{
|
||||
rename_rule: if let syn::Data::Enum(_) = di.data {
|
||||
RenameRule::SnakeCase
|
||||
} else {
|
||||
Default::default()
|
||||
|
@ -56,11 +56,10 @@ impl Core {
|
|||
}
|
||||
|
||||
fn as_codegen_default<'a>(&'a self) -> Option<codegen::DefaultExpression<'a>> {
|
||||
self.default.as_ref().map(|expr| {
|
||||
match *expr {
|
||||
DefaultExpression::Explicit(ref path) => codegen::DefaultExpression::Explicit(path),
|
||||
DefaultExpression::Inherit |
|
||||
DefaultExpression::Trait => codegen::DefaultExpression::Trait,
|
||||
self.default.as_ref().map(|expr| match *expr {
|
||||
DefaultExpression::Explicit(ref path) => codegen::DefaultExpression::Explicit(path),
|
||||
DefaultExpression::Inherit | DefaultExpression::Trait => {
|
||||
codegen::DefaultExpression::Trait
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -68,31 +67,31 @@ impl Core {
|
|||
|
||||
impl ParseAttribute for Core {
|
||||
fn parse_nested(&mut self, mi: &syn::Meta) -> Result<()> {
|
||||
match mi.name().as_ref() {
|
||||
match mi.name().to_string().as_str() {
|
||||
"default" => {
|
||||
if self.default.is_some() {
|
||||
Err(Error::duplicate_field("default"))
|
||||
} else {
|
||||
self.default = FromMetaItem::from_meta_item(mi)?;
|
||||
self.default = FromMeta::from_meta(mi)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
"rename_all" => {
|
||||
// WARNING: This may have been set based on body shape previously,
|
||||
// so an overwrite may be permissible.
|
||||
self.rename_rule = FromMetaItem::from_meta_item(mi)?;
|
||||
self.rename_rule = FromMeta::from_meta(mi)?;
|
||||
Ok(())
|
||||
}
|
||||
"map" => {
|
||||
if self.map.is_some() {
|
||||
Err(Error::duplicate_field("map"))
|
||||
} else {
|
||||
self.map = FromMetaItem::from_meta_item(mi)?;
|
||||
self.map = FromMeta::from_meta(mi)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
"bound" => {
|
||||
self.bound = FromMetaItem::from_meta_item(mi)?;
|
||||
self.bound = FromMeta::from_meta(mi)?;
|
||||
Ok(())
|
||||
}
|
||||
n => Err(Error::unknown_field(n.as_ref())),
|
||||
|
@ -117,9 +116,9 @@ impl ParseData for Core {
|
|||
let f = InputField::from_field(field, Some(&self))?;
|
||||
|
||||
match self.data {
|
||||
Data::Struct(Fields { style: Style::Unit, .. }) => {
|
||||
panic!("Core::parse_field should not be called on unit")
|
||||
}
|
||||
Data::Struct(Fields {
|
||||
style: Style::Unit, ..
|
||||
}) => panic!("Core::parse_field should not be called on unit"),
|
||||
Data::Struct(Fields { ref mut fields, .. }) => {
|
||||
fields.push(f);
|
||||
Ok(())
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use syn::NestedMeta;
|
||||
|
||||
use {FromMetaItem, Result};
|
||||
use util::IdentList;
|
||||
use {FromMeta, Result};
|
||||
|
||||
/// A rule about which attributes to forward to the generated struct.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
@ -20,7 +20,7 @@ impl ForwardAttrs {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromMetaItem for ForwardAttrs {
|
||||
impl FromMeta for ForwardAttrs {
|
||||
fn from_word() -> Result<Self> {
|
||||
Ok(ForwardAttrs::All)
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use syn::{self, Ident};
|
||||
|
||||
use {FromMetaItem, Result};
|
||||
use codegen;
|
||||
use options::{ParseAttribute, ParseData, OuterFrom, Shape};
|
||||
use options::{OuterFrom, ParseAttribute, ParseData, Shape};
|
||||
use {FromMeta, Result};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FdiOptions {
|
||||
|
@ -27,15 +27,19 @@ impl FdiOptions {
|
|||
generics: Default::default(),
|
||||
data: Default::default(),
|
||||
supports: Default::default(),
|
||||
}).parse_attributes(&di.attrs)?.parse_body(&di.data)
|
||||
}).parse_attributes(&di.attrs)?
|
||||
.parse_body(&di.data)
|
||||
}
|
||||
}
|
||||
|
||||
impl ParseAttribute for FdiOptions {
|
||||
fn parse_nested(&mut self, mi: &syn::Meta) -> Result<()> {
|
||||
match mi.name().as_ref() {
|
||||
"supports" => { self.supports = FromMetaItem::from_meta_item(mi)?; Ok(()) },
|
||||
_ => self.base.parse_nested(mi)
|
||||
match mi.name().to_string().as_str() {
|
||||
"supports" => {
|
||||
self.supports = FromMeta::from_meta(mi)?;
|
||||
Ok(())
|
||||
}
|
||||
_ => self.base.parse_nested(mi),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -46,11 +50,26 @@ impl ParseData for FdiOptions {
|
|||
}
|
||||
|
||||
fn parse_field(&mut self, field: &syn::Field) -> Result<()> {
|
||||
match field.ident.as_ref().map(|v| v.as_ref()) {
|
||||
Some("vis") => { self.vis = field.ident.clone(); Ok(()) }
|
||||
Some("data") => { self.data = field.ident.clone(); Ok(()) }
|
||||
Some("generics") => { self.generics = field.ident.clone(); Ok(()) }
|
||||
_ => self.base.parse_field(field)
|
||||
match field
|
||||
.ident
|
||||
.as_ref()
|
||||
.map(|v| v.to_string())
|
||||
.as_ref()
|
||||
.map(|v| v.as_str())
|
||||
{
|
||||
Some("vis") => {
|
||||
self.vis = field.ident.clone();
|
||||
Ok(())
|
||||
}
|
||||
Some("data") => {
|
||||
self.data = field.ident.clone();
|
||||
Ok(())
|
||||
}
|
||||
Some("generics") => {
|
||||
self.generics = field.ident.clone();
|
||||
Ok(())
|
||||
}
|
||||
_ => self.base.parse_field(field),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -59,8 +78,8 @@ impl<'a> From<&'a FdiOptions> for codegen::FromDeriveInputImpl<'a> {
|
|||
fn from(v: &'a FdiOptions) -> Self {
|
||||
codegen::FromDeriveInputImpl {
|
||||
base: (&v.base.container).into(),
|
||||
attr_names: v.base.attr_names.as_strs(),
|
||||
from_ident: Some(v.base.from_ident),
|
||||
attr_names: &v.base.attr_names,
|
||||
from_ident: v.base.from_ident,
|
||||
ident: v.base.ident.as_ref(),
|
||||
vis: v.vis.as_ref(),
|
||||
data: v.data.as_ref(),
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use syn::{self, Ident};
|
||||
|
||||
use {Result};
|
||||
use codegen::FromFieldImpl;
|
||||
use options::{ParseAttribute, ParseData, OuterFrom};
|
||||
use options::{OuterFrom, ParseAttribute, ParseData};
|
||||
use Result;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FromFieldOptions {
|
||||
|
@ -17,7 +17,8 @@ impl FromFieldOptions {
|
|||
base: OuterFrom::start(di),
|
||||
vis: Default::default(),
|
||||
ty: Default::default(),
|
||||
}).parse_attributes(&di.attrs)?.parse_body(&di.data)
|
||||
}).parse_attributes(&di.attrs)?
|
||||
.parse_body(&di.data)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,10 +34,22 @@ impl ParseData for FromFieldOptions {
|
|||
}
|
||||
|
||||
fn parse_field(&mut self, field: &syn::Field) -> Result<()> {
|
||||
match field.ident.as_ref().map(|v| v.as_ref()) {
|
||||
Some("vis") => { self.vis = field.ident.clone(); Ok(()) },
|
||||
Some("ty") => { self.ty = field.ident.clone(); Ok(()) }
|
||||
_ => self.base.parse_field(field)
|
||||
match field
|
||||
.ident
|
||||
.as_ref()
|
||||
.map(|v| v.to_string())
|
||||
.as_ref()
|
||||
.map(|v| v.as_str())
|
||||
{
|
||||
Some("vis") => {
|
||||
self.vis = field.ident.clone();
|
||||
Ok(())
|
||||
}
|
||||
Some("ty") => {
|
||||
self.ty = field.ident.clone();
|
||||
Ok(())
|
||||
}
|
||||
_ => self.base.parse_field(field),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,7 +62,7 @@ impl<'a> From<&'a FromFieldOptions> for FromFieldImpl<'a> {
|
|||
ty: v.ty.as_ref(),
|
||||
attrs: v.base.attrs.as_ref(),
|
||||
base: (&v.base.container).into(),
|
||||
attr_names: v.base.attr_names.as_strs(),
|
||||
attr_names: &v.base.attr_names,
|
||||
forward_attrs: v.base.forward_attrs.as_ref(),
|
||||
from_ident: v.base.from_ident,
|
||||
}
|
||||
|
|
|
@ -1,28 +1,29 @@
|
|||
use syn;
|
||||
|
||||
use Result;
|
||||
use codegen;
|
||||
use options::{Core, ParseAttribute, ParseData};
|
||||
use Result;
|
||||
|
||||
pub struct FmiOptions {
|
||||
base: Core
|
||||
pub struct FromMetaOptions {
|
||||
base: Core,
|
||||
}
|
||||
|
||||
impl FmiOptions {
|
||||
impl FromMetaOptions {
|
||||
pub fn new(di: &syn::DeriveInput) -> Result<Self> {
|
||||
(FmiOptions {
|
||||
(FromMetaOptions {
|
||||
base: Core::start(di),
|
||||
}).parse_attributes(&di.attrs)?.parse_body(&di.data)
|
||||
}).parse_attributes(&di.attrs)?
|
||||
.parse_body(&di.data)
|
||||
}
|
||||
}
|
||||
|
||||
impl ParseAttribute for FmiOptions {
|
||||
impl ParseAttribute for FromMetaOptions {
|
||||
fn parse_nested(&mut self, mi: &syn::Meta) -> Result<()> {
|
||||
self.base.parse_nested(mi)
|
||||
}
|
||||
}
|
||||
|
||||
impl ParseData for FmiOptions {
|
||||
impl ParseData for FromMetaOptions {
|
||||
fn parse_variant(&mut self, variant: &syn::Variant) -> Result<()> {
|
||||
self.base.parse_variant(variant)
|
||||
}
|
||||
|
@ -32,9 +33,9 @@ impl ParseData for FmiOptions {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a FmiOptions> for codegen::FmiImpl<'a> {
|
||||
fn from(v: &'a FmiOptions) -> Self {
|
||||
codegen::FmiImpl {
|
||||
impl<'a> From<&'a FromMetaOptions> for codegen::FromMetaImpl<'a> {
|
||||
fn from(v: &'a FromMetaOptions) -> Self {
|
||||
codegen::FromMetaImpl {
|
||||
base: (&v.base).into(),
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
use syn::{self, Ident};
|
||||
|
||||
use codegen::FromTypeParamImpl;
|
||||
use options::{OuterFrom, ParseAttribute, ParseData};
|
||||
use Result;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FromTypeParamOptions {
|
||||
pub base: OuterFrom,
|
||||
pub bounds: Option<Ident>,
|
||||
pub default: Option<Ident>,
|
||||
}
|
||||
|
||||
impl FromTypeParamOptions {
|
||||
pub fn new(di: &syn::DeriveInput) -> Result<Self> {
|
||||
(FromTypeParamOptions {
|
||||
base: OuterFrom::start(di),
|
||||
bounds: None,
|
||||
default: None,
|
||||
}).parse_attributes(&di.attrs)?
|
||||
.parse_body(&di.data)
|
||||
}
|
||||
}
|
||||
|
||||
impl ParseAttribute for FromTypeParamOptions {
|
||||
fn parse_nested(&mut self, mi: &syn::Meta) -> Result<()> {
|
||||
self.base.parse_nested(mi)
|
||||
}
|
||||
}
|
||||
|
||||
impl ParseData for FromTypeParamOptions {
|
||||
fn parse_variant(&mut self, variant: &syn::Variant) -> Result<()> {
|
||||
self.base.parse_variant(variant)
|
||||
}
|
||||
|
||||
fn parse_field(&mut self, field: &syn::Field) -> Result<()> {
|
||||
match field
|
||||
.ident
|
||||
.as_ref()
|
||||
.map(|v| v.to_string())
|
||||
.as_ref()
|
||||
.map(|v| v.as_str())
|
||||
{
|
||||
Some("bounds") => {
|
||||
self.bounds = field.ident.clone();
|
||||
Ok(())
|
||||
}
|
||||
Some("default") => {
|
||||
self.default = field.ident.clone();
|
||||
Ok(())
|
||||
}
|
||||
_ => self.base.parse_field(field),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a FromTypeParamOptions> for FromTypeParamImpl<'a> {
|
||||
fn from(v: &'a FromTypeParamOptions) -> Self {
|
||||
FromTypeParamImpl {
|
||||
base: (&v.base.container).into(),
|
||||
ident: v.base.ident.as_ref(),
|
||||
attrs: v.base.attrs.as_ref(),
|
||||
bounds: v.bounds.as_ref(),
|
||||
default: v.default.as_ref(),
|
||||
attr_names: &v.base.attr_names,
|
||||
forward_attrs: v.base.forward_attrs.as_ref(),
|
||||
from_ident: v.base.from_ident,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
use syn::{DeriveInput, Field, Ident, Meta};
|
||||
|
||||
use {FromMetaItem, Result};
|
||||
use codegen::FromVariantImpl;
|
||||
use options::{OuterFrom, ParseAttribute, ParseData, DataShape};
|
||||
use options::{DataShape, OuterFrom, ParseAttribute, ParseData};
|
||||
use {FromMeta, Result};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct FromVariantOptions {
|
||||
|
@ -17,7 +17,8 @@ impl FromVariantOptions {
|
|||
base: OuterFrom::start(di),
|
||||
fields: Default::default(),
|
||||
supports: Default::default(),
|
||||
}).parse_attributes(&di.attrs)?.parse_body(&di.data)
|
||||
}).parse_attributes(&di.attrs)?
|
||||
.parse_body(&di.data)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,9 +29,9 @@ impl<'a> From<&'a FromVariantOptions> for FromVariantImpl<'a> {
|
|||
ident: v.base.ident.as_ref(),
|
||||
fields: v.fields.as_ref(),
|
||||
attrs: v.base.attrs.as_ref(),
|
||||
attr_names: v.base.attr_names.as_strs(),
|
||||
attr_names: &v.base.attr_names,
|
||||
forward_attrs: v.base.forward_attrs.as_ref(),
|
||||
from_ident: Some(v.base.from_ident),
|
||||
from_ident: v.base.from_ident,
|
||||
supports: v.supports.as_ref(),
|
||||
}
|
||||
}
|
||||
|
@ -38,18 +39,30 @@ impl<'a> From<&'a FromVariantOptions> for FromVariantImpl<'a> {
|
|||
|
||||
impl ParseAttribute for FromVariantOptions {
|
||||
fn parse_nested(&mut self, mi: &Meta) -> Result<()> {
|
||||
match mi.name().as_ref() {
|
||||
"supports" => { self.supports = FromMetaItem::from_meta_item(mi)?; Ok(()) }
|
||||
_ => self.base.parse_nested(mi)
|
||||
match mi.name().to_string().as_str() {
|
||||
"supports" => {
|
||||
self.supports = FromMeta::from_meta(mi)?;
|
||||
Ok(())
|
||||
}
|
||||
_ => self.base.parse_nested(mi),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ParseData for FromVariantOptions {
|
||||
fn parse_field(&mut self, field: &Field) -> Result<()> {
|
||||
match field.ident.as_ref().map(|i| i.as_ref()) {
|
||||
Some("fields") => { self.fields = field.ident.clone(); Ok(()) }
|
||||
_ => self.base.parse_field(field)
|
||||
match field
|
||||
.ident
|
||||
.as_ref()
|
||||
.map(|v| v.to_string())
|
||||
.as_ref()
|
||||
.map(|v| v.as_str())
|
||||
{
|
||||
Some("fields") => {
|
||||
self.fields = field.ident.clone();
|
||||
Ok(())
|
||||
}
|
||||
_ => self.base.parse_field(field),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
use syn;
|
||||
|
||||
use ::{FromMetaItem, Error, Result};
|
||||
use codegen;
|
||||
use options::{Core, DefaultExpression, ParseAttribute};
|
||||
|
||||
use {Error, FromMeta, Result};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct InputField {
|
||||
|
@ -25,10 +24,14 @@ impl InputField {
|
|||
pub fn as_codegen_field<'a>(&'a self) -> codegen::Field<'a> {
|
||||
codegen::Field {
|
||||
ident: &self.ident,
|
||||
name_in_attr: self.attr_name.as_ref().map(|n| n.as_str()).unwrap_or(self.ident.as_ref()),
|
||||
name_in_attr: self.attr_name
|
||||
.clone()
|
||||
.unwrap_or(self.ident.to_string()),
|
||||
ty: &self.ty,
|
||||
default_expression: self.as_codegen_default(),
|
||||
with_path: self.with.clone().unwrap_or(parse_quote!(::darling::FromMetaItem::from_meta_item)),
|
||||
with_path: self.with
|
||||
.clone()
|
||||
.unwrap_or(parse_quote!(::darling::FromMeta::from_meta)),
|
||||
skip: self.skip,
|
||||
map: self.map.as_ref(),
|
||||
multiple: self.multiple,
|
||||
|
@ -38,12 +41,10 @@ impl InputField {
|
|||
/// Generate a codegen::DefaultExpression for this field. This requires the field name
|
||||
/// in the `Inherit` case.
|
||||
fn as_codegen_default<'a>(&'a self) -> Option<codegen::DefaultExpression<'a>> {
|
||||
self.default.as_ref().map(|expr| {
|
||||
match *expr {
|
||||
DefaultExpression::Explicit(ref path) => codegen::DefaultExpression::Explicit(path),
|
||||
DefaultExpression::Inherit => codegen::DefaultExpression::Inherit(&self.ident),
|
||||
DefaultExpression::Trait => codegen::DefaultExpression::Trait,
|
||||
}
|
||||
self.default.as_ref().map(|expr| match *expr {
|
||||
DefaultExpression::Explicit(ref path) => codegen::DefaultExpression::Explicit(path),
|
||||
DefaultExpression::Inherit => codegen::DefaultExpression::Inherit(&self.ident),
|
||||
DefaultExpression::Trait => codegen::DefaultExpression::Trait,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -61,7 +62,10 @@ impl InputField {
|
|||
}
|
||||
|
||||
pub fn from_field(f: &syn::Field, parent: Option<&Core>) -> Result<Self> {
|
||||
let ident = f.ident.clone().unwrap_or(syn::Ident::new("__unnamed", ::proc_macro2::Span::call_site()));
|
||||
let ident = f.ident.clone().unwrap_or(syn::Ident::new(
|
||||
"__unnamed",
|
||||
::proc_macro2::Span::call_site(),
|
||||
));
|
||||
let ty = f.ty.clone();
|
||||
let base = Self::new(ident, ty).parse_attributes(&f.attrs)?;
|
||||
|
||||
|
@ -78,7 +82,7 @@ impl InputField {
|
|||
// explicit renamings take precedence over rename rules on the container,
|
||||
// but in the absence of an explicit name we apply the rule.
|
||||
if self.attr_name.is_none() {
|
||||
self.attr_name = Some(parent.rename_rule.apply_to_field(&self.ident));
|
||||
self.attr_name = Some(parent.rename_rule.apply_to_field(self.ident.to_string()));
|
||||
}
|
||||
|
||||
// Determine the default expression for this field, based on three pieces of information:
|
||||
|
@ -109,12 +113,30 @@ impl ParseAttribute for InputField {
|
|||
fn parse_nested(&mut self, mi: &syn::Meta) -> Result<()> {
|
||||
let name = mi.name().to_string();
|
||||
match name.as_str() {
|
||||
"rename" => { self.attr_name = FromMetaItem::from_meta_item(mi)?; Ok(()) }
|
||||
"default" => { self.default = FromMetaItem::from_meta_item(mi)?; Ok(()) }
|
||||
"with" => { self.with = Some(FromMetaItem::from_meta_item(mi)?); Ok(()) }
|
||||
"skip" => { self.skip = FromMetaItem::from_meta_item(mi)?; Ok(()) }
|
||||
"map" => { self.map = Some(FromMetaItem::from_meta_item(mi)?); Ok(()) }
|
||||
"multiple" => { self.multiple = FromMetaItem::from_meta_item(mi)?; Ok(()) }
|
||||
"rename" => {
|
||||
self.attr_name = FromMeta::from_meta(mi)?;
|
||||
Ok(())
|
||||
}
|
||||
"default" => {
|
||||
self.default = FromMeta::from_meta(mi)?;
|
||||
Ok(())
|
||||
}
|
||||
"with" => {
|
||||
self.with = Some(FromMeta::from_meta(mi)?);
|
||||
Ok(())
|
||||
}
|
||||
"skip" => {
|
||||
self.skip = FromMeta::from_meta(mi)?;
|
||||
Ok(())
|
||||
}
|
||||
"map" => {
|
||||
self.map = Some(FromMeta::from_meta(mi)?);
|
||||
Ok(())
|
||||
}
|
||||
"multiple" => {
|
||||
self.multiple = FromMeta::from_meta(mi)?;
|
||||
Ok(())
|
||||
}
|
||||
n => Err(Error::unknown_field(n)),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use syn;
|
||||
|
||||
use {FromMetaItem, Error, Result};
|
||||
use ast::{Style, Fields};
|
||||
use ast::{Fields, Style};
|
||||
use codegen;
|
||||
use options::{Core, InputField, ParseAttribute};
|
||||
use {Error, FromMeta, Result};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct InputVariant {
|
||||
|
@ -18,7 +18,9 @@ impl InputVariant {
|
|||
codegen::Variant {
|
||||
ty_ident,
|
||||
variant_ident: &self.ident,
|
||||
name_in_attr: self.attr_name.as_ref().map(|s| s.as_str()).unwrap_or(self.ident.as_ref()),
|
||||
name_in_attr: self.attr_name
|
||||
.clone()
|
||||
.unwrap_or(self.ident.to_string()),
|
||||
data: self.data.as_ref().map(InputField::as_codegen_field),
|
||||
skip: self.skip,
|
||||
}
|
||||
|
@ -44,7 +46,7 @@ impl InputVariant {
|
|||
style: v.fields.clone().into(),
|
||||
fields: items,
|
||||
}
|
||||
},
|
||||
}
|
||||
syn::Fields::Named(ref fields) => {
|
||||
let mut items = Vec::with_capacity(fields.named.len());
|
||||
for item in &fields.named {
|
||||
|
@ -67,24 +69,23 @@ impl InputVariant {
|
|||
|
||||
fn with_inherited(mut self, parent: &Core) -> Self {
|
||||
if self.attr_name.is_none() {
|
||||
self.attr_name = Some(parent.rename_rule.apply_to_variant(&self.ident));
|
||||
self.attr_name = Some(parent.rename_rule.apply_to_variant(self.ident.to_string()));
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl ParseAttribute for InputVariant {
|
||||
fn parse_nested(&mut self, mi: &syn::Meta) -> Result<()> {
|
||||
let name = mi.name().to_string();
|
||||
match name.as_str() {
|
||||
"rename" => {
|
||||
self.attr_name = FromMetaItem::from_meta_item(mi)?;
|
||||
self.attr_name = FromMeta::from_meta(mi)?;
|
||||
Ok(())
|
||||
}
|
||||
"skip" => {
|
||||
self.skip = FromMetaItem::from_meta_item(mi)?;
|
||||
self.skip = FromMeta::from_meta(mi)?;
|
||||
Ok(())
|
||||
}
|
||||
n => Err(Error::unknown_field(n)),
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
use syn;
|
||||
|
||||
use {FromMetaItem, Result, Error};
|
||||
use {Error, FromMeta, Result};
|
||||
|
||||
mod core;
|
||||
mod forward_attrs;
|
||||
mod from_derive;
|
||||
mod from_field;
|
||||
mod from_meta_item;
|
||||
mod from_meta;
|
||||
mod from_type_param;
|
||||
mod from_variant;
|
||||
mod input_variant;
|
||||
mod input_field;
|
||||
mod input_variant;
|
||||
mod outer_from;
|
||||
mod shape;
|
||||
|
||||
|
@ -17,10 +18,11 @@ pub use self::core::Core;
|
|||
pub use self::forward_attrs::ForwardAttrs;
|
||||
pub use self::from_derive::FdiOptions;
|
||||
pub use self::from_field::FromFieldOptions;
|
||||
pub use self::from_meta_item::FmiOptions;
|
||||
pub use self::from_meta::FromMetaOptions;
|
||||
pub use self::from_type_param::FromTypeParamOptions;
|
||||
pub use self::from_variant::FromVariantOptions;
|
||||
pub use self::input_variant::InputVariant;
|
||||
pub use self::input_field::InputField;
|
||||
pub use self::input_variant::InputVariant;
|
||||
pub use self::outer_from::OuterFrom;
|
||||
pub use self::shape::{DataShape, Shape};
|
||||
|
||||
|
@ -35,13 +37,15 @@ pub enum DefaultExpression {
|
|||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl FromMetaItem for DefaultExpression {
|
||||
impl FromMeta for DefaultExpression {
|
||||
fn from_word() -> Result<Self> {
|
||||
Ok(DefaultExpression::Trait)
|
||||
}
|
||||
|
||||
fn from_string(lit: &str) -> Result<Self> {
|
||||
Ok(DefaultExpression::Explicit(syn::Path::from(lit)))
|
||||
Ok(DefaultExpression::Explicit(
|
||||
syn::parse_str(lit).map_err(|_| Error::unknown_value(lit))?
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,10 +66,6 @@ pub trait ParseAttribute: Sized {
|
|||
}
|
||||
|
||||
fn parse_attr<T: ParseAttribute>(attr: &syn::Attribute, target: &mut T) -> Result<()> {
|
||||
if attr.is_sugared_doc {
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
match attr.interpret_meta() {
|
||||
Some(syn::Meta::List(data)) => {
|
||||
for item in data.nested {
|
||||
|
@ -77,7 +77,7 @@ fn parse_attr<T: ParseAttribute>(attr: &syn::Attribute, target: &mut T) -> Resul
|
|||
}
|
||||
|
||||
Ok(())
|
||||
},
|
||||
}
|
||||
Some(ref item) => panic!("Wasn't able to parse: `{:?}`", item),
|
||||
None => panic!("Unable to parse {:?}", attr),
|
||||
}
|
||||
|
@ -88,24 +88,22 @@ pub trait ParseData: Sized {
|
|||
use syn::{Data, Fields};
|
||||
|
||||
match *body {
|
||||
Data::Struct(ref data) => {
|
||||
match data.fields {
|
||||
Fields::Unit => Ok(self),
|
||||
Fields::Named(ref fields) => {
|
||||
for field in &fields.named {
|
||||
self.parse_field(field)?;
|
||||
}
|
||||
Ok(self)
|
||||
}
|
||||
Fields::Unnamed(ref fields) => {
|
||||
for field in &fields.unnamed {
|
||||
self.parse_field(field)?;
|
||||
}
|
||||
|
||||
Ok(self)
|
||||
Data::Struct(ref data) => match data.fields {
|
||||
Fields::Unit => Ok(self),
|
||||
Fields::Named(ref fields) => {
|
||||
for field in &fields.named {
|
||||
self.parse_field(field)?;
|
||||
}
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
Fields::Unnamed(ref fields) => {
|
||||
for field in &fields.unnamed {
|
||||
self.parse_field(field)?;
|
||||
}
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
},
|
||||
Data::Enum(ref data) => {
|
||||
for variant in &data.variants {
|
||||
self.parse_variant(variant)?;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use syn::{self, Field, Ident, Meta};
|
||||
|
||||
use {FromMetaItem, Result};
|
||||
use options::{Core, DefaultExpression, ForwardAttrs, ParseAttribute, ParseData};
|
||||
use util::IdentList;
|
||||
use {FromMeta, Result};
|
||||
|
||||
/// Reusable base for `FromDeriveInput`, `FromVariant`, `FromField`, and other top-level
|
||||
/// `From*` traits.
|
||||
|
@ -42,9 +42,15 @@ impl OuterFrom {
|
|||
|
||||
impl ParseAttribute for OuterFrom {
|
||||
fn parse_nested(&mut self, mi: &Meta) -> Result<()> {
|
||||
match mi.name().as_ref() {
|
||||
"attributes" => { self.attr_names = FromMetaItem::from_meta_item(mi)?; Ok(()) }
|
||||
"forward_attrs" => { self.forward_attrs = FromMetaItem::from_meta_item(mi)?; Ok(()) },
|
||||
match mi.name().to_string().as_str() {
|
||||
"attributes" => {
|
||||
self.attr_names = FromMeta::from_meta(mi)?;
|
||||
Ok(())
|
||||
}
|
||||
"forward_attrs" => {
|
||||
self.forward_attrs = FromMeta::from_meta(mi)?;
|
||||
Ok(())
|
||||
}
|
||||
"from_ident" => {
|
||||
// HACK: Declaring that a default is present will cause fields to
|
||||
// generate correct code, but control flow isn't that obvious.
|
||||
|
@ -52,17 +58,29 @@ impl ParseAttribute for OuterFrom {
|
|||
self.from_ident = true;
|
||||
Ok(())
|
||||
}
|
||||
_ => self.container.parse_nested(mi)
|
||||
_ => self.container.parse_nested(mi),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ParseData for OuterFrom {
|
||||
fn parse_field(&mut self, field: &Field) -> Result<()> {
|
||||
match field.ident.as_ref().map(|v| v.as_ref()) {
|
||||
Some("ident") => { self.ident = field.ident.clone(); Ok(()) }
|
||||
Some("attrs") => { self.attrs = field.ident.clone(); Ok(()) }
|
||||
_ => self.container.parse_field(field)
|
||||
match field
|
||||
.ident
|
||||
.as_ref()
|
||||
.map(|v| v.to_string())
|
||||
.as_ref()
|
||||
.map(|v| v.as_str())
|
||||
{
|
||||
Some("ident") => {
|
||||
self.ident = field.ident.clone();
|
||||
Ok(())
|
||||
}
|
||||
Some("attrs") => {
|
||||
self.attrs = field.ident.clone();
|
||||
Ok(())
|
||||
}
|
||||
_ => self.container.parse_field(field),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use quote::{Tokens, ToTokens};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{TokenStreamExt, ToTokens};
|
||||
use syn::{Meta, NestedMeta};
|
||||
|
||||
use {Error, FromMetaItem, Result};
|
||||
use {Error, FromMeta, Result};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Shape {
|
||||
|
@ -29,16 +30,16 @@ impl Default for Shape {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromMetaItem for Shape {
|
||||
impl FromMeta for Shape {
|
||||
fn from_list(items: &[NestedMeta]) -> Result<Self> {
|
||||
let mut new = Shape::default();
|
||||
for item in items {
|
||||
if let NestedMeta::Meta(Meta::Word(ref ident)) = *item {
|
||||
let word = ident.as_ref();
|
||||
let word = ident.to_string();
|
||||
let word = word.as_str();
|
||||
if word == "any" {
|
||||
new.any = true;
|
||||
}
|
||||
else if word.starts_with("enum_") {
|
||||
} else if word.starts_with("enum_") {
|
||||
new.enum_values.set_word(word)?;
|
||||
} else if word.starts_with("struct_") {
|
||||
new.struct_values.set_word(word)?;
|
||||
|
@ -55,11 +56,10 @@ impl FromMetaItem for Shape {
|
|||
}
|
||||
|
||||
impl ToTokens for Shape {
|
||||
fn to_tokens(&self, tokens: &mut Tokens) {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let fn_body = if self.any == true {
|
||||
quote!(::darling::export::Ok(()))
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
let en = &self.enum_values;
|
||||
let st = &self.struct_values;
|
||||
quote! {
|
||||
|
@ -75,7 +75,8 @@ impl ToTokens for Shape {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
::syn::Data::Struct(ref data) => {
|
||||
::syn::Data::Struct(ref struct_data) => {
|
||||
let data = &struct_data.fields;
|
||||
#st
|
||||
}
|
||||
::syn::Data::Union(_) => unreachable!(),
|
||||
|
@ -144,12 +145,12 @@ impl DataShape {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromMetaItem for DataShape {
|
||||
impl FromMeta for DataShape {
|
||||
fn from_list(items: &[NestedMeta]) -> Result<Self> {
|
||||
let mut new = DataShape::default();
|
||||
for item in items {
|
||||
if let NestedMeta::Meta(Meta::Word(ref ident)) = *item {
|
||||
new.set_word(ident.as_ref())?;
|
||||
new.set_word(ident.to_string().as_str())?;
|
||||
} else {
|
||||
return Err(Error::unsupported_format("non-word"));
|
||||
}
|
||||
|
@ -160,12 +161,12 @@ impl FromMetaItem for DataShape {
|
|||
}
|
||||
|
||||
impl ToTokens for DataShape {
|
||||
fn to_tokens(&self, tokens: &mut Tokens) {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let body = if self.any {
|
||||
quote!(::darling::export::Ok(()))
|
||||
} else if self.supports_none() {
|
||||
let ty = self.prefix.trim_right_matches("_");
|
||||
quote!(::darling::export::Err(::darling::Error::unsupported_format(#ty)))
|
||||
quote!(::darling::export::Err(::darling::Error::unsupported_shape(#ty)))
|
||||
} else {
|
||||
let unit = match_arm("unit", self.unit);
|
||||
let newtype = match_arm("newtype", self.newtype);
|
||||
|
@ -195,49 +196,49 @@ impl ToTokens for DataShape {
|
|||
}
|
||||
}
|
||||
|
||||
fn match_arm(name: &'static str, is_supported: bool) -> Tokens {
|
||||
fn match_arm(name: &'static str, is_supported: bool) -> TokenStream {
|
||||
if is_supported {
|
||||
quote!(::darling::export::Ok(()))
|
||||
} else {
|
||||
quote!(::darling::export::Err(::darling::Error::unsupported_format(#name)))
|
||||
quote!(::darling::export::Err(::darling::Error::unsupported_shape(#name)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use proc_macro2::TokenStream;
|
||||
use syn;
|
||||
use quote::Tokens;
|
||||
|
||||
use super::Shape;
|
||||
use {FromMetaItem};
|
||||
use FromMeta;
|
||||
|
||||
/// parse a string as a syn::Meta instance.
|
||||
fn pmi(tokens: Tokens) -> ::std::result::Result<syn::Meta, String> {
|
||||
fn pm(tokens: TokenStream) -> ::std::result::Result<syn::Meta, String> {
|
||||
let attribute: syn::Attribute = parse_quote!(#[#tokens]);
|
||||
attribute.interpret_meta().ok_or("Unable to parse".into())
|
||||
}
|
||||
|
||||
fn fmi<T: FromMetaItem>(tokens: Tokens) -> T {
|
||||
FromMetaItem::from_meta_item(&pmi(tokens).expect("Tests should pass well-formed input"))
|
||||
fn fm<T: FromMeta>(tokens: TokenStream) -> T {
|
||||
FromMeta::from_meta(&pm(tokens).expect("Tests should pass well-formed input"))
|
||||
.expect("Tests should pass valid input")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn supports_any() {
|
||||
let decl = fmi::<Shape>(quote!(ignore(any)));
|
||||
let decl = fm::<Shape>(quote!(ignore(any)));
|
||||
assert_eq!(decl.any, true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn supports_struct() {
|
||||
let decl = fmi::<Shape>(quote!(ignore(struct_any, struct_newtype)));
|
||||
let decl = fm::<Shape>(quote!(ignore(struct_any, struct_newtype)));
|
||||
assert_eq!(decl.struct_values.any, true);
|
||||
assert_eq!(decl.struct_values.newtype, true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn supports_mixed() {
|
||||
let decl = fmi::<Shape>(quote!(ignore(struct_newtype, enum_newtype, enum_tuple)));
|
||||
let decl = fm::<Shape>(quote!(ignore(struct_newtype, enum_newtype, enum_tuple)));
|
||||
assert_eq!(decl.struct_values.newtype, true);
|
||||
assert_eq!(decl.enum_values.newtype, true);
|
||||
assert_eq!(decl.enum_values.tuple, true);
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
use syn::Generics;
|
||||
|
||||
use usage::{IdentSet, LifetimeSet};
|
||||
|
||||
/// Extension trait for pulling specific generics data from a generics AST representation.
|
||||
pub trait GenericsExt {
|
||||
/// Get the set of all lifetimes declared by the syntax element.
|
||||
/// This does not look for usage of the lifetime; see `UsesLifetimes` for that.
|
||||
fn declared_lifetimes(&self) -> LifetimeSet;
|
||||
|
||||
/// Get the set of all type parameters declared by the syntax element.
|
||||
/// This does not look for usage of the type parameter; see `UsesTypeParams` for that.
|
||||
fn declared_type_params(&self) -> IdentSet;
|
||||
}
|
||||
|
||||
impl GenericsExt for Generics {
|
||||
fn declared_lifetimes(&self) -> LifetimeSet {
|
||||
self.lifetimes().map(|lt| lt.lifetime.clone()).collect()
|
||||
}
|
||||
|
||||
fn declared_type_params(&self) -> IdentSet {
|
||||
self.type_params().map(|tp| tp.ident.clone()).collect()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
use fnv::FnvHashSet;
|
||||
use syn::Ident;
|
||||
|
||||
/// A set of idents.
|
||||
pub type IdentSet = FnvHashSet<Ident>;
|
||||
|
||||
/// A set of references to idents.
|
||||
pub type IdentRefSet<'a> = FnvHashSet<&'a Ident>;
|
|
@ -0,0 +1,322 @@
|
|||
use fnv::FnvHashSet;
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::{self, Lifetime, Type};
|
||||
|
||||
use usage::Options;
|
||||
|
||||
/// A set of lifetimes.
|
||||
pub type LifetimeSet = FnvHashSet<Lifetime>;
|
||||
|
||||
/// A set of references to lifetimes.
|
||||
pub type LifetimeRefSet<'a> = FnvHashSet<&'a Lifetime>;
|
||||
|
||||
/// Searcher for finding lifetimes in a syntax tree.
|
||||
/// This can be used to determine which lifetimes must be emitted in generated code.
|
||||
pub trait UsesLifetimes {
|
||||
/// Returns the subset of the queried lifetimes that are used by the implementing syntax element.
|
||||
///
|
||||
/// This method only accounts for direct usage by the element; indirect usage via bounds or `where`
|
||||
/// predicates are not detected.
|
||||
fn uses_lifetimes<'a>(
|
||||
&self,
|
||||
options: &Options,
|
||||
lifetimes: &'a LifetimeSet,
|
||||
) -> LifetimeRefSet<'a>;
|
||||
|
||||
/// Find all used lifetimes, then clone them and return that set.
|
||||
fn uses_lifetimes_cloned(&self, options: &Options, lifetimes: &LifetimeSet) -> LifetimeSet {
|
||||
self.uses_lifetimes(options, lifetimes)
|
||||
.into_iter()
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// Searcher for finding lifetimes in an iterator.
|
||||
///
|
||||
/// This trait extends iterators, providing a way to turn a filtered list of fields or variants into a set
|
||||
/// of lifetimes.
|
||||
pub trait CollectLifetimes {
|
||||
/// Consume an iterator, accumulating all lifetimes in the elements which occur in `lifetimes`.
|
||||
fn collect_lifetimes<'a>(
|
||||
self,
|
||||
options: &Options,
|
||||
lifetimes: &'a LifetimeSet,
|
||||
) -> LifetimeRefSet<'a>;
|
||||
|
||||
/// Consume an iterator using `collect_lifetimes`, then clone all found lifetimes and return that set.
|
||||
fn collect_lifetimes_cloned(self, options: &Options, lifetimes: &LifetimeSet) -> LifetimeSet;
|
||||
}
|
||||
|
||||
impl<'i, I, T> CollectLifetimes for T
|
||||
where
|
||||
T: IntoIterator<Item = &'i I>,
|
||||
I: 'i + UsesLifetimes,
|
||||
{
|
||||
fn collect_lifetimes<'a>(
|
||||
self,
|
||||
options: &Options,
|
||||
lifetimes: &'a LifetimeSet,
|
||||
) -> LifetimeRefSet<'a> {
|
||||
self.into_iter()
|
||||
.fold(Default::default(), |mut state, value| {
|
||||
state.extend(value.uses_lifetimes(options, lifetimes));
|
||||
state
|
||||
})
|
||||
}
|
||||
|
||||
fn collect_lifetimes_cloned(self, options: &Options, lifetimes: &LifetimeSet) -> LifetimeSet {
|
||||
self.collect_lifetimes(options, lifetimes)
|
||||
.into_iter()
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UsesLifetimes> UsesLifetimes for Vec<T> {
|
||||
fn uses_lifetimes<'a>(
|
||||
&self,
|
||||
options: &Options,
|
||||
lifetimes: &'a LifetimeSet,
|
||||
) -> LifetimeRefSet<'a> {
|
||||
self.collect_lifetimes(options, lifetimes)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UsesLifetimes, U> UsesLifetimes for Punctuated<T, U> {
|
||||
fn uses_lifetimes<'a>(
|
||||
&self,
|
||||
options: &Options,
|
||||
lifetimes: &'a LifetimeSet,
|
||||
) -> LifetimeRefSet<'a> {
|
||||
self.collect_lifetimes(options, lifetimes)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UsesLifetimes> UsesLifetimes for Option<T> {
|
||||
fn uses_lifetimes<'a>(
|
||||
&self,
|
||||
options: &Options,
|
||||
lifetimes: &'a LifetimeSet,
|
||||
) -> LifetimeRefSet<'a> {
|
||||
self.as_ref().map_or_else(
|
||||
|| Default::default(),
|
||||
|v| v.uses_lifetimes(options, lifetimes),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl UsesLifetimes for Lifetime {
|
||||
fn uses_lifetimes<'a>(&self, _: &Options, lifetimes: &'a LifetimeSet) -> LifetimeRefSet<'a> {
|
||||
lifetimes.iter().filter(|lt| *lt == self).collect()
|
||||
}
|
||||
}
|
||||
|
||||
uses_lifetimes!(syn::AngleBracketedGenericArguments, args);
|
||||
uses_lifetimes!(syn::BareFnArg, ty);
|
||||
uses_lifetimes!(syn::Binding, ty);
|
||||
uses_lifetimes!(syn::BoundLifetimes, lifetimes);
|
||||
uses_lifetimes!(syn::Constraint, bounds);
|
||||
uses_lifetimes!(syn::DataEnum, variants);
|
||||
uses_lifetimes!(syn::DataStruct, fields);
|
||||
uses_lifetimes!(syn::DataUnion, fields);
|
||||
uses_lifetimes!(syn::Field, ty);
|
||||
uses_lifetimes!(syn::FieldsNamed, named);
|
||||
uses_lifetimes!(syn::LifetimeDef, lifetime, bounds);
|
||||
uses_lifetimes!(syn::ParenthesizedGenericArguments, inputs, output);
|
||||
uses_lifetimes!(syn::Path, segments);
|
||||
uses_lifetimes!(syn::PathSegment, arguments);
|
||||
uses_lifetimes!(syn::PredicateEq, lhs_ty, rhs_ty);
|
||||
uses_lifetimes!(syn::PredicateLifetime, lifetime, bounds);
|
||||
uses_lifetimes!(syn::PredicateType, lifetimes, bounded_ty, bounds);
|
||||
uses_lifetimes!(syn::QSelf, ty);
|
||||
uses_lifetimes!(syn::TraitBound, path, lifetimes);
|
||||
uses_lifetimes!(syn::TypeArray, elem);
|
||||
uses_lifetimes!(syn::TypeBareFn, inputs, output);
|
||||
uses_lifetimes!(syn::TypeGroup, elem);
|
||||
uses_lifetimes!(syn::TypeImplTrait, bounds);
|
||||
uses_lifetimes!(syn::TypeParen, elem);
|
||||
uses_lifetimes!(syn::TypePtr, elem);
|
||||
uses_lifetimes!(syn::TypeReference, lifetime, elem);
|
||||
uses_lifetimes!(syn::TypeSlice, elem);
|
||||
uses_lifetimes!(syn::TypeTuple, elems);
|
||||
uses_lifetimes!(syn::TypeTraitObject, bounds);
|
||||
uses_lifetimes!(syn::Variant, fields);
|
||||
|
||||
impl UsesLifetimes for syn::Data {
|
||||
fn uses_lifetimes<'a>(
|
||||
&self,
|
||||
options: &Options,
|
||||
lifetimes: &'a LifetimeSet,
|
||||
) -> LifetimeRefSet<'a> {
|
||||
match *self {
|
||||
syn::Data::Struct(ref v) => v.uses_lifetimes(options, lifetimes),
|
||||
syn::Data::Enum(ref v) => v.uses_lifetimes(options, lifetimes),
|
||||
syn::Data::Union(ref v) => v.uses_lifetimes(options, lifetimes),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UsesLifetimes for Type {
|
||||
fn uses_lifetimes<'a>(
|
||||
&self,
|
||||
options: &Options,
|
||||
lifetimes: &'a LifetimeSet,
|
||||
) -> LifetimeRefSet<'a> {
|
||||
match *self {
|
||||
Type::Slice(ref v) => v.uses_lifetimes(options, lifetimes),
|
||||
Type::Array(ref v) => v.uses_lifetimes(options, lifetimes),
|
||||
Type::Ptr(ref v) => v.uses_lifetimes(options, lifetimes),
|
||||
Type::Reference(ref v) => v.uses_lifetimes(options, lifetimes),
|
||||
Type::BareFn(ref v) => v.uses_lifetimes(options, lifetimes),
|
||||
Type::Tuple(ref v) => v.uses_lifetimes(options, lifetimes),
|
||||
Type::Path(ref v) => v.uses_lifetimes(options, lifetimes),
|
||||
Type::Paren(ref v) => v.uses_lifetimes(options, lifetimes),
|
||||
Type::Group(ref v) => v.uses_lifetimes(options, lifetimes),
|
||||
Type::TraitObject(ref v) => v.uses_lifetimes(options, lifetimes),
|
||||
Type::ImplTrait(ref v) => v.uses_lifetimes(options, lifetimes),
|
||||
Type::Macro(_) | Type::Verbatim(_) | Type::Infer(_) | Type::Never(_) => {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UsesLifetimes for syn::Fields {
|
||||
fn uses_lifetimes<'a>(
|
||||
&self,
|
||||
options: &Options,
|
||||
lifetimes: &'a LifetimeSet,
|
||||
) -> LifetimeRefSet<'a> {
|
||||
self.collect_lifetimes(options, lifetimes)
|
||||
}
|
||||
}
|
||||
|
||||
impl UsesLifetimes for syn::TypePath {
|
||||
fn uses_lifetimes<'a>(
|
||||
&self,
|
||||
options: &Options,
|
||||
lifetimes: &'a LifetimeSet,
|
||||
) -> LifetimeRefSet<'a> {
|
||||
let mut hits = self.path.uses_lifetimes(options, lifetimes);
|
||||
|
||||
if options.include_type_path_qself() {
|
||||
hits.extend(self.qself.uses_lifetimes(options, lifetimes));
|
||||
}
|
||||
|
||||
hits
|
||||
}
|
||||
}
|
||||
|
||||
impl UsesLifetimes for syn::ReturnType {
|
||||
fn uses_lifetimes<'a>(
|
||||
&self,
|
||||
options: &Options,
|
||||
lifetimes: &'a LifetimeSet,
|
||||
) -> LifetimeRefSet<'a> {
|
||||
if let syn::ReturnType::Type(_, ref ty) = *self {
|
||||
ty.uses_lifetimes(options, lifetimes)
|
||||
} else {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UsesLifetimes for syn::PathArguments {
|
||||
fn uses_lifetimes<'a>(
|
||||
&self,
|
||||
options: &Options,
|
||||
lifetimes: &'a LifetimeSet,
|
||||
) -> LifetimeRefSet<'a> {
|
||||
match *self {
|
||||
syn::PathArguments::None => Default::default(),
|
||||
syn::PathArguments::AngleBracketed(ref v) => v.uses_lifetimes(options, lifetimes),
|
||||
syn::PathArguments::Parenthesized(ref v) => v.uses_lifetimes(options, lifetimes),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UsesLifetimes for syn::WherePredicate {
|
||||
fn uses_lifetimes<'a>(
|
||||
&self,
|
||||
options: &Options,
|
||||
lifetimes: &'a LifetimeSet,
|
||||
) -> LifetimeRefSet<'a> {
|
||||
match *self {
|
||||
syn::WherePredicate::Type(ref v) => v.uses_lifetimes(options, lifetimes),
|
||||
syn::WherePredicate::Lifetime(ref v) => v.uses_lifetimes(options, lifetimes),
|
||||
syn::WherePredicate::Eq(ref v) => v.uses_lifetimes(options, lifetimes),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UsesLifetimes for syn::GenericArgument {
|
||||
fn uses_lifetimes<'a>(
|
||||
&self,
|
||||
options: &Options,
|
||||
lifetimes: &'a LifetimeSet,
|
||||
) -> LifetimeRefSet<'a> {
|
||||
match *self {
|
||||
syn::GenericArgument::Type(ref v) => v.uses_lifetimes(options, lifetimes),
|
||||
syn::GenericArgument::Binding(ref v) => v.uses_lifetimes(options, lifetimes),
|
||||
syn::GenericArgument::Lifetime(ref v) => v.uses_lifetimes(options, lifetimes),
|
||||
syn::GenericArgument::Constraint(ref v) => v.uses_lifetimes(options, lifetimes),
|
||||
syn::GenericArgument::Const(_) => Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UsesLifetimes for syn::TypeParamBound {
|
||||
fn uses_lifetimes<'a>(
|
||||
&self,
|
||||
options: &Options,
|
||||
lifetimes: &'a LifetimeSet,
|
||||
) -> LifetimeRefSet<'a> {
|
||||
match *self {
|
||||
syn::TypeParamBound::Trait(ref v) => v.uses_lifetimes(options, lifetimes),
|
||||
syn::TypeParamBound::Lifetime(ref v) => v.uses_lifetimes(options, lifetimes),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use proc_macro2::Span;
|
||||
use syn::{self, DeriveInput};
|
||||
|
||||
use super::UsesLifetimes;
|
||||
use usage::GenericsExt;
|
||||
use usage::Purpose::*;
|
||||
|
||||
fn parse(src: &str) -> DeriveInput {
|
||||
syn::parse_str(src).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn struct_named() {
|
||||
let input = parse("struct Foo<'a, 'b: 'a> { parent: &'b Bar, child: &'a Baz, }");
|
||||
let omitted = syn::Lifetime::new("'c", Span::call_site());
|
||||
|
||||
let lifetimes = {
|
||||
let mut lt = input.generics.declared_lifetimes();
|
||||
lt.insert(omitted);
|
||||
lt
|
||||
};
|
||||
|
||||
let matches = input.data.uses_lifetimes(&BoundImpl.into(), &lifetimes);
|
||||
assert_eq!(matches.len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn qself() {
|
||||
let input = parse(
|
||||
"struct Foo<'a, 'b: 'a> { parent: &'b Bar, child: <Bar<'a> as MyIterator>::Item, }",
|
||||
);
|
||||
let lifetimes = input.generics.declared_lifetimes();
|
||||
let matches = input.data.uses_lifetimes(&BoundImpl.into(), &lifetimes);
|
||||
assert_eq!(matches.len(), 1);
|
||||
|
||||
let decl_matches = input.data.uses_lifetimes(&Declare.into(), &lifetimes);
|
||||
assert_eq!(decl_matches.len(), 2);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
//! Traits and types used for tracking the usage of generic parameters through a proc-macro input.
|
||||
//!
|
||||
//! When generating trait impls, libraries often want to automatically figure out which type parameters
|
||||
//! are used in which fields, and then emit bounds that will produce the most permissive compilable
|
||||
//! code.
|
||||
//!
|
||||
//! # Usage
|
||||
//!
|
||||
//! ## Example 1: Filtering
|
||||
//! This example accepts a proc-macro input, then finds all lifetimes and type parameters used
|
||||
//! by private fields.
|
||||
//!
|
||||
//! ```rust
|
||||
//! # extern crate darling_core;
|
||||
//! # extern crate syn;
|
||||
//! #
|
||||
//! # // in real-world usage, import from `darling`
|
||||
//! # use darling_core::usage::{self, CollectLifetimes, CollectTypeParams, GenericsExt, Purpose};
|
||||
//! # use syn::{Data, DeriveInput, GenericParam, Generics, Visibility};
|
||||
//! #
|
||||
//! # #[allow(dead_code)]
|
||||
//! fn process(input: &DeriveInput) -> Generics {
|
||||
//! let type_params = input.generics.declared_type_params();
|
||||
//! let lifetimes = input.generics.declared_lifetimes();
|
||||
//!
|
||||
//! let mut ret_generics = input.generics.clone();
|
||||
//!
|
||||
//! if let Data::Struct(ref body) = input.data {
|
||||
//! let internal_fields = body
|
||||
//! .fields
|
||||
//! .iter()
|
||||
//! .filter(|field| field.vis == Visibility::Inherited)
|
||||
//! .collect::<Vec<_>>();
|
||||
//!
|
||||
//! let int_type_params = internal_fields
|
||||
//! .collect_type_params(&Purpose::BoundImpl.into(), &type_params);
|
||||
//!
|
||||
//! // We could reuse the vec from above, but here we'll instead
|
||||
//! // directly consume the chained iterator.
|
||||
//! let int_lifetimes = body
|
||||
//! .fields
|
||||
//! .iter()
|
||||
//! .filter(|field| field.vis == Visibility::Inherited)
|
||||
//! .collect_lifetimes(&Purpose::BoundImpl.into(), &lifetimes);
|
||||
//!
|
||||
//!
|
||||
//! ret_generics.params = ret_generics
|
||||
//! .params
|
||||
//! .into_iter()
|
||||
//! .filter(|gp| {
|
||||
//! match gp {
|
||||
//! GenericParam::Type(ref ty) => int_type_params.contains(&ty.ident),
|
||||
//! GenericParam::Lifetime(ref lt) => int_lifetimes.contains(<.lifetime),
|
||||
//! _ => true,
|
||||
//! }
|
||||
//! })
|
||||
//! .collect();
|
||||
//! }
|
||||
//!
|
||||
//! ret_generics
|
||||
//! }
|
||||
//!
|
||||
//! # fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! ## Example 2: Integrating with `FromDeriveInput`
|
||||
//! It is possible to use `darling`'s magic fields feature in tandem with the `usage` feature set.
|
||||
//! While there is no custom derive for `UsesTypeParams` or `UsesLifetimes`, there are macros to
|
||||
//! generate impls.
|
||||
//!
|
||||
//! ```rust,ignore
|
||||
//! #![allow(dead_code)]
|
||||
//!
|
||||
//! #[derive(FromField)]
|
||||
//! #[darling(attributes(speak))]
|
||||
//! struct SpeakerField {
|
||||
//! ident: Option<syn::Ident>,
|
||||
//! ty: syn::Type,
|
||||
//! #[darling(default)]
|
||||
//! volume: Option<u32>,
|
||||
//! }
|
||||
//!
|
||||
//! uses_type_params!(SpeakerField, ty);
|
||||
//! uses_lifetimes!(SpeakerField, ty);
|
||||
//!
|
||||
//! #[derive(FromDeriveInput)]
|
||||
//! struct SpeakerOptions {
|
||||
//! generics: syn::Generics,
|
||||
//! data: darling::ast::Data<darling::util::Ignored, SpeakerField>,
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! At this point, you are able to call `uses_type_params` on `SpeakerOptions.data`, or any filtered
|
||||
//! view of it. `darling` internally uses this in conjunction with the `skip` meta-item to determine
|
||||
//! which type parameters don't require the `FromMeta` bound in generated impls.
|
||||
//!
|
||||
//! **Note:** If you are performing operations referencing generic params in meta-items parsed by `darling`,
|
||||
//! you should determine if those impact the emitted code and wire up `UsesTypeParams` accordingly for
|
||||
//! your field/variant.
|
||||
|
||||
mod generics_ext;
|
||||
mod ident_set;
|
||||
mod lifetimes;
|
||||
mod options;
|
||||
mod type_params;
|
||||
|
||||
pub use self::generics_ext::GenericsExt;
|
||||
pub use self::ident_set::{IdentRefSet, IdentSet};
|
||||
pub use self::lifetimes::{CollectLifetimes, LifetimeRefSet, LifetimeSet, UsesLifetimes};
|
||||
pub use self::options::{Options, Purpose};
|
||||
pub use self::type_params::{CollectTypeParams, UsesTypeParams};
|
|
@ -0,0 +1,58 @@
|
|||
/// The goal of tracing generic parameter usage.
|
||||
///
|
||||
/// Not all uses of type parameters imply a need to add bounds to a generated trait impl.
|
||||
/// For example, a field of type `<Vec<T> as a::b::Trait>::Associated` does not need a
|
||||
/// `where T: Serialize` bound in `serde`.
|
||||
/// However, a proc macro that is attempting to generate a helper struct _would_ need to
|
||||
/// know about this usage, or else the generated code would reference an unknown type `T`
|
||||
/// and fail to compile.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum Purpose {
|
||||
/// The tracing is being used to generate an `impl` block.
|
||||
///
|
||||
/// Uses such as `syn::TypePath.qself` will _not_ be returned.
|
||||
BoundImpl,
|
||||
/// The tracing is being used to generate a new struct or enum.
|
||||
///
|
||||
/// All uses will be returned.
|
||||
Declare,
|
||||
}
|
||||
|
||||
/// Control struct for searching type parameters.
|
||||
///
|
||||
/// This acts as the search context, preserving information that might have been
|
||||
/// kept on a visitor in a different implementation.
|
||||
/// Trait implementers are required to pass this through on any invocations they make.
|
||||
///
|
||||
/// # Usage
|
||||
/// For extensibility, `Options` hides all of its fields from consumers.
|
||||
/// To create an instance, use the `From<Purpose>` trait implementation:
|
||||
///
|
||||
/// ```rust
|
||||
/// # use darling_core::usage::{Options, Purpose};
|
||||
/// let opts: Options = Purpose::BoundImpl.into();
|
||||
/// assert!(!opts.include_type_path_qself());
|
||||
/// ```
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Options {
|
||||
purpose: Purpose,
|
||||
#[doc(hidden)]
|
||||
__nonexhaustive: (),
|
||||
}
|
||||
|
||||
impl From<Purpose> for Options {
|
||||
fn from(purpose: Purpose) -> Self {
|
||||
Self {
|
||||
purpose,
|
||||
__nonexhaustive: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Options {
|
||||
/// Returns `true` if the implementer of `UseTypeParams` should search
|
||||
/// `<___ as ...>::...` when looking for type parameter uses.
|
||||
pub fn include_type_path_qself(&self) -> bool {
|
||||
self.purpose == Purpose::Declare
|
||||
}
|
||||
}
|
|
@ -0,0 +1,362 @@
|
|||
use syn::punctuated::Punctuated;
|
||||
use syn::{self, Ident, Type};
|
||||
|
||||
use usage::{IdentRefSet, IdentSet, Options};
|
||||
|
||||
/// Searcher for finding type params in a syntax tree.
|
||||
/// This can be used to determine if a given type parameter needs to be bounded in a generated impl.
|
||||
pub trait UsesTypeParams {
|
||||
/// Returns the subset of the queried type parameters that are used by the implementing syntax element.
|
||||
///
|
||||
/// This method only accounts for direct usage by the element; indirect usage via bounds or `where`
|
||||
/// predicates are not detected.
|
||||
fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a>;
|
||||
|
||||
/// Find all type params using `uses_type_params`, then clone the found values and return the set.
|
||||
fn uses_type_params_cloned(&self, options: &Options, type_set: &IdentSet) -> IdentSet {
|
||||
self.uses_type_params(options, type_set)
|
||||
.into_iter()
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// Searcher for finding type params in an iterator.
|
||||
///
|
||||
/// This trait extends iterators, providing a way to turn a filtered list of fields or variants into a set
|
||||
/// of type parameter idents.
|
||||
pub trait CollectTypeParams {
|
||||
/// Consume an iterator, accumulating all type parameters in the elements which occur in `type_set`.
|
||||
fn collect_type_params<'a>(self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a>;
|
||||
|
||||
/// Consume an iterator using `collect_type_params`, then clone all found type params and return that set.
|
||||
fn collect_type_params_cloned(self, options: &Options, type_set: &IdentSet) -> IdentSet;
|
||||
}
|
||||
|
||||
impl<'i, T, I> CollectTypeParams for T
|
||||
where
|
||||
T: IntoIterator<Item = &'i I>,
|
||||
I: 'i + UsesTypeParams,
|
||||
{
|
||||
fn collect_type_params<'a>(self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
|
||||
self.into_iter().fold(
|
||||
IdentRefSet::with_capacity_and_hasher(type_set.len(), Default::default()),
|
||||
|state, value| union_in_place(state, value.uses_type_params(options, type_set)),
|
||||
)
|
||||
}
|
||||
|
||||
fn collect_type_params_cloned(self, options: &Options, type_set: &IdentSet) -> IdentSet {
|
||||
self.collect_type_params(options, type_set)
|
||||
.into_iter()
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// Insert the contents of `right` into `left`.
|
||||
fn union_in_place<'a>(mut left: IdentRefSet<'a>, right: IdentRefSet<'a>) -> IdentRefSet<'a> {
|
||||
left.extend(right);
|
||||
|
||||
left
|
||||
}
|
||||
|
||||
impl UsesTypeParams for () {
|
||||
fn uses_type_params<'a>(&self, _options: &Options, _type_set: &'a IdentSet) -> IdentRefSet<'a> {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UsesTypeParams> UsesTypeParams for Option<T> {
|
||||
fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
|
||||
self.as_ref()
|
||||
.map(|v| v.uses_type_params(options, type_set))
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UsesTypeParams> UsesTypeParams for Vec<T> {
|
||||
fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
|
||||
self.collect_type_params(options, type_set)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UsesTypeParams, U> UsesTypeParams for Punctuated<T, U> {
|
||||
fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
|
||||
self.collect_type_params(options, type_set)
|
||||
}
|
||||
}
|
||||
|
||||
uses_type_params!(syn::AngleBracketedGenericArguments, args);
|
||||
uses_type_params!(syn::BareFnArg, ty);
|
||||
uses_type_params!(syn::Binding, ty);
|
||||
uses_type_params!(syn::Constraint, bounds);
|
||||
uses_type_params!(syn::DataEnum, variants);
|
||||
uses_type_params!(syn::DataStruct, fields);
|
||||
uses_type_params!(syn::DataUnion, fields);
|
||||
uses_type_params!(syn::Field, ty);
|
||||
uses_type_params!(syn::FieldsNamed, named);
|
||||
uses_type_params!(syn::ParenthesizedGenericArguments, inputs, output);
|
||||
uses_type_params!(syn::PredicateEq, lhs_ty, rhs_ty);
|
||||
uses_type_params!(syn::PredicateType, bounded_ty, bounds);
|
||||
uses_type_params!(syn::QSelf, ty);
|
||||
uses_type_params!(syn::TraitBound, path);
|
||||
uses_type_params!(syn::TypeArray, elem);
|
||||
uses_type_params!(syn::TypeBareFn, inputs, output);
|
||||
uses_type_params!(syn::TypeGroup, elem);
|
||||
uses_type_params!(syn::TypeImplTrait, bounds);
|
||||
uses_type_params!(syn::TypeParen, elem);
|
||||
uses_type_params!(syn::TypePtr, elem);
|
||||
uses_type_params!(syn::TypeReference, elem);
|
||||
uses_type_params!(syn::TypeSlice, elem);
|
||||
uses_type_params!(syn::TypeTuple, elems);
|
||||
uses_type_params!(syn::TypeTraitObject, bounds);
|
||||
uses_type_params!(syn::Variant, fields);
|
||||
|
||||
impl UsesTypeParams for syn::Data {
|
||||
fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
|
||||
match *self {
|
||||
syn::Data::Struct(ref v) => v.uses_type_params(options, type_set),
|
||||
syn::Data::Enum(ref v) => v.uses_type_params(options, type_set),
|
||||
syn::Data::Union(ref v) => v.uses_type_params(options, type_set),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UsesTypeParams for syn::Fields {
|
||||
fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
|
||||
self.collect_type_params(options, type_set)
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if an Ident exactly matches one of the sought-after type parameters.
|
||||
impl UsesTypeParams for Ident {
|
||||
fn uses_type_params<'a>(&self, _options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
|
||||
type_set.iter().filter(|v| *v == self).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl UsesTypeParams for syn::ReturnType {
|
||||
fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
|
||||
if let syn::ReturnType::Type(_, ref ty) = *self {
|
||||
ty.uses_type_params(options, type_set)
|
||||
} else {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UsesTypeParams for Type {
|
||||
fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
|
||||
match *self {
|
||||
Type::Slice(ref v) => v.uses_type_params(options, type_set),
|
||||
Type::Array(ref v) => v.uses_type_params(options, type_set),
|
||||
Type::Ptr(ref v) => v.uses_type_params(options, type_set),
|
||||
Type::Reference(ref v) => v.uses_type_params(options, type_set),
|
||||
Type::BareFn(ref v) => v.uses_type_params(options, type_set),
|
||||
Type::Tuple(ref v) => v.uses_type_params(options, type_set),
|
||||
Type::Path(ref v) => v.uses_type_params(options, type_set),
|
||||
Type::Paren(ref v) => v.uses_type_params(options, type_set),
|
||||
Type::Group(ref v) => v.uses_type_params(options, type_set),
|
||||
Type::TraitObject(ref v) => v.uses_type_params(options, type_set),
|
||||
Type::ImplTrait(ref v) => v.uses_type_params(options, type_set),
|
||||
Type::Macro(_) | Type::Verbatim(_) | Type::Infer(_) | Type::Never(_) => {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UsesTypeParams for syn::TypePath {
|
||||
fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
|
||||
let hits = self.path.uses_type_params(options, type_set);
|
||||
|
||||
if options.include_type_path_qself() {
|
||||
union_in_place(hits, self.qself.uses_type_params(options, type_set))
|
||||
} else {
|
||||
hits
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UsesTypeParams for syn::Path {
|
||||
fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
|
||||
// Not sure if this is even possible, but a path with no segments definitely
|
||||
// can't use type parameters.
|
||||
if self.segments.is_empty() {
|
||||
return Default::default();
|
||||
}
|
||||
|
||||
// A path segment ident can only match if it is not global and it is the first segment
|
||||
// in the path.
|
||||
let ident_hits = if self.leading_colon.is_none() {
|
||||
self.segments[0].ident.uses_type_params(options, type_set)
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
|
||||
// Merge ident hit, if any, with all hits from path arguments
|
||||
self.segments.iter().fold(ident_hits, |state, segment| {
|
||||
union_in_place(state, segment.arguments.uses_type_params(options, type_set))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl UsesTypeParams for syn::PathArguments {
|
||||
fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
|
||||
match *self {
|
||||
syn::PathArguments::None => Default::default(),
|
||||
syn::PathArguments::AngleBracketed(ref v) => v.uses_type_params(options, type_set),
|
||||
syn::PathArguments::Parenthesized(ref v) => v.uses_type_params(options, type_set),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UsesTypeParams for syn::WherePredicate {
|
||||
fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
|
||||
match *self {
|
||||
syn::WherePredicate::Lifetime(_) => Default::default(),
|
||||
syn::WherePredicate::Type(ref v) => v.uses_type_params(options, type_set),
|
||||
syn::WherePredicate::Eq(ref v) => v.uses_type_params(options, type_set),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UsesTypeParams for syn::GenericArgument {
|
||||
fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
|
||||
match *self {
|
||||
syn::GenericArgument::Type(ref v) => v.uses_type_params(options, type_set),
|
||||
syn::GenericArgument::Binding(ref v) => v.uses_type_params(options, type_set),
|
||||
syn::GenericArgument::Constraint(ref v) => v.uses_type_params(options, type_set),
|
||||
syn::GenericArgument::Const(_) | syn::GenericArgument::Lifetime(_) => {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UsesTypeParams for syn::TypeParamBound {
|
||||
fn uses_type_params<'a>(&self, options: &Options, type_set: &'a IdentSet) -> IdentRefSet<'a> {
|
||||
match *self {
|
||||
syn::TypeParamBound::Trait(ref v) => v.uses_type_params(options, type_set),
|
||||
syn::TypeParamBound::Lifetime(_) => Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use proc_macro2::Span;
|
||||
use syn::{self, Ident};
|
||||
|
||||
use super::UsesTypeParams;
|
||||
use usage::IdentSet;
|
||||
use usage::Purpose::*;
|
||||
|
||||
fn given_src(src: &str) -> syn::DeriveInput {
|
||||
syn::parse_str(src).unwrap()
|
||||
}
|
||||
|
||||
fn ident_set(idents: Vec<&str>) -> IdentSet {
|
||||
idents
|
||||
.into_iter()
|
||||
.map(|s| Ident::new(s, Span::call_site()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn finds_simple() {
|
||||
let input = given_src("struct Foo<T, U>(T, i32, A, U);");
|
||||
let generics = ident_set(vec!["T", "U", "X"]);
|
||||
let matches = input.data.uses_type_params(&BoundImpl.into(), &generics);
|
||||
assert_eq!(matches.len(), 2);
|
||||
assert!(matches.contains::<Ident>(&parse_quote!(T)));
|
||||
assert!(matches.contains::<Ident>(&parse_quote!(U)));
|
||||
assert!(!matches.contains::<Ident>(&parse_quote!(X)));
|
||||
assert!(!matches.contains::<Ident>(&parse_quote!(A)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn finds_named() {
|
||||
let input = given_src(
|
||||
r#"
|
||||
struct Foo<T, U = usize> {
|
||||
bar: T,
|
||||
world: U,
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
let generics = ident_set(vec!["T", "U", "X"]);
|
||||
|
||||
let matches = input.data.uses_type_params(&BoundImpl.into(), &generics);
|
||||
|
||||
assert_eq!(matches.len(), 2);
|
||||
assert!(matches.contains::<Ident>(&parse_quote!(T)));
|
||||
assert!(matches.contains::<Ident>(&parse_quote!(U)));
|
||||
assert!(!matches.contains::<Ident>(&parse_quote!(X)));
|
||||
assert!(!matches.contains::<Ident>(&parse_quote!(A)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn finds_as_type_arg() {
|
||||
let input = given_src(
|
||||
r#"
|
||||
struct Foo<T, U> {
|
||||
bar: T,
|
||||
world: Vec<U>,
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
let generics = ident_set(vec!["T", "U", "X"]);
|
||||
|
||||
let matches = input.data.uses_type_params(&BoundImpl.into(), &generics);
|
||||
|
||||
assert_eq!(matches.len(), 2);
|
||||
assert!(matches.contains::<Ident>(&parse_quote!(T)));
|
||||
assert!(matches.contains::<Ident>(&parse_quote!(U)));
|
||||
assert!(!matches.contains::<Ident>(&parse_quote!(X)));
|
||||
assert!(!matches.contains::<Ident>(&parse_quote!(A)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn associated_type() {
|
||||
let input = given_src("struct Foo<'a, T> where T: Iterator { peek: T::Item }");
|
||||
let generics = ident_set(vec!["T", "INTO"]);
|
||||
let matches = input.data.uses_type_params(&BoundImpl.into(), &generics);
|
||||
assert_eq!(matches.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn box_fn_output() {
|
||||
let input = given_src("struct Foo<T>(Box<Fn() -> T>);");
|
||||
let generics = ident_set(vec!["T"]);
|
||||
let matches = input.data.uses_type_params(&BoundImpl.into(), &generics);
|
||||
assert_eq!(matches.len(), 1);
|
||||
assert!(matches.contains::<Ident>(&parse_quote!(T)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn box_fn_input() {
|
||||
let input = given_src("struct Foo<T>(Box<Fn(&T) -> ()>);");
|
||||
let generics = ident_set(vec!["T"]);
|
||||
let matches = input.data.uses_type_params(&BoundImpl.into(), &generics);
|
||||
assert_eq!(matches.len(), 1);
|
||||
assert!(matches.contains::<Ident>(&parse_quote!(T)));
|
||||
}
|
||||
|
||||
/// Test that `syn::TypePath` is correctly honoring the different modes a
|
||||
/// search can execute in.
|
||||
#[test]
|
||||
fn qself_vec() {
|
||||
let input = given_src("struct Foo<T>(<Vec<T> as a::b::Trait>::AssociatedItem);");
|
||||
let generics = ident_set(vec!["T", "U"]);
|
||||
|
||||
let bound_matches = input.data.uses_type_params(&BoundImpl.into(), &generics);
|
||||
assert_eq!(bound_matches.len(), 0);
|
||||
|
||||
let declare_matches = input.data.uses_type_params(&Declare.into(), &generics);
|
||||
assert_eq!(declare_matches.len(), 1);
|
||||
assert!(declare_matches.contains::<Ident>(&parse_quote!(T)));
|
||||
}
|
||||
}
|
|
@ -1,14 +1,15 @@
|
|||
use std::ops::Deref;
|
||||
use std::string::ToString;
|
||||
|
||||
use syn::{Ident, NestedMeta, Meta};
|
||||
use syn::{Ident, Meta, NestedMeta};
|
||||
|
||||
use {FromMetaItem, Result, Error};
|
||||
use {Error, FromMeta, Result};
|
||||
|
||||
/// A list of `syn::Ident` instances. This type is used to extract a list of words from an
|
||||
/// A list of `syn::Ident` instances. This type is used to extract a list of words from an
|
||||
/// attribute.
|
||||
///
|
||||
/// # Usage
|
||||
/// An `IdentList` field on a struct implementing `FromMetaItem` will turn `#[builder(derive(Debug, Clone))]` into:
|
||||
/// An `IdentList` field on a struct implementing `FromMeta` will turn `#[builder(derive(Debug, Clone))]` into:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// StructOptions {
|
||||
|
@ -24,9 +25,9 @@ impl IdentList {
|
|||
IdentList(vals.into_iter().map(T::into).collect())
|
||||
}
|
||||
|
||||
/// Creates a view of the contained identifiers as `&str`s.
|
||||
pub fn as_strs<'a>(&'a self) -> Vec<&'a str> {
|
||||
self.iter().map(|i| i.as_ref()).collect()
|
||||
/// Create a new `Vec` containing the string representation of each ident.
|
||||
pub fn to_strings(&self) -> Vec<String> {
|
||||
self.0.iter().map(ToString::to_string).collect()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,14 +45,14 @@ impl From<Vec<Ident>> for IdentList {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromMetaItem for IdentList {
|
||||
impl FromMeta for IdentList {
|
||||
fn from_list(v: &[NestedMeta]) -> Result<Self> {
|
||||
let mut idents = Vec::with_capacity(v.len());
|
||||
for nmi in v {
|
||||
if let NestedMeta::Meta(Meta::Word(ref ident)) = *nmi {
|
||||
idents.push(ident.clone());
|
||||
} else {
|
||||
return Err(Error::unexpected_type("non-word"))
|
||||
return Err(Error::unexpected_type("non-word"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,148 @@
|
|||
use std::fmt;
|
||||
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::ToTokens;
|
||||
use syn::{Ident, Meta};
|
||||
|
||||
use {FromMeta, Result};
|
||||
|
||||
/// A wrapper for an `Ident` which also keeps the value as a string.
|
||||
///
|
||||
/// This struct can be used to perform string comparisons and operations.
|
||||
#[derive(Clone, Hash, PartialOrd, Ord)]
|
||||
pub struct IdentString {
|
||||
ident: Ident,
|
||||
string: String,
|
||||
}
|
||||
|
||||
impl IdentString {
|
||||
/// Create a new `IdentString`.
|
||||
pub fn new(ident: Ident) -> Self {
|
||||
IdentString {
|
||||
string: ident.to_string(),
|
||||
ident,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the ident as a `proc_macro2::Ident`.
|
||||
pub fn as_ident(&self) -> &Ident {
|
||||
&self.ident
|
||||
}
|
||||
|
||||
/// Get the ident as a string.
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.string
|
||||
}
|
||||
|
||||
/// Get the location of this `Ident` in source.
|
||||
pub fn span(&self) -> Span {
|
||||
self.ident.span()
|
||||
}
|
||||
|
||||
/// Apply some transform to the ident's string representation.
|
||||
///
|
||||
/// # Panics
|
||||
/// This will panic if the transform produces an invalid ident.
|
||||
pub fn map<F, S>(self, map_fn: F) -> Self
|
||||
where
|
||||
F: FnOnce(String) -> S,
|
||||
S: AsRef<str>,
|
||||
{
|
||||
let span = self.span();
|
||||
let string = map_fn(self.string);
|
||||
Ident::new(string.as_ref(), span).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Ident> for IdentString {
|
||||
fn as_ref(&self) -> &Ident {
|
||||
self.as_ident()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for IdentString {
|
||||
fn as_ref(&self) -> &str {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Ident> for IdentString {
|
||||
fn from(ident: Ident) -> Self {
|
||||
IdentString::new(ident)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IdentString> for Ident {
|
||||
fn from(v: IdentString) -> Ident {
|
||||
v.ident
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IdentString> for String {
|
||||
fn from(v: IdentString) -> String {
|
||||
v.string
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for IdentString {}
|
||||
|
||||
impl PartialEq for IdentString {
|
||||
fn eq(&self, rhs: &Self) -> bool {
|
||||
self.ident == rhs.ident
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<String> for IdentString {
|
||||
fn eq(&self, rhs: &String) -> bool {
|
||||
self.as_str() == rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq<&'a str> for IdentString {
|
||||
fn eq(&self, rhs: &&str) -> bool {
|
||||
self.as_str() == *rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for IdentString {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
self.ident.to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for IdentString {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?}", self.ident)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for IdentString {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.ident)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromMeta for IdentString {
|
||||
fn from_meta(item: &Meta) -> Result<Self> {
|
||||
Ident::from_meta(item).map(IdentString::from)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::IdentString;
|
||||
|
||||
#[test]
|
||||
fn convert() {
|
||||
let i_str = IdentString::new(parse_quote!(t));
|
||||
assert_eq!(i_str.as_str(), "t");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn map_transform() {
|
||||
let i = IdentString::new(parse_quote!(my));
|
||||
let after = i.map(|v| format!("var_{}", v));
|
||||
assert_eq!(after, "var_my");
|
||||
assert_eq!(after, String::from("var_my"));
|
||||
}
|
||||
}
|
|
@ -1,38 +1,52 @@
|
|||
use syn;
|
||||
|
||||
use {FromMetaItem, FromDeriveInput, FromField, FromVariant, Result};
|
||||
use usage::{self, UsesLifetimes, UsesTypeParams};
|
||||
use {
|
||||
FromDeriveInput, FromField, FromGenericParam, FromGenerics, FromMeta, FromTypeParam,
|
||||
FromVariant, Result,
|
||||
};
|
||||
|
||||
/// An efficient way of discarding data from an attribute.
|
||||
/// An efficient way of discarding data from a syntax element.
|
||||
///
|
||||
/// All meta-items, fields, and variants will be successfully read into
|
||||
/// All syntax elements will be successfully read into
|
||||
/// the `Ignored` struct, with all properties discarded.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
|
||||
pub struct Ignored;
|
||||
|
||||
impl FromMetaItem for Ignored {
|
||||
fn from_meta_item(_: &syn::Meta) -> Result<Self> {
|
||||
Ok(Ignored)
|
||||
}
|
||||
macro_rules! ignored {
|
||||
($trayt:ident, $method:ident, $syn:path) => {
|
||||
impl $trayt for Ignored {
|
||||
fn $method(_: &$syn) -> Result<Self> {
|
||||
Ok(Ignored)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn from_nested_meta_item(_: &syn::NestedMeta) -> Result<Self> {
|
||||
Ok(Ignored)
|
||||
ignored!(FromGenericParam, from_generic_param, syn::GenericParam);
|
||||
ignored!(FromGenerics, from_generics, syn::Generics);
|
||||
ignored!(FromTypeParam, from_type_param, syn::TypeParam);
|
||||
ignored!(FromMeta, from_meta, syn::Meta);
|
||||
ignored!(FromDeriveInput, from_derive_input, syn::DeriveInput);
|
||||
ignored!(FromField, from_field, syn::Field);
|
||||
ignored!(FromVariant, from_variant, syn::Variant);
|
||||
|
||||
impl UsesTypeParams for Ignored {
|
||||
fn uses_type_params<'a>(
|
||||
&self,
|
||||
_opts: &usage::Options,
|
||||
_: &'a usage::IdentSet,
|
||||
) -> usage::IdentRefSet<'a> {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromDeriveInput for Ignored {
|
||||
fn from_derive_input(_: &syn::DeriveInput) -> Result<Self> {
|
||||
Ok(Ignored)
|
||||
impl UsesLifetimes for Ignored {
|
||||
fn uses_lifetimes<'a>(
|
||||
&self,
|
||||
_opts: &usage::Options,
|
||||
_: &'a usage::LifetimeSet,
|
||||
) -> usage::LifetimeRefSet<'a> {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromField for Ignored {
|
||||
fn from_field(_: &syn::Field) -> Result<Self> {
|
||||
Ok(Ignored)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromVariant for Ignored {
|
||||
fn from_variant(_: &syn::Variant) -> Result<Self> {
|
||||
Ok(Ignored)
|
||||
}
|
||||
}
|
|
@ -1,17 +1,21 @@
|
|||
//! Utility types for attribute parsing.
|
||||
|
||||
use std::ops::{Deref, Not, BitAnd, BitOr};
|
||||
use std::ops::{BitAnd, BitOr, Deref, Not};
|
||||
|
||||
use syn;
|
||||
use {FromMetaItem, Result};
|
||||
use {FromMeta, Result};
|
||||
|
||||
mod ident_list;
|
||||
mod ident_string;
|
||||
mod ignored;
|
||||
mod over_ride;
|
||||
mod with_original;
|
||||
|
||||
pub use self::ident_list::IdentList;
|
||||
pub use self::ident_string::IdentString;
|
||||
pub use self::ignored::Ignored;
|
||||
pub use self::over_ride::Override;
|
||||
pub use self::with_original::WithOriginal;
|
||||
|
||||
/// Marker type equivalent to `Option<()>` for use in attribute parsing.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
|
@ -31,9 +35,9 @@ impl Deref for Flag {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromMetaItem for Flag {
|
||||
fn from_meta_item(mi: &syn::Meta) -> Result<Self> {
|
||||
FromMetaItem::from_meta_item(mi).map(Flag)
|
||||
impl FromMeta for Flag {
|
||||
fn from_meta(mi: &syn::Meta) -> Result<Self> {
|
||||
FromMeta::from_meta(mi).map(Flag)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,7 +49,11 @@ impl From<Flag> for bool {
|
|||
|
||||
impl From<bool> for Flag {
|
||||
fn from(v: bool) -> Self {
|
||||
if v { Flag::present() } else { Flag(None) }
|
||||
if v {
|
||||
Flag::present()
|
||||
} else {
|
||||
Flag(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,4 +107,4 @@ impl BitOr for Flag {
|
|||
fn bitor(self, rhs: Self) -> Self {
|
||||
(self.into() || rhs.into()).into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::fmt;
|
|||
|
||||
use syn::{Lit, NestedMeta};
|
||||
|
||||
use {Result, FromMetaItem};
|
||||
use {FromMeta, Result};
|
||||
|
||||
use self::Override::*;
|
||||
|
||||
|
@ -42,7 +42,7 @@ pub enum Override<T> {
|
|||
Inherit,
|
||||
|
||||
/// Explicitly set the value.
|
||||
Explicit(T)
|
||||
Explicit(T),
|
||||
}
|
||||
|
||||
impl<T> Override<T> {
|
||||
|
@ -52,7 +52,7 @@ impl<T> Override<T> {
|
|||
pub fn as_ref<'a>(&'a self) -> Override<&'a T> {
|
||||
match *self {
|
||||
Inherit => Inherit,
|
||||
Explicit(ref val) => Explicit(val)
|
||||
Explicit(ref val) => Explicit(val),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,7 @@ impl<T> Override<T> {
|
|||
pub fn as_mut<'a>(&'a mut self) -> Override<&'a T> {
|
||||
match *self {
|
||||
Inherit => Inherit,
|
||||
Explicit(ref mut val) => Explicit(val)
|
||||
Explicit(ref mut val) => Explicit(val),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,7 +78,7 @@ impl<T> Override<T> {
|
|||
pub fn explicit(self) -> Option<T> {
|
||||
match self {
|
||||
Inherit => None,
|
||||
Explicit(val) => Some(val)
|
||||
Explicit(val) => Some(val),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -91,7 +91,10 @@ impl<T> Override<T> {
|
|||
}
|
||||
|
||||
/// Unwraps an override, yielding the content of an `Explicit`. Otherwise, it calls `op`.
|
||||
pub fn unwrap_or_else<F>(self, op: F) -> T where F: FnOnce() -> T {
|
||||
pub fn unwrap_or_else<F>(self, op: F) -> T
|
||||
where
|
||||
F: FnOnce() -> T,
|
||||
{
|
||||
match self {
|
||||
Inherit => op(),
|
||||
Explicit(val) => val,
|
||||
|
@ -131,17 +134,17 @@ impl<T: fmt::Display> fmt::Display for Override<T> {
|
|||
}
|
||||
|
||||
/// Parses a `Meta`. A bare word will produce `Override::Inherit`, while
|
||||
/// any value will be forwarded to `T::from_meta_item`.
|
||||
impl<T: FromMetaItem> FromMetaItem for Override<T> {
|
||||
/// any value will be forwarded to `T::from_meta`.
|
||||
impl<T: FromMeta> FromMeta for Override<T> {
|
||||
fn from_word() -> Result<Self> {
|
||||
Ok(Inherit)
|
||||
}
|
||||
|
||||
fn from_list(items: &[NestedMeta]) -> Result<Self> {
|
||||
Ok(Explicit(FromMetaItem::from_list(items)?))
|
||||
Ok(Explicit(FromMeta::from_list(items)?))
|
||||
}
|
||||
|
||||
fn from_value(lit: &Lit) -> Result<Self> {
|
||||
Ok(Explicit(FromMetaItem::from_value(lit)?))
|
||||
Ok(Explicit(FromMeta::from_value(lit)?))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
use syn;
|
||||
|
||||
use {FromDeriveInput, FromField, FromGenericParam, FromGenerics, FromMeta, FromTypeParam,
|
||||
FromVariant, Result};
|
||||
|
||||
/// A container to parse some syntax and retain access to the original.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct WithOriginal<T, O> {
|
||||
pub parsed: T,
|
||||
pub original: O,
|
||||
}
|
||||
|
||||
impl<T, O> WithOriginal<T, O> {
|
||||
pub fn new(parsed: T, original: O) -> Self {
|
||||
WithOriginal { parsed, original }
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! with_original {
|
||||
($trayt:ident, $func:ident, $syn:path) => {
|
||||
impl<T: $trayt> $trayt for WithOriginal<T, $syn> {
|
||||
fn $func(value: &$syn) -> Result<Self> {
|
||||
Ok(WithOriginal::new($trayt::$func(value)?, value.clone()))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
with_original!(FromDeriveInput, from_derive_input, syn::DeriveInput);
|
||||
with_original!(FromField, from_field, syn::Field);
|
||||
with_original!(FromGenerics, from_generics, syn::Generics);
|
||||
with_original!(FromGenericParam, from_generic_param, syn::GenericParam);
|
||||
with_original!(FromMeta, from_meta, syn::Meta);
|
||||
with_original!(FromTypeParam, from_type_param, syn::TypeParam);
|
||||
with_original!(FromVariant, from_variant, syn::Variant);
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"cf9d579ee6af881a7aa52d43d637b4afa9cf589bfda3fa63159538d681855330","src/lib.rs":"d900da894985945215cb4494ebd4e8b5f697c19bf9e624a1bb03d22a0a5367a5"},"package":"eb69a38fdeaeaf3db712e1df170de67ee9dfc24fb88ca3e9d21e703ec25a4d8e"}
|
||||
{"files":{"Cargo.toml":"2603037629b2df2d1ce38ee7a163f745d4f08cd5a8c4c39efc828cd718445fbe","src/lib.rs":"8343bf4500faeaf72e0cafb9e2c378be176bfe41529ba8f0d5e315336de422bf"},"package":"9973050ba46be2a2935a7b316147f41a808ac604b8f0fef6eba77fd47a89daeb"}
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
[package]
|
||||
name = "darling_macro"
|
||||
version = "0.4.0"
|
||||
version = "0.8.0"
|
||||
authors = ["Ted Driggs <ted.driggs@outlook.com>"]
|
||||
description = "Internal support for a proc-macro library for reading attributes into structs when\nimplementing custom derives. Use https://crates.io/crates/darling in your code.\n"
|
||||
license = "MIT"
|
||||
|
@ -21,10 +21,10 @@ repository = "https://github.com/TedDriggs/darling"
|
|||
[lib]
|
||||
proc-macro = true
|
||||
[dependencies.darling_core]
|
||||
version = "=0.4.0"
|
||||
version = "=0.8.0"
|
||||
|
||||
[dependencies.quote]
|
||||
version = "0.5"
|
||||
version = "0.6"
|
||||
|
||||
[dependencies.syn]
|
||||
version = "0.13"
|
||||
version = "0.15"
|
||||
|
|
|
@ -1,27 +1,35 @@
|
|||
extern crate proc_macro;
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
#[macro_use]
|
||||
extern crate syn;
|
||||
|
||||
extern crate darling_core;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
use darling_core::{options, codegen};
|
||||
use darling_core::{codegen, options};
|
||||
|
||||
#[proc_macro_derive(FromMetaItem, attributes(darling))]
|
||||
pub fn derive_from_meta_item(input: TokenStream) -> TokenStream {
|
||||
let ast: syn::DeriveInput = syn::parse(input).unwrap();
|
||||
let container = options::FmiOptions::new(&ast).unwrap();
|
||||
let trait_impl = codegen::FmiImpl::from(&container);
|
||||
#[proc_macro_derive(FromMeta, attributes(darling))]
|
||||
pub fn derive_from_meta(input: TokenStream) -> TokenStream {
|
||||
let ast = parse_macro_input!(input as syn::DeriveInput);
|
||||
|
||||
let container = options::FromMetaOptions::new(&ast).unwrap();
|
||||
let trait_impl = codegen::FromMetaImpl::from(&container);
|
||||
let result = quote!(#trait_impl);
|
||||
|
||||
result.into()
|
||||
}
|
||||
|
||||
#[proc_macro_derive(FromMetaItem, attributes(darling))]
|
||||
pub fn derive_from_meta_item(_input: TokenStream) -> TokenStream {
|
||||
panic!("darling::FromMetaItem has been replaced by darling::FromMeta");
|
||||
}
|
||||
|
||||
|
||||
#[proc_macro_derive(FromDeriveInput, attributes(darling))]
|
||||
pub fn derive_from_input(input: TokenStream) -> TokenStream {
|
||||
let ast: syn::DeriveInput = syn::parse(input).unwrap();
|
||||
let ast = parse_macro_input!(input as syn::DeriveInput);
|
||||
|
||||
let container = options::FdiOptions::new(&ast).unwrap();
|
||||
let trait_impl = codegen::FromDeriveInputImpl::from(&container);
|
||||
|
@ -32,7 +40,7 @@ pub fn derive_from_input(input: TokenStream) -> TokenStream {
|
|||
|
||||
#[proc_macro_derive(FromField, attributes(darling))]
|
||||
pub fn derive_field(input: TokenStream) -> TokenStream {
|
||||
let ast: syn::DeriveInput = syn::parse(input).unwrap();
|
||||
let ast = parse_macro_input!(input as syn::DeriveInput);
|
||||
|
||||
let fdic = options::FromFieldOptions::new(&ast).unwrap();
|
||||
let trait_impl = codegen::FromFieldImpl::from(&fdic);
|
||||
|
@ -41,9 +49,20 @@ pub fn derive_field(input: TokenStream) -> TokenStream {
|
|||
result.into()
|
||||
}
|
||||
|
||||
#[proc_macro_derive(FromTypeParam, attributes(darling))]
|
||||
pub fn derive_type_param(input: TokenStream) -> TokenStream {
|
||||
let ast = parse_macro_input!(input as syn::DeriveInput);
|
||||
|
||||
let fdic = options::FromTypeParamOptions::new(&ast).unwrap();
|
||||
let trait_impl = codegen::FromTypeParamImpl::from(&fdic);
|
||||
let result = quote!(#trait_impl);
|
||||
|
||||
result.into()
|
||||
}
|
||||
|
||||
#[proc_macro_derive(FromVariant, attributes(darling))]
|
||||
pub fn derive_variant(input: TokenStream) -> TokenStream {
|
||||
let ast: syn::DeriveInput = syn::parse(input).unwrap();
|
||||
let ast = parse_macro_input!(input as syn::DeriveInput);
|
||||
|
||||
let fdic = options::FromVariantOptions::new(&ast).unwrap();
|
||||
let trait_impl = codegen::FromVariantImpl::from(&fdic);
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"19864ecb948c0e7be14ead11068a2c689a4d31a684c85b6ad1bdf4a26d893516","README.md":"772c547b8e78764f07cc22f2294cb7c691cb20f30d459ed45a65c2434b1ca8a9","lib.rs":"2e2be31e9c90c9b2b0fe223f64f4b4bb24487e370e1cd2fbcce70d30f50fc452"},"package":"6cc484842f1e2884faf56f529f960cc12ad8c71ce96cc7abba0a067c98fee344"}
|
||||
{"files":{"Cargo.toml":"8a89e16dc6b373aa151fb2d1221c699b39b1dd5599aa616897fa85511b71104f","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"65fdb6c76cd61612070c066eec9ecdb30ee74fb27859d0d9af58b9f499fd0c3e","README.md":"9398b0785fdaf32fe61dca3d6f16e69cf53ab2911c9435053d1ec962cd92b8fa","lib.rs":"0303c8c75e9cf35f5379f67cfc003ba0b51e9643dc8f3bd346322595d7685d97"},"package":"2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"}
|
|
@ -1,12 +1,24 @@
|
|||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g. crates.io) dependencies
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
|
||||
[package]
|
||||
name = "fnv"
|
||||
version = "1.0.5"
|
||||
version = "1.0.6"
|
||||
authors = ["Alex Crichton <alex@alexcrichton.com>"]
|
||||
description = "Fowler–Noll–Vo hash function"
|
||||
license = "Apache-2.0 / MIT"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/servo/rust-fnv"
|
||||
documentation = "https://doc.servo.org/fnv/"
|
||||
readme = "README.md"
|
||||
license = "Apache-2.0 / MIT"
|
||||
repository = "https://github.com/servo/rust-fnv"
|
||||
|
||||
[lib]
|
||||
name = "fnv"
|
||||
|
|
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -0,0 +1,25 @@
|
|||
Copyright (c) 2017 Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
|
@ -77,5 +77,5 @@ set.insert(2);
|
|||
```
|
||||
|
||||
[chongo]: http://www.isthe.com/chongo/tech/comp/fnv/index.html
|
||||
[faq]: https://www.rust-lang.org/faq.html#why-are-rusts-hashmaps-slow
|
||||
[faq]: https://www.rust-lang.org/en-US/faq.html#why-are-rusts-hashmaps-slow
|
||||
[graphs]: http://cglab.ca/~abeinges/blah/hash-rs/
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
//! ```
|
||||
//!
|
||||
//! [chongo]: http://www.isthe.com/chongo/tech/comp/fnv/index.html
|
||||
//! [faq]: https://www.rust-lang.org/faq.html#why-are-rusts-hashmaps-slow
|
||||
//! [faq]: https://www.rust-lang.org/en-US/faq.html#why-are-rusts-hashmaps-slow
|
||||
//! [graphs]: http://cglab.ca/~abeinges/blah/hash-rs/
|
||||
|
||||
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
{"files":{"Cargo.toml":"936c905633b5e43ea48d8a7f34cfe32741e2cabfc5b71541e1e1fa214c9afbe2","LICENSE":"219920e865eee70b7dcfc948a86b099e7f4fe2de01bcca2ca9a20c0a033f2b59","README.md":"16135f8089978a256141f3ffedfeb60df584bbd3f4dd928437cf839fc6920ff9","src/lib.rs":"7bcbf539b948ebb5a77cd636f496598662ad440416c265be1b6bda43ed3f19fb","src/macros.rs":"d9a58b66620003e7500cf53699410639f104c36146fe612d48128f293210a524"},"package":"98cad891cd238c98e1f0aec9f7c0f620aa696e4e5f7daba56ac67b5e86a6b049"}
|
|
@ -1,40 +0,0 @@
|
|||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g. crates.io) dependencies
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
|
||||
[package]
|
||||
name = "synstructure"
|
||||
version = "0.8.1"
|
||||
authors = ["Nika Layzell <nika@thelayzells.com>"]
|
||||
include = ["src/**/*", "Cargo.toml", "README.md", "LICENSE"]
|
||||
description = "Helper methods and macros for custom derives"
|
||||
documentation = "https://docs.rs/synstructure"
|
||||
readme = "README.md"
|
||||
keywords = ["syn", "macros", "derive", "expand_substructure", "enum"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/mystor/synstructure"
|
||||
[dependencies.proc-macro2]
|
||||
version = "0.3"
|
||||
|
||||
[dependencies.quote]
|
||||
version = "0.5"
|
||||
|
||||
[dependencies.syn]
|
||||
version = "0.13"
|
||||
features = ["visit", "extra-traits"]
|
||||
|
||||
[dependencies.unicode-xid]
|
||||
version = "0.1"
|
||||
[dev-dependencies.synstructure_test_traits]
|
||||
version = "0.1"
|
||||
|
||||
[features]
|
||||
simple-derive = []
|
|
@ -1,7 +0,0 @@
|
|||
Copyright 2016 Nika Layzell
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -1,157 +0,0 @@
|
|||
# synstructure
|
||||
|
||||
[![Latest Version](https://img.shields.io/crates/v/synstructure.svg)](https://crates.io/crates/synstructure)
|
||||
[![Documentation](https://docs.rs/synstructure/badge.svg)](https://docs.rs/synstructure)
|
||||
[![Build Status](https://travis-ci.org/mystor/synstructure.svg?branch=master)](https://travis-ci.org/mystor/synstructure)
|
||||
[![Rustc Version 1.15+](https://img.shields.io/badge/rustc-1.15+-lightgray.svg)](https://blog.rust-lang.org/2017/02/02/Rust-1.15.html)
|
||||
|
||||
> NOTE: What follows is an exerpt from the module level documentation. For full
|
||||
> details read the docs on [docs.rs](https://docs.rs/synstructure/)
|
||||
|
||||
This crate provides helper types for matching against enum variants, and
|
||||
extracting bindings to each of the fields in the deriving Struct or Enum in
|
||||
a generic way.
|
||||
|
||||
If you are writing a `#[derive]` which needs to perform some operation on
|
||||
every field, then you have come to the right place!
|
||||
|
||||
# Example: `WalkFields`
|
||||
### Trait Implementation
|
||||
```rust
|
||||
pub trait WalkFields: std::any::Any {
|
||||
fn walk_fields(&self, walk: &mut FnMut(&WalkFields));
|
||||
}
|
||||
impl WalkFields for i32 {
|
||||
fn walk_fields(&self, _walk: &mut FnMut(&WalkFields)) {}
|
||||
}
|
||||
```
|
||||
|
||||
### Custom Derive
|
||||
```rust
|
||||
#[macro_use]
|
||||
extern crate synstructure;
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
|
||||
fn walkfields_derive(s: synstructure::Structure) -> quote::Tokens {
|
||||
let body = s.each(|bi| quote!{
|
||||
walk(#bi)
|
||||
});
|
||||
|
||||
s.bound_impl(quote!(example_traits::WalkFields), quote!{
|
||||
fn walk_fields(&self, walk: &mut FnMut(&example_traits::WalkFields)) {
|
||||
match *self { #body }
|
||||
}
|
||||
})
|
||||
}
|
||||
decl_derive!([WalkFields] => walkfields_derive);
|
||||
|
||||
/*
|
||||
* Test Case
|
||||
*/
|
||||
fn main() {
|
||||
test_derive! {
|
||||
walkfields_derive {
|
||||
enum A<T> {
|
||||
B(i32, T),
|
||||
C(i32),
|
||||
}
|
||||
}
|
||||
expands to {
|
||||
#[allow(non_upper_case_globals)]
|
||||
const _DERIVE_example_traits_WalkFields_FOR_A: () = {
|
||||
extern crate example_traits;
|
||||
impl<T> example_traits::WalkFields for A<T>
|
||||
where T: example_traits::WalkFields
|
||||
{
|
||||
fn walk_fields(&self, walk: &mut FnMut(&example_traits::WalkFields)) {
|
||||
match *self {
|
||||
A::B(ref __binding_0, ref __binding_1,) => {
|
||||
{ walk(__binding_0) }
|
||||
{ walk(__binding_1) }
|
||||
}
|
||||
A::C(ref __binding_0,) => {
|
||||
{ walk(__binding_0) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# Example: `Interest`
|
||||
### Trait Implementation
|
||||
```rust
|
||||
pub trait Interest {
|
||||
fn interesting(&self) -> bool;
|
||||
}
|
||||
impl Interest for i32 {
|
||||
fn interesting(&self) -> bool { *self > 0 }
|
||||
}
|
||||
```
|
||||
|
||||
### Custom Derive
|
||||
```rust
|
||||
#[macro_use]
|
||||
extern crate synstructure;
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
|
||||
fn interest_derive(mut s: synstructure::Structure) -> quote::Tokens {
|
||||
let body = s.fold(false, |acc, bi| quote!{
|
||||
#acc || example_traits::Interest::interesting(#bi)
|
||||
});
|
||||
|
||||
s.bound_impl(quote!(example_traits::Interest), quote!{
|
||||
fn interesting(&self) -> bool {
|
||||
match *self {
|
||||
#body
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
decl_derive!([Interest] => interest_derive);
|
||||
|
||||
/*
|
||||
* Test Case
|
||||
*/
|
||||
fn main() {
|
||||
test_derive!{
|
||||
interest_derive {
|
||||
enum A<T> {
|
||||
B(i32, T),
|
||||
C(i32),
|
||||
}
|
||||
}
|
||||
expands to {
|
||||
#[allow(non_upper_case_globals)]
|
||||
const _DERIVE_example_traits_Interest_FOR_A: () = {
|
||||
extern crate example_traits;
|
||||
impl<T> example_traits::Interest for A<T>
|
||||
where T: example_traits::Interest
|
||||
{
|
||||
fn interesting(&self) -> bool {
|
||||
match *self {
|
||||
A::B(ref __binding_0, ref __binding_1,) => {
|
||||
false ||
|
||||
example_traits::Interest::interesting(__binding_0) ||
|
||||
example_traits::Interest::interesting(__binding_1)
|
||||
}
|
||||
A::C(ref __binding_0,) => {
|
||||
false ||
|
||||
example_traits::Interest::interesting(__binding_0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For more example usage, consider investigating the `abomonation_derive` crate,
|
||||
which makes use of this crate, and is fairly simple.
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,411 +0,0 @@
|
|||
//! This module provides two utility macros for testing custom derives. They can
|
||||
//! be used together to eliminate some of the boilerplate required in order to
|
||||
//! declare and test custom derive implementations.
|
||||
|
||||
// Re-exports used by the decl_derive! and test_derive!
|
||||
pub use syn::{parse_str, parse, DeriveInput};
|
||||
pub use quote::Tokens;
|
||||
pub use proc_macro::TokenStream as TokenStream;
|
||||
pub use proc_macro2::TokenStream as TokenStream2;
|
||||
|
||||
/// The `decl_derive!` macro declares a custom derive wrapper. It will parse the
|
||||
/// incoming `TokenStream` into a `synstructure::Structure` object, and pass it
|
||||
/// into the inner function.
|
||||
///
|
||||
/// Your inner function should have the following type:
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate quote;
|
||||
/// # extern crate synstructure;
|
||||
/// fn derive(input: synstructure::Structure) -> quote::Tokens {
|
||||
/// unimplemented!()
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Usage
|
||||
///
|
||||
/// ### Without Attributes
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate quote;
|
||||
/// # extern crate synstructure;
|
||||
/// # fn main() {}
|
||||
/// fn derive_interesting(_input: synstructure::Structure) -> quote::Tokens {
|
||||
/// quote! { ... }
|
||||
/// }
|
||||
///
|
||||
/// # const _IGNORE: &'static str = stringify! {
|
||||
/// decl_derive!([Interesting] => derive_interesting);
|
||||
/// # };
|
||||
/// ```
|
||||
///
|
||||
/// ### With Attributes
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate quote;
|
||||
/// # extern crate synstructure;
|
||||
/// # fn main() {}
|
||||
/// fn derive_interesting(_input: synstructure::Structure) -> quote::Tokens {
|
||||
/// quote! { ... }
|
||||
/// }
|
||||
///
|
||||
/// # const _IGNORE: &'static str = stringify! {
|
||||
/// decl_derive!([Interesting, attributes(interesting_ignore)] => derive_interesting);
|
||||
/// # };
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! decl_derive {
|
||||
// XXX: Switch to using this variant everywhere?
|
||||
([$derives:ident $($derive_t:tt)*] => $inner:path) => {
|
||||
#[proc_macro_derive($derives $($derive_t)*)]
|
||||
#[allow(non_snake_case)]
|
||||
pub fn $derives(
|
||||
i: $crate::macros::TokenStream
|
||||
) -> $crate::macros::TokenStream
|
||||
{
|
||||
let parsed = $crate::macros::parse::<$crate::macros::DeriveInput>(i)
|
||||
.expect(concat!("Failed to parse input to `#[derive(",
|
||||
stringify!($derives),
|
||||
")]`"));
|
||||
$inner($crate::Structure::new(&parsed)).into()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Run a test on a custom derive. This macro expands both the original struct
|
||||
/// and the expansion to ensure that they compile correctly, and confirms that
|
||||
/// feeding the original struct into the named derive will produce the written
|
||||
/// output.
|
||||
///
|
||||
/// You can add `no_build` to the end of the macro invocation to disable
|
||||
/// checking that the written code compiles. This is useful in contexts where
|
||||
/// the procedural macro cannot depend on the crate where it is used during
|
||||
/// tests.
|
||||
///
|
||||
/// # Usage
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate quote;
|
||||
/// # #[macro_use] extern crate synstructure;
|
||||
/// fn test_derive_example(_s: synstructure::Structure) -> quote::Tokens {
|
||||
/// quote! { const YOUR_OUTPUT: &'static str = "here"; }
|
||||
/// }
|
||||
///
|
||||
/// fn main() {
|
||||
/// test_derive!{
|
||||
/// test_derive_example {
|
||||
/// struct A;
|
||||
/// }
|
||||
/// expands to {
|
||||
/// const YOUR_OUTPUT: &'static str = "here";
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! test_derive {
|
||||
($name:path { $($i:tt)* } expands to { $($o:tt)* }) => {
|
||||
{
|
||||
#[allow(dead_code)]
|
||||
fn ensure_compiles() {
|
||||
$($i)*
|
||||
$($o)*
|
||||
}
|
||||
|
||||
test_derive!($name { $($i)* } expands to { $($o)* } no_build);
|
||||
}
|
||||
};
|
||||
|
||||
($name:path { $($i:tt)* } expands to { $($o:tt)* } no_build) => {
|
||||
{
|
||||
let i = stringify!( $($i)* );
|
||||
let parsed = $crate::macros::parse_str::<$crate::macros::DeriveInput>(i)
|
||||
.expect(concat!("Failed to parse input to `#[derive(",
|
||||
stringify!($name),
|
||||
")]`"));
|
||||
|
||||
let res = $name($crate::Structure::new(&parsed));
|
||||
let expected = stringify!( $($o)* )
|
||||
.parse::<$crate::macros::TokenStream2>()
|
||||
.expect("output should be a valid TokenStream");
|
||||
let mut expected_toks = $crate::macros::Tokens::new();
|
||||
expected_toks.append_all(expected);
|
||||
|
||||
if res != expected_toks {
|
||||
panic!("\
|
||||
test_derive failed:
|
||||
expected:
|
||||
```
|
||||
{}
|
||||
```
|
||||
|
||||
got:
|
||||
```
|
||||
{}
|
||||
```\n",
|
||||
$crate::unpretty_print(&expected_toks),
|
||||
$crate::unpretty_print(&res),
|
||||
);
|
||||
}
|
||||
// assert_eq!(res, expected_toks)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// A helper macro for declaring relatively straightforward derive
|
||||
/// implementations. It provides mechanisms for operating over structures
|
||||
/// performing modifications on each field etc.
|
||||
///
|
||||
/// This macro doesn't define the actual derive, but rather the implementation
|
||||
/// method. Use `decl_derive!` to generate the implementation wrapper.
|
||||
///
|
||||
/// # Stability Warning
|
||||
///
|
||||
/// This is an unstable experimental macro API, which may be changed or removed
|
||||
/// in a future version. I'm not yet confident enough that this API is useful
|
||||
/// enough to warrant its complexity and inclusion in `synstructure`.
|
||||
///
|
||||
/// # Caveat
|
||||
///
|
||||
/// The `quote!` macro from `quote` must be imported in the calling crate, as
|
||||
/// this macro depends on it.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This feature is implemented behind the `simple-derive` feature, and is only
|
||||
/// avaliable when that feature is enabled.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// #[macro_use]
|
||||
/// extern crate quote;
|
||||
/// #[macro_use]
|
||||
/// extern crate synstructure;
|
||||
/// # const _IGNORE: &'static str = stringify! {
|
||||
/// decl_derive!([Interest] => derive_interest);
|
||||
/// # };
|
||||
///
|
||||
/// simple_derive! {
|
||||
/// // This macro implements the `Interesting` method exported by the `aa`
|
||||
/// // crate. It will explicitly add an `extern crate` invocation to import the
|
||||
/// // crate into the expanded context.
|
||||
/// derive_interest impl synstructure_test_traits::Interest {
|
||||
/// // A "filter" block can be added. It evaluates its body with the (s)
|
||||
/// // variable bound to a mutable reference to the input `Structure`
|
||||
/// // object.
|
||||
/// //
|
||||
/// // This block can be used to perform general transformations, such as
|
||||
/// // filtering out fields which should be ignored by all methods and for
|
||||
/// // the purposes of binding type parameters.
|
||||
/// filter(s) {
|
||||
/// s.filter(|bi| bi.ast().ident != Some("a".into()));
|
||||
/// }
|
||||
///
|
||||
/// // This is an implementation of a method in the implemented crate. The
|
||||
/// // return value should be the series of match patterns to destructure
|
||||
/// // the `self` argument with.
|
||||
/// fn interesting(&self as s) -> bool {
|
||||
/// s.fold(false, |acc, bi| {
|
||||
/// quote!(#acc || synstructure_test_traits::Interest::interesting(#bi))
|
||||
/// })
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// fn main() {
|
||||
/// test_derive!{
|
||||
/// derive_interest {
|
||||
/// struct A<T> {
|
||||
/// x: i32,
|
||||
/// a: bool, // Will be ignored by filter
|
||||
/// c: T,
|
||||
/// }
|
||||
/// }
|
||||
/// expands to {
|
||||
/// #[allow(non_upper_case_globals)]
|
||||
/// const _DERIVE_synstructure_test_traits_Interest_FOR_A: () = {
|
||||
/// extern crate synstructure_test_traits;
|
||||
/// impl<T> synstructure_test_traits::Interest for A<T>
|
||||
/// where T: synstructure_test_traits::Interest
|
||||
/// {
|
||||
/// fn interesting(&self) -> bool {
|
||||
/// match *self {
|
||||
/// A {
|
||||
/// x: ref __binding_0,
|
||||
/// c: ref __binding_2,
|
||||
/// ..
|
||||
/// } => {
|
||||
/// false ||
|
||||
/// synstructure_test_traits::Interest::interesting(__binding_0) ||
|
||||
/// synstructure_test_traits::Interest::interesting(__binding_2)
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// };
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg(feature = "simple-derive")]
|
||||
#[macro_export]
|
||||
macro_rules! simple_derive {
|
||||
// entry point
|
||||
(
|
||||
$iname:ident impl $path:path { $($rest:tt)* }
|
||||
) => {
|
||||
simple_derive!(__I [$iname, $path] { $($rest)* } [] []);
|
||||
};
|
||||
|
||||
// Adding a filter block
|
||||
(
|
||||
__I $opt:tt {
|
||||
filter($s:ident) {
|
||||
$($body:tt)*
|
||||
}
|
||||
$($rest:tt)*
|
||||
} [$($done:tt)*] [$($filter:tt)*]
|
||||
) => {
|
||||
simple_derive!(
|
||||
__I $opt { $($rest)* } [$($done)*] [
|
||||
$($filter)*
|
||||
[
|
||||
st_name = $s,
|
||||
body = {
|
||||
$($body)*
|
||||
},
|
||||
]
|
||||
]
|
||||
);
|
||||
};
|
||||
|
||||
// &self bound method
|
||||
(
|
||||
__I $opt:tt {
|
||||
fn $fn_name:ident (&self as $s:ident $($params:tt)*) $(-> $t:ty)* {
|
||||
$($body:tt)*
|
||||
}
|
||||
$($rest:tt)*
|
||||
} [$($done:tt)*] [$($filter:tt)*]
|
||||
) => {
|
||||
simple_derive!(
|
||||
__I $opt { $($rest)* } [
|
||||
$($done)*
|
||||
[
|
||||
st_name = $s,
|
||||
bind_style = Ref,
|
||||
body = { $($body)* },
|
||||
result = result,
|
||||
expanded = {
|
||||
fn $fn_name(&self $($params)*) $(-> $t)* {
|
||||
match *self { #result }
|
||||
}
|
||||
},
|
||||
]
|
||||
] [$($filter)*]
|
||||
);
|
||||
};
|
||||
|
||||
// &mut self bound method
|
||||
(
|
||||
__I $opt:tt {
|
||||
fn $fn_name:ident (&mut self as $s:ident $($params:tt)*) $(-> $t:ty)* {
|
||||
$($body:tt)*
|
||||
}
|
||||
$($rest:tt)*
|
||||
} [$($done:tt)*] [$($filter:tt)*]
|
||||
) => {
|
||||
simple_derive!(
|
||||
__I $opt { $($rest)* } [
|
||||
$($done)*
|
||||
[
|
||||
st_name = $s,
|
||||
bind_style = RefMut,
|
||||
body = { $($body)* },
|
||||
result = result,
|
||||
expanded = {
|
||||
fn $fn_name(&mut self $($params)*) $(-> $t)* {
|
||||
match *self { #result }
|
||||
}
|
||||
},
|
||||
]
|
||||
] [$($filter)*]
|
||||
);
|
||||
};
|
||||
|
||||
// self bound method
|
||||
(
|
||||
__I $opt:tt {
|
||||
fn $fn_name:ident (self as $s:ident $($params:tt)*) $(-> $t:ty)* {
|
||||
$($body:tt)*
|
||||
}
|
||||
$($rest:tt)*
|
||||
} [$($done:tt)*] [$($filter:tt)*]
|
||||
) => {
|
||||
simple_derive!(
|
||||
__I $opt { $($rest)* } [
|
||||
$($done)*
|
||||
[
|
||||
st_name = $s,
|
||||
bind_style = Move,
|
||||
body = { $($body)* },
|
||||
result = result,
|
||||
expanded = {
|
||||
fn $fn_name(self $($params)*) $(-> $t)* {
|
||||
match self { #result }
|
||||
}
|
||||
},
|
||||
]
|
||||
] [$($filter)*]
|
||||
);
|
||||
};
|
||||
|
||||
// XXX: Static methods?
|
||||
|
||||
// codegen after data collection
|
||||
(
|
||||
__I [$iname:ident, $path:path] {} [$(
|
||||
[
|
||||
st_name = $st_name:ident,
|
||||
bind_style = $bind_style:ident,
|
||||
body = $body:tt,
|
||||
result = $result:ident,
|
||||
expanded = { $($expanded:tt)* },
|
||||
]
|
||||
)*] [$(
|
||||
[
|
||||
st_name = $filter_st_name:ident,
|
||||
body = $filter_body:tt,
|
||||
]
|
||||
)*]
|
||||
) => {
|
||||
fn $iname(mut st: $crate::Structure) -> $crate::macros::Tokens {
|
||||
let _ = &mut st; // Silence the unused mut warning
|
||||
|
||||
// Filter/transform the `Structure` object before cloning it for
|
||||
// individual methods.
|
||||
$(
|
||||
{
|
||||
let $filter_st_name = &mut st;
|
||||
$filter_body
|
||||
}
|
||||
)*
|
||||
|
||||
// Clone the `Structure` object and set the correct binding style,
|
||||
// then perform method specific expansion.
|
||||
$(
|
||||
let $result = {
|
||||
let mut $st_name = st.clone();
|
||||
$st_name.bind_with(|_| ::synstructure::BindStyle::$bind_style);
|
||||
let $result = {
|
||||
$body
|
||||
};
|
||||
quote!{ $($expanded)* }
|
||||
};
|
||||
)*
|
||||
|
||||
st.bound_impl(quote!($path), quote!{
|
||||
$(#$result)*
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче