instability-0.3.9/.cargo_vcs_info.json0000644000000001360000000000100134000ustar { "git": { "sha1": "4cf23b4370a28b03ab2581b9634f09503e263890" }, "path_in_vcs": "" }instability-0.3.9/.editorconfig000064400000000000000000000002251046102023000146440ustar 00000000000000[*] indent_style = space indent_size = 4 charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [*.{yaml,yml}] indent_size = 2 instability-0.3.9/.github/workflows/check.yml000064400000000000000000000043061046102023000173700ustar 00000000000000name: Check on: push: branches: - main pull_request: env: CARGO_TERM_COLOR: always # ensure that the workflow is only triggered once per PR, subsequent pushes to the PR will cancel # and restart the workflow. See https://docs.github.com/en/actions/using-jobs/using-concurrency concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true jobs: rustfmt: name: Rustfmt runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Install Rust stable uses: dtolnay/rust-toolchain@stable with: components: rustfmt - name: Run cargo fmt run: cargo fmt -- --check - name: Cache Cargo dependencies uses: Swatinem/rust-cache@v2 clippy: name: Clippy (${{ matrix.toolchain }}) runs-on: ubuntu-latest permissions: checks: write strategy: fail-fast: false matrix: # Get early warnings about new lints introduced in the beta channel toolchain: [stable, beta] steps: - name: Checkout uses: actions/checkout@v4 - name: Install Rust stable uses: dtolnay/rust-toolchain@stable with: components: clippy - name: Run clippy action uses: clechasseur/rs-clippy-check@v3 - name: Cache Cargo dependencies uses: Swatinem/rust-cache@v2 docs: # run docs generation on nightly rather than stable. This enables features like # https://doc.rust-lang.org/beta/unstable-book/language-features/doc-cfg.html which allows an # API be documented as only available in some specific platforms. name: Check docs runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install Rust nightly uses: dtolnay/rust-toolchain@nightly - name: Run cargo doc run: cargo doc --no-deps --all-features env: RUSTDOCFLAGS: --cfg docsrs msrv: # check that we can build using the minimal rust version that is specified by this crate name: Check MSRV (1.64) runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@1.64 - run: cargo check instability-0.3.9/.github/workflows/release.yml000064400000000000000000000031561046102023000177350ustar 00000000000000name: Release # see https://release-plz.ieni.dev/docs/github # for more information permissions: pull-requests: write contents: write id-token: write on: push: branches: - main jobs: release-plz-release: name: Release-plz Release runs-on: ubuntu-latest environment: release permissions: contents: write # Required for updating the tags etc. id-token: write # Required for OIDC token exchange if: ${{ github.repository_owner == 'ratatui' }} steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 - name: Install Rust stable uses: dtolnay/rust-toolchain@stable - name: Authenticate with crates.io uses: rust-lang/crates-io-auth-action@v1 id: auth - name: Run release-plz uses: release-plz/action@v0.5 with: command: release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }} release-plz-pr: name: Release-plz PR runs-on: ubuntu-latest permissions: contents: write # Required for updating the tags etc. pull-requests: write # Required for creating the PR if: ${{ github.repository_owner == 'ratatui' }} steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 - name: Install Rust stable uses: dtolnay/rust-toolchain@stable - name: Run release-plz uses: release-plz/action@v0.5 with: command: release-pr env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} instability-0.3.9/.github/workflows/test.yml000064400000000000000000000157561046102023000173050ustar 00000000000000name: Test # This is the main CI workflow that runs the test suite on all pushes to main and all pull requests. # It runs the following jobs: # - required: runs the test suite on ubuntu with stable and beta rust toolchains # - minimal: runs the test suite with the minimal versions of the dependencies that satisfy the # requirements of this crate, and its dependencies # - os-check: runs the test suite on mac and windows # - coverage: runs the test suite and collects coverage information # See check.yml for information about how the concurrency cancellation and workflow triggering works on: push: branches: - main pull_request: # ensure that the workflow is only triggered once per PR, subsequent pushes to the PR will cancel # and restart the workflow. See https://docs.github.com/en/actions/using-jobs/using-concurrency concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true jobs: required: runs-on: ubuntu-latest name: ubuntu (${{ matrix.toolchain }}) strategy: matrix: # run on stable and beta to ensure that tests won't break on the next version of the rust # toolchain toolchain: [stable, beta] steps: - uses: actions/checkout@v4 - name: Install Rust ${{ matrix.toolchain }} uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.toolchain }} # enable this ci template to run regardless of whether the lockfile is checked in or not - name: cargo generate-lockfile if: hashFiles('Cargo.lock') == '' run: cargo generate-lockfile - name: cargo test --locked run: cargo test --locked --all-features --all-targets - name: cargo test with instability_exclude_unstable_docs env: RUSTFLAGS: --cfg instability_disable_unstable_docs run: cargo test --locked --all-features --all-targets - name: cargo test --doc run: cargo test --locked --all-features --doc minimal-versions: # This action chooses the oldest version of the dependencies permitted by Cargo.toml to ensure # that this crate is compatible with the minimal version that this crate and its dependencies # require. This will pickup issues where this create relies on functionality that was introduced # later than the actual version specified (e.g., when we choose just a major version, but a # method was added after this version). # # This particular check can be difficult to get to succeed as often transitive dependencies may # be incorrectly specified (e.g., a dependency specifies 1.0 but really requires 1.1.5). There # is an alternative flag available -Zdirect-minimal-versions that uses the minimal versions for # direct dependencies of this crate, while selecting the maximal versions for the transitive # dependencies. Alternatively, you can add a line in your Cargo.toml to artificially increase # the minimal dependency, which you do with e.g.: # ```toml # # for minimal-versions # [target.'cfg(any())'.dependencies] # openssl = { version = "0.10.55", optional = true } # needed to allow foo to build with -Zminimal-versions # ``` # The optional = true is necessary in case that dependency isn't otherwise transitively required # by your library, and the target bit is so that this dependency edge never actually affects # Cargo build order. See also # https://github.com/jonhoo/fantoccini/blob/fde336472b712bc7ebf5b4e772023a7ba71b2262/Cargo.toml#L47-L49. # This action is run on ubuntu with the stable toolchain, as it is not expected to fail runs-on: ubuntu-latest name: minimal-versions steps: - uses: actions/checkout@v4 - name: Install Rust stable uses: dtolnay/rust-toolchain@stable - name: Install Rust nightly for -Zdirect-minimal-versions uses: dtolnay/rust-toolchain@nightly - name: rustup default stable run: rustup default stable - name: cargo update -Zdirect-minimal-versions run: cargo +nightly update -Zdirect-minimal-versions - name: cargo test run: cargo test --locked --all-features --all-targets - name: cargo test with instability_exclude_unstable_docs env: RUSTFLAGS: --cfg instability_disable_unstable_docs run: cargo test --locked --all-features --all-targets - name: Cache Cargo dependencies uses: Swatinem/rust-cache@v2 os-check: # run cargo test on mac and windows runs-on: ${{ matrix.os }} name: ${{ matrix.os }} (stable) strategy: fail-fast: false matrix: os: [macos-latest, windows-latest] steps: - name: Checkout uses: actions/checkout@v4 - name: Install Rust stable uses: dtolnay/rust-toolchain@stable - name: cargo generate-lockfile if: hashFiles('Cargo.lock') == '' run: cargo generate-lockfile - name: cargo test run: cargo test --locked --all-features --all-targets - name: cargo test with instability_exclude_unstable_docs env: RUSTFLAGS: --cfg instability_disable_unstable_docs run: cargo test --locked --all-features --all-targets - name: Cache Cargo dependencies uses: Swatinem/rust-cache@v2 coverage: # use llvm-cov to build and collect coverage and outputs in a format that # is compatible with codecov.io # # note that codecov as of v4 requires that CODECOV_TOKEN from # # https://app.codecov.io/gh///settings # # is set in two places on your repo: # # - https://github.com/jonhoo/guardian/settings/secrets/actions # - https://github.com/jonhoo/guardian/settings/secrets/dependabot # # (the former is needed for codecov uploads to work with Dependabot PRs) # # PRs coming from forks of your repo will not have access to the token, but # for those, codecov allows uploading coverage reports without a token. # it's all a little weird and inconvenient. see # # https://github.com/codecov/feedback/issues/112 # # for lots of more discussion runs-on: ubuntu-latest name: coverage (stable) steps: - name: Checkout uses: actions/checkout@v4 - name: Install Rust stable uses: dtolnay/rust-toolchain@stable with: components: llvm-tools-preview - name: cargo install cargo-llvm-cov uses: taiki-e/install-action@cargo-llvm-cov - name: cargo generate-lockfile if: hashFiles('Cargo.lock') == '' run: cargo generate-lockfile - name: cargo llvm-cov run: cargo llvm-cov --locked --all-features --lcov --output-path lcov.info - name: Record Rust version run: echo "RUST=$(rustc --version)" >> "$GITHUB_ENV" - name: Cache Cargo dependencies uses: Swatinem/rust-cache@v2 - name: Upload to codecov.io uses: codecov/codecov-action@v4 with: fail_ci_if_error: true token: ${{ secrets.CODECOV_TOKEN }} env_vars: OS,RUST instability-0.3.9/.gitignore000064400000000000000000000000241046102023000141540ustar 00000000000000/target /Cargo.lock instability-0.3.9/CHANGELOG.md000064400000000000000000000055661046102023000140150ustar 00000000000000# Changelog All notable changes to this project will be documented in this file. ## Unreleased ## [0.3.9](https://github.com/ratatui/instability/compare/instability-v0.3.8...instability-v0.3.9) - 2025-07-18 ### Other - split release jobs, fixup permissions ([#28](https://github.com/ratatui/instability/pull/28)) ## [0.3.8](https://github.com/ratatui/instability/compare/instability-v0.3.7...instability-v0.3.8) - 2025-07-11 ### Added - Add `instability_disable_unstable_docs` RUSTFLAGS ([#23](https://github.com/ratatui/instability/pull/23)) ## [0.3.7](https://github.com/ratatui/instability/compare/instability-v0.3.6...instability-v0.3.7) - 2025-01-10 ### Other - Add #[allow(unused_imports)] lint to unstable reexports ([#21](https://github.com/ratatui/instability/pull/21)) ## [0.3.6](https://github.com/ratatui/instability/compare/instability-v0.3.5...instability-v0.3.6) - 2025-01-04 ### Other - Move pretty_assertions to dev-dependencies ([#19](https://github.com/ratatui/instability/pull/19)) ## [0.3.5](https://github.com/ratatui/instability/compare/instability-v0.3.4...instability-v0.3.5) - 2024-12-21 ### Other - prepare instability-example for publish (#18) - clippy --fix ## [0.3.4](https://github.com/ratatui/instability/compare/instability-v0.3.3...instability-v0.3.4) - 2024-12-21 ### Added - Allow marking impl blocks unstable/stable ([#15](https://github.com/ratatui/instability/pull/15)) ## [0.3.3](https://github.com/ratatui/instability/compare/instability-v0.3.2...instability-v0.3.3) - 2024-11-12 ### Added - add stable macro ([#14](https://github.com/ratatui/instability/pull/14)) - use doc(cfg) ### Fixed - tests ([#13](https://github.com/ratatui/instability/pull/13)) - change master to main in lib.rs ### Other - bump msrv to 1.63 - use proc_macro2 and add tests - use darling instead of manual parsing for better error messages on attributes ## [0.3.2](https://github.com/ratatui/instability/compare/instability-v0.3.1...instability-v0.3.2) - 2024-06-27 ### Fixed - readme link to license ([#7](https://github.com/ratatui/instability/pull/7)) - readme badge ([#5](https://github.com/ratatui/instability/pull/5)) ## [0.3.1](https://github.com/ratatui/instability/compare/instability-v0.3.0...instability-v0.3.1) - 2024-06-27 ### Added - allow use statements to be marked unstable ([#3](https://github.com/ratatui/instability/pull/3)) ### Other - tweak doc wording and formatting ([#4](https://github.com/ratatui/instability/pull/4)) - add release automation ([#1](https://github.com/ratatui/instability/pull/1)) ## [0.3.0] - 2024-06-27 ### โš™๏ธ Miscellaneous Tasks - Fork and change name to instabilty ## [0.2.0] - 2024-04-02 ### ๐Ÿšœ Refactor - Migrate to syn 2.x ([#8](https://github.com/ratatui/instability/issues/8)) ## [0.1.1] - 2022-01-11 ### ๐Ÿš€ Features - Support `const`, `static` and `type` ## [0.1.0] - 2020-10-23 instability-0.3.9/Cargo.lock0000644000000063730000000000100113640ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "darling" version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ "darling_core", "darling_macro", ] [[package]] name = "darling_core" version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", "syn", ] [[package]] name = "darling_macro" version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", "syn", ] [[package]] name = "diff" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "ident_case" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "indoc" version = "2.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" [[package]] name = "instability" version = "0.3.9" dependencies = [ "darling", "indoc", "pretty_assertions", "proc-macro2", "quote", "syn", ] [[package]] name = "pretty_assertions" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" dependencies = [ "diff", "yansi", ] [[package]] name = "proc-macro2" version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] [[package]] name = "strsim" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "yansi" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" instability-0.3.9/Cargo.toml0000644000000025330000000000100114010ustar # 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.64" name = "instability" version = "0.3.9" authors = [ "Stephen M. Coakley ", "The Ratatui Developers", ] build = "build.rs" autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Rust API stability attributes for the rest of us. A fork of the `stability` crate." documentation = "https://docs.rs/instability/" readme = "README.md" license = "MIT" repository = "https://github.com/ratatui/instability" [lib] name = "instability" path = "src/lib.rs" proc-macro = true [dependencies.darling] version = "0.20.10" [dependencies.indoc] version = "2.0.5" [dependencies.proc-macro2] version = "1.0.92" [dependencies.quote] version = "1.0.37" [dependencies.syn] version = "2.0.90" features = [ "derive", "full", ] [dev-dependencies.pretty_assertions] version = "1.4.1" instability-0.3.9/Cargo.toml.orig000064400000000000000000000016501046102023000150610ustar 00000000000000[workspace] resolver = "2" members = ["example"] [workspace.package] authors = ["Stephen M. Coakley ", "The Ratatui Developers"] license = "MIT" version = "0.3.9" edition = "2021" rust-version = "1.64" repository = "https://github.com/ratatui/instability" [workspace.dependencies] instability = { path = ".", version = "0.3.9" } [package] name = "instability" description = "Rust API stability attributes for the rest of us. A fork of the `stability` crate." documentation = "https://docs.rs/instability/" authors.workspace = true license.workspace = true version.workspace = true edition.workspace = true rust-version.workspace = true repository.workspace = true readme = "README.md" [dependencies] darling = "0.20.10" indoc = "2.0.5" proc-macro2 = "1.0.92" quote = "1.0.37" syn = { version = "2.0.90", features = ["derive", "full"] } [dev-dependencies] pretty_assertions = "1.4.1" [lib] proc-macro = true instability-0.3.9/LICENSE.md000064400000000000000000000021321046102023000135720ustar 00000000000000# MIT License Copyright (c) 2020 Stephen M. Coakley Copyright (c) The Ratatui Developers 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. instability-0.3.9/README.md000064400000000000000000000043561046102023000134570ustar 00000000000000# Instability Rust API stability attributes for the rest of us. [![Crate Badge]][Crate] [![Build Badge]][Build] [![Docs Badge]][Docs] [![License Badge]][License] ![MSRV Badge] ## Overview This crate provides attribute macros for specifying API stability of public API items of a crate. It is a [fork] of the [Stability] original created by Stephen M. Coakley ([@sagebind]). ## Usage Add the `instability` crate to your `Cargo.toml` file: ```shell cargo add instability ``` Then, use the `#[instability::stable]` and `#[instability::unstable]` attributes to specify the stability of your API items: ```rust /// This function does something really risky! #[instability::unstable(feature = "risky-function")] pub fn risky_function() { println!("This function is unstable!"); } /// This function is safe to use! #[instability::stable(since = "1.0.0")] pub fn stable_function() { println!("This function is stable!"); } ``` A feature flag prefixed with "unstable-" will be created that can be used to enable unstable items. The macro will append an extra documentation comment that describes the stability of the item. The visibility of the item will be changed to `pub(crate)` when the feature is not enabled (or when the attribute is on an impl block, the entire block will be removed). Check out the [Docs] for detailed usage. See [instability-example] for a complete example. ## MSRV The minimum supported Rust version (MSRV) is 1.64.0. ## License This project's source code and documentation are licensed under the MIT [License]. [Crate Badge]: https://img.shields.io/crates/v/instability [Build Badge]: https://img.shields.io/github/actions/workflow/status/ratatui/instability/check.yml [Docs Badge]: https://img.shields.io/docsrs/instability [License Badge]: https://img.shields.io/crates/l/instability [MSRV Badge]: https://img.shields.io/crates/msrv/instability [Crate]: https://crates.io/crates/instability [Build]: https://github.com/ratatui/instability/actions/workflows/check.yml [Docs]: https://docs.rs/instability [License]: ./LICENSE.md [stability]: https://crates.io/crates/stability [@Sagebind]: https://github.com/sagebind [fork]: https://github.com/sagebind/stability/issues/12 [instability-example]: https://crates.io/crates/instability-example instability-0.3.9/build.rs000064400000000000000000000001341046102023000136330ustar 00000000000000fn main() { println!("cargo:rustc-check-cfg=cfg(instability_disable_unstable_docs)"); } instability-0.3.9/cliff.toml000064400000000000000000000066011046102023000141530ustar 00000000000000# git-cliff ~ default configuration file # https://git-cliff.org/docs/configuration # # Lines starting with "#" are comments. # Configuration options are organized into tables and keys. # See documentation for more information on available options. [changelog] # template for the changelog footer header = """ # Changelog\n All notable changes to this project will be documented in this file.\n """ # template for the changelog body # https://keats.github.io/tera/docs/#introduction body = """ {% if version %}\ ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }} {% else %}\ ## [unreleased] {% endif %}\ {% for group, commits in commits | group_by(attribute="group") %} ### {{ group | striptags | trim | upper_first }} {% for commit in commits %} - {% if commit.scope %}*({{ commit.scope }})* {% endif %}\ {% if commit.breaking %}[**breaking**] {% endif %}\ {{ commit.message | upper_first }}\ {% endfor %} {% endfor %}\n """ # template for the changelog footer footer = """ """ # remove the leading and trailing s trim = true # postprocessors postprocessors = [ { pattern = '', replace = "https://github.com/ratatui/instability" }, # replace repository URL ] [git] # parse the commits based on https://www.conventionalcommits.org conventional_commits = true # filter out the commits that are not conventional filter_unconventional = false # process each line of a commit as an individual commit split_commits = false # regex for preprocessing the commit messages commit_preprocessors = [ # Replace issue numbers { pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](/issues/${2}))" }, # Check spelling of the commit with https://github.com/crate-ci/typos # If the spelling is incorrect, it will be automatically fixed. # { pattern = '.*', replace_command = 'typos --write-changes -' }, ] # regex for parsing and grouping commits commit_parsers = [ { message = "^feat", group = "๐Ÿš€ Features" }, { message = "^fix", group = "๐Ÿ› Bug Fixes" }, { message = "^doc", group = "๐Ÿ“š Documentation" }, { message = "^perf", group = "โšก Performance" }, { message = "^refactor", group = "๐Ÿšœ Refactor" }, { message = "^style", group = "๐ŸŽจ Styling" }, { message = "^test", group = "๐Ÿงช Testing" }, { message = "^chore\\(release\\): prepare for", skip = true }, # { message = "^chore\\(deps.*\\)", skip = true }, { message = "^chore\\(pr\\)", skip = true }, { message = "^chore\\(pull\\)", skip = true }, { message = "^chore|^ci", group = "โš™๏ธ Miscellaneous Tasks" }, { body = ".*security", group = "๐Ÿ›ก๏ธ Security" }, { message = "^revert", group = "โ—€๏ธ Revert" }, ] # protect breaking changes from being skipped due to matching a skipping commit_parser protect_breaking_commits = false # filter out the commits that are not matched by commit parsers filter_commits = false # regex for matching git tags # tag_pattern = "v[0-9].*" # regex for skipping tags # skip_tags = "" # regex for ignoring tags # ignore_tags = "" # sort the tags topologically topo_order = false # sort the commits inside sections by oldest/newest order sort_commits = "oldest" # limit the number of commits included in the changelog. # limit_commits = 42 instability-0.3.9/src/item_like.rs000064400000000000000000000062721046102023000152760ustar 00000000000000use syn::Visibility; pub trait Stability { #[allow(unused)] fn attrs(&self) -> &[syn::Attribute]; fn push_attr(&mut self, attr: syn::Attribute); } pub trait ItemLike: Stability { fn visibility(&self) -> &Visibility; fn set_visibility(&mut self, visibility: Visibility); fn is_public(&self) -> bool { matches!(self.visibility(), Visibility::Public(_)) } fn allowed_lints(&self) -> Vec; } /// Implement `ItemLike` for the given type. /// /// This makes each of the syn::Item* types implement our `ItemLike` trait to make it possible to /// work with them in a more uniform way. /// /// A single type can be passed to this macro, or multiple types can be passed at once. /// Each type can be passed with a list of lints that are allowed for that type (defaulting to /// `dead_code` if not specified). macro_rules! impl_item_like { // run impl_item_like for each item in a list of items ($($(#[allow($($lint:ident),*)])? $ty:ty ),+ ,) => { $( impl_item_like!($(#[allow($($lint),*)])? $ty ); )* }; // run impl_item_like for a single item without any lints ($ty:ty) => { impl_item_like!(#[allow(dead_code)] $ty ); }; // Implement `ItemLike` for the given type. (#[allow($($lint:ident),*)] $ty:ty) => { impl Stability for $ty { fn attrs(&self) -> &[syn::Attribute] { &self.attrs } fn push_attr(&mut self, attr: syn::Attribute) { self.attrs.push(attr); } } impl ItemLike for $ty { fn visibility(&self) -> &Visibility { &self.vis } fn set_visibility(&mut self, visibility: Visibility) { self.vis = visibility; } fn allowed_lints(&self) -> Vec { vec![ $(syn::Ident::new(stringify!($lint), proc_macro2::Span::call_site()),)* ] } } }; } impl_item_like!( syn::ItemType, syn::ItemEnum, syn::ItemFn, syn::ItemMod, syn::ItemTrait, syn::ItemConst, syn::ItemStatic, #[allow(unused_imports)] syn::ItemUse, ); impl Stability for syn::ItemStruct { fn attrs(&self) -> &[syn::Attribute] { &self.attrs } fn push_attr(&mut self, attr: syn::Attribute) { self.attrs.push(attr); } } impl ItemLike for syn::ItemStruct { fn visibility(&self) -> &Visibility { &self.vis } fn set_visibility(&mut self, visibility: Visibility) { // Also constrain visibility of all fields to be at most the given // item visibility. self.fields .iter_mut() .filter(|field| matches!(&field.vis, Visibility::Public(_))) .for_each(|field| field.vis = visibility.clone()); self.vis = visibility; } fn allowed_lints(&self) -> Vec { vec![syn::Ident::new("dead_code", proc_macro2::Span::call_site())] } } impl Stability for syn::ItemImpl { fn attrs(&self) -> &[syn::Attribute] { &self.attrs } fn push_attr(&mut self, attr: syn::Attribute) { self.attrs.push(attr); } } instability-0.3.9/src/lib.rs000064400000000000000000000176311046102023000141030ustar 00000000000000//! This crate provides attribute macros for specifying API stability of public API items of a //! crate. //! //! The Rust standard library has a concept of [API stability] and custom attributes for managing //! that on a per-item basis, but most of these attributes are not available for normal crates to //! use, with the exception of the [`deprecated`] attribute. This crate seeks to provide similar //! attributes on stable Rust, though tuned more toward what the needs of normal crate authors. //! //! For complete examples of how to use this crate, check out the source code for the //! [`instability-example`] crate in the repository //! //! Currently, only the [`unstable`] attribute is available. Please see the documentation of that //! macro for an explanation on what it does and how to use it. //! //! [API stability]: https://rustc-dev-guide.rust-lang.org/stability.html //! [`deprecated`]: //! https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-deprecated-attribute //! [`instability-example`]: https://github.com/ratatui/instability/tree/main/example //! [`unstable`]: macro@unstable use proc_macro::TokenStream; use stable::stable_macro; use unstable::unstable_macro; mod item_like; mod stable; mod unstable; /// Mark an API as unstable. /// /// You can apply this attribute to an item in your public API that you would like to expose to /// users, but are not yet ready for general use. This is useful when you want to let users try out /// some new functionality for an API you haven't finished testing or designing, or for whatever /// reason do not want to commit any stability guarantees for. /// /// This attribute does the following things to annotated items: /// /// - Changes the visibility of the item from `pub` to `pub(crate)` unless a certain crate feature /// is enabled. This ensures that internal code within the crate can always use the item, but /// downstream consumers cannot access it unless they opt-in to the unstable API. /// - Annotated `impl` blocks will instead be removed. /// - Changes the Visibility of certain child items of the annotated item (such as struct fields) to /// match the item's visibility. Children that are not public will not be affected. /// - Appends an "Stability" section to the item's documentation that notes that the item is /// unstable and indicates the name of the crate feature to enable it. /// /// Child items of annotated modules are unchanged, as it might be desirable to be able to re-export /// them even if the module visibility is restricted. You should apply the attribute to each item /// within the module with the same feature name if you want to restrict the module's contents /// itself and not just the module namespace. /// /// Note that unlike the [`unstable`][std-unstable] attribute used in the standard library, this /// attribute does not apply itself recursively to child items. /// /// [std-unstable]: https://rustc-dev-guide.rust-lang.org/stability.html /// /// Applying this attribute to non-`pub` items is pointless and does nothing. /// /// # Arguments /// /// The `unstable` attribute supports optional arguments that can be passed to control its behavior. /// /// - `feature`: the name of the unstable feature that should control this item's availability. This /// will have the string `unstable-` prepended to it. If not specified, the item will instead be /// guarded by a catch-all `unstable` feature. /// - `issue`: a link or reference to a tracking issue for the unstable feature. This will be /// included in the item's documentation. /// /// # Disabling during documentation generation /// /// By default, this macro will include the unstable item when generating documentation by gating /// using a composite configuration flag that includes `docs`. In some cases, this may not be /// desirable, such as when checking docs with `cargo-semver-checks`. You can disable this by adding /// the `instability_disable_unstable_docs` RUSTFLAGS configuration flag to your build script. E.g.: /// /// ```shell /// RUSTFLAGS="--cfg instability_disable_unstable_docs" cargo build /// ``` /// /// This will not prevent the item from being compiled when the unstable feature flag is enabled. /// /// # Examples /// /// We can apply the attribute to a public function like so: /// /// ``` /// /// This function does something really risky! /// /// /// /// Don't use it yet! /// #[instability::unstable(feature = "risky-function")] /// pub fn risky_function() { /// unimplemented!() /// } /// ``` /// /// This will essentially be expanded to the following: /// /// ``` /// /// This function does something really risky! /// /// /// /// Don't use it yet! /// /// /// /// # Availability /// /// /// /// **This API is marked as unstable** and is only available when the `unstable-risky-function` /// /// crate feature is enabled. This comes with no stability guarantees, and could be changed or /// /// removed at any time. /// #[cfg(feature = "unstable-risky-function")] /// pub fn risky_function() { /// unimplemented!() /// } /// /// /// This function does something really risky! /// /// /// /// Don't use it yet! /// #[cfg(not(feature = "unstable-risky-function"))] /// pub(crate) fn risky_function() { /// unimplemented!() /// } /// ``` /// /// We can also apply the attribute to an `impl` block like so: /// /// ``` /// /// This structure is responsible for bar. /// pub struct Foo; /// /// #[instability::unstable(feature = "unstable-dependency")] /// impl Default for Foo { /// fn default() -> Self { /// unimplemented!() /// } /// } /// ``` #[proc_macro_attribute] pub fn unstable(args: TokenStream, input: TokenStream) -> TokenStream { unstable_macro(args.into(), input.into()).into() } /// Mark an API as stable. /// /// You can apply this attribute to an item in your public API that you would like to expose to /// users, and are ready to make a stability guarantee for. This is useful when you have finished /// testing and designing an API and are ready to commit to its design and stability. /// /// This attribute does the following things to annotated items: /// /// - Appends a "Stability" section to the item's documentation that notes that the item is stable /// and indicates the version at which it was stabilized. /// /// # Arguments /// /// The `stable` attribute supports optional arguments that can be passed to control its behavior. /// /// - `since`: the version at which the item was stabilized. This should be a string that follows /// the [Semantic Versioning](https://semver.org) convention. If not specified, the item will be /// marked as stable with no version information. /// - `issue`: a link or reference to a tracking issue for the stabilized feature. This will be /// included in the item's documentation. /// /// # Examples /// /// We can apply the attribute to a public function like so: /// /// ``` /// /// This function does something really risky! /// /// /// /// Don't use it yet! /// #[instability::stable(since = "v1.0.0")] /// pub fn stable_function() { /// unimplemented!() /// } /// ``` /// /// This will essentially be expanded to the following: /// /// ``` /// /// This function does something really risky! /// /// /// /// Don't use it yet! /// /// /// /// # Stability /// /// /// /// This API was stabilized in version 1.0.0. /// pub fn stable_function() { /// unimplemented!() /// } /// ``` /// /// Applying this attribute to non-`pub` items is pointless and does nothing. /// /// # Panics /// /// This macro will panic if applied to an unsupported item type. /// /// # Limitations /// /// This attribute does not change the visibility of the annotated item. You should ensure that the /// item's visibility is set to `pub` if you want it to be part of your crate's public API. /// /// # See also /// /// - The [`unstable`] attribute for marking an API as unstable. /// /// [`unstable`]: macro@unstable #[proc_macro_attribute] pub fn stable(args: TokenStream, input: TokenStream) -> TokenStream { stable_macro(args.into(), input.into()).into() } instability-0.3.9/src/stable.rs000064400000000000000000000221351046102023000146020ustar 00000000000000use darling::{ast::NestedMeta, Error, FromMeta}; use indoc::formatdoc; use proc_macro2::TokenStream; use quote::ToTokens; use syn::{parse_quote, Item}; use crate::item_like::{ItemLike, Stability}; pub fn stable_macro(args: TokenStream, input: TokenStream) -> TokenStream { let attributes = match NestedMeta::parse_meta_list(args) { Ok(attributes) => attributes, Err(err) => return Error::from(err).write_errors(), }; let unstable_attribute = match StableAttribute::from_list(&attributes) { Ok(attributes) => attributes, Err(err) => return err.write_errors(), }; match syn::parse2::(input) { Ok(item) => match item { Item::Type(item_type) => unstable_attribute.expand(item_type), Item::Enum(item_enum) => unstable_attribute.expand(item_enum), Item::Struct(item_struct) => unstable_attribute.expand(item_struct), Item::Fn(item_fn) => unstable_attribute.expand(item_fn), Item::Mod(item_mod) => unstable_attribute.expand(item_mod), Item::Trait(item_trait) => unstable_attribute.expand(item_trait), Item::Const(item_const) => unstable_attribute.expand(item_const), Item::Static(item_static) => unstable_attribute.expand(item_static), Item::Use(item_use) => unstable_attribute.expand(item_use), Item::Impl(item_impl) => unstable_attribute.expand_impl(item_impl), _ => panic!("unsupported item type"), }, Err(err) => Error::from(err).write_errors(), } } #[derive(Debug, Default, FromMeta)] pub struct StableAttribute { /// The version at which the item was stabilized. since: Option, /// A link or reference to a tracking issue for the feature. issue: Option, } impl StableAttribute { pub fn expand(&self, item: impl ItemLike + ToTokens + Clone) -> TokenStream { if !item.is_public() { // We only care about public items. return item.into_token_stream(); } self.expand_impl(item) } pub fn expand_impl(&self, mut item: impl Stability + ToTokens) -> TokenStream { let doc = if let Some(ref version) = self.since { formatdoc! {" # Stability This API was stabilized in version {}.", version.trim_start_matches('v') } } else { formatdoc! {" # Stability This API is stable."} }; item.push_attr(parse_quote! { #[doc = #doc] }); if let Some(issue) = &self.issue { let doc = format!("The tracking issue is: `{}`.", issue); item.push_attr(parse_quote! { #[doc = #doc] }); } item.into_token_stream() } } #[cfg(test)] mod tests { use pretty_assertions::assert_eq; use quote::quote; use syn::parse_quote; use super::*; #[test] fn expand_non_public_item() { let item: syn::ItemStruct = parse_quote! { struct MyStruct; }; let stable = StableAttribute::default(); let tokens = stable.expand(item.clone()); assert_eq!(tokens.to_string(), quote! { struct MyStruct; }.to_string()); } const STABLE_DOC: &str = "# Stability\n\nThis API is stable."; const SINCE_DOC: &str = "# Stability\n\nThis API was stabilized in version 1.0.0."; const ISSUE_DOC: &str = "The tracking issue is: `#123`."; #[test] fn expand_with_since() { let item: syn::ItemType = parse_quote! { pub type Foo = Bar; }; let stable = StableAttribute { since: Some("v1.0.0".to_string()), issue: None, }; let tokens = stable.expand(item); let expected = quote! { #[doc = #SINCE_DOC] pub type Foo = Bar; }; assert_eq!(tokens.to_string(), expected.to_string()); } #[test] fn expand_with_issue() { let item: syn::ItemType = parse_quote! { pub type Foo = Bar; }; let stable = StableAttribute { since: None, issue: Some("#123".to_string()), }; let tokens = stable.expand(item); let expected = quote! { #[doc = #STABLE_DOC] #[doc = #ISSUE_DOC] pub type Foo = Bar; }; assert_eq!(tokens.to_string(), expected.to_string()); } #[test] fn expand_with_since_and_issue() { let item: syn::ItemType = parse_quote! { pub type Foo = Bar; }; let stable = StableAttribute { since: Some("v1.0.0".to_string()), issue: Some("#123".to_string()), }; let tokens = stable.expand(item); let expected = quote! { #[doc = #SINCE_DOC] #[doc = #ISSUE_DOC] pub type Foo = Bar; }; assert_eq!(tokens.to_string(), expected.to_string()); } #[test] fn expand_public_type() { let item: syn::ItemType = parse_quote! { pub type Foo = Bar; }; let stable = StableAttribute::default(); let tokens = stable.expand(item); let expected = quote! { #[doc = #STABLE_DOC] pub type Foo = Bar; }; assert_eq!(tokens.to_string(), expected.to_string()); } #[test] fn expand_public_struct() { let item: syn::ItemStruct = parse_quote! { pub struct Foo { pub field: i32, } }; let stable = StableAttribute::default(); let tokens = stable.expand(item); let expected = quote! { #[doc = #STABLE_DOC] pub struct Foo { pub field: i32, } }; assert_eq!(tokens.to_string(), expected.to_string()); } #[test] fn expand_public_enum() { let item: syn::ItemEnum = parse_quote! { pub enum Foo { A, B, } }; let stable = StableAttribute::default(); let tokens = stable.expand(item); let expected = quote! { #[doc = #STABLE_DOC] pub enum Foo { A, B, } }; assert_eq!(tokens.to_string(), expected.to_string()); } #[test] fn expand_public_fn() { let item: syn::ItemFn = parse_quote! { pub fn foo() {} }; let stable = StableAttribute::default(); let tokens = stable.expand(item); let expected = quote! { #[doc = #STABLE_DOC] pub fn foo() {} }; assert_eq!(tokens.to_string(), expected.to_string()); } #[test] fn expand_public_trait() { let item: syn::ItemTrait = parse_quote! { pub trait Foo { fn bar(&self); } }; let stable = StableAttribute::default(); let tokens = stable.expand(item); let expected = quote! { #[doc = #STABLE_DOC] pub trait Foo { fn bar(&self); } }; assert_eq!(tokens.to_string(), expected.to_string()); } #[test] fn expand_public_const() { let item: syn::ItemConst = parse_quote! { pub const FOO: i32 = 42; }; let stable = StableAttribute::default(); let tokens = stable.expand(item); let expected = quote! { #[doc = #STABLE_DOC] pub const FOO: i32 = 42; }; assert_eq!(tokens.to_string(), expected.to_string()); } #[test] fn expand_public_static() { let item: syn::ItemStatic = parse_quote! { pub static FOO: i32 = 42; }; let stable = StableAttribute::default(); let tokens = stable.expand(item); let expected = quote! { #[doc = #STABLE_DOC] pub static FOO: i32 = 42; }; assert_eq!(tokens.to_string(), expected.to_string()); } #[test] fn expand_public_mod() { let item: syn::ItemMod = parse_quote! { pub mod foo { pub fn bar() {} } }; let stable = StableAttribute::default(); let tokens = stable.expand(item); let expected = quote! { #[doc = #STABLE_DOC] pub mod foo { pub fn bar() {} } }; assert_eq!(tokens.to_string(), expected.to_string()); } #[test] fn expand_public_use() { let item: syn::ItemUse = parse_quote! { pub use crate::foo::bar; }; let stable = StableAttribute::default(); let tokens = stable.expand(item); let expected = quote! { #[doc = #STABLE_DOC] pub use crate::foo::bar; }; assert_eq!(tokens.to_string(), expected.to_string()); } #[test] fn expand_impl_block() { let item: syn::ItemImpl = parse_quote! { impl Default for crate::foo::Foo {} }; let tokens = StableAttribute::default().expand_impl(item); let expected = quote! { #[doc = #STABLE_DOC] impl Default for crate::foo::Foo {} }; assert_eq!(tokens.to_string(), expected.to_string()); } } instability-0.3.9/src/unstable.rs000064400000000000000000000400701046102023000151430ustar 00000000000000use darling::{ast::NestedMeta, Error, FromMeta}; use indoc::formatdoc; use proc_macro2::TokenStream; use quote::{quote, ToTokens}; use syn::{parse_quote, Item}; use crate::item_like::{ItemLike, Stability}; pub fn unstable_macro(args: TokenStream, input: TokenStream) -> TokenStream { let attributes = match NestedMeta::parse_meta_list(args) { Ok(attributes) => attributes, Err(err) => return Error::from(err).write_errors(), }; let unstable_attribute = match UnstableAttribute::from_list(&attributes) { Ok(attributes) => attributes, Err(err) => return err.write_errors(), }; match syn::parse2::(input) { Ok(item) => match item { Item::Type(item_type) => unstable_attribute.expand(item_type), Item::Enum(item_enum) => unstable_attribute.expand(item_enum), Item::Struct(item_struct) => unstable_attribute.expand(item_struct), Item::Fn(item_fn) => unstable_attribute.expand(item_fn), Item::Mod(item_mod) => unstable_attribute.expand(item_mod), Item::Trait(item_trait) => unstable_attribute.expand(item_trait), Item::Const(item_const) => unstable_attribute.expand(item_const), Item::Static(item_static) => unstable_attribute.expand(item_static), Item::Use(item_use) => unstable_attribute.expand(item_use), Item::Impl(item_impl) => unstable_attribute.expand_impl(item_impl), _ => panic!("unsupported item type"), }, Err(err) => Error::from(err).write_errors(), } } #[derive(Debug, Default, FromMeta)] pub struct UnstableAttribute { /// The name of the feature that enables the unstable API. /// /// If not specified, the item will instead be guarded by a catch-all `unstable` feature. feature: Option, /// A link or reference to a tracking issue for the unstable feature. /// /// This will be included in the item's documentation. issue: Option, } impl UnstableAttribute { pub fn expand(&self, mut item: impl ItemLike + ToTokens + Clone) -> TokenStream { if !item.is_public() { // We only care about public items. return item.into_token_stream(); } let feature_flag = self.feature_flag(); self.add_doc(&mut item); let mut hidden_item = item.clone(); hidden_item.set_visibility(parse_quote! { pub(crate) }); let allows = item .allowed_lints() .into_iter() .map(|ident| quote! { #[allow(#ident)] }); #[cfg(not(instability_disable_unstable_docs))] let (cfg_gate_unstable, cfg_gate_stable) = ( quote! { #[cfg(any(doc, feature = #feature_flag))] }, quote! { #[cfg(not(any(doc, feature = #feature_flag)))] }, ); #[cfg(instability_disable_unstable_docs)] let (cfg_gate_unstable, cfg_gate_stable) = ( quote! { #[cfg(feature = #feature_flag)] }, quote! { #[cfg(not(feature = #feature_flag))] }, ); quote! { #cfg_gate_unstable #[cfg_attr(docsrs, doc(cfg(feature = #feature_flag)))] #item #cfg_gate_stable #(#allows)* #hidden_item } } pub fn expand_impl(&self, mut item: impl Stability + ToTokens) -> TokenStream { let feature_flag = self.feature_flag(); self.add_doc(&mut item); #[cfg(not(instability_disable_unstable_docs))] let cfg_gate_unstable = quote! { #[cfg(any(doc, feature = #feature_flag))] }; #[cfg(instability_disable_unstable_docs)] let cfg_gate_unstable = quote! { #[cfg(feature = #feature_flag)] }; quote! { #cfg_gate_unstable #[cfg_attr(docsrs, doc(cfg(feature = #feature_flag)))] #item } } fn add_doc(&self, item: &mut impl Stability) { let feature_flag = self.feature_flag(); let doc = formatdoc! {" # Stability **This API is marked as unstable** and is only available when the `{feature_flag}` crate feature is enabled. This comes with no stability guarantees, and could be changed or removed at any time."}; item.push_attr(parse_quote! { #[doc = #doc] }); if let Some(issue) = &self.issue { let doc = format!("The tracking issue is: `{}`.", issue); item.push_attr(parse_quote! { #[doc = #doc] }); } } fn feature_flag(&self) -> String { self.feature .as_deref() .map_or(String::from("unstable"), |name| format!("unstable-{name}")) } } #[cfg(all(test, not(instability_disable_unstable_docs)))] mod tests { use pretty_assertions::assert_eq; use quote::quote; use syn::parse_quote; use super::*; #[test] fn unstable_feature_flag_default() { let unstable = UnstableAttribute::default(); assert_eq!(unstable.feature_flag(), "unstable"); } #[test] fn unstable_feature_flag_with_feature() { let unstable = UnstableAttribute { feature: Some("experimental".to_string()), issue: None, }; assert_eq!(unstable.feature_flag(), "unstable-experimental"); } #[test] fn expand_non_public_item() { let item: syn::ItemStruct = parse_quote! { struct MyStruct; }; let unstable = UnstableAttribute::default(); let tokens = unstable.expand(item.clone()); assert_eq!(tokens.to_string(), quote! { struct MyStruct; }.to_string()); } const DEFAULT_DOC: &str = "# Stability\n\n**This API is marked as unstable** and is only available when the `unstable`\ncrate feature is enabled. This comes with no stability guarantees, and could be changed\nor removed at any time."; const WITH_FEATURES_DOC: &str = "# Stability\n\n**This API is marked as unstable** and is only available when the `unstable-experimental`\ncrate feature is enabled. This comes with no stability guarantees, and could be changed\nor removed at any time."; const ISSUE_DOC: &str = "The tracking issue is: `#123`."; #[test] fn expand_with_feature() { let item: syn::ItemType = parse_quote! { pub type Foo = Bar; }; let unstable = UnstableAttribute { feature: Some("experimental".to_string()), issue: None, }; let tokens = unstable.expand(item); let expected = quote! { #[cfg(any(doc, feature = "unstable-experimental"))] #[cfg_attr(docsrs, doc(cfg(feature = "unstable-experimental")))] #[doc = #WITH_FEATURES_DOC] pub type Foo = Bar; #[cfg(not(any(doc, feature = "unstable-experimental")))] #[allow(dead_code)] #[doc = #WITH_FEATURES_DOC] pub(crate) type Foo = Bar; }; assert_eq!(tokens.to_string(), expected.to_string()); } #[test] fn expand_with_issue() { let item: syn::ItemType = parse_quote! { pub type Foo = Bar; }; let unstable = UnstableAttribute { feature: None, issue: Some("#123".to_string()), }; let tokens = unstable.expand(item); let expected = quote! { #[cfg(any(doc, feature = "unstable"))] #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] #[doc = #DEFAULT_DOC] #[doc = #ISSUE_DOC] pub type Foo = Bar; #[cfg(not(any(doc, feature = "unstable")))] #[allow(dead_code)] #[doc = #DEFAULT_DOC] #[doc = #ISSUE_DOC] pub(crate) type Foo = Bar; }; assert_eq!(tokens.to_string(), expected.to_string()); } #[test] fn expand_public_type() { let item: syn::ItemType = parse_quote! { pub type Foo = Bar; }; let tokens = UnstableAttribute::default().expand(item); let expected = quote! { #[cfg(any(doc, feature = "unstable"))] #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] #[doc = #DEFAULT_DOC] pub type Foo = Bar; #[cfg(not(any(doc, feature = "unstable")))] #[allow(dead_code)] #[doc = #DEFAULT_DOC] pub(crate) type Foo = Bar; }; assert_eq!(tokens.to_string(), expected.to_string()); } #[test] fn expand_public_struct() { let item: syn::ItemStruct = parse_quote! { pub struct Foo { pub field: i32, } }; let tokens = UnstableAttribute::default().expand(item); let expected = quote! { #[cfg(any(doc, feature = "unstable"))] #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] #[doc = #DEFAULT_DOC] pub struct Foo { pub field: i32, } #[cfg(not(any(doc, feature = "unstable")))] #[allow(dead_code)] #[doc = #DEFAULT_DOC] pub(crate) struct Foo { pub (crate) field: i32, } }; assert_eq!(tokens.to_string(), expected.to_string()); } #[test] fn expand_public_enum() { let item: syn::ItemEnum = parse_quote! { pub enum Foo { A, B, } }; let tokens = UnstableAttribute::default().expand(item); let expected = quote! { #[cfg(any(doc, feature = "unstable"))] #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] #[doc = #DEFAULT_DOC] pub enum Foo { A, B, } #[cfg(not(any(doc, feature = "unstable")))] #[allow(dead_code)] #[doc = #DEFAULT_DOC] pub(crate) enum Foo { A, B, } }; assert_eq!(tokens.to_string(), expected.to_string()); } #[test] fn expand_public_fn() { let item: syn::ItemFn = parse_quote! { pub fn foo() {} }; let tokens = UnstableAttribute::default().expand(item); let expected = quote! { #[cfg(any(doc, feature = "unstable"))] #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] #[doc = #DEFAULT_DOC] pub fn foo() {} #[cfg(not(any(doc, feature = "unstable")))] #[allow(dead_code)] #[doc = #DEFAULT_DOC] pub(crate) fn foo() {} }; assert_eq!(tokens.to_string(), expected.to_string()); } #[test] fn expand_public_trait() { let item: syn::ItemTrait = parse_quote! { pub trait Foo { fn bar(&self); } }; let tokens = UnstableAttribute::default().expand(item); let expected = quote! { #[cfg(any(doc, feature = "unstable"))] #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] #[doc = #DEFAULT_DOC] pub trait Foo { fn bar(&self); } #[cfg(not(any(doc, feature = "unstable")))] #[allow(dead_code)] #[doc = #DEFAULT_DOC] pub(crate) trait Foo { fn bar(&self); } }; assert_eq!(tokens.to_string(), expected.to_string()); } #[test] fn expand_public_const() { let item: syn::ItemConst = parse_quote! { pub const FOO: i32 = 42; }; let tokens = UnstableAttribute::default().expand(item); let expected = quote! { #[cfg(any(doc, feature = "unstable"))] #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] #[doc = #DEFAULT_DOC] pub const FOO: i32 = 42; #[cfg(not(any(doc, feature = "unstable")))] #[allow(dead_code)] #[doc = #DEFAULT_DOC] pub(crate) const FOO: i32 = 42; }; assert_eq!(tokens.to_string(), expected.to_string()); } #[test] fn expand_public_static() { let item: syn::ItemStatic = parse_quote! { pub static FOO: i32 = 42; }; let tokens = UnstableAttribute::default().expand(item); let expected = quote! { #[cfg(any(doc, feature = "unstable"))] #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] #[doc = #DEFAULT_DOC] pub static FOO: i32 = 42; #[cfg(not(any(doc, feature = "unstable")))] #[allow(dead_code)] #[doc = #DEFAULT_DOC] pub(crate) static FOO: i32 = 42; }; assert_eq!(tokens.to_string(), expected.to_string()); } #[test] fn expand_public_mod() { let item: syn::ItemMod = parse_quote! { pub mod foo { pub fn bar() {} } }; let tokens = UnstableAttribute::default().expand(item); let expected = quote! { #[cfg(any(doc, feature = "unstable"))] #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] #[doc = #DEFAULT_DOC] pub mod foo { pub fn bar() {} } #[cfg(not(any(doc, feature = "unstable")))] #[allow(dead_code)] #[doc = #DEFAULT_DOC] pub(crate) mod foo { pub fn bar() {} } }; assert_eq!(tokens.to_string(), expected.to_string()); } #[test] fn expand_public_use() { let item: syn::ItemUse = parse_quote! { pub use crate::foo::bar; }; let tokens = UnstableAttribute::default().expand(item); let expected = quote! { #[cfg(any(doc, feature = "unstable"))] #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] #[doc = #DEFAULT_DOC] pub use crate::foo::bar; #[cfg(not(any(doc, feature = "unstable")))] #[allow(unused_imports)] #[doc = #DEFAULT_DOC] pub(crate) use crate::foo::bar; }; assert_eq!(tokens.to_string(), expected.to_string()); } #[test] fn expand_impl_block() { let item: syn::ItemImpl = parse_quote! { impl Default for crate::foo::Foo {} }; let tokens = UnstableAttribute::default().expand_impl(item); let expected = quote! { #[cfg(any(doc, feature = "unstable"))] #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] #[doc = #DEFAULT_DOC] impl Default for crate::foo::Foo {} }; assert_eq!(tokens.to_string(), expected.to_string()); } } #[cfg(all(test, instability_disable_unstable_docs))] mod tests { use pretty_assertions::assert_eq; use quote::quote; use syn::parse_quote; use super::*; const DEFAULT_DOC: &str = "# Stability\n\n**This API is marked as unstable** and is only available when the `unstable`\ncrate feature is enabled. This comes with no stability guarantees, and could be changed\nor removed at any time."; #[test] fn expand_public_fn() { let item: syn::ItemFn = parse_quote! { pub fn foo() {} }; let tokens = UnstableAttribute::default().expand(item); let expected = quote! { #[cfg(feature = "unstable")] #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] #[doc = #DEFAULT_DOC] pub fn foo() {} #[cfg(not(feature = "unstable"))] #[allow(dead_code)] #[doc = #DEFAULT_DOC] pub(crate) fn foo() {} }; assert_eq!(tokens.to_string(), expected.to_string()); } #[test] fn expand_public_use() { let item: syn::ItemUse = parse_quote! { pub use crate::foo::bar; }; let tokens = UnstableAttribute::default().expand(item); let expected = quote! { #[cfg(feature = "unstable")] #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] #[doc = #DEFAULT_DOC] pub use crate::foo::bar; #[cfg(not(feature = "unstable"))] #[allow(unused_imports)] #[doc = #DEFAULT_DOC] pub(crate) use crate::foo::bar; }; assert_eq!(tokens.to_string(), expected.to_string()); } }