attribute-derive-0.10.5/.cargo_vcs_info.json0000644000000001360000000000100143760ustar { "git": { "sha1": "b676fb397927bb1bad94b095feac5515c9cf52a3" }, "path_in_vcs": "" }attribute-derive-0.10.5/Cargo.lock0000644000000174730000000000100123650ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "attribute-derive" version = "0.10.5" dependencies = [ "attribute-derive-macro", "derive-where", "insta", "manyhow", "proc-macro2", "quote", "static_assertions", "syn", ] [[package]] name = "attribute-derive-macro" version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a7cdbbd4bd005c5d3e2e9c885e6fa575db4f4a3572335b974d8db853b6beb61" dependencies = [ "collection_literals", "interpolator", "manyhow", "proc-macro-utils 0.10.0", "proc-macro2", "quote", "quote-use", "syn", ] [[package]] name = "collection_literals" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "186dce98367766de751c42c4f03970fc60fc012296e706ccbb9d5df9b6c1e271" [[package]] name = "console" version = "0.15.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" dependencies = [ "encode_unicode", "lazy_static", "libc", "windows-sys", ] [[package]] name = "derive-where" version = "1.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62d671cc41a825ebabc75757b62d3d168c577f9149b2d49ece1dad1f72119d25" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "encode_unicode" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "insta" version = "1.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "810ae6042d48e2c9e9215043563a58a80b877bc863228a74cf10c49d4620a6f5" dependencies = [ "console", "lazy_static", "linked-hash-map", "similar", ] [[package]] name = "interpolator" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71dd52191aae121e8611f1e8dc3e324dd0dd1dee1e6dd91d10ee07a3cfb4d9d8" [[package]] name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "linked-hash-map" version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "manyhow" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5b8ea82a2287fe7b26aea89c0c02957886d7e97eabffc1f9d2031feaa6f82e6" dependencies = [ "manyhow-macros", "proc-macro2", "quote", "syn", ] [[package]] name = "manyhow-macros" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae36e8d9b4095531e43de72ed424f0f4a98cba40f7e5a99366f9818769489272" dependencies = [ "proc-macro-utils 0.8.0", "proc-macro2", "quote", ] [[package]] name = "proc-macro-utils" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f59e109e2f795a5070e69578c4dc101068139f74616778025ae1011d4cd41a8" dependencies = [ "proc-macro2", "quote", "smallvec", ] [[package]] name = "proc-macro-utils" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eeaf08a13de400bc215877b5bdc088f241b12eb42f0a548d3390dc1c56bb7071" dependencies = [ "proc-macro2", "quote", "smallvec", ] [[package]] name = "proc-macro2" version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] [[package]] name = "quote-use" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48e96ac59974192a2fa6ee55a41211cf1385c5b2a8636a4c3068b3b3dd599ece" dependencies = [ "quote", "quote-use-macros", ] [[package]] name = "quote-use-macros" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c57308e9dde4d7be9af804f6deeaa9951e1de1d5ffce6142eb964750109f7e" dependencies = [ "derive-where", "proc-macro-utils 0.8.0", "proc-macro2", "quote", "syn", ] [[package]] name = "similar" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e" [[package]] name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "syn" version = "2.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" attribute-derive-0.10.5/Cargo.toml0000644000000041230000000000100123740ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "attribute-derive" version = "0.10.5" build = false include = [ "src/**/*", "LICENSE-*", "README.md", "docs/**/*", ] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Clap like parsing for attributes in proc-macros" documentation = "https://docs.rs/attribute-derive" readme = "README.md" keywords = [ "derive", "macro", "attribute", "arguments", "syn", ] categories = [ "rust-patterns", "development-tools::procedural-macro-helpers", "parsing", ] license = "MIT OR Apache-2.0" repository = "https://github.com/ModProg/attribute-derive" [package.metadata.release] shared-version = true [[package.metadata.release.pre-release-replacements]] file = "CHANGELOG.md" search = '## \[Unreleased\]' replace = """ ## [Unreleased] ## [{{version}}] - {{date}}""" [[package.metadata.release.pre-release-replacements]] file = "CHANGELOG.md" search = '\[unreleased\]: (.*)/(v.*)\.\.\.HEAD' replace = """ [unreleased]: $1/{{tag_name}}...HEAD [{{version}}]: $1/$2...{{tag_name}}""" [features] syn-full = ["syn/full"] [lib] name = "attribute_derive" path = "src/lib.rs" [dependencies.attribute-derive-macro] version = "0.10.5" [dependencies.derive-where] version = "1.2.7" [dependencies.manyhow] version = "0.11.3" [dependencies.proc-macro2] version = "1" [dependencies.quote] version = "1" [dependencies.syn] version = "2" [dev-dependencies.insta] version = "1.39.0" [dev-dependencies.quote] version = "1" [dev-dependencies.static_assertions] version = "1.1.0" [dev-dependencies.syn] version = "2" features = ["extra-traits"] attribute-derive-0.10.5/Cargo.toml.orig000064400000000000000000000025101046102023000160530ustar 00000000000000[workspace] members = ["example"] [package] categories = ["rust-patterns", "development-tools::procedural-macro-helpers", "parsing"] description = "Clap like parsing for attributes in proc-macros" documentation = "https://docs.rs/attribute-derive" include = ["src/**/*", "LICENSE-*", "README.md", "docs/**/*"] keywords = ["derive", "macro", "attribute", "arguments", "syn"] license = "MIT OR Apache-2.0" readme = "README.md" repository = "https://github.com/ModProg/attribute-derive" name = "attribute-derive" version = "0.10.5" edition = "2021" [lib] [dependencies] derive-where = "1.2.7" manyhow = "0.11.3" proc-macro2 = "1" quote = "1" syn = "2" [features] # default = ["syn-full"] syn-full = ["syn/full"] [dependencies.attribute-derive-macro] version = "0.10.5" path = "macro" [dev-dependencies] insta = "1.39.0" quote = "1" static_assertions = "1.1.0" syn = { version = "2", features = ["extra-traits"] } [package.metadata.release] shared-version = true [[package.metadata.release.pre-release-replacements]] file = "CHANGELOG.md" search = '## \[Unreleased\]' replace = """ ## [Unreleased] ## [{{version}}] - {{date}}\ """ [[package.metadata.release.pre-release-replacements]] file = "CHANGELOG.md" search = '\[unreleased\]: (.*)/(v.*)\.\.\.HEAD' replace = """ [unreleased]: $1/{{tag_name}}...HEAD [{{version}}]: $1/$2...{{tag_name}}\ """ attribute-derive-0.10.5/LICENSE-APACHE000064400000000000000000000261351046102023000151210ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a peretual, worldwide, non-exclusive, no-cpharge, 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 it s 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. attribute-derive-0.10.5/LICENSE-MIT000064400000000000000000000020631046102023000146230ustar 00000000000000MIT License Copyright (c) 2024 Roland Fredenhagen 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. attribute-derive-0.10.5/README.md000064400000000000000000000064371046102023000144570ustar 00000000000000# attribute-derive [![docs.rs](https://img.shields.io/docsrs/attribute-derive)](https://docs.rs/attribute-derive/latest/attribute_derive/) [![lib.rs](https://img.shields.io/crates/v/attribute-derive)](https://lib.rs/crates/attribute-derive) [![MIT](https://img.shields.io/crates/l/attribute-derive)](LICENSE) [![Documentation for `main`](https://img.shields.io/badge/docs-main-informational)](https://modprog.github.io/attribute-derive/attribute_derive/) Basically clap for attribute macros: ```rust use attribute_derive::Attribute; use syn::Type; #[derive(Attribute)] #[attribute(ident = collection)] #[attribute(error(missing_field = "`{field}` was not specified"))] struct CollectionAttribute { // Options are optional by default (will be set to None if not specified) authority: Option, name: String, // Any type implementing default can be flagged as default // This will be set to Vec::default() when not specified #[attribute(optional)] views: Vec, // Booleans can be used without assigning a value, i.e., as a flag. // If omitted they are set to false some_flag: bool, } ``` Will be able to parse an attribute like this: ```rust #[collection(authority="Some String", name = r#"Another string"#, views = [Option, ()])] ``` ## Limitations There are some limitations in syntax parsing that will be lifted future releases. - literals in top level (meaning something like `#[attr(42, 3.14, "hi")]` - function like arguments (something like `#[attr(view(a = "test"))]` - other syntaxes, maybe something like `key: value` ## Parse methods There are multiple ways of parsing a struct deriving [`Attribute`](https://docs.rs/attribute-derive/latest/attribute_derive/trait.Attribute.html). For helper attributes there is: - [`Attribute::from_attributes`](https://docs.rs/attribute-derive/latest/attribute_derive/trait.Attribute.html#tymethod.from_attributes) which takes in an [`IntoIterator`](https://docs.rs/syn/latest/syn/struct.Attribute.html)). Most useful for derive macros. - [`Attribute::remove_attributes`](https://docs.rs/attribute-derive/latest/attribute_derive/trait.Attribute.html#tymethod.remove_attributes) which takes an [`&mut Vec`](https://docs.rs/syn/latest/syn/struct.Attribute.html) and does not only parse the [`Attribute`](https://docs.rs/attribute-derive/latest/attribute_derive/trait.Attribute.html#tymethod.from_attributes) but also removes those matching. Useful for helper attributes for proc macros, where the helper attributes need to be removed. For parsing a single [`TokenStream`](https://docs.rs/proc-macro2/latest/proc_macro2/struct.TokenStream.html) e.g. for parsing the proc macro input there a two ways: - [`Attribute::from_args`](https://docs.rs/attribute-derive/latest/attribute_derive/trait.Attribute.html#tymethod.from_args) taking in a [`TokenStream`](https://docs.rs/proc-macro2/latest/proc_macro2/struct.TokenStream.html) - As `derive(Attribute)` also derives [`Parse`](https://docs.rs/syn/latest/syn/parse/trait.Parse.html) so you can use the [parse](https://docs.rs/syn/latest/syn/parse/index.html) API, e.g. with [`parse_macro_input!(tokens as Attribute)`](https://docs.rs/syn/latest/syn/macro.parse_macro_input.html). attribute-derive-0.10.5/docs/traits.html000064400000000000000000000107571046102023000163240ustar 00000000000000

