displaydoc-0.2.5/.cargo_vcs_info.json0000644000000001360000000000100131730ustar { "git": { "sha1": "e4028851bfb82998300237f7568a45f589a19e40" }, "path_in_vcs": "" }displaydoc-0.2.5/.github/workflows/ci.yml000064400000000000000000000051111046102023000164740ustar 00000000000000on: push: pull_request: schedule: - cron: '0 0 * * *' name: Continuous integration jobs: check: name: Check runs-on: ubuntu-latest strategy: matrix: rust: - stable - 1.56.0 steps: - uses: actions/checkout@v1 - uses: actions-rs/toolchain@v1 with: toolchain: ${{ matrix.rust }} override: true - uses: actions-rs/cargo@v1 with: command: check test: name: Test Suite runs-on: ubuntu-latest strategy: matrix: rust: - stable - nightly steps: - uses: actions/checkout@v1 - uses: actions-rs/toolchain@v1 with: toolchain: ${{ matrix.rust }} override: true - uses: Swatinem/rust-cache@v1 - name: Install cargo-nextest uses: baptiste0928/cargo-install@v1 with: crate: cargo-nextest version: 0.9 - uses: actions-rs/cargo@v1 with: command: nextest args: run - uses: actions-rs/cargo@v1 with: command: nextest args: run --no-default-features - uses: actions-rs/cargo@v1 with: command: test args: --doc test-msrv: name: msrv Test Suite runs-on: ubuntu-latest strategy: matrix: rust: - 1.56.0 steps: - uses: actions/checkout@v1 - uses: actions-rs/toolchain@v1 with: toolchain: ${{ matrix.rust }} override: true - uses: Swatinem/rust-cache@v1 - uses: actions-rs/cargo@v1 with: command: test - uses: actions-rs/cargo@v1 with: command: test args: --no-default-features fmt: name: Rustfmt runs-on: ubuntu-latest strategy: matrix: rust: - stable - 1.56.0 steps: - uses: actions/checkout@v1 - uses: actions-rs/toolchain@v1 with: toolchain: ${{ matrix.rust }} override: true - run: rustup component add rustfmt - uses: actions-rs/cargo@v1 with: command: fmt args: --all -- --check clippy: name: Clippy runs-on: ubuntu-latest strategy: matrix: rust: - stable steps: - uses: actions/checkout@v1 - uses: actions-rs/toolchain@v1 with: toolchain: ${{ matrix.rust }} override: true - run: rustup component add clippy - uses: actions-rs/cargo@v1 with: command: clippy args: -- -D warnings displaydoc-0.2.5/.gitignore000064400000000000000000000000361046102023000137520ustar 00000000000000/target **/*.rs.bk Cargo.lock displaydoc-0.2.5/CHANGELOG.md000064400000000000000000000037241046102023000136020ustar 00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] - ReleaseDate # [0.2.5] - 2024-06-20 # Changed - Don't name the output of the const block to work around `non_local_definitions` error (#47) - Reference the `core` crate correctly to avoid clashes with modules named `core` (#45) - Explicitly list MSRV in Cargo.toml (#51) - Bump edition to 2021 (#51) # [0.2.4] - 2022-05-02 ## Added - Updated `syn` dependency to 2.0 - Support for empty enums - Implicitly require fmt::Display on all type parameters unless overridden ## Changed - Bumped MSRV to 1.56 # [0.2.3] - 2021-07-16 ## Added - Added `#[displaydoc("..")]` attribute for overriding a doc comment # [0.2.2] - 2021-07-01 ## Added - Added prefix feature to use the doc comment from an enum and prepend it before the error message from each variant. # [0.2.1] - 2021-03-26 ## Added - Added opt in support for ignoring extra doc attributes # [0.2.0] - 2021-03-16 ## Changed - (BREAKING) disallow multiple `doc` attributes in display impl [https://github.com/yaahc/displaydoc/pull/22]. Allowing and ignoring extra doc attributes made it too easy to accidentally create a broken display implementation with missing context without realizing it, this change turns that into a hard error and directs users towards block comments if multiple lines are needed. [Unreleased]: https://github.com/yaahc/displaydoc/compare/v0.2.4...HEAD [0.2.4]: https://github.com/yaahc/displaydoc/compare/v0.2.3...v0.2.4 [0.2.3]: https://github.com/yaahc/displaydoc/compare/v0.2.2...v0.2.3 [0.2.2]: https://github.com/yaahc/displaydoc/compare/v0.2.1...v0.2.2 [0.2.1]: https://github.com/yaahc/displaydoc/compare/v0.2.0...v0.2.1 [0.2.0]: https://github.com/yaahc/displaydoc/releases/tag/v0.2.0 displaydoc-0.2.5/Cargo.lock0000644000000250060000000000100111510ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "ansi_term" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" dependencies = [ "winapi", ] [[package]] name = "ctor" version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" dependencies = [ "quote", "syn 1.0.109", ] [[package]] name = "difference" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" [[package]] name = "displaydoc" version = "0.2.5" dependencies = [ "libc", "pretty_assertions", "proc-macro2", "quote", "rustversion", "static_assertions", "syn 2.0.66", "thiserror", "trybuild", ] [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "glob" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "indexmap" version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown", ] [[package]] name = "itoa" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "libc" version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "output_vt100" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66" dependencies = [ "winapi", ] [[package]] name = "pretty_assertions" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427" dependencies = [ "ansi_term", "ctor", "difference", "output_vt100", ] [[package]] name = "proc-macro2" version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" 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 = "rustversion" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "ryu" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "serde" version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", "syn 2.0.66", ] [[package]] name = "serde_json" version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ "itoa", "ryu", "serde", ] [[package]] name = "serde_spanned" version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" dependencies = [ "serde", ] [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "syn" version = "2.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "termcolor" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] [[package]] name = "thiserror" version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", "syn 2.0.66", ] [[package]] name = "toml" version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" dependencies = [ "serde", "serde_spanned", "toml_datetime", "toml_edit", ] [[package]] name = "toml_datetime" version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" dependencies = [ "serde", ] [[package]] name = "toml_edit" version = "0.22.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", "winnow", ] [[package]] name = "trybuild" version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33a5f13f11071020bb12de7a16b925d2d58636175c20c11dc5f96cb64bb6c9b3" dependencies = [ "glob", "serde", "serde_derive", "serde_json", "termcolor", "toml", ] [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ "windows-sys", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[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.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" 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.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" [[package]] name = "windows_i686_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" dependencies = [ "memchr", ] displaydoc-0.2.5/Cargo.toml0000644000000051640000000000100111770ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.56.0" name = "displaydoc" version = "0.2.5" authors = ["Jane Lusby "] description = """ A derive macro for implementing the display Trait via a doc comment and string interpolation """ homepage = "https://github.com/yaahc/displaydoc" documentation = "https://docs.rs/displaydoc" readme = "README.md" keywords = [ "display", "derive", ] license = "MIT OR Apache-2.0" repository = "https://github.com/yaahc/displaydoc" [package.metadata.docs.rs] all-features = true rustdoc-args = [ "--cfg", "docsrs", ] [package.metadata.release] no-dev-version = true pre-release-hook = ["./update-readme.sh"] [[package.metadata.release.pre-release-replacements]] file = "CHANGELOG.md" replace = "{{version}}" search = "Unreleased" [[package.metadata.release.pre-release-replacements]] exactly = 1 file = "src/lib.rs" replace = "#![doc(html_root_url = \"https://docs.rs/{{crate_name}}/{{version}}\")]" search = '#!\[doc\(html_root_url.*' [[package.metadata.release.pre-release-replacements]] file = "CHANGELOG.md" replace = "{{date}}" search = "ReleaseDate" [[package.metadata.release.pre-release-replacements]] exactly = 1 file = "CHANGELOG.md" replace = """ # [Unreleased] - ReleaseDate""" search = "" [[package.metadata.release.pre-release-replacements]] exactly = 1 file = "CHANGELOG.md" replace = "...{{tag_name}}" search = '\.\.\.HEAD' [[package.metadata.release.pre-release-replacements]] exactly = 1 file = "CHANGELOG.md" replace = """ [Unreleased]: https://github.com/yaahc/{{crate_name}}/compare/{{tag_name}}...HEAD""" search = "" [lib] path = "src/lib.rs" proc-macro = true [dependencies.proc-macro2] version = "1.0" [dependencies.quote] version = "1.0" [dependencies.syn] version = "2.0" [dev-dependencies.libc] version = "0.2" default-features = false [dev-dependencies.pretty_assertions] version = "0.6.1" [dev-dependencies.rustversion] version = "1.0.0" [dev-dependencies.static_assertions] version = "1.1" [dev-dependencies.thiserror] version = "1.0.24" [dev-dependencies.trybuild] version = "1.0" [features] default = ["std"] std = [] displaydoc-0.2.5/Cargo.toml.orig000064400000000000000000000041021046102023000146470ustar 00000000000000[package] name = "displaydoc" version = "0.2.5" rust-version = "1.56.0" authors = ["Jane Lusby "] edition = "2021" license = "MIT OR Apache-2.0" readme = "README.md" repository = "https://github.com/yaahc/displaydoc" homepage = "https://github.com/yaahc/displaydoc" documentation = "https://docs.rs/displaydoc" keywords = ["display", "derive"] description = """ A derive macro for implementing the display Trait via a doc comment and string interpolation """ [lib] proc-macro = true path = "src/lib.rs" [features] default = ["std"] std = [] [dependencies] syn = "2.0" quote = "1.0" proc-macro2 = "1.0" [dev-dependencies] trybuild = "1.0" static_assertions = "1.1" libc = { version = "0.2", default-features = false } rustversion = "1.0.0" pretty_assertions = "0.6.1" thiserror = "1.0.24" [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] [package.metadata.release] no-dev-version = true pre-release-hook = ["./update-readme.sh"] [[package.metadata.release.pre-release-replacements]] file = "CHANGELOG.md" search = "Unreleased" replace="{{version}}" [[package.metadata.release.pre-release-replacements]] file = "src/lib.rs" search = "#!\\[doc\\(html_root_url.*" replace = "#![doc(html_root_url = \"https://docs.rs/{{crate_name}}/{{version}}\")]" exactly = 1 [[package.metadata.release.pre-release-replacements]] file = "CHANGELOG.md" search = "ReleaseDate" replace="{{date}}" [[package.metadata.release.pre-release-replacements]] file="CHANGELOG.md" search="" replace="\n\n# [Unreleased] - ReleaseDate" exactly=1 # Disable this replacement on the very first release [[package.metadata.release.pre-release-replacements]] file = "CHANGELOG.md" search = "\\.\\.\\.HEAD" replace="...{{tag_name}}" exactly = 1 # END SECTION, do not comment out the replacement below this, and do not reorder them [[package.metadata.release.pre-release-replacements]] file="CHANGELOG.md" search="" replace="\n[Unreleased]: https://github.com/yaahc/{{crate_name}}/compare/{{tag_name}}...HEAD" exactly=1 displaydoc-0.2.5/LICENSE-APACHE000064400000000000000000000251371046102023000137170ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. displaydoc-0.2.5/LICENSE-MIT000064400000000000000000000017771046102023000134330ustar 00000000000000Permission 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. displaydoc-0.2.5/README.md000064400000000000000000000110101046102023000132330ustar 00000000000000derive(Display) /// `From` =============== [![Latest Version](https://img.shields.io/crates/v/displaydoc.svg)](https://crates.io/crates/displaydoc) [![Rust Documentation](https://img.shields.io/badge/api-rustdoc-blue.svg)](https://docs.rs/displaydoc) This library provides a convenient derive macro for the standard library's [`core::fmt::Display`] trait. [`core::fmt::Display`]: https://doc.rust-lang.org/std/fmt/trait.Display.html ```toml [dependencies] displaydoc = "0.2" ``` *Compiler support: requires rustc 1.56+*
### Example *Demonstration alongside the [`Error`][std::error::Error] derive macro from [`thiserror`](https://docs.rs/thiserror/1.0.25/thiserror/index.html), to propagate source locations from [`io::Error`][std::io::Error] with the `#[source]` attribute:* ```rust use std::io; use displaydoc::Display; use thiserror::Error; #[derive(Display, Error, Debug)] pub enum DataStoreError { /// data store disconnected Disconnect(#[source] io::Error), /// the data for key `{0}` is not available Redaction(String), /// invalid header (expected {expected:?}, found {found:?}) InvalidHeader { expected: String, found: String, }, /// unknown data store error Unknown, } let error = DataStoreError::Redaction("CLASSIFIED CONTENT".to_string()); assert!("the data for key `CLASSIFIED CONTENT` is not available" == &format!("{}", error)); ``` *Note that although [`io::Error`][std::io::Error] implements `Display`, we do not add it to the generated message for `DataStoreError::Disconnect`, since it is already made available via `#[source]`. See further context on avoiding duplication in error reports at the rust blog [here](https://github.com/yaahc/blog.rust-lang.org/blob/master/posts/inside-rust/2021-05-15-What-the-error-handling-project-group-is-working-towards.md#duplicate-information-issue).*
### Details - A `fmt::Display` impl is generated for your enum if you provide a docstring comment on each variant as shown above in the example. The `Display` derive macro supports a shorthand for interpolating fields from the error: - `/// {var}` ⟶ `write!("{}", self.var)` - `/// {0}` ⟶ `write!("{}", self.0)` - `/// {var:?}` ⟶ `write!("{:?}", self.var)` - `/// {0:?}` ⟶ `write!("{:?}", self.0)` - This also works with structs and [generic types][crate::Display#generic-type-parameters]: ```rust /// oh no, an error: {0} #[derive(Display)] pub struct Error(pub E); let error: Error<&str> = Error("muahaha i am an error"); assert!("oh no, an error: muahaha i am an error" == &format!("{}", error)); ``` - Two optional attributes can be added to your types next to the derive: - `#[ignore_extra_doc_attributes]` makes the macro ignore any doc comment attributes (or `///` lines) after the first. Multi-line comments using `///` are otherwise treated as an error, so use this attribute or consider switching to block doc comments (`/** */`). - `#[prefix_enum_doc_attributes]` combines the doc comment message on your enum itself with the messages for each variant, in the format “enum: variant”. When added to an enum, the doc comment on the enum becomes mandatory. When added to any other type, it has no effect. - In case you want to have an independent doc comment, the `#[displaydoc("...")` atrribute may be used on the variant or struct to override it.
### FAQ 1. **Is this crate `no_std` compatible?** * Yes! This crate implements the [`core::fmt::Display`] trait, not the [`std::fmt::Display`] trait, so it should work in `std` and `no_std` environments. Just add `default-features = false`. 2. **Does this crate work with `Path` and `PathBuf` via the `Display` trait?** * Yuuup. This crate uses @dtolnay's [autoref specialization technique](https://github.com/dtolnay/case-studies/blob/master/autoref-specialization/README.md) to add a special trait for types to get the display impl. It then specializes for `Path` and `PathBuf`, and when either of these types are found, it calls `self.display()` to get a `std::path::Display<'_>` type which can be used with the `Display` format specifier! #### License Licensed under either of Apache License, Version 2.0 or MIT license at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. displaydoc-0.2.5/README.tpl000064400000000000000000000012611046102023000134410ustar 00000000000000derive(Display) /// `From` =============== [![Latest Version](https://img.shields.io/crates/v/displaydoc.svg)](https://crates.io/crates/displaydoc) [![Rust Documentation](https://img.shields.io/badge/api-rustdoc-blue.svg)](https://docs.rs/displaydoc) {{readme}} #### License Licensed under either of Apache License, Version 2.0 or MIT license at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. displaydoc-0.2.5/examples/simple.rs000064400000000000000000000016741046102023000154500ustar 00000000000000use displaydoc::Display; #[derive(Debug, Display)] pub enum DataStoreError { /// data store disconnected Disconnect, /// the data for key `{0}` is not available Redaction(String), /// invalid header (expected {expected:?}, found {found:?}) InvalidHeader { expected: String, found: String }, /// unknown data store error Unknown, } fn main() { let disconnect = DataStoreError::Disconnect; println!( "Enum value `Disconnect` should be printed as:\n\t{}", disconnect ); let redaction = DataStoreError::Redaction(String::from("Dummy")); println!( "Enum value `Redaction` should be printed as:\n\t{}", redaction ); let invalid_header = DataStoreError::InvalidHeader { expected: String::from("https"), found: String::from("http"), }; println!( "Enum value `InvalidHeader` should be printed as:\n\t{}", invalid_header ); } displaydoc-0.2.5/src/attr.rs000064400000000000000000000102651046102023000140760ustar 00000000000000use proc_macro2::TokenStream; use quote::{quote, ToTokens}; use syn::{Attribute, LitStr, Meta, Result}; #[derive(Clone)] pub(crate) struct Display { pub(crate) fmt: LitStr, pub(crate) args: TokenStream, } pub(crate) struct VariantDisplay { pub(crate) r#enum: Option, pub(crate) variant: Display, } impl ToTokens for Display { fn to_tokens(&self, tokens: &mut TokenStream) { let fmt = &self.fmt; let args = &self.args; tokens.extend(quote! { write!(formatter, #fmt #args) }); } } impl ToTokens for VariantDisplay { fn to_tokens(&self, tokens: &mut TokenStream) { if let Some(ref r#enum) = self.r#enum { r#enum.to_tokens(tokens); tokens.extend(quote! { ?; write!(formatter, ": ")?; }); } self.variant.to_tokens(tokens); } } pub(crate) struct AttrsHelper { ignore_extra_doc_attributes: bool, prefix_enum_doc_attributes: bool, } impl AttrsHelper { pub(crate) fn new(attrs: &[Attribute]) -> Self { let ignore_extra_doc_attributes = attrs .iter() .any(|attr| attr.path().is_ident("ignore_extra_doc_attributes")); let prefix_enum_doc_attributes = attrs .iter() .any(|attr| attr.path().is_ident("prefix_enum_doc_attributes")); Self { ignore_extra_doc_attributes, prefix_enum_doc_attributes, } } pub(crate) fn display(&self, attrs: &[Attribute]) -> Result> { let displaydoc_attr = attrs.iter().find(|attr| attr.path().is_ident("displaydoc")); if let Some(displaydoc_attr) = displaydoc_attr { let lit = displaydoc_attr .parse_args() .expect("#[displaydoc(\"foo\")] must contain string arguments"); let mut display = Display { fmt: lit, args: TokenStream::new(), }; display.expand_shorthand(); return Ok(Some(display)); } let num_doc_attrs = attrs .iter() .filter(|attr| attr.path().is_ident("doc")) .count(); if !self.ignore_extra_doc_attributes && num_doc_attrs > 1 { panic!("Multi-line comments are disabled by default by displaydoc. Please consider using block doc comments (/** */) or adding the #[ignore_extra_doc_attributes] attribute to your type next to the derive."); } for attr in attrs { if attr.path().is_ident("doc") { let lit = match &attr.meta { Meta::NameValue(syn::MetaNameValue { value: syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(lit), .. }), .. }) => lit, _ => unimplemented!(), }; // Make an attempt at cleaning up multiline doc comments. let doc_str = lit .value() .lines() .map(|line| line.trim().trim_start_matches('*').trim()) .collect::>() .join("\n"); let lit = LitStr::new(doc_str.trim(), lit.span()); let mut display = Display { fmt: lit, args: TokenStream::new(), }; display.expand_shorthand(); return Ok(Some(display)); } } Ok(None) } pub(crate) fn display_with_input( &self, r#enum: &[Attribute], variant: &[Attribute], ) -> Result> { let r#enum = if self.prefix_enum_doc_attributes { let result = self .display(r#enum)? .expect("Missing doc comment on enum with #[prefix_enum_doc_attributes]. Please remove the attribute or add a doc comment to the enum itself."); Some(result) } else { None }; Ok(self .display(variant)? .map(|variant| VariantDisplay { r#enum, variant })) } } displaydoc-0.2.5/src/expand.rs000064400000000000000000000373741046102023000144150ustar 00000000000000use super::attr::AttrsHelper; use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote}; use syn::{ punctuated::Punctuated, token::{Colon, Comma, PathSep, Plus, Where}, Data, DataEnum, DataStruct, DeriveInput, Error, Fields, Generics, Ident, Path, PathArguments, PathSegment, PredicateType, Result, TraitBound, TraitBoundModifier, Type, TypeParam, TypeParamBound, TypePath, WhereClause, WherePredicate, }; use std::collections::HashMap; pub(crate) fn derive(input: &DeriveInput) -> Result { let impls = match &input.data { Data::Struct(data) => impl_struct(input, data), Data::Enum(data) => impl_enum(input, data), Data::Union(_) => Err(Error::new_spanned(input, "Unions are not supported")), }?; let helpers = specialization(); Ok(quote! { #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] const _: () = { #helpers #impls }; }) } #[cfg(feature = "std")] fn specialization() -> TokenStream { quote! { trait DisplayToDisplayDoc { fn __displaydoc_display(&self) -> Self; } impl DisplayToDisplayDoc for &T { fn __displaydoc_display(&self) -> Self { self } } // If the `std` feature gets enabled we want to ensure that any crate // using displaydoc can still reference the std crate, which is already // being compiled in by whoever enabled the `std` feature in // `displaydoc`, even if the crates using displaydoc are no_std. extern crate std; trait PathToDisplayDoc { fn __displaydoc_display(&self) -> std::path::Display<'_>; } impl PathToDisplayDoc for std::path::Path { fn __displaydoc_display(&self) -> std::path::Display<'_> { self.display() } } impl PathToDisplayDoc for std::path::PathBuf { fn __displaydoc_display(&self) -> std::path::Display<'_> { self.display() } } } } #[cfg(not(feature = "std"))] fn specialization() -> TokenStream { quote! {} } fn impl_struct(input: &DeriveInput, data: &DataStruct) -> Result { let ty = &input.ident; let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); let where_clause = generate_where_clause(&input.generics, where_clause); let helper = AttrsHelper::new(&input.attrs); let display = helper.display(&input.attrs)?.map(|display| { let pat = match &data.fields { Fields::Named(fields) => { let var = fields.named.iter().map(|field| &field.ident); quote!(Self { #(#var),* }) } Fields::Unnamed(fields) => { let var = (0..fields.unnamed.len()).map(|i| format_ident!("_{}", i)); quote!(Self(#(#var),*)) } Fields::Unit => quote!(_), }; quote! { impl #impl_generics ::core::fmt::Display for #ty #ty_generics #where_clause { fn fmt(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { // NB: This destructures the fields of `self` into named variables (for unnamed // fields, it uses _0, _1, etc as above). The `#[allow(unused_variables)]` // section means it doesn't have to parse the individual field references out of // the docstring. #[allow(unused_variables)] let #pat = self; #display } } } }); Ok(quote! { #display }) } /// Create a `where` predicate for `ident`, without any [bound][TypeParamBound]s yet. fn new_empty_where_type_predicate(ident: Ident) -> PredicateType { let mut path_segments = Punctuated::::new(); path_segments.push_value(PathSegment { ident, arguments: PathArguments::None, }); PredicateType { lifetimes: None, bounded_ty: Type::Path(TypePath { qself: None, path: Path { leading_colon: None, segments: path_segments, }, }), colon_token: Colon { spans: [Span::call_site()], }, bounds: Punctuated::::new(), } } /// Create a `where` clause that we can add [WherePredicate]s to. fn new_empty_where_clause() -> WhereClause { WhereClause { where_token: Where { span: Span::call_site(), }, predicates: Punctuated::::new(), } } enum UseGlobalPrefix { LeadingColon, #[allow(dead_code)] NoLeadingColon, } /// Create a path with segments composed of [Idents] *without* any [PathArguments]. fn join_paths(name_segments: &[&str], use_global_prefix: UseGlobalPrefix) -> Path { let mut segments = Punctuated::::new(); assert!(!name_segments.is_empty()); segments.push_value(PathSegment { ident: Ident::new(name_segments[0], Span::call_site()), arguments: PathArguments::None, }); for name in name_segments[1..].iter() { segments.push_punct(PathSep { spans: [Span::call_site(), Span::mixed_site()], }); segments.push_value(PathSegment { ident: Ident::new(name, Span::call_site()), arguments: PathArguments::None, }); } Path { leading_colon: match use_global_prefix { UseGlobalPrefix::LeadingColon => Some(PathSep { spans: [Span::call_site(), Span::mixed_site()], }), UseGlobalPrefix::NoLeadingColon => None, }, segments, } } /// Push `new_type_predicate` onto the end of `where_clause`. fn append_where_clause_type_predicate( where_clause: &mut WhereClause, new_type_predicate: PredicateType, ) { // Push a comma at the end if there are already any `where` predicates. if !where_clause.predicates.is_empty() { where_clause.predicates.push_punct(Comma { spans: [Span::call_site()], }); } where_clause .predicates .push_value(WherePredicate::Type(new_type_predicate)); } /// Add a requirement for [core::fmt::Display] to a `where` predicate for some type. fn add_display_constraint_to_type_predicate( predicate_that_needs_a_display_impl: &mut PredicateType, ) { // Create a `Path` of `::core::fmt::Display`. let display_path = join_paths(&["core", "fmt", "Display"], UseGlobalPrefix::LeadingColon); let display_bound = TypeParamBound::Trait(TraitBound { paren_token: None, modifier: TraitBoundModifier::None, lifetimes: None, path: display_path, }); if !predicate_that_needs_a_display_impl.bounds.is_empty() { predicate_that_needs_a_display_impl.bounds.push_punct(Plus { spans: [Span::call_site()], }); } predicate_that_needs_a_display_impl .bounds .push_value(display_bound); } /// Map each declared generic type parameter to the set of all trait boundaries declared on it. /// /// These boundaries may come from the declaration site: /// pub enum E { ... } /// or a `where` clause after the parameter declarations: /// pub enum E where T: MyTrait { ... } /// This method will return the boundaries from both of those cases. fn extract_trait_constraints_from_source( where_clause: &WhereClause, type_params: &[&TypeParam], ) -> HashMap> { // Add trait bounds provided at the declaration site of type parameters for the struct/enum. let mut param_constraint_mapping: HashMap> = type_params .iter() .map(|type_param| { let trait_bounds: Vec = type_param .bounds .iter() .flat_map(|bound| match bound { TypeParamBound::Trait(trait_bound) => Some(trait_bound), _ => None, }) .cloned() .collect(); (type_param.ident.clone(), trait_bounds) }) .collect(); // Add trait bounds from `where` clauses, which may be type parameters or types containing // those parameters. for predicate in where_clause.predicates.iter() { // We only care about type and not lifetime constraints here. if let WherePredicate::Type(ref pred_ty) = predicate { let ident = match &pred_ty.bounded_ty { Type::Path(TypePath { path, qself: None }) => match path.get_ident() { None => continue, Some(ident) => ident, }, _ => continue, }; // We ignore any type constraints that aren't direct references to type // parameters of the current enum of struct definition. No types can be // constrained in a `where` clause unless they are a type parameter or a generic // type instantiated with one of the type parameters, so by only allowing single // identifiers, we can be sure that the constrained type is a type parameter // that is contained in `param_constraint_mapping`. if let Some((_, ref mut known_bounds)) = param_constraint_mapping .iter_mut() .find(|(id, _)| *id == ident) { for bound in pred_ty.bounds.iter() { // We only care about trait bounds here. if let TypeParamBound::Trait(ref bound) = bound { known_bounds.push(bound.clone()); } } } } } param_constraint_mapping } /// Hygienically add `where _: Display` to the set of [TypeParamBound]s for `ident`, creating such /// a set if necessary. fn ensure_display_in_where_clause_for_type(where_clause: &mut WhereClause, ident: Ident) { for pred_ty in where_clause .predicates .iter_mut() // Find the `where` predicate constraining the current type param, if it exists. .flat_map(|predicate| match predicate { WherePredicate::Type(pred_ty) => Some(pred_ty), // We're looking through type constraints, not lifetime constraints. _ => None, }) { // Do a complicated destructuring in order to check if the type being constrained in this // `where` clause is the type we're looking for, so we can use the mutable reference to // `pred_ty` if so. let matches_desired_type = matches!( &pred_ty.bounded_ty, Type::Path(TypePath { path, .. }) if Some(&ident) == path.get_ident()); if matches_desired_type { add_display_constraint_to_type_predicate(pred_ty); return; } } // If there is no `where` predicate for the current type param, we will construct one. let mut new_type_predicate = new_empty_where_type_predicate(ident); add_display_constraint_to_type_predicate(&mut new_type_predicate); append_where_clause_type_predicate(where_clause, new_type_predicate); } /// For all declared type parameters, add a [core::fmt::Display] constraint, unless the type /// parameter already has any type constraint. fn ensure_where_clause_has_display_for_all_unconstrained_members( where_clause: &mut WhereClause, type_params: &[&TypeParam], ) { let param_constraint_mapping = extract_trait_constraints_from_source(where_clause, type_params); for (ident, known_bounds) in param_constraint_mapping.into_iter() { // If the type parameter has any constraints already, we don't want to touch it, to avoid // breaking use cases where a type parameter only needs to impl `Debug`, for example. if known_bounds.is_empty() { ensure_display_in_where_clause_for_type(where_clause, ident); } } } /// Generate a `where` clause that ensures all generic type parameters `impl` /// [core::fmt::Display] unless already constrained. /// /// This approach allows struct/enum definitions deriving [crate::Display] to avoid hardcoding /// a [core::fmt::Display] constraint into every type parameter. /// /// If the type parameter isn't already constrained, we add a `where _: Display` clause to our /// display implementation to expect to be able to format every enum case or struct member. /// /// In fact, we would preferably only require `where _: Display` or `where _: Debug` where the /// format string actually requires it. However, while [`std::fmt` defines a formal syntax for /// `format!()`][format syntax], it *doesn't* expose the actual logic to parse the format string, /// which appears to live in [`rustc_parse_format`]. While we use the [`syn`] crate to parse rust /// syntax, it also doesn't currently provide any method to introspect a `format!()` string. It /// would be nice to contribute this upstream in [`syn`]. /// /// [format syntax]: std::fmt#syntax /// [`rustc_parse_format`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse_format/index.html fn generate_where_clause(generics: &Generics, where_clause: Option<&WhereClause>) -> WhereClause { let mut where_clause = where_clause.cloned().unwrap_or_else(new_empty_where_clause); let type_params: Vec<&TypeParam> = generics.type_params().collect(); ensure_where_clause_has_display_for_all_unconstrained_members(&mut where_clause, &type_params); where_clause } fn impl_enum(input: &DeriveInput, data: &DataEnum) -> Result { let ty = &input.ident; let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); let where_clause = generate_where_clause(&input.generics, where_clause); let helper = AttrsHelper::new(&input.attrs); let displays = data .variants .iter() .map(|variant| helper.display_with_input(&input.attrs, &variant.attrs)) .collect::>>()?; if data.variants.is_empty() { Ok(quote! { impl #impl_generics ::core::fmt::Display for #ty #ty_generics #where_clause { fn fmt(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { unreachable!("empty enums cannot be instantiated and thus cannot be printed") } } }) } else if displays.iter().any(Option::is_some) { let arms = data .variants .iter() .zip(displays) .map(|(variant, display)| { let display = display.ok_or_else(|| Error::new_spanned(variant, "missing doc comment"))?; let ident = &variant.ident; Ok(match &variant.fields { Fields::Named(fields) => { let var = fields.named.iter().map(|field| &field.ident); quote!(Self::#ident { #(#var),* } => { #display }) } Fields::Unnamed(fields) => { let var = (0..fields.unnamed.len()).map(|i| format_ident!("_{}", i)); quote!(Self::#ident(#(#var),*) => { #display }) } Fields::Unit => quote!(Self::#ident => { #display }), }) }) .collect::>>()?; Ok(quote! { impl #impl_generics ::core::fmt::Display for #ty #ty_generics #where_clause { fn fmt(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { #[allow(unused_variables)] match self { #(#arms,)* } } } }) } else { Err(Error::new_spanned(input, "Missing doc comments")) } } displaydoc-0.2.5/src/fmt.rs000064400000000000000000000077641046102023000137240ustar 00000000000000use crate::attr::Display; use proc_macro2::TokenStream; use quote::quote_spanned; use syn::{Ident, LitStr}; macro_rules! peek_next { ($read:ident) => { match $read.chars().next() { Some(next) => next, None => return, } }; } impl Display { // Transform `"error {var}"` to `"error {}", var`. pub(crate) fn expand_shorthand(&mut self) { let span = self.fmt.span(); let fmt = self.fmt.value(); let mut read = fmt.as_str(); let mut out = String::new(); let mut args = TokenStream::new(); while let Some(brace) = read.find('{') { out += &read[..=brace]; read = &read[brace + 1..]; // skip cases where we find a {{ if read.starts_with('{') { out.push('{'); read = &read[1..]; continue; } let next = peek_next!(read); let var = match next { '0'..='9' => take_int(&mut read), 'a'..='z' | 'A'..='Z' | '_' => take_ident(&mut read), _ => return, }; let ident = Ident::new(&var, span); let next = peek_next!(read); let arg = if cfg!(feature = "std") && next == '}' { quote_spanned!(span=> , #ident.__displaydoc_display()) } else { quote_spanned!(span=> , #ident) }; args.extend(arg); } out += read; self.fmt = LitStr::new(&out, self.fmt.span()); self.args = args; } } fn take_int(read: &mut &str) -> String { let mut int = String::new(); int.push('_'); for (i, ch) in read.char_indices() { match ch { '0'..='9' => int.push(ch), _ => { *read = &read[i..]; break; } } } int } fn take_ident(read: &mut &str) -> String { let mut ident = String::new(); for (i, ch) in read.char_indices() { match ch { 'a'..='z' | 'A'..='Z' | '0'..='9' | '_' => ident.push(ch), _ => { *read = &read[i..]; break; } } } ident } #[cfg(test)] mod tests { use super::*; use pretty_assertions::assert_eq; use proc_macro2::Span; fn assert(input: &str, fmt: &str, args: &str) { let mut display = Display { fmt: LitStr::new(input, Span::call_site()), args: TokenStream::new(), }; display.expand_shorthand(); assert_eq!(fmt, display.fmt.value()); assert_eq!(args, display.args.to_string()); } #[test] fn test_expand() { assert("fn main() {{ }}", "fn main() {{ }}", ""); } #[test] #[cfg_attr(not(feature = "std"), ignore)] fn test_std_expand() { assert( "{v} {v:?} {0} {0:?}", "{} {:?} {} {:?}", ", v . __displaydoc_display () , v , _0 . __displaydoc_display () , _0", ); assert("error {var}", "error {}", ", var . __displaydoc_display ()"); assert( "error {var1}", "error {}", ", var1 . __displaydoc_display ()", ); assert( "error {var1var}", "error {}", ", var1var . __displaydoc_display ()", ); assert( "The path {0}", "The path {}", ", _0 . __displaydoc_display ()", ); assert("The path {0:?}", "The path {:?}", ", _0"); } #[test] #[cfg_attr(feature = "std", ignore)] fn test_nostd_expand() { assert( "{v} {v:?} {0} {0:?}", "{} {:?} {} {:?}", ", v , v , _0 , _0", ); assert("error {var}", "error {}", ", var"); assert("The path {0}", "The path {}", ", _0"); assert("The path {0:?}", "The path {:?}", ", _0"); assert("error {var1}", "error {}", ", var1"); assert("error {var1var}", "error {}", ", var1var"); } } displaydoc-0.2.5/src/lib.rs000064400000000000000000000165631046102023000137010ustar 00000000000000//! This library provides a convenient derive macro for the standard library's //! [`core::fmt::Display`] trait. //! //! [`core::fmt::Display`]: https://doc.rust-lang.org/std/fmt/trait.Display.html //! //! ```toml //! [dependencies] //! displaydoc = "0.2" //! ``` //! //! *Compiler support: requires rustc 1.56+* //! //!
//! //! ## Example //! //! *Demonstration alongside the [`Error`][std::error::Error] derive macro from [`thiserror`](https://docs.rs/thiserror/1.0.25/thiserror/index.html), //! to propagate source locations from [`io::Error`][std::io::Error] with the `#[source]` attribute:* //! ```rust //! use std::io; //! use displaydoc::Display; //! use thiserror::Error; //! //! #[derive(Display, Error, Debug)] //! pub enum DataStoreError { //! /// data store disconnected //! Disconnect(#[source] io::Error), //! /// the data for key `{0}` is not available //! Redaction(String), //! /// invalid header (expected {expected:?}, found {found:?}) //! InvalidHeader { //! expected: String, //! found: String, //! }, //! /// unknown data store error //! Unknown, //! } //! //! let error = DataStoreError::Redaction("CLASSIFIED CONTENT".to_string()); //! assert!("the data for key `CLASSIFIED CONTENT` is not available" == &format!("{}", error)); //! ``` //! *Note that although [`io::Error`][std::io::Error] implements `Display`, we do not add it to the //! generated message for `DataStoreError::Disconnect`, since it is already made available via //! `#[source]`. See further context on avoiding duplication in error reports at the rust blog //! [here](https://github.com/yaahc/blog.rust-lang.org/blob/master/posts/inside-rust/2021-05-15-What-the-error-handling-project-group-is-working-towards.md#duplicate-information-issue).* //! //!
//! //! ## Details //! //! - A `fmt::Display` impl is generated for your enum if you provide //! a docstring comment on each variant as shown above in the example. The //! `Display` derive macro supports a shorthand for interpolating fields from //! the error: //! - `/// {var}` ⟶ `write!("{}", self.var)` //! - `/// {0}` ⟶ `write!("{}", self.0)` //! - `/// {var:?}` ⟶ `write!("{:?}", self.var)` //! - `/// {0:?}` ⟶ `write!("{:?}", self.0)` //! - This also works with structs and [generic types][crate::Display#generic-type-parameters]: //! ```rust //! # use displaydoc::Display; //! /// oh no, an error: {0} //! #[derive(Display)] //! pub struct Error(pub E); //! //! let error: Error<&str> = Error("muahaha i am an error"); //! assert!("oh no, an error: muahaha i am an error" == &format!("{}", error)); //! ``` //! //! - Two optional attributes can be added to your types next to the derive: //! //! - `#[ignore_extra_doc_attributes]` makes the macro ignore any doc //! comment attributes (or `///` lines) after the first. Multi-line //! comments using `///` are otherwise treated as an error, so use this //! attribute or consider switching to block doc comments (`/** */`). //! //! - `#[prefix_enum_doc_attributes]` combines the doc comment message on //! your enum itself with the messages for each variant, in the format //! “enum: variant”. When added to an enum, the doc comment on the enum //! becomes mandatory. When added to any other type, it has no effect. //! //! - In case you want to have an independent doc comment, the //! `#[displaydoc("...")` atrribute may be used on the variant or struct to //! override it. //! //!
//! //! ## FAQ //! //! 1. **Is this crate `no_std` compatible?** //! * Yes! This crate implements the [`core::fmt::Display`] trait, not the [`std::fmt::Display`] trait, so it should work in `std` and `no_std` environments. Just add `default-features = false`. //! //! 2. **Does this crate work with `Path` and `PathBuf` via the `Display` trait?** //! * Yuuup. This crate uses @dtolnay's [autoref specialization technique](https://github.com/dtolnay/case-studies/blob/master/autoref-specialization/README.md) to add a special trait for types to get the display impl. It then specializes for `Path` and `PathBuf`, and when either of these types are found, it calls `self.display()` to get a `std::path::Display<'_>` type which can be used with the `Display` format specifier! #![doc(html_root_url = "https://docs.rs/displaydoc/0.2.3")] #![cfg_attr(docsrs, feature(doc_cfg))] #![warn( rust_2018_idioms, unreachable_pub, bad_style, dead_code, improper_ctypes, non_shorthand_field_patterns, no_mangle_generic_items, overflowing_literals, path_statements, patterns_in_fns_without_body, unconditional_recursion, unused, unused_allocation, unused_comparisons, unused_parens, while_true )] #![allow(clippy::try_err)] #[allow(unused_extern_crates)] extern crate proc_macro; mod attr; mod expand; mod fmt; use proc_macro::TokenStream; use syn::{parse_macro_input, DeriveInput}; /// [Custom `#[derive(...)]` macro](https://doc.rust-lang.org/edition-guide/rust-2018/macros/custom-derive.html) /// for implementing [`fmt::Display`][core::fmt::Display] via doc comment attributes. /// /// ### Generic Type Parameters /// /// Type parameters to an enum or struct using this macro should *not* need to /// have an explicit `Display` constraint at the struct or enum definition /// site. A `Display` implementation for the `derive`d struct or enum is /// generated assuming each type parameter implements `Display`, but that should /// be possible without adding the constraint to the struct definition itself: /// ```rust /// use displaydoc::Display; /// /// /// oh no, an error: {0} /// #[derive(Display)] /// pub struct Error(pub E); /// /// // No need to require `E: Display`, since `displaydoc::Display` adds that implicitly. /// fn generate_error(e: E) -> Error { Error(e) } /// /// assert!("oh no, an error: muahaha" == &format!("{}", generate_error("muahaha"))); /// ``` /// /// ### Using [`Debug`][core::fmt::Debug] Implementations with Type Parameters /// However, if a type parameter must instead be constrained with the /// [`Debug`][core::fmt::Debug] trait so that some field may be printed with /// `{:?}`, that constraint must currently still also be specified redundantly /// at the struct or enum definition site. If a struct or enum field is being /// formatted with `{:?}` via [`displaydoc`][crate], and a generic type /// parameter must implement `Debug` to do that, then that struct or enum /// definition will need to propagate the `Debug` constraint to every type /// parameter it's instantiated with: /// ```rust /// use core::fmt::Debug; /// use displaydoc::Display; /// /// /// oh no, an error: {0:?} /// #[derive(Display)] /// pub struct Error(pub E); /// /// // `E: Debug` now has to propagate to callers. /// fn generate_error(e: E) -> Error { Error(e) } /// /// assert!("oh no, an error: \"cool\"" == &format!("{}", generate_error("cool"))); /// /// // Try this with a struct that doesn't impl `Display` at all, unlike `str`. /// #[derive(Debug)] /// pub struct Oh; /// assert!("oh no, an error: Oh" == &format!("{}", generate_error(Oh))); /// ``` #[proc_macro_derive( Display, attributes(ignore_extra_doc_attributes, prefix_enum_doc_attributes, displaydoc) )] pub fn derive_error(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); expand::derive(&input) .unwrap_or_else(|err| err.to_compile_error()) .into() } displaydoc-0.2.5/tests/compile_tests.rs000064400000000000000000000017741046102023000163560ustar 00000000000000#[allow(unused_attributes)] #[rustversion::attr(not(nightly), ignore)] #[test] fn no_std() { let t = trybuild::TestCases::new(); #[cfg(not(feature = "std"))] t.compile_fail("tests/no_std/without.rs"); #[cfg(not(feature = "std"))] t.compile_fail("tests/no_std/multi_line.rs"); #[cfg(not(feature = "std"))] t.pass("tests/no_std/multi_line_allow.rs"); #[cfg(not(feature = "std"))] t.compile_fail("tests/no_std/enum_prefix_missing.rs"); #[cfg(not(feature = "std"))] t.pass("tests/no_std/enum_prefix.rs"); #[cfg(feature = "std")] t.compile_fail("tests/std/without.rs"); #[cfg(feature = "std")] t.compile_fail("tests/std/multi_line.rs"); #[cfg(feature = "std")] t.pass("tests/std/multi_line_allow.rs"); #[cfg(feature = "std")] t.compile_fail("tests/std/enum_prefix_missing.rs"); #[cfg(feature = "std")] t.pass("tests/std/enum_prefix.rs"); #[cfg(feature = "std")] t.pass("tests/std/multiple.rs"); t.pass("tests/no_std/with.rs"); } displaydoc-0.2.5/tests/happy.rs000064400000000000000000000076121046102023000146220ustar 00000000000000use displaydoc::Display; #[cfg(feature = "std")] use std::path::PathBuf; #[derive(Display)] /// Just a basic struct {thing} struct HappyStruct { thing: &'static str, } #[derive(Display)] #[ignore_extra_doc_attributes] /// Just a basic struct {thing} /// and this line should get ignored struct HappyStruct2 { thing: &'static str, } #[derive(Display)] enum Happy { /// I really like Variant1 Variant1, /// Variant2 is pretty swell 2 Variant2, /// Variant3 is okay {sometimes} Variant3 { sometimes: &'static str }, /** * Variant4 wants to have a lot of lines * * Lets see how this works out for it */ Variant4, /// Variant5 has a parameter {0} and some regular comments // A regular comment that won't get picked Variant5(u32), /// The path {0} #[cfg(feature = "std")] Variant6(PathBuf), /// These docs are ignored #[displaydoc("Variant7 has a parameter {0} and uses #[displaydoc]")] /// These docs are also ignored Variant7(u32), } // Used for testing indented doc comments mod inner_mod { use super::Display; #[derive(Display)] pub enum InnerHappy { /// I really like Variant1 Variant1, /// Variant2 is pretty swell 2 Variant2, /// Variant3 is okay {sometimes} Variant3 { sometimes: &'static str }, /** * Variant4 wants to have a lot of lines * * Lets see how this works out for it */ Variant4, /// Variant5 has a parameter {0} and some regular comments // A regular comment that won't get picked Variant5(u32), /** what happens if we * put text on the first line? */ Variant6, /** what happens if we don't use *? */ Variant7, /** * * what about extra new lines? */ Variant8, } } fn assert_display(input: T, expected: &'static str) { let out = format!("{}", input); assert_eq!(expected, out); } #[test] fn does_it_print() { assert_display(Happy::Variant1, "I really like Variant1"); assert_display(Happy::Variant2, "Variant2 is pretty swell 2"); assert_display(Happy::Variant3 { sometimes: "hi" }, "Variant3 is okay hi"); assert_display( Happy::Variant4, "Variant4 wants to have a lot of lines\n\nLets see how this works out for it", ); assert_display( Happy::Variant5(2), "Variant5 has a parameter 2 and some regular comments", ); assert_display( Happy::Variant7(2), "Variant7 has a parameter 2 and uses #[displaydoc]", ); assert_display(HappyStruct { thing: "hi" }, "Just a basic struct hi"); assert_display(HappyStruct2 { thing: "hi2" }, "Just a basic struct hi2"); assert_display(inner_mod::InnerHappy::Variant1, "I really like Variant1"); assert_display( inner_mod::InnerHappy::Variant2, "Variant2 is pretty swell 2", ); assert_display( inner_mod::InnerHappy::Variant3 { sometimes: "hi" }, "Variant3 is okay hi", ); assert_display( inner_mod::InnerHappy::Variant4, "Variant4 wants to have a lot of lines\n\nLets see how this works out for it", ); assert_display( inner_mod::InnerHappy::Variant5(2), "Variant5 has a parameter 2 and some regular comments", ); assert_display( inner_mod::InnerHappy::Variant6, "what happens if we\nput text on the first line?", ); assert_display( inner_mod::InnerHappy::Variant7, "what happens if we don\'t use *?", ); assert_display( inner_mod::InnerHappy::Variant8, "what about extra new lines?", ); } #[test] #[cfg(feature = "std")] fn does_it_print_path() { assert_display( Happy::Variant6(PathBuf::from("/var/log/happy")), "The path /var/log/happy", ); } displaydoc-0.2.5/tests/no_std/enum_prefix.rs000064400000000000000000000014461046102023000173070ustar 00000000000000#![cfg_attr(not(feature = "std"), allow(internal_features), feature(lang_items, start))] #![cfg_attr(not(feature = "std"), no_std)] #[cfg_attr(not(feature = "std"), start)] fn start(_argc: isize, _argv: *const *const u8) -> isize { 0 } #[lang = "eh_personality"] #[no_mangle] #[cfg(not(feature = "std"))] pub extern "C" fn rust_eh_personality() {} #[panic_handler] #[cfg(not(feature = "std"))] fn panic(_info: &core::panic::PanicInfo) -> ! { unsafe { libc::abort(); } } use displaydoc::Display; /// this type is pretty swell #[derive(Display)] #[prefix_enum_doc_attributes] enum TestType { /// this variant is too Variant1, /// this variant is two Variant2, } static_assertions::assert_impl_all!(TestType: core::fmt::Display); #[cfg(feature = "std")] fn main() {} displaydoc-0.2.5/tests/no_std/enum_prefix_missing.rs000064400000000000000000000014101046102023000210270ustar 00000000000000#![cfg_attr(not(feature = "std"), allow(internal_features), feature(lang_items, start))] #![cfg_attr(not(feature = "std"), no_std)] #[cfg_attr(not(feature = "std"), start)] fn start(_argc: isize, _argv: *const *const u8) -> isize { 0 } #[lang = "eh_personality"] #[no_mangle] #[cfg(not(feature = "std"))] pub extern "C" fn rust_eh_personality() {} #[panic_handler] #[cfg(not(feature = "std"))] fn panic(_info: &core::panic::PanicInfo) -> ! { unsafe { libc::abort(); } } use displaydoc::Display; #[derive(Display)] #[prefix_enum_doc_attributes] enum TestType { /// this variant is too Variant1, /// this variant is two Variant2, } static_assertions::assert_impl_all!(TestType: core::fmt::Display); #[cfg(feature = "std")] fn main() {} displaydoc-0.2.5/tests/no_std/enum_prefix_missing.stderr000064400000000000000000000021771046102023000217210ustar 00000000000000error: proc-macro derive panicked --> $DIR/enum_prefix_missing.rs:22:10 | 22 | #[derive(Display)] | ^^^^^^^ | = help: message: Missing doc comment on enum with #[prefix_enum_doc_attributes]. Please remove the attribute or add a doc comment to the enum itself. error[E0277]: `TestType` doesn't implement `Display` --> $DIR/enum_prefix_missing.rs:32:37 | 32 | static_assertions::assert_impl_all!(TestType: core::fmt::Display); | ^^^^^^^^ `TestType` cannot be formatted with the default formatter | = help: the trait `Display` is not implemented for `TestType` = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead note: required by a bound in `assert_impl_all` --> $DIR/enum_prefix_missing.rs:32:1 | 32 | static_assertions::assert_impl_all!(TestType: core::fmt::Display); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_impl_all` = note: this error originates in the macro `static_assertions::assert_impl_all` (in Nightly builds, run with -Z macro-backtrace for more info) displaydoc-0.2.5/tests/no_std/multi_line.rs000064400000000000000000000014211046102023000171200ustar 00000000000000#![cfg_attr(not(feature = "std"), allow(internal_features), feature(lang_items, start))] #![cfg_attr(not(feature = "std"), no_std)] #[cfg_attr(not(feature = "std"), start)] fn start(_argc: isize, _argv: *const *const u8) -> isize { 0 } #[lang = "eh_personality"] #[no_mangle] #[cfg(not(feature = "std"))] pub extern "C" fn rust_eh_personality() {} #[panic_handler] #[cfg(not(feature = "std"))] fn panic(_info: &core::panic::PanicInfo) -> ! { unsafe { libc::abort(); } } use displaydoc::Display; /// this type is pretty swell #[derive(Display)] enum TestType { /// This one is okay Variant1, /// Multi /// line /// doc. Variant2, } static_assertions::assert_impl_all!(TestType: core::fmt::Display); #[cfg(feature = "std")] fn main() {} displaydoc-0.2.5/tests/no_std/multi_line.stderr000064400000000000000000000022441046102023000200030ustar 00000000000000error: proc-macro derive panicked --> $DIR/multi_line.rs:23:10 | 23 | #[derive(Display)] | ^^^^^^^ | = help: message: Multi-line comments are disabled by default by displaydoc. Please consider using block doc comments (/** */) or adding the #[ignore_extra_doc_attributes] attribute to your type next to the derive. error[E0277]: `TestType` doesn't implement `Display` --> $DIR/multi_line.rs:34:37 | 34 | static_assertions::assert_impl_all!(TestType: core::fmt::Display); | ^^^^^^^^ `TestType` cannot be formatted with the default formatter | = help: the trait `Display` is not implemented for `TestType` = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead note: required by a bound in `assert_impl_all` --> $DIR/multi_line.rs:34:1 | 34 | static_assertions::assert_impl_all!(TestType: core::fmt::Display); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_impl_all` = note: this error originates in the macro `static_assertions::assert_impl_all` (in Nightly builds, run with -Z macro-backtrace for more info) displaydoc-0.2.5/tests/no_std/multi_line_allow.rs000064400000000000000000000014601046102023000203210ustar 00000000000000#![cfg_attr(not(feature = "std"), allow(internal_features), feature(lang_items, start))] #![cfg_attr(not(feature = "std"), no_std)] #[cfg_attr(not(feature = "std"), start)] fn start(_argc: isize, _argv: *const *const u8) -> isize { 0 } #[lang = "eh_personality"] #[no_mangle] #[cfg(not(feature = "std"))] pub extern "C" fn rust_eh_personality() {} #[panic_handler] #[cfg(not(feature = "std"))] fn panic(_info: &core::panic::PanicInfo) -> ! { unsafe { libc::abort(); } } use displaydoc::Display; /// this type is pretty swell #[derive(Display)] #[ignore_extra_doc_attributes] enum TestType { /// This one is okay Variant1, /// Multi /// line /// doc. Variant2, } static_assertions::assert_impl_all!(TestType: core::fmt::Display); #[cfg(feature = "std")] fn main() {} displaydoc-0.2.5/tests/no_std/with.rs000064400000000000000000000011271046102023000157350ustar 00000000000000#![feature(lang_items, start)] #![no_std] #[start] #[cfg(not(feature = "std"))] fn start(_argc: isize, _argv: *const *const u8) -> isize { 0 } #[lang = "eh_personality"] #[no_mangle] #[cfg(not(feature = "std"))] pub extern "C" fn rust_eh_personality() {} #[panic_handler] #[cfg(not(feature = "std"))] fn panic(_info: &core::panic::PanicInfo) -> ! { unsafe { libc::abort(); } } #[cfg(feature = "std")] fn main() {} use displaydoc::Display; /// this type is pretty swell #[derive(Display)] struct FakeType; static_assertions::assert_impl_all!(FakeType: core::fmt::Display); displaydoc-0.2.5/tests/no_std/without.rs000064400000000000000000000012371046102023000164670ustar 00000000000000#![cfg_attr(not(feature = "std"), allow(internal_features), feature(lang_items, start))] #![cfg_attr(not(feature = "std"), no_std)] #[cfg_attr(not(feature = "std"), start)] fn start(_argc: isize, _argv: *const *const u8) -> isize { 0 } #[lang = "eh_personality"] #[no_mangle] #[cfg(not(feature = "std"))] pub extern "C" fn rust_eh_personality() {} #[panic_handler] #[cfg(not(feature = "std"))] fn panic(_info: &core::panic::PanicInfo) -> ! { unsafe { libc::abort(); } } use displaydoc::Display; /// this type is pretty swell struct FakeType; static_assertions::assert_impl_all!(FakeType: core::fmt::Display); #[cfg(feature = "std")] fn main() {} displaydoc-0.2.5/tests/no_std/without.stderr000064400000000000000000000020151046102023000173410ustar 00000000000000warning: unused import: `displaydoc::Display` --> $DIR/without.rs:20:5 | 20 | use displaydoc::Display; | ^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(unused_imports)]` on by default error[E0277]: `FakeType` doesn't implement `Display` --> $DIR/without.rs:25:37 | 25 | static_assertions::assert_impl_all!(FakeType: core::fmt::Display); | ^^^^^^^^ `FakeType` cannot be formatted with the default formatter | = help: the trait `Display` is not implemented for `FakeType` = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead note: required by a bound in `assert_impl_all` --> $DIR/without.rs:25:1 | 25 | static_assertions::assert_impl_all!(FakeType: core::fmt::Display); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_impl_all` = note: this error originates in the macro `static_assertions::assert_impl_all` (in Nightly builds, run with -Z macro-backtrace for more info) displaydoc-0.2.5/tests/num_in_field.rs000064400000000000000000000006451046102023000161300ustar 00000000000000/// {foo1} {foo2} #[derive(displaydoc::Display)] pub struct Test { foo1: String, foo2: String, } fn assert_display(input: T, expected: &'static str) { let out = format!("{}", input); assert_eq!(expected, out); } #[test] fn does_it_print() { assert_display( Test { foo1: "hi".into(), foo2: "hello".into(), }, "hi hello", ); } displaydoc-0.2.5/tests/std/enum_prefix.rs000064400000000000000000000014141046102023000166060ustar 00000000000000#![cfg_attr(not(feature = "std"), feature(lang_items, start))] #![cfg_attr(not(feature = "std"), no_std)] #[cfg_attr(not(feature = "std"), start)] fn start(_argc: isize, _argv: *const *const u8) -> isize { 0 } #[lang = "eh_personality"] #[no_mangle] #[cfg(not(feature = "std"))] pub extern "C" fn rust_eh_personality() {} #[panic_handler] #[cfg(not(feature = "std"))] fn panic(_info: &core::panic::PanicInfo) -> ! { unsafe { libc::abort(); } } use displaydoc::Display; /// this type is pretty swell #[derive(Display)] #[prefix_enum_doc_attributes] enum TestType { /// this variant is too Variant1, /// this variant is two Variant2, } static_assertions::assert_impl_all!(TestType: core::fmt::Display); #[cfg(feature = "std")] fn main() {} displaydoc-0.2.5/tests/std/enum_prefix_missing.rs000064400000000000000000000013561046102023000203440ustar 00000000000000#![cfg_attr(not(feature = "std"), feature(lang_items, start))] #![cfg_attr(not(feature = "std"), no_std)] #[cfg_attr(not(feature = "std"), start)] fn start(_argc: isize, _argv: *const *const u8) -> isize { 0 } #[lang = "eh_personality"] #[no_mangle] #[cfg(not(feature = "std"))] pub extern "C" fn rust_eh_personality() {} #[panic_handler] #[cfg(not(feature = "std"))] fn panic(_info: &core::panic::PanicInfo) -> ! { unsafe { libc::abort(); } } use displaydoc::Display; #[derive(Display)] #[prefix_enum_doc_attributes] enum TestType { /// this variant is too Variant1, /// this variant is two Variant2, } static_assertions::assert_impl_all!(TestType: core::fmt::Display); #[cfg(feature = "std")] fn main() {} displaydoc-0.2.5/tests/std/enum_prefix_missing.stderr000064400000000000000000000022231046102023000212150ustar 00000000000000error: proc-macro derive panicked --> $DIR/enum_prefix_missing.rs:22:10 | 22 | #[derive(Display)] | ^^^^^^^ | = help: message: Missing doc comment on enum with #[prefix_enum_doc_attributes]. Please remove the attribute or add a doc comment to the enum itself. error[E0277]: `TestType` doesn't implement `std::fmt::Display` --> $DIR/enum_prefix_missing.rs:32:37 | 32 | static_assertions::assert_impl_all!(TestType: core::fmt::Display); | ^^^^^^^^ `TestType` cannot be formatted with the default formatter | = help: the trait `std::fmt::Display` is not implemented for `TestType` = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead note: required by a bound in `assert_impl_all` --> $DIR/enum_prefix_missing.rs:32:1 | 32 | static_assertions::assert_impl_all!(TestType: core::fmt::Display); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_impl_all` = note: this error originates in the macro `static_assertions::assert_impl_all` (in Nightly builds, run with -Z macro-backtrace for more info) displaydoc-0.2.5/tests/std/multi_line.rs000064400000000000000000000014211046102023000164240ustar 00000000000000#![cfg_attr(not(feature = "std"), allow(internal_features), feature(lang_items, start))] #![cfg_attr(not(feature = "std"), no_std)] #[cfg_attr(not(feature = "std"), start)] fn start(_argc: isize, _argv: *const *const u8) -> isize { 0 } #[lang = "eh_personality"] #[no_mangle] #[cfg(not(feature = "std"))] pub extern "C" fn rust_eh_personality() {} #[panic_handler] #[cfg(not(feature = "std"))] fn panic(_info: &core::panic::PanicInfo) -> ! { unsafe { libc::abort(); } } use displaydoc::Display; /// this type is pretty swell #[derive(Display)] enum TestType { /// This one is okay Variant1, /// Multi /// line /// doc. Variant2, } static_assertions::assert_impl_all!(TestType: core::fmt::Display); #[cfg(feature = "std")] fn main() {} displaydoc-0.2.5/tests/std/multi_line.stderr000064400000000000000000000022701046102023000173060ustar 00000000000000error: proc-macro derive panicked --> $DIR/multi_line.rs:23:10 | 23 | #[derive(Display)] | ^^^^^^^ | = help: message: Multi-line comments are disabled by default by displaydoc. Please consider using block doc comments (/** */) or adding the #[ignore_extra_doc_attributes] attribute to your type next to the derive. error[E0277]: `TestType` doesn't implement `std::fmt::Display` --> $DIR/multi_line.rs:34:37 | 34 | static_assertions::assert_impl_all!(TestType: core::fmt::Display); | ^^^^^^^^ `TestType` cannot be formatted with the default formatter | = help: the trait `std::fmt::Display` is not implemented for `TestType` = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead note: required by a bound in `assert_impl_all` --> $DIR/multi_line.rs:34:1 | 34 | static_assertions::assert_impl_all!(TestType: core::fmt::Display); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_impl_all` = note: this error originates in the macro `static_assertions::assert_impl_all` (in Nightly builds, run with -Z macro-backtrace for more info) displaydoc-0.2.5/tests/std/multi_line_allow.rs000064400000000000000000000014261046102023000176270ustar 00000000000000#![cfg_attr(not(feature = "std"), feature(lang_items, start))] #![cfg_attr(not(feature = "std"), no_std)] #[cfg_attr(not(feature = "std"), start)] fn start(_argc: isize, _argv: *const *const u8) -> isize { 0 } #[lang = "eh_personality"] #[no_mangle] #[cfg(not(feature = "std"))] pub extern "C" fn rust_eh_personality() {} #[panic_handler] #[cfg(not(feature = "std"))] fn panic(_info: &core::panic::PanicInfo) -> ! { unsafe { libc::abort(); } } use displaydoc::Display; /// this type is pretty swell #[derive(Display)] #[ignore_extra_doc_attributes] enum TestType { /// This one is okay Variant1, /// Multi /// line /// doc. Variant2, } static_assertions::assert_impl_all!(TestType: core::fmt::Display); #[cfg(feature = "std")] fn main() {} displaydoc-0.2.5/tests/std/multiple.rs000064400000000000000000000013411046102023000161170ustar 00000000000000#![feature(lang_items, start)] #![no_std] #[start] #[cfg(not(feature = "std"))] fn start(_argc: isize, _argv: *const *const u8) -> isize { 0 } #[lang = "eh_personality"] #[no_mangle] #[cfg(not(feature = "std"))] pub extern "C" fn rust_eh_personality() {} #[panic_handler] #[cfg(not(feature = "std"))] fn panic(_info: &core::panic::PanicInfo) -> ! { unsafe { libc::abort(); } } #[cfg(feature = "std")] fn main() {} use displaydoc::Display; /// this type is pretty swell #[derive(Display)] struct FakeType; static_assertions::assert_impl_all!(FakeType: core::fmt::Display); /// this type is pretty swell2 #[derive(Display)] struct FakeType2; static_assertions::assert_impl_all!(FakeType2: core::fmt::Display); displaydoc-0.2.5/tests/std/without.rs000064400000000000000000000012371046102023000157730ustar 00000000000000#![cfg_attr(not(feature = "std"), allow(internal_features), feature(lang_items, start))] #![cfg_attr(not(feature = "std"), no_std)] #[cfg_attr(not(feature = "std"), start)] fn start(_argc: isize, _argv: *const *const u8) -> isize { 0 } #[lang = "eh_personality"] #[no_mangle] #[cfg(not(feature = "std"))] pub extern "C" fn rust_eh_personality() {} #[panic_handler] #[cfg(not(feature = "std"))] fn panic(_info: &core::panic::PanicInfo) -> ! { unsafe { libc::abort(); } } use displaydoc::Display; /// this type is pretty swell struct FakeType; static_assertions::assert_impl_all!(FakeType: core::fmt::Display); #[cfg(feature = "std")] fn main() {} displaydoc-0.2.5/tests/std/without.stderr000064400000000000000000000020411046102023000166440ustar 00000000000000warning: unused import: `displaydoc::Display` --> $DIR/without.rs:20:5 | 20 | use displaydoc::Display; | ^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(unused_imports)]` on by default error[E0277]: `FakeType` doesn't implement `std::fmt::Display` --> $DIR/without.rs:25:37 | 25 | static_assertions::assert_impl_all!(FakeType: core::fmt::Display); | ^^^^^^^^ `FakeType` cannot be formatted with the default formatter | = help: the trait `std::fmt::Display` is not implemented for `FakeType` = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead note: required by a bound in `assert_impl_all` --> $DIR/without.rs:25:1 | 25 | static_assertions::assert_impl_all!(FakeType: core::fmt::Display); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `assert_impl_all` = note: this error originates in the macro `static_assertions::assert_impl_all` (in Nightly builds, run with -Z macro-backtrace for more info) displaydoc-0.2.5/tests/variantless.rs000064400000000000000000000002101046102023000160170ustar 00000000000000use displaydoc::Display; #[derive(Display)] enum EmptyInside {} static_assertions::assert_impl_all!(EmptyInside: core::fmt::Display); displaydoc-0.2.5/update-readme.sh000075500000000000000000000001531046102023000150360ustar 00000000000000#! /usr/bin/env bash cargo readme > ./README.md git add ./README.md git commit -m "Update readme" || true