FromAttr

Main entry point. Derived via macro. Anything that can be parsed from one or multiple attributes.

AttributeNamed

Values that can be parsed named, e.g. name(<value>), name = <value>, name (as flag).

This is the default parsing mode used for fields in derived FromAttr implementations.

AttributePositional

Values that can be parsed positionally, i.e., without a name, e.g. "literal", a + b, true.

When deriving FromAttr this is enabled via putting #[attr(positional)] on the field.

impl <T: AttributeValue> FromAttr for T
impl <T: AttributeValue> AttributeNamed for T
impl <T: AttributeValue + PositionalValue> AttributePositional for T

AttributeValue

Any attribute that has the concept of a value, e.g., "positional", meta(<value>), key = <value>.

PositionalValue

Empty marker trait, defining which AttributeValue implement AttributePositional.
impl <T: AttributeMeta> AttributeValue for T

AttributeMeta

Values in function or meta style attributes, i.e., meta(<value>).

attribute-derive-0.10.5/src/from_partial.rs000064400000000000000000000052721046102023000170100ustar 00000000000000//! Contains utilities for implementing [`FromPartial`]. use crate::*; /// Converts from a [`Partial`](AttributeBase::Partial) value. pub trait FromPartial: Sized { /// Creates `Self` from `T`. /// /// # Errors /// Returns a [`syn::Error`] when `T` does not represent a valid `Self`, /// e.g., due to missing or conflicting fields. fn from(partial: T) -> Result; /// Creates `Self` from optional `T`. /// /// # Errors /// The default implementation errors with `error_missing` when `partial` is /// [`None`], or when `Self::from` errors. /// /// Implementors might override this for types with expected default values. fn from_option(partial: Option, error_missing: &str) -> Result { // Pass in surrounding span partial .map(Self::from) .transpose()? .ok_or_else(|| syn::Error::new(Span::call_site(), error_missing)) } /// Defines how two arguments for the same parameter should be handled. /// /// # Errors /// The default implementation errors if `first` is already present and /// `specified_twice_error` is returned with the correct spans. fn join( first: Option>, second: SpannedValue, specified_twice_error: &str, ) -> Result>> { if let Some(first) = first { if let Some(span) = first .span() .span_joined() .and_then(|s| Some((s, second.span().span_joined()?))) .and_then(|(a, b)| a.join(b)) { Err(Error::new(span, specified_twice_error)) } else { let mut error = Error::new(first.span().start, specified_twice_error); error.combine(Error::new(second.span().start, specified_twice_error)); Err(error) } } else { Ok(Some(second)) } } } impl FromPartial for T { fn from(partial: T) -> Result { Ok(partial) } } /// [`FromPartial`] wrapper that uses [`Default`] value when not specified. #[derive(Clone)] pub struct Defaulting(pub T); impl> FromPartial> for T { fn from(partial: Defaulting

) -> Result { Self::from(partial.0) } fn from_option(partial: Option>, _error: &str) -> Result { partial .map(|d| Self::from(d.0)) .transpose() .map(Option::unwrap_or_default) } } /// Utility struct to avoid duplicate trait definition when using `Self` for /// ` as BaseAttribute>::Partial`. pub struct Partial(pub T); attribute-derive-0.10.5/src/lib.rs000064400000000000000000000411061046102023000150730ustar 00000000000000#![warn(missing_docs)] //! Basically clap for attribute macros: //! ``` //! use attribute_derive::FromAttr; //! #[derive(FromAttr)] //! #[from_attr(ident = attr_name)] //! // overriding the builtin error messages //! #[from_attr(error(missing_field = "`{field}` was not specified"))] //! struct MyAttribute { //! // Positional values need to be specified before any named ones //! #[from_attr(positional)] //! positional: u8, //! // Options are optional by default (will be set to None if not specified) //! optional: Option, //! required: String, //! // Any type implementing default can be flagged as default //! // This will be set to Vec::default() when not specified //! #[from_attr(optional)] //! list: Vec, //! // Booleans can be used without assigning a value, i.e., as a flag. //! // If omitted they are set to false //! some_flag: bool, //! } //! ``` //! //! Will be able to parse an attribute like this: //! ```rust //! # #[cfg(no)] //! #[attr_name(5, optional="some", required = r#"string"#, some_flag, list = [Option, ()])] //! // or //! #[attr_name(5, required = "string", list(Option, ()))] //! # struct Placeholder; //! ``` //! //! Any type that for [`AttributeNamed`] or [`AttributePositional`] are //! implemented respectively are supported. These should be the general types //! that [`syn`] supports like [`LitStr`](struct@LitStr) or [`Type`] or that //! have a direct equivalent in those like [`String`], [`char`] or [`f32`]. A //! special treatment have [`Vecs`](Vec) which are parsed as either `name = [a, //! b, c]` or `name(a, b, c)` and [`Options`](Option) that will be [`None`] if //! not specified and [`Some`] when the value is specified via the attribute. It //! is not specified via `Some(value)` but as just `value`. [`Bools`](bool) are //! used for flags, i.e., without a value. Most should just behave as expected, //! see [`parsing`] for details. //! //! Tuple structs can derive [`FromAttr`] as well, but all fields will be //! positional. Tuples with a single field //! ([new types](https://rust-unofficial.github.io/patterns/patterns/behavioural/newtype.html)) //! will copy the behavior of the contained field, e.g. for [`bool`]: //! //! ``` //! use syn::{Attribute, parse_quote}; //! use attribute_derive::FromAttr; //! //! #[derive(FromAttr, PartialEq, Debug)] //! #[attribute(ident = flag)] //! struct Flag(bool); //! //! let attr: Attribute = parse_quote!(#[flag]); //! assert_eq!(Flag::from_attribute(attr).unwrap(), Flag(true)); //! //! let attr: Attribute = parse_quote!(#[flag = true]); //! assert_eq!(Flag::from_attribute(attr).unwrap(), Flag(true)); //! //! let attr: Attribute = parse_quote!(#[flag(false)]); //! assert_eq!(Flag::from_attribute(attr).unwrap(), Flag(false)); //! ``` //! //! # Attributes //! //! The parsing of attributes can be modified with the following parameters via //! the `#[attribute()]` attribute. All of them are optional. Error //! messages are formatted using [interpolator], and only support display and //! lists `i` formatting. See [interpolator] docs for details. //! //! ### Struct //! //! - `ident = ` The attribute ident. Improves error messages and enables //! the [`from_attributes`](FromAttr::from_attributes) and //! [`remove_attributes`](FromAttr::remove_attributes) functions. //! - `aliases = [, ...]` Aliases for the attribute ident. //! - `error = ""` Overrides default error message. //! - `error(` //! - ``unknown_field = "supported fields are {expected_fields:i..-1(`{}`)(, //! )} and `{expected_fields:i-1}`",`` Custom error message printed if an //! unknown property is specified and attribute has more than one field. //! Placeholders: `{expected_fields:i}`. //! - ``unknown_field_single = "expected supported field //! `{expected_field}`",`` Custom error message printed if an unknown //! property is specified, and attribute only has a single field. //! Placeholders: `{expected_field}`. //! - ``unknown_field_empty = "expected empty attribute",`` Custom error //! message printed if a property is specified, and attribute has no //! fields. //! - ``duplicate_field = "`{field}` is specified multiple times",`` Custom //! error message printed if a property is specified multiple times. //! Placeholders: `{field}`. //! - ``missing_field = "required `{field}` is not specified",`` Custom //! error message printed if a required property is not specified. //! Placeholders: `{field}`. //! - ``field_help = "try `#[{attribute}({field}={example})]`",`` Additional //! help message printed if a required property is not specified or has an //! error. Placeholders: `{attribute}`, `{field}` and `{example}`. //! - ``conflict = "`{first}` conflicts with mutually exclusive //! `{second}`"`` Custom error message printed when conflicting properties //! are specified. Placeholders: `{first}` and `{second}`. //! //! `)` // //! - `duplicate = AggregateOrError` Change the behavior for duplicate arguments // //! (also across multiple attributes). // //! - `AggregateOrError` Aggregate multiple [`Vec`], error on everything else. // //! - `Error` Disables aggregation, errors on all duplicates. // //! - `AggregateOrOverride` Aggregate multiple [`Vec`], take the last // //! specified for everything else. // //! - `Override` Disables aggregation, always take the last value. //! ### Fields //! //! - `optional` If field is not specified, the default value is used instead. //! - `default = ` provides a default to be used instead of //! [`Default::default()`]. Enables `optional`. //! - `conflicts(, ...)` Conflicting fields //! - `example = ""` //! //! # Parse methods //! //! There are multiple ways of parsing a struct deriving [`FromAttr`]. //! //! For helper attributes there is: //! - [`FromAttr::from_attributes`] which takes in an [`IntoIterator`](syn::Attribute)). Most useful for derive macros. //! - [`FromAttr::remove_attributes`] which takes a [`&mut //! Vec`](syn::Attribute) and does not only parse the //! attributes, but also removes those matching. Useful for helper attributes //! for attribute macros, where the helper attributes need to be removed. //! //! For parsing a single [`TokenStream`] e.g. for parsing the proc macro input //! there are two ways: //! //! - [`FromAttr::from_args`] taking in a [`TokenStream`] //! - As `derive(FromAttr)` also derives [`Parse`] so you can use the //! [parse](mod@syn::parse) API, e.g. with [`parse_macro_input!(tokens as //! Attribute)`](syn::parse_macro_input!). //! //! [interpolator]: https://docs.rs/interpolator/latest/interpolator/ use std::borrow::Borrow; use std::fmt::Debug; use std::iter; #[doc(hidden)] pub use attribute_derive_macro::Attribute; pub use attribute_derive_macro::FromAttr; use manyhow::SpanRanged; #[cfg(doc)] use parsing::*; use parsing::{AttributeBase, SpannedValue}; use proc_macro2::{Span, TokenStream}; use syn::parse::{ParseStream, Parser, Result}; #[cfg(doc)] use syn::{parse::Parse, LitStr, Type}; use syn::{Error, Path}; #[doc(hidden)] pub use tmp::FromAttr as Attribute; pub use tmp::FromAttr; #[doc(hidden)] pub mod __private { pub use {proc_macro2, quote, syn}; } mod std_impls; mod syn_impls; pub mod utils; pub use utils::FlagOrValue; pub mod parsing; pub mod from_partial; pub use from_partial::FromPartial; mod tmp { use quote::ToTokens; use syn::Meta; use super::*; /// The trait you actually derive on your attribute struct. /// /// Basic gist is a struct like this: /// ``` /// # use attribute_derive::FromAttr; /// # use syn::Type; /// #[derive(FromAttr)] /// #[attribute(ident = collection)] /// #[attribute(error(missing_field = "`{field}` was not specified"))] /// struct CollectionAttribute { /// // Options are optional by default (will be set to None if not specified) /// authority: Option, /// name: String, /// // Any type implementing default can be flagged as optional /// // This will be set to Vec::default() when not specified /// #[attribute(optional)] /// views: Vec, /// // Booleans can be used without assiging a value. as a flag. /// // If omitted they are set to false /// some_flag: bool, /// } /// ``` /// /// Will be able to parse an attribute like this: /// ```text /// #[collection(authority="Some String", name = r#"Another string"#, views = [Option, ()], some_flag)] /// ``` pub trait FromAttr: Sized + AttributeBase { /// Parses an [`IntoIterator`] of [`syn::Attributes`](syn::Attribute) /// e.g. [`Vec`](Vec). Only available if you specify /// the attribute ident: `#[attribute(ident="")]` when /// using the derive macro. /// /// It can therefore parse fields set over multiple attributes like: /// ```text /// #[collection(authority = "Authority", name = "Name")] /// #[collection(views = [A, B])] /// ``` /// And also catch duplicate/conflicting settings over those. /// /// This is best used for derive macros, where you don't need to remove /// your attributes. /// /// # Errors /// Fails with a [`syn::Error`] so you can conveniently return that as a /// compiler error in a proc macro in the following cases /// /// - A required parameter is omitted /// - Invalid input is given for a parameter /// - A non aggregating parameter is specified multiple times /// - An attribute called [`IDENTS`](const@AttributeIdent::IDENTS) has /// invalid syntax (e.g. `#attr(a: "a")`) fn from_attributes>( attrs: impl IntoIterator, ) -> Result where Self: AttributeIdent, { attrs .into_iter() .filter(|attr| Self::is_ident(attr.borrow().path())) .map(Self::from_attribute_partial) .try_fold(None, |acc, item| { Self::join( acc, SpannedValue::call_site(item?), &format!("`{}` was specified twice", Self::ident()), ) }) .and_then(|o| { Self::from_option( o.map(SpannedValue::value), &format!("`{}` is not set", Self::ident()), ) }) } /// Parses a [`&mut Vec`](syn::Attribute). Removing /// matching attributes. Only available if you specify an ident: /// `#[attribute(ident="")]` when using the derive macro. /// /// It can therefore parse fields set over multiple attributes like: /// ```text /// #[collection(authority = "Authority", name = "Name")] /// #[collection(views = [A, B])] /// ``` /// And also catch duplicate/conflicting settings over those. /// /// Use this if you are implementing an attribute macro, and need to /// remove your helper attributes. /// /// ``` /// use syn::parse_quote; /// use attribute_derive::FromAttr; /// let mut attrs = vec![ /// parse_quote!(#[ignored]), parse_quote!(#[test]), /// parse_quote!(#[also_ignored]), parse_quote!(#[test]) /// ]; /// #[derive(FromAttr)] /// #[attribute(ident = test)] /// struct Test {} /// assert!(Test::remove_attributes(&mut attrs).is_ok()); /// /// assert_eq!(attrs, vec![parse_quote!(#[ignored]), parse_quote!(#[also_ignored])]); /// ``` /// /// # Errors /// Fails with a [`syn::Error`], so you can conveniently return that as /// a compiler error in a proc macro in the following cases /// /// - A necessary parameter is omitted /// - Invalid input is given for a parameter /// - A non aggregating parameter is specified multiple times /// - An attribute called [`IDENTS`](const@AttributeIdent::IDENTS) has /// invalid syntax (e.g. `#attr(a: "a")`) fn remove_attributes(attrs: &mut Vec) -> Result where Self: AttributeIdent, { let mut i = 0; Self::from_attributes(iter::from_fn(|| { while i < attrs.len() { if Self::is_ident(attrs[i].path()) { return Some(attrs.remove(i)); } i += 1; } None })) } /// Parses from a single attribute. Ignoring the name. /// /// This is available even without `#[attribute(ident = ...)]`, because /// it ignores the attribute's path, allowing to use it to parse e.g. /// literals: /// ``` /// use attribute_derive::FromAttr; /// /// let attr: syn::Attribute = syn::parse_quote!(#[test = "hello"]); /// assert_eq!(String::from_attribute(attr).unwrap(), "hello"); /// /// let attr: syn::Attribute = syn::parse_quote!(#[test]); /// assert_eq!(bool::from_attribute(attr).unwrap(), true); /// ``` fn from_attribute(attr: impl Borrow) -> Result { Self::from_attribute_partial(attr).and_then(Self::from) } #[doc(hidden)] #[deprecated = "use `from_input` instead"] fn from_args(tokens: TokenStream) -> Result { Self::from_input(tokens) } /// Parses a [`TokenStream`](proc_macro2::TokenStream). /// /// Useful for implementing general proc macros to parse the input of /// your macro. /// /// This is a convenience over [`parse_input`](Self::parse_input). More /// details are documented there. fn from_input(input: impl Into) -> Result { Self::parse_input.parse2(input.into()) } /// Parses input as the complete attribute. /// /// Due to this only parsing the input for a single attribute it is not /// able to aggregate input spread over multiple attributes. /// /// # Errors /// Fails with a [`syn::Error`], so you can conveniently return that as /// a compiler error in a proc macro in the following cases /// /// - A necessary parameter is omitted /// - Invalid input is given for a parameter /// - A non aggregating parameter is specified multiple times fn parse_input(input: ParseStream) -> Result { Self::parse_partial(input).and_then(Self::from) } /// Like [`parse_partial`](Self::parse_partial) but instead takes an /// [`Attribute`](syn::Attribute). /// /// This allows it to support all three, `#[flag]`, `#[function(like)]` /// and `#[name = value]` attributes. fn from_attribute_partial(attr: impl Borrow) -> Result { let tokens = match attr.borrow().meta { Meta::Path(_) => TokenStream::new(), Meta::List(ref list) => list.tokens.clone(), Meta::NameValue(ref nv) => nv.value.to_token_stream(), }; Self::parse_partial.parse2(tokens) } /// Actual implementation for parsing the attribute. This is the only /// function required to implement in this trait and derived by the /// [`FromAttr`](macro@FromAttr) derive macro. fn parse_partial(input: ParseStream) -> Result; } } /// Helper trait providing the path for an attribute. /// /// Automatically derived with [`FromAttr`], if `#[attribute(ident = /// "some_ident")]` is provided. pub trait AttributeIdent { /// List of idents, must contain at least one ident. const IDENTS: &'static [&'static str]; /// Tests if Attribute matches one of the idents. fn is_ident(path: &Path) -> bool { Self::IDENTS.iter().any(|ident| path.is_ident(ident)) } /// Returns default ident. /// /// # Panics /// The default implementation panics if `IDENTS` is empty. Implementors /// should ensure this is not the case. fn ident() -> &'static str { Self::IDENTS .first() .expect("`AttributeIdent::IDENTS` should not be empty") } } attribute-derive-0.10.5/src/parsing.rs000064400000000000000000000306211046102023000157700ustar 00000000000000//! This module defines the traits defining how parsing works. //! //! `attribute-derive` reuses the same traits for nested values and the root //! attribute, this is why all traits in this module are named `Attribute*`. #![doc = include_str!("../docs/traits.html")] use std::ops::Range; use manyhow::{span_range, SpanRanged}; use proc_macro2::{Ident, Span}; use quote::ToTokens; use syn::ext::IdentExt; use syn::parse::discouraged::Speculative; #[cfg(doc)] use syn::parse::ParseBuffer; use syn::parse::{Parse, ParseStream}; use syn::token::Paren; use syn::{parenthesized, Result, Token}; use crate::from_partial::FromPartial; use crate::FromAttr; /// Values that can be parsed named, e.g. `()`, ` = `, /// `` (as flag). /// /// This is the default parsing mode used for fields in derived [`FromAttr`] /// implementations. pub trait AttributeNamed: AttributeBase { /// What open delimiter to use when providing error messages. /// /// For ` = `, this is `" = "`, for `()`, it is /// `"("`. /// /// As named attributes can allow both ` = ` and /// `name()`, this might not be the only way this attribute can be /// used. const PREFERRED_OPEN_DELIMITER: &'static str = " = "; /// What close delimiter to use when providing error messages. /// /// For ` = `, this is `""`, for `()`, it is /// `")"`. /// /// As named attributes can allow both ` = ` and /// `()`, this might not be the only way this attribute can be /// used. const PREFERRED_CLOSE_DELIMITER: &'static str = ""; /// Parses an attribute containing `Self` called `name`. /// /// While this function can be implemented freely, the provided /// implementations support ` = `, `()` and /// ``. /// /// **Note:** This needs to stop parsing at the end of the value, before a /// possible following `,` and further arguments. fn parse_named( name: &'static str, input: ParseStream, ) -> Result>>>; } /// Values that can be parsed positionally, i.e., without a name, e.g. /// `"literal"`, `a + b`, `true`. /// /// When deriving [`FromAttr`] this behavior is enabled via putting /// `#[attr(positional)]` on the field. /// /// The trait is implemented for each [`AttributeValue`] that implements the /// marker trait [`PositionalValue`]. pub trait AttributePositional: AttributeBase { /// Parses `Self`, positionally. /// /// **Note:** This needs to stop parsing at the end of the value, before a /// possible following `,` and further arguments. fn parse_positional(input: ParseStream) -> Result>>; } /// Any values that can be parsed in an attribute input. /// /// This is probably the trait you want to implement when you created a custom /// type for field inside [`#[derive(FromAttr)]`](FromAttr), as it will provide /// implementations for [`FromAttr`], [`AttributeNamed`] and, if you implement /// the marker trait [`PositionalValue`], [`AttributePositional`] as well. /// /// For named attributes by default it will support both ` = ` and /// `()`, though this can be tweaked in the implementation. pub trait AttributeValue: AttributeBase { /// Printed when not encountering a `(` or `=` respectively while trying to /// parse a [`AttributeNamed`]. const EXPECTED: &'static str = "expected `=` or `(`"; /// What open delimiter to use when providing error messages. /// /// For ` = `, this is `" = "`, for `()`, it is /// `"("`. /// /// As named attributes can allow both ` = ` and /// `name()`, this might not be the only way this attribute can be /// used. const PREFERRED_OPEN_DELIMITER: &'static str = " = "; /// What close delimiter to use when providing error messages. /// /// For ` = `, this is `""`, for `()`, it is /// `")"`. /// /// As named attributes can allow both ` = ` and /// `()`, this might not be the only way this attribute can be /// used. const PREFERRED_CLOSE_DELIMITER: &'static str = ""; /// Parses the attribute value when parentheses (`(`) were peeked. /// /// Note: this is the input with the parentheses, and potentially following /// arguments. /// /// ```text /// attribute(value), ... /// ^^^^^^^^^^^^ /// ``` /// /// In the default implementation this calls through to /// [`parse_value`](Self::parse_value) after removing the parentheses. fn parse_value_meta(input: ParseStream) -> Result> { let content; let paren = parenthesized!(content in input); Self::parse_value(&content) .map(SpannedValue::value) .map(SpannedValue::with(paren.span.join())) } /// Parses the attribute value when an equals (`=`) was peeked. /// /// Note: this is the input with the equals, and potentially following /// arguments. /// /// ```text /// attribute = value, ... /// ^^^^^^^^^^^^ /// ``` /// /// In the default implementation this calls through to /// [`parse_value`](Self::parse_value) after removing the `=`. fn parse_value_eq(input: ParseStream) -> Result> { ::parse(input)?; Self::parse_value(input) } /// Parses the plain attribute value without leading `=` or enclosing /// parenthesis. /// /// **Note:** this input includes potentially a trailing `,` and following /// arguments. /// /// ```text /// attribute = value, ... /// ^^^^^^^^^^ /// ``` /// /// For simple syntax this is the only function needed to implement, as the /// default implementations for [`parse_value_meta`](Self::parse_value_meta) /// and [`parse_value_eq`](Self::parse_value_eq). fn parse_value(input: ParseStream) -> Result>; } impl FromAttr for T { fn parse_partial(input: ParseStream) -> Result { Self::parse_value(input).map(SpannedValue::value) } } impl AttributeNamed for T { const PREFERRED_CLOSE_DELIMITER: &'static str = Self::PREFERRED_CLOSE_DELIMITER; const PREFERRED_OPEN_DELIMITER: &'static str = Self::PREFERRED_OPEN_DELIMITER; fn parse_named( name: &'static str, input: ParseStream, ) -> Result>>> { let Some(name) = parse_name(input, name) else { return Ok(None); }; let value = if input.peek(Token![=]) { Self::parse_value_eq(input)? } else if input.peek(Paren) { Self::parse_value_meta(input)? } else { return Err(input.error(Self::EXPECTED)); }; Ok(Some(Named { name, value })) } } /// Marker trait that enables the blanket implementation of /// [`AttributePositional`] for [`AttributeValue`]. pub trait PositionalValue {} impl AttributePositional for T { fn parse_positional(input: ParseStream) -> Result>> { Self::parse_value(input).map(Some) } } /// Trait implementing parsing for `()` attributes. /// /// This is the trait defining the parsing of both top level attributes deriving /// [`FromAttr`] and sub attributes. /// ``` /// # quote::quote!( /// #[attribute(sub_attribute("hello", "world"))] /// # ); /// ``` pub trait AttributeMeta: AttributeBase { /// Parses the content of the parenthesis: /// /// ```text /// attribute(value) /// ^^^^^ /// ``` fn parse_inner(input: ParseStream) -> Result; } impl AttributeValue for T { const EXPECTED: &'static str = "expected `(`"; const PREFERRED_CLOSE_DELIMITER: &'static str = ")"; const PREFERRED_OPEN_DELIMITER: &'static str = "("; fn parse_value_eq(input: ParseStream) -> Result> { Err(input.error(Self::EXPECTED)) } fn parse_value(input: ParseStream) -> Result> { Self::parse_inner(input).map(SpannedValue::call_site) } } /// Trait implemented for attributes that can be parsed optionally as a /// positional argument, and the requirement for `Option` to implement /// [`AttributePositional`]. pub trait AttributePeekable { /// Used to decide whether to parse optional positional values. /// /// While most implementations should not mutate `input`, it might be good /// to call this on a [`fork`](ParseBuffer::fork) of the original /// [`ParseStream`] to ensure no mutation is persisted. /// /// # Implementation notes /// This should not try to parse `input`, if you cannot decide if `input` /// matches using [`ParseBuffer::peek`] ([peek2](ParseBuffer::peek2), /// [peek3](ParseBuffer::peek3)), consider not implementing /// [`AttributePeekable`]. /// /// `attribute-derive` will always [`fork`](ParseBuffer::fork) before /// calling this function to allow `peek` to modify `input` without /// effecting further parsing. fn peek(input: ParseStream) -> bool; } #[derive(Debug)] /// Helper struct to hold a value and the ident of its property. pub struct Named { /// The value. pub value: T, /// The argument name. pub name: Ident, } impl Named { #[doc(hidden)] pub fn error_span(&self) -> Span { self.name.span() } } impl Named> { /// The value. pub fn value(self) -> T { self.value.value } } /// Parses the name, if it matches returns `Some(name)` and removes the `name` /// from input, if `None` it does not modify the input. pub fn parse_name(input: ParseStream, name: &str) -> Option { let fork = &input.fork(); let ident: Ident = Ident::parse_any(fork).ok()?; if ident == name { input.advance_to(fork); Some(ident) } else { None } } /// Utility crate holding `Self::Partial` used in most attribute traits, i.e., /// [`FromAttr`], [`AttributeValue`], [`AttributePositional`], ... pub trait AttributeBase: FromPartial { /// Partial type for this attribute. In most cases this can be `Self`, /// unless the attribute can be parsed in multiple on-its-own-incomplete /// parts or needs special handling on the conversion. type Partial; } /// Helper struct to hold a value and the corresponding range. #[derive(Debug)] pub struct SpannedValue { /// The value. pub value: T, /// The value's span. pub span: Range, } impl Default for SpannedValue { fn default() -> Self { Self::call_site(Default::default()) } } impl SpannedValue { /// The value. pub fn value(self) -> T { self.value } /// The value's span. pub fn span(&self) -> Range { self.span.clone() } /// Map the value to a new type, keeping the span. pub fn map_value(self, map: impl FnOnce(T) -> I) -> SpannedValue { SpannedValue { span: self.span(), value: map(self.value()), } } pub(crate) fn with(span: impl SpanRanged) -> impl Fn(T) -> Self { move |value| Self::new(value, span.span_range()) } /// Creates a new `SpannedValue` from a `value` implementing [`ToTokens`]. pub fn from_to_tokens(value: T) -> Self where T: ToTokens, { Self { span: span_range!(value), value, } } /// Creates a new `SpannedValue` from a `value` and a [`span`](SpanRanged). pub fn new(value: T, span: impl SpanRanged) -> SpannedValue { Self { value, span: span.span_range(), } } /// Creates a new `SpannedValue` with the span [`Span::call_site()`]. pub fn call_site(value: T) -> SpannedValue { Self::new(value, Span::call_site()) } #[doc(hidden)] pub fn error_span(&self) -> Span { self.span() .span_joined() .unwrap_or_else(|| self.span().start) } } impl SpanRanged for SpannedValue { fn span_range(&self) -> Range { self.span.clone() } } attribute-derive-0.10.5/src/std_impls.rs000064400000000000000000000162651046102023000163330ustar 00000000000000use manyhow::{span_range, SpanRanged}; use syn::parse::discouraged::{AnyDelimiter, Speculative}; use syn::parse::{Parse, ParseStream}; use syn::token::{Bracket, Paren}; use syn::{bracketed, parenthesized, LitBool, LitChar, LitFloat, LitInt, LitStr, Result, Token}; use super::FromAttr; use crate::from_partial::{Defaulting, FromPartial, Partial}; use crate::parsing::{ parse_name, AttributeBase, AttributeNamed, AttributePeekable, AttributePositional, AttributeValue, Named, PositionalValue, SpannedValue, }; use crate::AttributeIdent; macro_rules! ParseAs { ($parse:ty => $($type:ty),+; $value:ident $($tt:tt)*) => { ParseAs! {$parse => $($type),+; |$value| $value $($tt)*} }; ($parse:ty => $($type:ty),+; |$value:ident| $conv:expr) => { $(impl AttributeBase for $type { type Partial = Self; } impl AttributeValue for $type { fn parse_value(input: ParseStream) -> Result> { let $value: $parse = input.parse()?; let span = span_range!($value); Ok(SpannedValue::new($conv, span)) } } impl PositionalValue for $type {} )+ }; } ParseAs!(LitStr => String; v.value()); ParseAs!(LitChar => char; v.value()); // TODO: This could be achieved with auto-deref or real specilization // ParseAs!(LitByteStr => Vec; v.value()); ParseAs!(LitInt => u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, usize, isize; v.base10_parse()?); ParseAs!(LitFloat => f32, f64; v.base10_parse()?); impl, P> FromPartial>> for Option { fn from(partial: Partial>) -> Result { partial.0.map(T::from).transpose() } } impl AttributeBase for Option { type Partial = Defaulting>>; } impl AttributePositional for Option { fn parse_positional(input: ParseStream) -> Result>> { Ok(if T::peek(input) { T::parse_positional(input)?.map(|SpannedValue { value, span }| SpannedValue { value: Defaulting(Partial(Some(value))), span, }) } else { None }) } } impl AttributeNamed for Option { fn parse_named( name: &'static str, input: ParseStream, ) -> Result>>> { T::parse_named(name, input).map(|value| { value.map(|value| Named { value: SpannedValue { value: Defaulting(Partial(Some(value.value.value))), span: value.value.span, }, name: value.name, }) }) } } impl FromAttr for Option { fn parse_partial(input: ParseStream) -> Result { T::parse_partial(input) .map(Some) .map(Partial) .map(Defaulting) } } impl AttributeIdent for Option { const IDENTS: &'static [&'static str] = T::IDENTS; } impl, P> FromPartial>> for Vec { fn from(partial: Partial>) -> Result { partial.0.into_iter().map(T::from).collect() } fn join( first: Option>>>, second: SpannedValue>>, _: &str, ) -> Result>>>> { if let Some(mut first) = first { first.value.0.extend(second.value.0); Ok(Some(first)) } else { Ok(Some(second)) } } } impl AttributeBase for Vec { type Partial = Partial>; } impl AttributePeekable for Vec { fn peek(input: ParseStream) -> bool { input.peek(Paren) || input.peek(Bracket) } } impl PositionalValue for Vec {} fn parse_vec_inner( input: ParseStream, ) -> Result< as AttributeBase>::Partial> { // TODO parse_positional that return Ok(None) will behave wierd let i = input.parse_terminated(T::parse_positional, Token!(,))?; Ok(Partial(i.into_iter().flatten().map(|v| v.value).collect())) } impl AttributeValue for Vec { fn parse_value_meta(input: ParseStream) -> Result> { let content; let paren = parenthesized!(content in input); let fork = &content.fork(); match parse_vec_inner::(fork) { Ok(value) => { content.advance_to(fork); Ok(SpannedValue { value, span: paren.span.join().span_range(), }) } Err(err) => { if let Ok((.., span, content)) = content.parse_any_delimiter() { if let Ok(value) = parse_vec_inner::(&content) { return Ok(SpannedValue { value, span: span.join().span_range(), }); } } Err(err) } } } fn parse_value(input: ParseStream) -> Result> { if input.peek(Paren) { Self::parse_value_meta(input) } else if input.peek(Bracket) { let content; let b = bracketed!(content in input); let value = parse_vec_inner::(&content)?; Ok(SpannedValue { value, span: b.span.join().span_range(), }) } else { Err(input.error("expected `[` or `(`")) } } } impl AttributeBase for bool { type Partial = Defaulting; } impl AttributePositional for bool { fn parse_positional(input: ParseStream) -> Result>> { let lit = LitBool::parse_value(input)?; Ok(Some(SpannedValue { value: Defaulting(lit.value.value), span: lit.span.span_range(), })) } } impl AttributePeekable for bool { fn peek(input: ParseStream) -> bool { input.peek(LitBool) } } impl AttributeNamed for bool { fn parse_named( name: &'static str, input: ParseStream, ) -> Result>>> { let Some(ident) = parse_name(input, name) else { return Ok(None); }; let value = if input.parse::().is_ok() { Self::parse_positional(input)?.unwrap() } else if input.peek(Paren) { let content; parenthesized!(content in input); Self::parse_positional(&content)?.unwrap() } else { SpannedValue { value: Defaulting(true), span: ident.span().span_range(), } }; Ok(Some(Named { value, name: ident })) } } impl crate::FromAttr for bool { fn parse_partial(input: ParseStream) -> Result { if input.is_empty() { Ok(Defaulting(true)) } else { Ok(Defaulting(LitBool::parse(input)?.value())) } } } attribute-derive-0.10.5/src/syn_impls.rs000064400000000000000000000215541046102023000163470ustar 00000000000000use proc_macro2::{Group, Literal, Punct, TokenStream, TokenTree}; use syn::token::{ Abstract, And, AndAnd, AndEq, As, Async, At, Auto, Await, Become, Break, Caret, CaretEq, Colon, Comma, Const, Continue, Crate, Do, Dollar, Dot, DotDot, DotDotDot, DotDotEq, Dyn, Else, Enum, Eq, EqEq, Extern, FatArrow, Final, Fn, For, Ge, Gt, If, Impl, In, LArrow, Le, Let, Loop, Lt, Match, Minus, MinusEq, Mod, Move, Mut, Ne, Not, Or, OrEq, OrOr, Override, PathSep, Percent, PercentEq, Plus, PlusEq, Pound, Priv, Pub, Question, RArrow, Ref, Return, SelfType, SelfValue, Semi, Shl, ShlEq, Shr, ShrEq, Slash, SlashEq, Star, StarEq, Static, Struct, Super, Tilde, Trait, Try, Typeof, Underscore, Union, Unsafe, Unsized, Use, Virtual, Where, While, Yield, }; use syn::{ Abi, AngleBracketedGenericArguments, BareFnArg, BinOp, BoundLifetimes, ConstParam, DeriveInput, Expr, FieldsNamed, FieldsUnnamed, GenericArgument, GenericParam, Generics, Ident, Index, Lifetime, Lit, LitBool, LitByteStr, LitChar, LitFloat, LitInt, LitStr, Member, Meta, MetaList, MetaNameValue, ParenthesizedGenericArguments, Path, PathSegment, ReturnType, TraitBound, TraitBoundModifier, Type, TypeArray, TypeBareFn, TypeGroup, TypeImplTrait, TypeInfer, TypeMacro, TypeNever, TypeParam, TypeParamBound, TypeParen, TypePath, TypePtr, TypeReference, TypeSlice, TypeTraitObject, TypeTuple, UnOp, Variant, Visibility, WhereClause, WherePredicate, }; use crate::parsing::*; use crate::*; /// Macro to easily implement [`AttributeValue`] for types implementing /// [`Parse`] and [`ToTokens`]. #[macro_export] macro_rules! impl_Attribute_for_Parse_and_ToTokens { ($($type:ty),+ $(,)?) => {$( impl $crate::parsing::AttributeBase for $type { type Partial = Self; } impl $crate::parsing::AttributeValue for $type { fn parse_value(input: $crate::__private::syn::parse::ParseStream) -> $crate::__private::syn::Result<$crate::parsing::SpannedValue> { input.parse().map($crate::parsing::SpannedValue::from_to_tokens) } } impl $crate::parsing::PositionalValue for $type {} )*} } impl AttributeBase for TokenStream { type Partial = Self; } impl AttributeMeta for TokenStream { fn parse_inner(input: ParseStream) -> Result { input.parse() } } // /// Macro to easily implement [`ConvertParsed`] for syn types. // macro_rules! ParseToTokensAttribute { // ($(#[$meta:meta])* $type:path) => { // $(#[$meta])* // impl ConvertParsed for $type { // type Type = $type; // fn convert(s: Self) -> Result { // Ok(s) // } // } // }; // [$($type:path),* $(,)?] => { // $( // impl ConvertParsed for $type { // type Type = $type; // fn convert(s: Self) -> Result { // Ok(s) // } // } // )* // }; // ($from:path => $to:path) => { // impl ConvertParsed<$from> for $to { // fn convert(value: $from) -> Result<$to> { // Ok(value.into()) // } // } // }; // ($from:path => $($to:path),+ : $with:path ) => { // $( // impl ConvertParsed for $to { // type Type = $from; // fn convert(value: $from) -> Result<$to> { // Ok($with(&value)) // } // } // )* // }; // ($from:path => $($to:path),+ :? $with:path ) => { // $( // impl ConvertParsed for $to { // type Type = $from; // fn convert(value: $from) -> Result<$to> { // $with(&value) // } // } // )* // }; // } impl_Attribute_for_Parse_and_ToTokens!(Type); impl_Attribute_for_Parse_and_ToTokens!(Path); impl_Attribute_for_Parse_and_ToTokens!(Lit); impl_Attribute_for_Parse_and_ToTokens![LitStr, LitByteStr, LitChar, LitInt, LitFloat, LitBool]; impl_Attribute_for_Parse_and_ToTokens!(Expr); impl_Attribute_for_Parse_and_ToTokens![TokenTree, Group, Punct, Literal]; // // TODO make this warning better visable // ParseToTokensAttribute! { // /// Try to avoid using this, as it will consume everything behind, so it // needs to be defined as the /// last parameter. // /// // /// In the future there might be something to allow better handling of // this (maybe by putting it /// into `()`) // TokenStream // } // Some probably useless stuff impl_Attribute_for_Parse_and_ToTokens![ Abi, Abstract, Plus, PlusEq, And, AndAnd, AndEq, AngleBracketedGenericArguments, As, Async, At, Auto, Await, Not, BareFnArg, Become, BinOp, BoundLifetimes, Break, Caret, CaretEq, Colon, PathSep, Comma, Const, ConstParam, Continue, Crate, DeriveInput, Slash, SlashEq, Do, Dollar, Dot, DotDot, DotDotDot, DotDotEq, Dyn, Else, Enum, Eq, EqEq, Extern, FatArrow, FieldsNamed, FieldsUnnamed, Final, Fn, For, Ge, GenericArgument, GenericParam, Generics, Gt, Ident, If, Impl, In, Index, LArrow, Le, Let, Lifetime, Loop, Lt, Match, Member, Meta, MetaList, MetaNameValue, Mod, Move, StarEq, Mut, Ne, Or, OrEq, OrOr, Override, ParenthesizedGenericArguments, PathSegment, Pound, Priv, Pub, Question, RArrow, Ref, Percent, PercentEq, Return, ReturnType, SelfType, SelfValue, Semi, Shl, ShlEq, Shr, ShrEq, Star, Static, Struct, Minus, MinusEq, Super, Tilde, Trait, TraitBound, TraitBoundModifier, Try, TypeArray, TypeBareFn, TypeGroup, TypeImplTrait, TypeInfer, TypeMacro, TypeNever, TypeParam, TypeParamBound, TypeParen, TypePath, TypePtr, TypeReference, TypeSlice, TypeTraitObject, TypeTuple, Typeof, UnOp, Underscore, Union, Unsafe, Unsized, Use, Variant, Virtual, Visibility, Where, WhereClause, WherePredicate, While, Yield, syn::Macro, syn::token::Box, syn::token::Default, syn::token::Macro, syn::token::Type, ]; #[cfg(feature = "syn-full")] mod syn_full { use syn::{ Arm, Block, ExprArray, ExprAssign, ExprAsync, ExprBinary, ExprBlock, ExprBreak, ExprCall, ExprCast, ExprClosure, ExprContinue, ExprField, ExprForLoop, ExprIf, ExprIndex, ExprLet, ExprLit, ExprLoop, ExprMacro, ExprMatch, ExprMethodCall, ExprParen, ExprPath, ExprRange, ExprReference, ExprRepeat, ExprReturn, ExprStruct, ExprTry, ExprTryBlock, ExprTuple, ExprUnary, ExprUnsafe, ExprWhile, ExprYield, FieldValue, File, FnArg, ForeignItem, ForeignItemFn, ForeignItemMacro, ForeignItemStatic, ForeignItemType, ImplItem, ImplItemConst, ImplItemMacro, ImplItemType, Item, ItemConst, ItemEnum, ItemExternCrate, ItemFn, ItemForeignMod, ItemImpl, ItemMacro, ItemMod, ItemStatic, ItemStruct, ItemTrait, ItemTraitAlias, ItemType, ItemUnion, ItemUse, Label, RangeLimits, Receiver, Signature, Stmt, TraitItem, TraitItemConst, TraitItemMacro, TraitItemType, UseTree, }; use super::*; impl_Attribute_for_Parse_and_ToTokens![ Arm, Block, ExprArray, ExprAssign, ExprAsync, ExprBinary, ExprBlock, ExprBreak, ExprCall, ExprCast, ExprClosure, ExprContinue, ExprField, ExprForLoop, ExprIf, ExprIndex, ExprLet, ExprLit, ExprLoop, ExprMacro, ExprMatch, ExprMethodCall, ExprParen, ExprPath, ExprRange, ExprReference, ExprRepeat, ExprReturn, ExprStruct, ExprTry, ExprTryBlock, ExprTuple, ExprUnary, ExprUnsafe, ExprWhile, ExprYield, FieldValue, File, FnArg, ForeignItem, ForeignItemFn, ForeignItemMacro, ForeignItemStatic, ForeignItemType, ImplItem, ImplItemConst, ImplItemMacro, ImplItemType, Item, ItemConst, ItemEnum, ItemExternCrate, ItemFn, ItemForeignMod, ItemImpl, ItemMacro, ItemMod, ItemStatic, ItemStruct, ItemTrait, ItemTraitAlias, ItemType, ItemUnion, ItemUse, Label, RangeLimits, Receiver, Signature, Stmt, TraitItem, TraitItemConst, TraitItemMacro, TraitItemType, UseTree, ]; } attribute-derive-0.10.5/src/utils.rs000064400000000000000000000147141046102023000154720ustar 00000000000000//! Utilities implementing useful patterns for fields inside an attribute. use from_partial::{FromPartial, Partial}; use manyhow::SpanRanged; use parsing::{AttributeNamed, AttributeValue}; use syn::token::Paren; use syn::Token; use self::parsing::parse_name; use crate::parsing::Named; use crate::{SpannedValue, *}; /// [`FromAttr`] value that can be used both as a flag and with a value. /// /// When parameter is specified both as flag and as value, the value will /// dominate. /// /// ``` /// # use attribute_derive::{FromAttr, utils::FlagOrValue}; /// # use quote::quote; /// #[derive(FromAttr)] /// struct Test { /// param: FlagOrValue, /// } /// /// assert_eq!( /// Test::from_args(quote!(param)).unwrap().param, /// FlagOrValue::Flag /// ); /// assert_eq!( /// Test::from_args(quote!(param = "value")).unwrap().param, /// FlagOrValue::Value("value".into()) /// ); /// assert_eq!( /// Test::from_args(quote!(param, param = "value", param)) /// .unwrap() /// .param, /// FlagOrValue::Value("value".into()) /// ); /// assert_eq!(Test::from_args(quote!()).unwrap().param, FlagOrValue::None); /// ``` #[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord)] pub enum FlagOrValue { /// Was not specified. #[default] None, /// Was specified as a flag, i.e., without a value. Flag, /// Was specified with a value. Value(T), } impl FlagOrValue { /// Was not specified. pub fn is_none(&self) -> bool { matches!(self, Self::None) } /// Was specified as a flag, i.e., without a value. pub fn is_flag(&self) -> bool { matches!(self, Self::Flag,) } /// Was specified with a value. pub fn is_value(&self) -> bool { matches!(self, Self::Value(_),) } /// Returns value if set. pub fn into_value(self) -> Option { match self { FlagOrValue::Value(value) => Some(value), _ => None, } } /// Returns value if set. pub fn as_value(&self) -> Option<&T> { match self { FlagOrValue::Value(value) => Some(value), _ => None, } } /// Maps the `value` if present. pub fn map_value(self, map: impl FnOnce(T) -> I) -> FlagOrValue { match self { FlagOrValue::None => FlagOrValue::None, FlagOrValue::Flag => FlagOrValue::Flag, FlagOrValue::Value(value) => FlagOrValue::Value(map(value)), } } } /// Enables the `transpose` function on [`FlagOrValue`] containing or being /// contained in [`Option`] or [`Result`](std::result::Result). pub trait Transpose { /// Should behave equivalent to the built-in `transpose` functions available /// on [`Result