feat: add display UI with splash screen, dashboard, and RSSI sparkline
Implement full SSD1306 OLED display support: - Splash screen on boot (1.5s) with project name, version, board - Dashboard showing radio state, config, counters, RSSI/SNR - RSSI history sparkline (64-sample ring buffer, 2px-wide bars) - DisplayOn/DisplayOff host commands routed via dedicated channel - Pure render functions against generic DrawTarget (no hardware coupling) - Display on/off control bypasses radio task entirely Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
17624 files changed
+4564928
-38
No files matched your search
+1
@@ -0,0 +1 @@
|
||||
{"files":{".cargo_vcs_info.json":"46e97545da2af8c28851bd4245ae491a3a21bf7daeee00d42255498f033a9b47",".github/FUNDING.yml":"b017158736b3c9751a2d21edfce7fe61c8954e2fced8da8dd3013c2f3e295bd9",".github/workflows/ci.yml":"e98de87708c724fa3d93dcc94c9ba1ff806ead4623b29022c10d7ba08a4d8861","Cargo.lock":"64886081d2ea8ec65c9d43e7bda6166cfbef17419f8b8ddab9a7f5ff604cec1d","Cargo.toml":"bc8ab0d1d8f4577fd77547771d96bbb0429cac23d94d9e4f6f4d408ac9e38440","Cargo.toml.orig":"f76fc49b69e9da3089d1669309baaad6ece097ca19857305722682be1841679e","LICENSE-APACHE":"62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"32cf3a0db3986500584c1b3cdd59b2c7b977477e68a20aedd92c2424685fe5ed","build.rs":"18bf4b4af0f507c7fdfe4dbfc98b5ac3d6a5ef924f46ee56125fd8b22fd61b4c","build/probe.rs":"8df55471d6b75623d423b17ebbf493335ee66140d1ddd232c88db3e59f61298c","rust-toolchain.toml":"6bbb61302978c736b2da03e4fb40e3beab908f85d533ab46fd541e637b5f3e0f","src/aserror.rs":"c4504c7db4639c5700882fd8133bf257d6755a9dad28b55b6daf02e1fb64be56","src/display.rs":"0e9f6faedf726e2d4b4a05c802bdaf536ec40d91fec6cc0a587e9f7378038a1c","src/lib.rs":"979af6da74e17c36de9eac1c12e29805f982851c71bae810ec0dfe0c292608db","src/private.rs":"e23c4d1d1a8568f1cdfb02c43b03cc2fd4c33185bfe74b56c33c4591747fdec0","src/provide.rs":"a58cb09f60ad0181c7a47c0442c9d23b8e1f79f93a2b488db3b27c211e3f497f","src/var.rs":"a7390e29fe607bfccc690c2333e8d1b85923ece2eb62555dd07fa01f7b37e226","tests/compiletest.rs":"4e381aa8ca3eabb7ac14d1e0c3700b3223e47640547a6988cfa13ad68255f60f","tests/test_backtrace.rs":"b4b2ab8057b76a46491503da7326a2411df824eab34d7d4c853081f6887b6f2f","tests/test_display.rs":"27ea50c27ed4ee8b3bb089a76ea5c5556d2ae9d3b623a3796d09424f4b6382a1","tests/test_error.rs":"eba9a2bae06e45365dc9f1be39d00e5cdee57127909c5f4b1f5558fe1dbb60ef","tests/test_expr.rs":"8d53ad09cadc513d1210fa7124e4f33f5db7bd5ec39b38861d08ddf4c0d64b70","tests/test_from.rs":"36bd22be7b048cd187a19076aeac1456040f20a0b677b01c6003998b63439ea1","tests/test_generics.rs":"219d0e2ad94f65134ca9b8b4735a211addbbff0dd6820a40a227fc6e5bf6960a","tests/test_lints.rs":"6039037e5d76702e5538105ac113e5d38293650244d70aca67e513e63b0ff472","tests/test_option.rs":"71606cb816c15d5373065579d8ab3c9206bb6f3738b0bfbea56511c43bcb05e7","tests/test_path.rs":"92f218304955022aec69d5ce09c9454cf62e17b05eb795c1ffb80d46fa42758c","tests/test_source.rs":"36a6de84484bdae8eae487ef41a93e5c71ae88d1c7e1194911295672db0b15ab","tests/test_transparent.rs":"c224b3030b7d3e3dc6851787255bea30ff063c4b3f7767a5d7b43b56959a8347","tests/ui/bad-field-attr.rs":"c5b567e3091969a01061843fb2d95c5e1aa3fa81edfeecdf416a84a6fba40aa8","tests/ui/bad-field-attr.stderr":"78f576d5ec66464a77f1cdf0f5bb7dcdf18f7f04f1165983a6239ec59d908ea3","tests/ui/concat-display.rs":"3995bd6b3bdd67df7bb16499775d89600c0dd20895633fe807396a64c117078d","tests/ui/concat-display.stderr":"de94f6f66296e8202e6b7e385599ce0e70fb8d9c055c486aac7941a6f140f489","tests/ui/display-underscore.rs":"0cab8048b4a37a676b00c5f68414c235573c920423d9a2f4dfac887a6f805399","tests/ui/display-underscore.stderr":"bb11d3c000cbd110e3539cc3fee053f824f1bdd3a8b1d7ad9548f6ceae96e684","tests/ui/duplicate-enum-source.rs":"bfe28ce18042d446a76c7411aa233598211ce1157fdd3cb87bff3b3fa7c33131","tests/ui/duplicate-enum-source.stderr":"3d32fead420b27b4497be49080bc3b78f7f0ba339ead3de6c94e5dc20302c18f","tests/ui/duplicate-fmt.rs":"ba89e292892043fdc7dfefec13f0f8bc5f52b3ab03cd0278a019612ce34dcd85","tests/ui/duplicate-fmt.stderr":"65e78d22526d30a37e663ffe63aaa1c37576aba81c6a8dc5b9ee65cc00956872","tests/ui/duplicate-struct-source.rs":"f3d5f8e3d6fccfcdbb630db291353709583a920c6bf46f9f9de9966b67ea4c0f","tests/ui/duplicate-struct-source.stderr":"fb761d76668ac42357cf37b03c0abdbae5de0a828034990850291c9cb6ab766d","tests/ui/duplicate-transparent.rs":"41a9447e85f1a47027023442acde55c3d8610ec46d91b39bd43a42d7a004d747","tests/ui/duplicate-transparent.stderr":"4975abad43e973df158f18098d9bcb9dc39f8e75d3e733ed5d6620d1ee065c11","tests/ui/expression-fallback.rs":"00fcfa22a9860feb3c737cc4eca44512de85412d2cce69a690afa9e4bdc8758b","tests/ui/expression-fallback.stderr":"f1901b1f0329e4ea0e7ad14fedfb9225aad105310d85eed4169598e624690dbd","tests/ui/fallback-impl-with-display.rs":"0b8ce57ba958008f90aaa078fa1066b47b8d4a5146b6a91ec7d8d96122f0637a","tests/ui/fallback-impl-with-display.stderr":"1b3dad712b97598fbee70125471de1a8106eb161d42ce1f790ae07be8c8984ba","tests/ui/from-backtrace-backtrace.rs":"0caac64486c0eb9c076553c0502d468fbc477602a9a2595165a1dcd95524e5ff","tests/ui/from-backtrace-backtrace.stderr":"e24156ae0828878f3282341732b6e032eaa8cb4b4db366a6b5437ed0731d40a7","tests/ui/from-not-source.rs":"a8dff2e92c21b1b97c48af89b7832bfb6b3bb4437a691bb882779e63fc792869","tests/ui/from-not-source.stderr":"525038e8b841707b927434cca4549168f73bd305faca17552a0d1fffa542ccc4","tests/ui/invalid-input-impl-anyway.rs":"6de91f71ddf038dffa3b9da33763a2ec3a5aa0047528e19ba998d5efe3aada5b","tests/ui/invalid-input-imLine truncated
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"git": {
|
||||
"sha1": "dc0f6a23a3fb6ae34ef117133ec43650450c4b32"
|
||||
},
|
||||
"path_in_vcs": ""
|
||||
}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
github: dtolnay
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
schedule: [cron: "40 1 * * *"]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
RUSTFLAGS: -Dwarnings
|
||||
|
||||
jobs:
|
||||
pre_ci:
|
||||
uses: dtolnay/.github/.github/workflows/pre_ci.yml@master
|
||||
|
||||
test:
|
||||
name: Rust ${{matrix.rust}}
|
||||
needs: pre_ci
|
||||
if: needs.pre_ci.outputs.continue
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
rust: [nightly, beta, stable, 1.81.0, 1.76.0]
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: ${{matrix.rust}}
|
||||
components: rust-src
|
||||
- name: Enable type layout randomization
|
||||
run: echo RUSTFLAGS=${RUSTFLAGS}\ -Zrandomize-layout >> $GITHUB_ENV
|
||||
if: matrix.rust == 'nightly'
|
||||
- name: Enable nightly-only tests
|
||||
run: echo RUSTFLAGS=${RUSTFLAGS}\ --cfg=thiserror_nightly_testing >> $GITHUB_ENV
|
||||
if: matrix.rust == 'nightly'
|
||||
- run: cargo test --workspace --exclude thiserror_no_std_test
|
||||
- run: cargo test --manifest-path tests/no-std/Cargo.toml
|
||||
if: matrix.rust != '1.76.0'
|
||||
- run: cargo test --no-default-features
|
||||
- uses: actions/upload-artifact@v6
|
||||
if: matrix.rust == 'nightly' && always()
|
||||
with:
|
||||
name: Cargo.lock
|
||||
path: Cargo.lock
|
||||
continue-on-error: true
|
||||
|
||||
msrv:
|
||||
name: Rust 1.68.0
|
||||
needs: pre_ci
|
||||
if: needs.pre_ci.outputs.continue
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: dtolnay/rust-toolchain@1.68.0
|
||||
with:
|
||||
components: rust-src
|
||||
- run: cargo check
|
||||
|
||||
minimal:
|
||||
name: Minimal versions
|
||||
needs: pre_ci
|
||||
if: needs.pre_ci.outputs.continue
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
- run: cargo generate-lockfile -Z minimal-versions
|
||||
- run: cargo check --locked
|
||||
|
||||
doc:
|
||||
name: Documentation
|
||||
needs: pre_ci
|
||||
if: needs.pre_ci.outputs.continue
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
env:
|
||||
RUSTDOCFLAGS: -Dwarnings
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
with:
|
||||
components: rust-src
|
||||
- uses: dtolnay/install@cargo-docs-rs
|
||||
- run: cargo docs-rs
|
||||
|
||||
clippy:
|
||||
name: Clippy
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name != 'pull_request'
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
with:
|
||||
components: clippy, rust-src
|
||||
- run: cargo clippy --tests --workspace -- -Dclippy::all -Dclippy::pedantic
|
||||
|
||||
miri:
|
||||
name: Miri
|
||||
needs: pre_ci
|
||||
if: needs.pre_ci.outputs.continue
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: dtolnay/rust-toolchain@miri
|
||||
- run: cargo miri setup
|
||||
- run: cargo miri test
|
||||
env:
|
||||
MIRIFLAGS: -Zmiri-strict-provenance
|
||||
|
||||
outdated:
|
||||
name: Outdated
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name != 'pull_request'
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: dtolnay/install@cargo-outdated
|
||||
- run: cargo outdated --workspace --exit-code 1
|
||||
+295
@@ -0,0 +1,295 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
|
||||
|
||||
[[package]]
|
||||
name = "dissimilar"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8975ffdaa0ef3661bfe02dbdcc06c9f829dfafe6a3c474de366a8d5e44276921"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.105"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ref-cast"
|
||||
version = "1.0.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d"
|
||||
dependencies = [
|
||||
"ref-cast-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ref-cast-impl"
|
||||
version = "1.0.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.149"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"zmij",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.114"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "target-triple"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "591ef38edfb78ca4771ee32cf494cb8771944bee237a9b91fc9c1424ac4b777b"
|
||||
|
||||
[[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 = "2.0.18"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"ref-cast",
|
||||
"rustversion",
|
||||
"thiserror-impl",
|
||||
"trybuild",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.9.11+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3afc9a848309fe1aaffaed6e1546a7a14de1f935dc9d89d32afd9a44bab7c46"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde_core",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_parser",
|
||||
"toml_writer",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.7.5+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_parser"
|
||||
version = "1.0.6+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44"
|
||||
dependencies = [
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_writer"
|
||||
version = "1.0.6+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607"
|
||||
|
||||
[[package]]
|
||||
name = "trybuild"
|
||||
version = "1.0.114"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e17e807bff86d2a06b52bca4276746584a78375055b6e45843925ce2802b335"
|
||||
dependencies = [
|
||||
"dissimilar",
|
||||
"glob",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"target-triple",
|
||||
"termcolor",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829"
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94f63c051f4fe3c1509da62131a678643c5b6fbdc9273b2b79d4378ebda003d2"
|
||||
Vendored
+119
@@ -0,0 +1,119 @@
|
||||
# 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.68"
|
||||
name = "thiserror"
|
||||
version = "2.0.18"
|
||||
authors = ["David Tolnay <dtolnay@gmail.com>"]
|
||||
build = "build.rs"
|
||||
autolib = false
|
||||
autobins = false
|
||||
autoexamples = false
|
||||
autotests = false
|
||||
autobenches = false
|
||||
description = "derive(Error)"
|
||||
documentation = "https://docs.rs/thiserror"
|
||||
readme = "README.md"
|
||||
keywords = [
|
||||
"error",
|
||||
"error-handling",
|
||||
"derive",
|
||||
]
|
||||
categories = [
|
||||
"rust-patterns",
|
||||
"no-std",
|
||||
]
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/dtolnay/thiserror"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
rustdoc-args = [
|
||||
"--generate-link-to-definition",
|
||||
"--generate-macro-expansion",
|
||||
"--extern-html-root-url=core=https://doc.rust-lang.org",
|
||||
"--extern-html-root-url=alloc=https://doc.rust-lang.org",
|
||||
"--extern-html-root-url=std=https://doc.rust-lang.org",
|
||||
]
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = []
|
||||
|
||||
[lib]
|
||||
name = "thiserror"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[[test]]
|
||||
name = "compiletest"
|
||||
path = "tests/compiletest.rs"
|
||||
|
||||
[[test]]
|
||||
name = "test_backtrace"
|
||||
path = "tests/test_backtrace.rs"
|
||||
|
||||
[[test]]
|
||||
name = "test_display"
|
||||
path = "tests/test_display.rs"
|
||||
|
||||
[[test]]
|
||||
name = "test_error"
|
||||
path = "tests/test_error.rs"
|
||||
|
||||
[[test]]
|
||||
name = "test_expr"
|
||||
path = "tests/test_expr.rs"
|
||||
|
||||
[[test]]
|
||||
name = "test_from"
|
||||
path = "tests/test_from.rs"
|
||||
|
||||
[[test]]
|
||||
name = "test_generics"
|
||||
path = "tests/test_generics.rs"
|
||||
|
||||
[[test]]
|
||||
name = "test_lints"
|
||||
path = "tests/test_lints.rs"
|
||||
|
||||
[[test]]
|
||||
name = "test_option"
|
||||
path = "tests/test_option.rs"
|
||||
|
||||
[[test]]
|
||||
name = "test_path"
|
||||
path = "tests/test_path.rs"
|
||||
|
||||
[[test]]
|
||||
name = "test_source"
|
||||
path = "tests/test_source.rs"
|
||||
|
||||
[[test]]
|
||||
name = "test_transparent"
|
||||
path = "tests/test_transparent.rs"
|
||||
|
||||
[dependencies.thiserror-impl]
|
||||
version = "=2.0.18"
|
||||
|
||||
[dev-dependencies.anyhow]
|
||||
version = "1.0.73"
|
||||
|
||||
[dev-dependencies.ref-cast]
|
||||
version = "1.0.18"
|
||||
|
||||
[dev-dependencies.rustversion]
|
||||
version = "1.0.13"
|
||||
|
||||
[dev-dependencies.trybuild]
|
||||
version = "1.0.108"
|
||||
features = ["diff"]
|
||||
Vendored
+176
@@ -0,0 +1,176 @@
|
||||
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
|
||||
Vendored
+23
@@ -0,0 +1,23 @@
|
||||
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.
|
||||
Vendored
+238
@@ -0,0 +1,238 @@
|
||||
derive(Error)
|
||||
=============
|
||||
|
||||
[<img alt="github" src="https://img.shields.io/badge/github-dtolnay/thiserror-8da0cb?style=for-the-badge&labelColor=555555&logo=github" height="20">](https://github.com/dtolnay/thiserror)
|
||||
[<img alt="crates.io" src="https://img.shields.io/crates/v/thiserror.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/thiserror)
|
||||
[<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-thiserror-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs" height="20">](https://docs.rs/thiserror)
|
||||
[<img alt="build status" src="https://img.shields.io/github/actions/workflow/status/dtolnay/thiserror/ci.yml?branch=master&style=for-the-badge" height="20">](https://github.com/dtolnay/thiserror/actions?query=branch%3Amaster)
|
||||
|
||||
This library provides a convenient derive macro for the standard library's
|
||||
[`std::error::Error`] trait.
|
||||
|
||||
[`std::error::Error`]: https://doc.rust-lang.org/std/error/trait.Error.html
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
thiserror = "2"
|
||||
```
|
||||
|
||||
*Compiler support: requires rustc 1.68+*
|
||||
|
||||
<br>
|
||||
|
||||
## Example
|
||||
|
||||
```rust
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum DataStoreError {
|
||||
#[error("data store disconnected")]
|
||||
Disconnect(#[from] io::Error),
|
||||
#[error("the data for key `{0}` is not available")]
|
||||
Redaction(String),
|
||||
#[error("invalid header (expected {expected:?}, found {found:?})")]
|
||||
InvalidHeader {
|
||||
expected: String,
|
||||
found: String,
|
||||
},
|
||||
#[error("unknown data store error")]
|
||||
Unknown,
|
||||
}
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
## Details
|
||||
|
||||
- Thiserror deliberately does not appear in your public API. You get the same
|
||||
thing as if you had written an implementation of `std::error::Error` by hand,
|
||||
and switching from handwritten impls to thiserror or vice versa is not a
|
||||
breaking change.
|
||||
|
||||
- Errors may be enums, structs with named fields, tuple structs, or unit
|
||||
structs.
|
||||
|
||||
- A `Display` impl is generated for your error if you provide `#[error("...")]`
|
||||
messages on the struct or each variant of your enum, as shown above in the
|
||||
example.
|
||||
|
||||
The messages support a shorthand for interpolating fields from the error.
|
||||
|
||||
- `#[error("{var}")]` ⟶ `write!("{}", self.var)`
|
||||
- `#[error("{0}")]` ⟶ `write!("{}", self.0)`
|
||||
- `#[error("{var:?}")]` ⟶ `write!("{:?}", self.var)`
|
||||
- `#[error("{0:?}")]` ⟶ `write!("{:?}", self.0)`
|
||||
|
||||
These shorthands can be used together with any additional format args, which
|
||||
may be arbitrary expressions. For example:
|
||||
|
||||
```rust
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("invalid rdo_lookahead_frames {0} (expected < {max})", max = i32::MAX)]
|
||||
InvalidLookahead(u32),
|
||||
}
|
||||
```
|
||||
|
||||
If one of the additional expression arguments needs to refer to a field of the
|
||||
struct or enum, then refer to named fields as `.var` and tuple fields as `.0`.
|
||||
|
||||
```rust
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("first letter must be lowercase but was {:?}", first_char(.0))]
|
||||
WrongCase(String),
|
||||
#[error("invalid index {idx}, expected at least {} and at most {}", .limits.lo, .limits.hi)]
|
||||
OutOfBounds { idx: usize, limits: Limits },
|
||||
}
|
||||
```
|
||||
|
||||
- A `From` impl is generated for each variant that contains a `#[from]`
|
||||
attribute.
|
||||
|
||||
The variant using `#[from]` must not contain any other fields beyond the
|
||||
source error (and possibly a backtrace — see below). Usually `#[from]`
|
||||
fields are unnamed, but `#[from]` is allowed on a named field too.
|
||||
|
||||
```rust
|
||||
#[derive(Error, Debug)]
|
||||
pub enum MyError {
|
||||
Io(#[from] io::Error),
|
||||
Glob(#[from] globset::Error),
|
||||
}
|
||||
```
|
||||
|
||||
- The Error trait's `source()` method is implemented to return whichever field
|
||||
has a `#[source]` attribute or is named `source`, if any. This is for
|
||||
identifying the underlying lower level error that caused your error.
|
||||
|
||||
The `#[from]` attribute always implies that the same field is `#[source]`, so
|
||||
you don't ever need to specify both attributes.
|
||||
|
||||
Any error type that implements `std::error::Error` or dereferences to `dyn
|
||||
std::error::Error` will work as a source.
|
||||
|
||||
```rust
|
||||
#[derive(Error, Debug)]
|
||||
pub struct MyError {
|
||||
msg: String,
|
||||
#[source] // optional if field name is `source`
|
||||
source: anyhow::Error,
|
||||
}
|
||||
```
|
||||
|
||||
- The Error trait's `provide()` method is implemented to provide whichever field
|
||||
has a type named `Backtrace`, if any, as a `std::backtrace::Backtrace`. Using
|
||||
`Backtrace` in errors requires a nightly compiler with Rust version 1.73 or
|
||||
newer.
|
||||
|
||||
```rust
|
||||
use std::backtrace::Backtrace;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub struct MyError {
|
||||
msg: String,
|
||||
backtrace: Backtrace, // automatically detected
|
||||
}
|
||||
```
|
||||
|
||||
- If a field is both a source (named `source`, or has `#[source]` or `#[from]`
|
||||
attribute) *and* is marked `#[backtrace]`, then the Error trait's `provide()`
|
||||
method is forwarded to the source's `provide` so that both layers of the error
|
||||
share the same backtrace. The `#[backtrace]` attribute requires a nightly
|
||||
compiler with Rust version 1.73 or newer.
|
||||
|
||||
|
||||
```rust
|
||||
#[derive(Error, Debug)]
|
||||
pub enum MyError {
|
||||
Io {
|
||||
#[backtrace]
|
||||
source: io::Error,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
- For variants that use `#[from]` and also contain a `Backtrace` field, a
|
||||
backtrace is captured from within the `From` impl.
|
||||
|
||||
```rust
|
||||
#[derive(Error, Debug)]
|
||||
pub enum MyError {
|
||||
Io {
|
||||
#[from]
|
||||
source: io::Error,
|
||||
backtrace: Backtrace,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
- Errors may use `error(transparent)` to forward the source and Display methods
|
||||
straight through to an underlying error without adding an additional message.
|
||||
This would be appropriate for enums that need an "anything else" variant.
|
||||
|
||||
```rust
|
||||
#[derive(Error, Debug)]
|
||||
pub enum MyError {
|
||||
...
|
||||
|
||||
#[error(transparent)]
|
||||
Other(#[from] anyhow::Error), // source and Display delegate to anyhow::Error
|
||||
}
|
||||
```
|
||||
|
||||
Another use case is hiding implementation details of an error representation
|
||||
behind an opaque error type, so that the representation is able to evolve
|
||||
without breaking the crate's public API.
|
||||
|
||||
```rust
|
||||
// PublicError is public, but opaque and easy to keep compatible.
|
||||
#[derive(Error, Debug)]
|
||||
#[error(transparent)]
|
||||
pub struct PublicError(#[from] ErrorRepr);
|
||||
|
||||
impl PublicError {
|
||||
// Accessors for anything we do want to expose publicly.
|
||||
}
|
||||
|
||||
// Private and free to change across minor version of the crate.
|
||||
#[derive(Error, Debug)]
|
||||
enum ErrorRepr {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
- See also the [`anyhow`] library for a convenient single error type to use in
|
||||
application code.
|
||||
|
||||
[`anyhow`]: https://github.com/dtolnay/anyhow
|
||||
|
||||
<br>
|
||||
|
||||
## Comparison to anyhow
|
||||
|
||||
Use thiserror if you care about designing your own dedicated error type(s) so
|
||||
that the caller receives exactly the information that you choose in the event of
|
||||
failure. This most often applies to library-like code. Use [Anyhow] if you don't
|
||||
care what error type your functions return, you just want it to be easy. This is
|
||||
common in application-like code.
|
||||
|
||||
[Anyhow]: https://github.com/dtolnay/anyhow
|
||||
|
||||
<br>
|
||||
|
||||
#### License
|
||||
|
||||
<sup>
|
||||
Licensed under either of <a href="LICENSE-APACHE">Apache License, Version
|
||||
2.0</a> or <a href="LICENSE-MIT">MIT license</a> at your option.
|
||||
</sup>
|
||||
|
||||
<br>
|
||||
|
||||
<sub>
|
||||
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.
|
||||
</sub>
|
||||
Vendored
+195
@@ -0,0 +1,195 @@
|
||||
use std::env;
|
||||
use std::ffi::OsString;
|
||||
use std::fs;
|
||||
use std::io::ErrorKind;
|
||||
use std::iter;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{self, Command, Stdio};
|
||||
use std::str;
|
||||
|
||||
const PRIVATE: &str = "\
|
||||
#[doc(hidden)]
|
||||
pub mod __private$$ {
|
||||
#[doc(hidden)]
|
||||
pub use crate::private::*;
|
||||
}
|
||||
";
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=build/probe.rs");
|
||||
|
||||
println!("cargo:rustc-check-cfg=cfg(error_generic_member_access)");
|
||||
println!("cargo:rustc-check-cfg=cfg(thiserror_nightly_testing)");
|
||||
println!("cargo:rustc-check-cfg=cfg(thiserror_no_backtrace_type)");
|
||||
|
||||
let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||
let patch_version = env::var("CARGO_PKG_VERSION_PATCH").unwrap();
|
||||
let module = PRIVATE.replace("$$", &patch_version);
|
||||
fs::write(out_dir.join("private.rs"), module).unwrap();
|
||||
|
||||
let error_generic_member_access;
|
||||
let consider_rustc_bootstrap;
|
||||
if compile_probe(false) {
|
||||
// This is a nightly or dev compiler, so it supports unstable features
|
||||
// regardless of RUSTC_BOOTSTRAP. No need to rerun build script if
|
||||
// RUSTC_BOOTSTRAP is changed.
|
||||
error_generic_member_access = true;
|
||||
consider_rustc_bootstrap = false;
|
||||
} else if let Some(rustc_bootstrap) = env::var_os("RUSTC_BOOTSTRAP") {
|
||||
if compile_probe(true) {
|
||||
// This is a stable or beta compiler for which the user has set
|
||||
// RUSTC_BOOTSTRAP to turn on unstable features. Rerun build script
|
||||
// if they change it.
|
||||
error_generic_member_access = true;
|
||||
consider_rustc_bootstrap = true;
|
||||
} else if rustc_bootstrap == "1" {
|
||||
// This compiler does not support the generic member access API in
|
||||
// the form that thiserror expects. No need to pay attention to
|
||||
// RUSTC_BOOTSTRAP.
|
||||
error_generic_member_access = false;
|
||||
consider_rustc_bootstrap = false;
|
||||
} else {
|
||||
// This is a stable or beta compiler for which RUSTC_BOOTSTRAP is
|
||||
// set to restrict the use of unstable features by this crate.
|
||||
error_generic_member_access = false;
|
||||
consider_rustc_bootstrap = true;
|
||||
}
|
||||
} else {
|
||||
// Without RUSTC_BOOTSTRAP, this compiler does not support the generic
|
||||
// member access API in the form that thiserror expects, but try again
|
||||
// if the user turns on unstable features.
|
||||
error_generic_member_access = false;
|
||||
consider_rustc_bootstrap = true;
|
||||
}
|
||||
|
||||
if error_generic_member_access {
|
||||
println!("cargo:rustc-cfg=error_generic_member_access");
|
||||
}
|
||||
|
||||
if consider_rustc_bootstrap {
|
||||
println!("cargo:rerun-if-env-changed=RUSTC_BOOTSTRAP");
|
||||
}
|
||||
|
||||
// core::error::Error stabilized in Rust 1.81
|
||||
// https://blog.rust-lang.org/2024/09/05/Rust-1.81.0.html#coreerrorerror
|
||||
let rustc = rustc_minor_version();
|
||||
if cfg!(not(feature = "std")) && rustc.map_or(false, |rustc| rustc < 81) {
|
||||
println!("cargo:rustc-cfg=feature=\"std\"");
|
||||
}
|
||||
|
||||
let Some(rustc) = rustc else {
|
||||
return;
|
||||
};
|
||||
|
||||
// std::backtrace::Backtrace stabilized in Rust 1.65
|
||||
// https://blog.rust-lang.org/2022/11/03/Rust-1.65.0.html#stabilized-apis
|
||||
if rustc < 65 {
|
||||
println!("cargo:rustc-cfg=thiserror_no_backtrace_type");
|
||||
}
|
||||
}
|
||||
|
||||
fn compile_probe(rustc_bootstrap: bool) -> bool {
|
||||
if env::var_os("RUSTC_STAGE").is_some() {
|
||||
// We are running inside rustc bootstrap. This is a highly non-standard
|
||||
// environment with issues such as:
|
||||
//
|
||||
// https://github.com/rust-lang/cargo/issues/11138
|
||||
// https://github.com/rust-lang/rust/issues/114839
|
||||
//
|
||||
// Let's just not use nightly features here.
|
||||
return false;
|
||||
}
|
||||
|
||||
let rustc = cargo_env_var("RUSTC");
|
||||
let out_dir = cargo_env_var("OUT_DIR");
|
||||
let out_subdir = Path::new(&out_dir).join("probe");
|
||||
let probefile = Path::new("build").join("probe.rs");
|
||||
|
||||
if let Err(err) = fs::create_dir(&out_subdir) {
|
||||
if err.kind() != ErrorKind::AlreadyExists {
|
||||
eprintln!("Failed to create {}: {}", out_subdir.display(), err);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
let rustc_wrapper = env::var_os("RUSTC_WRAPPER").filter(|wrapper| !wrapper.is_empty());
|
||||
let rustc_workspace_wrapper =
|
||||
env::var_os("RUSTC_WORKSPACE_WRAPPER").filter(|wrapper| !wrapper.is_empty());
|
||||
let mut rustc = rustc_wrapper
|
||||
.into_iter()
|
||||
.chain(rustc_workspace_wrapper)
|
||||
.chain(iter::once(rustc));
|
||||
let mut cmd = Command::new(rustc.next().unwrap());
|
||||
cmd.args(rustc);
|
||||
|
||||
if !rustc_bootstrap {
|
||||
cmd.env_remove("RUSTC_BOOTSTRAP");
|
||||
}
|
||||
|
||||
cmd.stderr(Stdio::null())
|
||||
.arg("--edition=2018")
|
||||
.arg("--crate-name=thiserror")
|
||||
.arg("--crate-type=lib")
|
||||
.arg("--cap-lints=allow")
|
||||
.arg("--emit=dep-info,metadata")
|
||||
.arg("--out-dir")
|
||||
.arg(&out_subdir)
|
||||
.arg(probefile);
|
||||
|
||||
if let Some(target) = env::var_os("TARGET") {
|
||||
cmd.arg("--target").arg(target);
|
||||
}
|
||||
|
||||
// If Cargo wants to set RUSTFLAGS, use that.
|
||||
if let Ok(rustflags) = env::var("CARGO_ENCODED_RUSTFLAGS") {
|
||||
if !rustflags.is_empty() {
|
||||
for arg in rustflags.split('\x1f') {
|
||||
cmd.arg(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let success = match cmd.status() {
|
||||
Ok(status) => status.success(),
|
||||
Err(_) => false,
|
||||
};
|
||||
|
||||
// Clean up to avoid leaving nondeterministic absolute paths in the dep-info
|
||||
// file in OUT_DIR, which causes nonreproducible builds in build systems
|
||||
// that treat the entire OUT_DIR as an artifact.
|
||||
if let Err(err) = fs::remove_dir_all(&out_subdir) {
|
||||
// libc::ENOTEMPTY
|
||||
// Some filesystems (NFSv3) have timing issues under load where '.nfs*'
|
||||
// dummy files can continue to get created for a short period after the
|
||||
// probe command completes, breaking remove_dir_all.
|
||||
// To be replaced with ErrorKind::DirectoryNotEmpty (Rust 1.83+).
|
||||
const ENOTEMPTY: i32 = 39;
|
||||
|
||||
if !(err.kind() == ErrorKind::NotFound
|
||||
|| (cfg!(target_os = "linux") && err.raw_os_error() == Some(ENOTEMPTY)))
|
||||
{
|
||||
eprintln!("Failed to clean up {}: {}", out_subdir.display(), err);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
success
|
||||
}
|
||||
|
||||
fn rustc_minor_version() -> Option<u32> {
|
||||
let rustc = cargo_env_var("RUSTC");
|
||||
let output = Command::new(rustc).arg("--version").output().ok()?;
|
||||
let version = str::from_utf8(&output.stdout).ok()?;
|
||||
let mut pieces = version.split('.');
|
||||
if pieces.next() != Some("rustc 1") {
|
||||
return None;
|
||||
}
|
||||
pieces.next()?.parse().ok()
|
||||
}
|
||||
|
||||
fn cargo_env_var(key: &str) -> OsString {
|
||||
env::var_os(key).unwrap_or_else(|| {
|
||||
eprintln!("Environment variable ${key} is not set during execution of build script");
|
||||
process::exit(1);
|
||||
})
|
||||
}
|
||||
Vendored
+33
@@ -0,0 +1,33 @@
|
||||
// This code exercises the surface area that we expect of the Error generic
|
||||
// member access API. If the current toolchain is able to compile it, then
|
||||
// thiserror is able to provide backtrace support.
|
||||
|
||||
#![no_std]
|
||||
#![feature(error_generic_member_access)]
|
||||
|
||||
use core::error::{Error, Request};
|
||||
use core::fmt::{self, Debug, Display};
|
||||
|
||||
struct MyError(Thing);
|
||||
struct Thing;
|
||||
|
||||
impl Debug for MyError {
|
||||
fn fmt(&self, _formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for MyError {
|
||||
fn fmt(&self, _formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for MyError {
|
||||
fn provide<'a>(&'a self, request: &mut Request<'a>) {
|
||||
request.provide_ref(&self.0);
|
||||
}
|
||||
}
|
||||
|
||||
// Include in sccache cache key.
|
||||
const _: Option<&str> = option_env!("RUSTC_BOOTSTRAP");
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
[toolchain]
|
||||
components = ["rust-src"]
|
||||
Vendored
+50
@@ -0,0 +1,50 @@
|
||||
use core::error::Error;
|
||||
use core::panic::UnwindSafe;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait AsDynError<'a>: Sealed {
|
||||
fn as_dyn_error(&self) -> &(dyn Error + 'a);
|
||||
}
|
||||
|
||||
impl<'a, T: Error + 'a> AsDynError<'a> for T {
|
||||
#[inline]
|
||||
fn as_dyn_error(&self) -> &(dyn Error + 'a) {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AsDynError<'a> for dyn Error + 'a {
|
||||
#[inline]
|
||||
fn as_dyn_error(&self) -> &(dyn Error + 'a) {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AsDynError<'a> for dyn Error + Send + 'a {
|
||||
#[inline]
|
||||
fn as_dyn_error(&self) -> &(dyn Error + 'a) {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AsDynError<'a> for dyn Error + Send + Sync + 'a {
|
||||
#[inline]
|
||||
fn as_dyn_error(&self) -> &(dyn Error + 'a) {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AsDynError<'a> for dyn Error + Send + Sync + UnwindSafe + 'a {
|
||||
#[inline]
|
||||
fn as_dyn_error(&self) -> &(dyn Error + 'a) {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait Sealed {}
|
||||
impl<T: Error> Sealed for T {}
|
||||
impl Sealed for dyn Error + '_ {}
|
||||
impl Sealed for dyn Error + Send + '_ {}
|
||||
impl Sealed for dyn Error + Send + Sync + '_ {}
|
||||
impl Sealed for dyn Error + Send + Sync + UnwindSafe + '_ {}
|
||||
Vendored
+82
@@ -0,0 +1,82 @@
|
||||
use core::fmt::Display;
|
||||
#[cfg(feature = "std")]
|
||||
use std::path::{self, Path, PathBuf};
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait AsDisplay<'a>: Sealed {
|
||||
// TODO: convert to generic associated type.
|
||||
// https://github.com/dtolnay/thiserror/pull/253
|
||||
type Target: Display;
|
||||
|
||||
fn as_display(&'a self) -> Self::Target;
|
||||
}
|
||||
|
||||
impl<'a, T> AsDisplay<'a> for &T
|
||||
where
|
||||
T: Display + ?Sized + 'a,
|
||||
{
|
||||
type Target = &'a T;
|
||||
|
||||
fn as_display(&'a self) -> Self::Target {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<'a> AsDisplay<'a> for Path {
|
||||
type Target = path::Display<'a>;
|
||||
|
||||
#[inline]
|
||||
fn as_display(&'a self) -> Self::Target {
|
||||
self.display()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<'a> AsDisplay<'a> for PathBuf {
|
||||
type Target = path::Display<'a>;
|
||||
|
||||
#[inline]
|
||||
fn as_display(&'a self) -> Self::Target {
|
||||
self.display()
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait Sealed {}
|
||||
impl<T: Display + ?Sized> Sealed for &T {}
|
||||
#[cfg(feature = "std")]
|
||||
impl Sealed for Path {}
|
||||
#[cfg(feature = "std")]
|
||||
impl Sealed for PathBuf {}
|
||||
|
||||
// Add a synthetic second impl of AsDisplay to prevent the "single applicable
|
||||
// impl" rule from making too weird inference decision based on the single impl
|
||||
// for &T, which could lead to code that compiles with thiserror's std feature
|
||||
// off but breaks under feature unification when std is turned on by an
|
||||
// unrelated crate.
|
||||
#[cfg(not(feature = "std"))]
|
||||
mod placeholder {
|
||||
use super::{AsDisplay, Sealed};
|
||||
use core::fmt::{self, Display};
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct Placeholder;
|
||||
|
||||
impl<'a> AsDisplay<'a> for Placeholder {
|
||||
type Target = Self;
|
||||
|
||||
#[inline]
|
||||
fn as_display(&'a self) -> Self::Target {
|
||||
Placeholder
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Placeholder {
|
||||
fn fmt(&self, _formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Sealed for Placeholder {}
|
||||
}
|
||||
Vendored
+291
@@ -0,0 +1,291 @@
|
||||
//! [![github]](https://github.com/dtolnay/thiserror) [![crates-io]](https://crates.io/crates/thiserror) [![docs-rs]](https://docs.rs/thiserror)
|
||||
//!
|
||||
//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
|
||||
//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
|
||||
//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs
|
||||
//!
|
||||
//! <br>
|
||||
//!
|
||||
//! This library provides a convenient derive macro for the standard library's
|
||||
//! [`std::error::Error`] trait.
|
||||
//!
|
||||
//! <br>
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```rust
|
||||
//! # use std::io;
|
||||
//! use thiserror::Error;
|
||||
//!
|
||||
//! #[derive(Error, Debug)]
|
||||
//! pub enum DataStoreError {
|
||||
//! #[error("data store disconnected")]
|
||||
//! Disconnect(#[from] io::Error),
|
||||
//! #[error("the data for key `{0}` is not available")]
|
||||
//! Redaction(String),
|
||||
//! #[error("invalid header (expected {expected:?}, found {found:?})")]
|
||||
//! InvalidHeader {
|
||||
//! expected: String,
|
||||
//! found: String,
|
||||
//! },
|
||||
//! #[error("unknown data store error")]
|
||||
//! Unknown,
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! <br>
|
||||
//!
|
||||
//! # Details
|
||||
//!
|
||||
//! - Thiserror deliberately does not appear in your public API. You get the
|
||||
//! same thing as if you had written an implementation of
|
||||
//! [`std::error::Error`] by hand, and switching from handwritten impls to
|
||||
//! thiserror or vice versa is not a breaking change.
|
||||
//!
|
||||
//! - Errors may be enums, structs with named fields, tuple structs, or unit
|
||||
//! structs.
|
||||
//!
|
||||
//! - A [`Display`] impl is generated for your error if you provide
|
||||
//! `#[error("...")]` messages on the struct or each variant of your enum, as
|
||||
//! shown above in the example.
|
||||
//!
|
||||
//! The messages support a shorthand for interpolating fields from the error.
|
||||
//!
|
||||
//! - `#[error("{var}")]` ⟶ `write!("{}", self.var)`
|
||||
//! - `#[error("{0}")]` ⟶ `write!("{}", self.0)`
|
||||
//! - `#[error("{var:?}")]` ⟶ `write!("{:?}", self.var)`
|
||||
//! - `#[error("{0:?}")]` ⟶ `write!("{:?}", self.0)`
|
||||
//!
|
||||
//! These shorthands can be used together with any additional format args,
|
||||
//! which may be arbitrary expressions. For example:
|
||||
//!
|
||||
//! ```rust
|
||||
//! # use core::i32;
|
||||
//! # use thiserror::Error;
|
||||
//! #
|
||||
//! #[derive(Error, Debug)]
|
||||
//! pub enum Error {
|
||||
//! #[error("invalid rdo_lookahead_frames {0} (expected < {max})", max = i32::MAX)]
|
||||
//! InvalidLookahead(u32),
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! If one of the additional expression arguments needs to refer to a field of
|
||||
//! the struct or enum, then refer to named fields as `.var` and tuple fields
|
||||
//! as `.0`.
|
||||
//!
|
||||
//! ```rust
|
||||
//! # use thiserror::Error;
|
||||
//! #
|
||||
//! # fn first_char(s: &String) -> char {
|
||||
//! # s.chars().next().unwrap()
|
||||
//! # }
|
||||
//! #
|
||||
//! # #[derive(Debug)]
|
||||
//! # struct Limits {
|
||||
//! # lo: usize,
|
||||
//! # hi: usize,
|
||||
//! # }
|
||||
//! #
|
||||
//! #[derive(Error, Debug)]
|
||||
//! pub enum Error {
|
||||
//! #[error("first letter must be lowercase but was {:?}", first_char(.0))]
|
||||
//! WrongCase(String),
|
||||
//! #[error("invalid index {idx}, expected at least {} and at most {}", .limits.lo, .limits.hi)]
|
||||
//! OutOfBounds { idx: usize, limits: Limits },
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! - A [`From`] impl is generated for each variant that contains a `#[from]`
|
||||
//! attribute.
|
||||
//!
|
||||
//! The variant using `#[from]` must not contain any other fields beyond the
|
||||
//! source error (and possibly a backtrace — see below). Usually
|
||||
//! `#[from]` fields are unnamed, but `#[from]` is allowed on a named field
|
||||
//! too.
|
||||
//!
|
||||
//! ```rust
|
||||
//! # use core::fmt::{self, Display};
|
||||
//! # use std::io;
|
||||
//! # use thiserror::Error;
|
||||
//! #
|
||||
//! # mod globset {
|
||||
//! # #[derive(thiserror::Error, Debug)]
|
||||
//! # #[error("...")]
|
||||
//! # pub struct Error;
|
||||
//! # }
|
||||
//! #
|
||||
//! #[derive(Error, Debug)]
|
||||
//! pub enum MyError {
|
||||
//! Io(#[from] io::Error),
|
||||
//! Glob(#[from] globset::Error),
|
||||
//! }
|
||||
//! #
|
||||
//! # impl Display for MyError {
|
||||
//! # fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
//! # unimplemented!()
|
||||
//! # }
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! - The Error trait's [`source()`] method is implemented to return whichever
|
||||
//! field has a `#[source]` attribute or is named `source`, if any. This is
|
||||
//! for identifying the underlying lower level error that caused your error.
|
||||
//!
|
||||
//! The `#[from]` attribute always implies that the same field is `#[source]`,
|
||||
//! so you don't ever need to specify both attributes.
|
||||
//!
|
||||
//! Any error type that implements `std::error::Error` or dereferences to `dyn
|
||||
//! std::error::Error` will work as a source.
|
||||
//!
|
||||
//! ```rust
|
||||
//! # use core::fmt::{self, Display};
|
||||
//! # use thiserror::Error;
|
||||
//! #
|
||||
//! #[derive(Error, Debug)]
|
||||
//! pub struct MyError {
|
||||
//! msg: String,
|
||||
//! #[source] // optional if field name is `source`
|
||||
//! source: anyhow::Error,
|
||||
//! }
|
||||
//! #
|
||||
//! # impl Display for MyError {
|
||||
//! # fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
//! # unimplemented!()
|
||||
//! # }
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! - The Error trait's [`provide()`] method is implemented to provide whichever
|
||||
//! field has a type named `Backtrace`, if any, as a
|
||||
//! [`std::backtrace::Backtrace`]. Using `Backtrace` in errors requires a
|
||||
//! nightly compiler with Rust version 1.73 or newer.
|
||||
//!
|
||||
//! ```rust
|
||||
//! # const IGNORE: &str = stringify! {
|
||||
//! use std::backtrace::Backtrace;
|
||||
//!
|
||||
//! #[derive(Error, Debug)]
|
||||
//! pub struct MyError {
|
||||
//! msg: String,
|
||||
//! backtrace: Backtrace, // automatically detected
|
||||
//! }
|
||||
//! # };
|
||||
//! ```
|
||||
//!
|
||||
//! - If a field is both a source (named `source`, or has `#[source]` or
|
||||
//! `#[from]` attribute) *and* is marked `#[backtrace]`, then the Error
|
||||
//! trait's [`provide()`] method is forwarded to the source's `provide` so
|
||||
//! that both layers of the error share the same backtrace. The `#[backtrace]`
|
||||
//! attribute requires a nightly compiler with Rust version 1.73 or newer.
|
||||
//!
|
||||
//! ```rust
|
||||
//! # const IGNORE: &str = stringify! {
|
||||
//! #[derive(Error, Debug)]
|
||||
//! pub enum MyError {
|
||||
//! Io {
|
||||
//! #[backtrace]
|
||||
//! source: io::Error,
|
||||
//! },
|
||||
//! }
|
||||
//! # };
|
||||
//! ```
|
||||
//!
|
||||
//! - For variants that use `#[from]` and also contain a `Backtrace` field, a
|
||||
//! backtrace is captured from within the `From` impl.
|
||||
//!
|
||||
//! ```rust
|
||||
//! # const IGNORE: &str = stringify! {
|
||||
//! #[derive(Error, Debug)]
|
||||
//! pub enum MyError {
|
||||
//! Io {
|
||||
//! #[from]
|
||||
//! source: io::Error,
|
||||
//! backtrace: Backtrace,
|
||||
//! },
|
||||
//! }
|
||||
//! # };
|
||||
//! ```
|
||||
//!
|
||||
//! - Errors may use `error(transparent)` to forward the source and [`Display`]
|
||||
//! methods straight through to an underlying error without adding an
|
||||
//! additional message. This would be appropriate for enums that need an
|
||||
//! "anything else" variant.
|
||||
//!
|
||||
//! ```
|
||||
//! # use thiserror::Error;
|
||||
//! #
|
||||
//! #[derive(Error, Debug)]
|
||||
//! pub enum MyError {
|
||||
//! # /*
|
||||
//! ...
|
||||
//! # */
|
||||
//!
|
||||
//! #[error(transparent)]
|
||||
//! Other(#[from] anyhow::Error), // source and Display delegate to anyhow::Error
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Another use case is hiding implementation details of an error
|
||||
//! representation behind an opaque error type, so that the representation is
|
||||
//! able to evolve without breaking the crate's public API.
|
||||
//!
|
||||
//! ```
|
||||
//! # use thiserror::Error;
|
||||
//! #
|
||||
//! // PublicError is public, but opaque and easy to keep compatible.
|
||||
//! #[derive(Error, Debug)]
|
||||
//! #[error(transparent)]
|
||||
//! pub struct PublicError(#[from] ErrorRepr);
|
||||
//!
|
||||
//! impl PublicError {
|
||||
//! // Accessors for anything we do want to expose publicly.
|
||||
//! }
|
||||
//!
|
||||
//! // Private and free to change across minor version of the crate.
|
||||
//! #[derive(Error, Debug)]
|
||||
//! enum ErrorRepr {
|
||||
//! # /*
|
||||
//! ...
|
||||
//! # */
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! - See also the [`anyhow`] library for a convenient single error type to use
|
||||
//! in application code.
|
||||
//!
|
||||
//! [`anyhow`]: https://github.com/dtolnay/anyhow
|
||||
//! [`source()`]: std::error::Error::source
|
||||
//! [`provide()`]: std::error::Error::provide
|
||||
//! [`Display`]: std::fmt::Display
|
||||
|
||||
#![no_std]
|
||||
#![doc(html_root_url = "https://docs.rs/thiserror/2.0.18")]
|
||||
#![allow(
|
||||
clippy::elidable_lifetime_names,
|
||||
clippy::module_name_repetitions,
|
||||
clippy::needless_lifetimes,
|
||||
clippy::return_self_not_must_use,
|
||||
clippy::wildcard_imports
|
||||
)]
|
||||
#![cfg_attr(error_generic_member_access, feature(error_generic_member_access))]
|
||||
|
||||
#[cfg(all(thiserror_nightly_testing, not(error_generic_member_access)))]
|
||||
compile_error!("Build script probe failed to compile.");
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
extern crate std;
|
||||
#[cfg(feature = "std")]
|
||||
extern crate std as core;
|
||||
|
||||
mod aserror;
|
||||
mod display;
|
||||
#[cfg(error_generic_member_access)]
|
||||
mod provide;
|
||||
mod var;
|
||||
|
||||
pub use thiserror_impl::*;
|
||||
|
||||
mod private;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/private.rs"));
|
||||
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
#[doc(hidden)]
|
||||
pub use crate::aserror::AsDynError;
|
||||
#[doc(hidden)]
|
||||
pub use crate::display::AsDisplay;
|
||||
#[cfg(error_generic_member_access)]
|
||||
#[doc(hidden)]
|
||||
pub use crate::provide::ThiserrorProvide;
|
||||
#[doc(hidden)]
|
||||
pub use crate::var::Var;
|
||||
#[doc(hidden)]
|
||||
pub use core::error::Error;
|
||||
#[cfg(all(feature = "std", not(thiserror_no_backtrace_type)))]
|
||||
#[doc(hidden)]
|
||||
pub use std::backtrace::Backtrace;
|
||||
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
use core::error::{Error, Request};
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait ThiserrorProvide: Sealed {
|
||||
fn thiserror_provide<'a>(&'a self, request: &mut Request<'a>);
|
||||
}
|
||||
|
||||
impl<T> ThiserrorProvide for T
|
||||
where
|
||||
T: Error + ?Sized,
|
||||
{
|
||||
#[inline]
|
||||
fn thiserror_provide<'a>(&'a self, request: &mut Request<'a>) {
|
||||
self.provide(request);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait Sealed {}
|
||||
impl<T: Error + ?Sized> Sealed for T {}
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
use core::fmt::{self, Pointer};
|
||||
|
||||
pub struct Var<'a, T: ?Sized>(pub &'a T);
|
||||
|
||||
impl<'a, T: Pointer + ?Sized> Pointer for Var<'a, T> {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
Pointer::fmt(self.0, formatter)
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
#[rustversion::attr(not(nightly), ignore = "requires nightly")]
|
||||
#[cfg_attr(miri, ignore = "incompatible with miri")]
|
||||
#[test]
|
||||
fn ui() {
|
||||
let t = trybuild::TestCases::new();
|
||||
t.compile_fail("tests/ui/*.rs");
|
||||
}
|
||||
+289
@@ -0,0 +1,289 @@
|
||||
#![cfg(feature = "std")]
|
||||
#![cfg_attr(thiserror_nightly_testing, feature(error_generic_member_access))]
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("...")]
|
||||
pub struct Inner;
|
||||
|
||||
#[cfg(thiserror_nightly_testing)]
|
||||
#[derive(Error, Debug)]
|
||||
#[error("...")]
|
||||
pub struct InnerBacktrace {
|
||||
backtrace: std::backtrace::Backtrace,
|
||||
}
|
||||
|
||||
#[cfg(thiserror_nightly_testing)]
|
||||
pub mod structs {
|
||||
use super::{Inner, InnerBacktrace};
|
||||
use std::backtrace::Backtrace;
|
||||
use std::error::{self, Error};
|
||||
use std::sync::Arc;
|
||||
use thiserror::Error;
|
||||
|
||||
mod not_backtrace {
|
||||
#[derive(Debug)]
|
||||
pub struct Backtrace;
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("...")]
|
||||
pub struct PlainBacktrace {
|
||||
backtrace: Backtrace,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("...")]
|
||||
pub struct ExplicitBacktrace {
|
||||
#[backtrace]
|
||||
backtrace: Backtrace,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("...")]
|
||||
pub struct NotBacktrace {
|
||||
backtrace: crate::structs::not_backtrace::r#Backtrace,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("...")]
|
||||
pub struct OptBacktrace {
|
||||
#[backtrace]
|
||||
backtrace: Option<Backtrace>,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("...")]
|
||||
pub struct ArcBacktrace {
|
||||
#[backtrace]
|
||||
backtrace: Arc<Backtrace>,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("...")]
|
||||
pub struct BacktraceFrom {
|
||||
#[from]
|
||||
source: Inner,
|
||||
#[backtrace]
|
||||
backtrace: Backtrace,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("...")]
|
||||
pub struct CombinedBacktraceFrom {
|
||||
#[from]
|
||||
#[backtrace]
|
||||
source: InnerBacktrace,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("...")]
|
||||
pub struct OptBacktraceFrom {
|
||||
#[from]
|
||||
source: Inner,
|
||||
#[backtrace]
|
||||
backtrace: Option<Backtrace>,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("...")]
|
||||
pub struct ArcBacktraceFrom {
|
||||
#[from]
|
||||
source: Inner,
|
||||
#[backtrace]
|
||||
backtrace: Arc<Backtrace>,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("...")]
|
||||
pub struct AnyhowBacktrace {
|
||||
#[backtrace]
|
||||
source: anyhow::Error,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("...")]
|
||||
pub struct BoxDynErrorBacktrace {
|
||||
#[backtrace]
|
||||
source: Box<dyn Error>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_backtrace() {
|
||||
let error = PlainBacktrace {
|
||||
backtrace: Backtrace::capture(),
|
||||
};
|
||||
assert!(error::request_ref::<Backtrace>(&error).is_some());
|
||||
|
||||
let error = ExplicitBacktrace {
|
||||
backtrace: Backtrace::capture(),
|
||||
};
|
||||
assert!(error::request_ref::<Backtrace>(&error).is_some());
|
||||
|
||||
let error = OptBacktrace {
|
||||
backtrace: Some(Backtrace::capture()),
|
||||
};
|
||||
assert!(error::request_ref::<Backtrace>(&error).is_some());
|
||||
|
||||
let error = ArcBacktrace {
|
||||
backtrace: Arc::new(Backtrace::capture()),
|
||||
};
|
||||
assert!(error::request_ref::<Backtrace>(&error).is_some());
|
||||
|
||||
let error = BacktraceFrom::from(Inner);
|
||||
assert!(error::request_ref::<Backtrace>(&error).is_some());
|
||||
|
||||
let error = CombinedBacktraceFrom::from(InnerBacktrace {
|
||||
backtrace: Backtrace::capture(),
|
||||
});
|
||||
assert!(error::request_ref::<Backtrace>(&error).is_some());
|
||||
|
||||
let error = OptBacktraceFrom::from(Inner);
|
||||
assert!(error::request_ref::<Backtrace>(&error).is_some());
|
||||
|
||||
let error = ArcBacktraceFrom::from(Inner);
|
||||
assert!(error::request_ref::<Backtrace>(&error).is_some());
|
||||
|
||||
let error = AnyhowBacktrace {
|
||||
source: anyhow::Error::msg("..."),
|
||||
};
|
||||
assert!(error::request_ref::<Backtrace>(&error).is_some());
|
||||
|
||||
let error = BoxDynErrorBacktrace {
|
||||
source: Box::new(PlainBacktrace {
|
||||
backtrace: Backtrace::capture(),
|
||||
}),
|
||||
};
|
||||
assert!(error::request_ref::<Backtrace>(&error).is_some());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(thiserror_nightly_testing)]
|
||||
pub mod enums {
|
||||
use super::{Inner, InnerBacktrace};
|
||||
use std::backtrace::Backtrace;
|
||||
use std::error;
|
||||
use std::sync::Arc;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum PlainBacktrace {
|
||||
#[error("...")]
|
||||
Test { backtrace: Backtrace },
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ExplicitBacktrace {
|
||||
#[error("...")]
|
||||
Test {
|
||||
#[backtrace]
|
||||
backtrace: Backtrace,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum OptBacktrace {
|
||||
#[error("...")]
|
||||
Test {
|
||||
#[backtrace]
|
||||
backtrace: Option<Backtrace>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ArcBacktrace {
|
||||
#[error("...")]
|
||||
Test {
|
||||
#[backtrace]
|
||||
backtrace: Arc<Backtrace>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum BacktraceFrom {
|
||||
#[error("...")]
|
||||
Test {
|
||||
#[from]
|
||||
source: Inner,
|
||||
#[backtrace]
|
||||
backtrace: Backtrace,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum CombinedBacktraceFrom {
|
||||
#[error("...")]
|
||||
Test {
|
||||
#[from]
|
||||
#[backtrace]
|
||||
source: InnerBacktrace,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum OptBacktraceFrom {
|
||||
#[error("...")]
|
||||
Test {
|
||||
#[from]
|
||||
source: Inner,
|
||||
#[backtrace]
|
||||
backtrace: Option<Backtrace>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ArcBacktraceFrom {
|
||||
#[error("...")]
|
||||
Test {
|
||||
#[from]
|
||||
source: Inner,
|
||||
#[backtrace]
|
||||
backtrace: Arc<Backtrace>,
|
||||
},
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_backtrace() {
|
||||
let error = PlainBacktrace::Test {
|
||||
backtrace: Backtrace::capture(),
|
||||
};
|
||||
assert!(error::request_ref::<Backtrace>(&error).is_some());
|
||||
|
||||
let error = ExplicitBacktrace::Test {
|
||||
backtrace: Backtrace::capture(),
|
||||
};
|
||||
assert!(error::request_ref::<Backtrace>(&error).is_some());
|
||||
|
||||
let error = OptBacktrace::Test {
|
||||
backtrace: Some(Backtrace::capture()),
|
||||
};
|
||||
assert!(error::request_ref::<Backtrace>(&error).is_some());
|
||||
|
||||
let error = ArcBacktrace::Test {
|
||||
backtrace: Arc::new(Backtrace::capture()),
|
||||
};
|
||||
assert!(error::request_ref::<Backtrace>(&error).is_some());
|
||||
|
||||
let error = BacktraceFrom::from(Inner);
|
||||
assert!(error::request_ref::<Backtrace>(&error).is_some());
|
||||
|
||||
let error = CombinedBacktraceFrom::from(InnerBacktrace {
|
||||
backtrace: Backtrace::capture(),
|
||||
});
|
||||
assert!(error::request_ref::<Backtrace>(&error).is_some());
|
||||
|
||||
let error = OptBacktraceFrom::from(Inner);
|
||||
assert!(error::request_ref::<Backtrace>(&error).is_some());
|
||||
|
||||
let error = ArcBacktraceFrom::from(Inner);
|
||||
assert!(error::request_ref::<Backtrace>(&error).is_some());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(
|
||||
not(thiserror_nightly_testing),
|
||||
ignore = "requires `--cfg=thiserror_nightly_testing`"
|
||||
)]
|
||||
fn test_backtrace() {}
|
||||
+478
@@ -0,0 +1,478 @@
|
||||
#![allow(
|
||||
clippy::elidable_lifetime_names,
|
||||
clippy::needless_lifetimes,
|
||||
clippy::needless_raw_string_hashes,
|
||||
clippy::trivially_copy_pass_by_ref,
|
||||
clippy::uninlined_format_args
|
||||
)]
|
||||
|
||||
use core::fmt::{self, Display};
|
||||
use thiserror::Error;
|
||||
|
||||
fn assert<T: Display>(expected: &str, value: T) {
|
||||
assert_eq!(expected, value.to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_braced() {
|
||||
#[derive(Error, Debug)]
|
||||
#[error("braced error: {msg}")]
|
||||
struct Error {
|
||||
msg: String,
|
||||
}
|
||||
|
||||
let msg = "T".to_owned();
|
||||
assert("braced error: T", Error { msg });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_braced_unused() {
|
||||
#[derive(Error, Debug)]
|
||||
#[error("braced error")]
|
||||
struct Error {
|
||||
extra: usize,
|
||||
}
|
||||
|
||||
assert("braced error", Error { extra: 0 });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tuple() {
|
||||
#[derive(Error, Debug)]
|
||||
#[error("tuple error: {0}")]
|
||||
struct Error(usize);
|
||||
|
||||
assert("tuple error: 0", Error(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unit() {
|
||||
#[derive(Error, Debug)]
|
||||
#[error("unit error")]
|
||||
struct Error;
|
||||
|
||||
assert("unit error", Error);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_enum() {
|
||||
#[derive(Error, Debug)]
|
||||
enum Error {
|
||||
#[error("braced error: {id}")]
|
||||
Braced { id: usize },
|
||||
#[error("tuple error: {0}")]
|
||||
Tuple(usize),
|
||||
#[error("unit error")]
|
||||
Unit,
|
||||
}
|
||||
|
||||
assert("braced error: 0", Error::Braced { id: 0 });
|
||||
assert("tuple error: 0", Error::Tuple(0));
|
||||
assert("unit error", Error::Unit);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_constants() {
|
||||
#[derive(Error, Debug)]
|
||||
#[error("{MSG}: {id:?} (code {CODE:?})")]
|
||||
struct Error {
|
||||
id: &'static str,
|
||||
}
|
||||
|
||||
const MSG: &str = "failed to do";
|
||||
const CODE: usize = 9;
|
||||
|
||||
assert("failed to do: \"\" (code 9)", Error { id: "" });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_inherit() {
|
||||
#[derive(Error, Debug)]
|
||||
#[error("{0}")]
|
||||
enum Error {
|
||||
Some(&'static str),
|
||||
#[error("other error")]
|
||||
Other(&'static str),
|
||||
}
|
||||
|
||||
assert("some error", Error::Some("some error"));
|
||||
assert("other error", Error::Other("..."));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_brace_escape() {
|
||||
#[derive(Error, Debug)]
|
||||
#[error("fn main() {{}}")]
|
||||
struct Error;
|
||||
|
||||
assert("fn main() {}", Error);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expr() {
|
||||
#[derive(Error, Debug)]
|
||||
#[error("1 + 1 = {}", 1 + 1)]
|
||||
struct Error;
|
||||
assert("1 + 1 = 2", Error);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nested() {
|
||||
#[derive(Error, Debug)]
|
||||
#[error("!bool = {}", not(.0))]
|
||||
struct Error(bool);
|
||||
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
fn not(bool: &bool) -> bool {
|
||||
!*bool
|
||||
}
|
||||
|
||||
assert("!bool = false", Error(true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_match() {
|
||||
#[derive(Error, Debug)]
|
||||
#[error("{intro}: {0}", intro = match .1 {
|
||||
Some(n) => format!("error occurred with {}", n),
|
||||
None => "there was an empty error".to_owned(),
|
||||
})]
|
||||
struct Error(String, Option<usize>);
|
||||
|
||||
assert(
|
||||
"error occurred with 1: ...",
|
||||
Error("...".to_owned(), Some(1)),
|
||||
);
|
||||
assert(
|
||||
"there was an empty error: ...",
|
||||
Error("...".to_owned(), None),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nested_display() {
|
||||
// Same behavior as the one in `test_match`, but without String allocations.
|
||||
#[derive(Error, Debug)]
|
||||
#[error("{}", {
|
||||
struct Msg<'a>(&'a String, &'a Option<usize>);
|
||||
impl<'a> Display for Msg<'a> {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self.1 {
|
||||
Some(n) => write!(formatter, "error occurred with {}", n),
|
||||
None => write!(formatter, "there was an empty error"),
|
||||
}?;
|
||||
write!(formatter, ": {}", self.0)
|
||||
}
|
||||
}
|
||||
Msg(.0, .1)
|
||||
})]
|
||||
struct Error(String, Option<usize>);
|
||||
|
||||
assert(
|
||||
"error occurred with 1: ...",
|
||||
Error("...".to_owned(), Some(1)),
|
||||
);
|
||||
assert(
|
||||
"there was an empty error: ...",
|
||||
Error("...".to_owned(), None),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_void() {
|
||||
#[allow(clippy::empty_enums)]
|
||||
#[derive(Error, Debug)]
|
||||
#[error("...")]
|
||||
pub enum Error {}
|
||||
|
||||
let _: Error;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mixed() {
|
||||
#[derive(Error, Debug)]
|
||||
#[error("a={a} :: b={} :: c={c} :: d={d}", 1, c = 2, d = 3)]
|
||||
struct Error {
|
||||
a: usize,
|
||||
d: usize,
|
||||
}
|
||||
|
||||
assert("a=0 :: b=1 :: c=2 :: d=3", Error { a: 0, d: 0 });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ints() {
|
||||
#[derive(Error, Debug)]
|
||||
enum Error {
|
||||
#[error("error {0}")]
|
||||
Tuple(usize, usize),
|
||||
#[error("error {0}", '?')]
|
||||
Struct { v: usize },
|
||||
}
|
||||
|
||||
assert("error 9", Error::Tuple(9, 0));
|
||||
assert("error ?", Error::Struct { v: 0 });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_trailing_comma() {
|
||||
#[derive(Error, Debug)]
|
||||
#[error(
|
||||
"error {0}",
|
||||
)]
|
||||
#[rustfmt::skip]
|
||||
struct Error(char);
|
||||
|
||||
assert("error ?", Error('?'));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_field() {
|
||||
#[derive(Debug)]
|
||||
struct Inner {
|
||||
data: usize,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("{}", .0.data)]
|
||||
struct Error(Inner);
|
||||
|
||||
assert("0", Error(Inner { data: 0 }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nested_tuple_field() {
|
||||
#[derive(Debug)]
|
||||
struct Inner(usize);
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("{}", .0.0)]
|
||||
struct Error(Inner);
|
||||
|
||||
assert("0", Error(Inner(0)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pointer() {
|
||||
#[derive(Error, Debug)]
|
||||
#[error("{field:p}")]
|
||||
pub struct Struct {
|
||||
field: Box<i32>,
|
||||
}
|
||||
|
||||
let s = Struct {
|
||||
field: Box::new(-1),
|
||||
};
|
||||
assert_eq!(s.to_string(), format!("{:p}", s.field));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_macro_rules_variant_from_call_site() {
|
||||
// Regression test for https://github.com/dtolnay/thiserror/issues/86
|
||||
|
||||
macro_rules! decl_error {
|
||||
($variant:ident($value:ident)) => {
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error0 {
|
||||
#[error("{0:?}")]
|
||||
$variant($value),
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("{0:?}")]
|
||||
pub enum Error1 {
|
||||
$variant($value),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
decl_error!(Repro(u8));
|
||||
|
||||
assert("0", Error0::Repro(0));
|
||||
assert("0", Error1::Repro(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_macro_rules_message_from_call_site() {
|
||||
// Regression test for https://github.com/dtolnay/thiserror/issues/398
|
||||
|
||||
macro_rules! decl_error {
|
||||
($($errors:tt)*) => {
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
$($errors)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
decl_error! {
|
||||
#[error("{0}")]
|
||||
Unnamed(u8),
|
||||
#[error("{x}")]
|
||||
Named { x: u8 },
|
||||
}
|
||||
|
||||
assert("0", Error::Unnamed(0));
|
||||
assert("0", Error::Named { x: 0 });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_raw() {
|
||||
#[derive(Error, Debug)]
|
||||
#[error("braced raw error: {fn}")]
|
||||
struct Error {
|
||||
r#fn: &'static str,
|
||||
}
|
||||
|
||||
assert("braced raw error: T", Error { r#fn: "T" });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_raw_enum() {
|
||||
#[derive(Error, Debug)]
|
||||
enum Error {
|
||||
#[error("braced raw error: {fn}")]
|
||||
Braced { r#fn: &'static str },
|
||||
}
|
||||
|
||||
assert("braced raw error: T", Error::Braced { r#fn: "T" });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_keyword() {
|
||||
#[derive(Error, Debug)]
|
||||
#[error("error: {type}", type = 1)]
|
||||
struct Error;
|
||||
|
||||
assert("error: 1", Error);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_self() {
|
||||
#[derive(Error, Debug)]
|
||||
#[error("error: {self:?}")]
|
||||
struct Error;
|
||||
|
||||
assert("error: Error", Error);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_str_special_chars() {
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("brace left {{")]
|
||||
BraceLeft,
|
||||
#[error("brace left 2 \x7B\x7B")]
|
||||
BraceLeft2,
|
||||
#[error("brace left 3 \u{7B}\u{7B}")]
|
||||
BraceLeft3,
|
||||
#[error("brace right }}")]
|
||||
BraceRight,
|
||||
#[error("brace right 2 \x7D\x7D")]
|
||||
BraceRight2,
|
||||
#[error("brace right 3 \u{7D}\u{7D}")]
|
||||
BraceRight3,
|
||||
#[error(
|
||||
"new_\
|
||||
line"
|
||||
)]
|
||||
NewLine,
|
||||
#[error("escape24 \u{78}")]
|
||||
Escape24,
|
||||
}
|
||||
|
||||
assert("brace left {", Error::BraceLeft);
|
||||
assert("brace left 2 {", Error::BraceLeft2);
|
||||
assert("brace left 3 {", Error::BraceLeft3);
|
||||
assert("brace right }", Error::BraceRight);
|
||||
assert("brace right 2 }", Error::BraceRight2);
|
||||
assert("brace right 3 }", Error::BraceRight3);
|
||||
assert("new_line", Error::NewLine);
|
||||
assert("escape24 x", Error::Escape24);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_raw_str() {
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error(r#"raw brace left {{"#)]
|
||||
BraceLeft,
|
||||
#[error(r#"raw brace left 2 \x7B"#)]
|
||||
BraceLeft2,
|
||||
#[error(r#"raw brace right }}"#)]
|
||||
BraceRight,
|
||||
#[error(r#"raw brace right 2 \x7D"#)]
|
||||
BraceRight2,
|
||||
}
|
||||
|
||||
assert(r#"raw brace left {"#, Error::BraceLeft);
|
||||
assert(r#"raw brace left 2 \x7B"#, Error::BraceLeft2);
|
||||
assert(r#"raw brace right }"#, Error::BraceRight);
|
||||
assert(r#"raw brace right 2 \x7D"#, Error::BraceRight2);
|
||||
}
|
||||
|
||||
mod util {
|
||||
use core::fmt::{self, Octal};
|
||||
|
||||
pub fn octal<T: Octal>(value: &T, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "0o{:o}", value)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fmt_path() {
|
||||
fn unit(formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("unit=")
|
||||
}
|
||||
|
||||
fn pair(k: &i32, v: &i32, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "pair={k}:{v}")
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error(fmt = unit)]
|
||||
Unit,
|
||||
#[error(fmt = pair)]
|
||||
Tuple(i32, i32),
|
||||
#[error(fmt = pair)]
|
||||
Entry { k: i32, v: i32 },
|
||||
#[error(fmt = crate::util::octal)]
|
||||
I16(i16),
|
||||
#[error(fmt = crate::util::octal::<i32>)]
|
||||
I32 { n: i32 },
|
||||
#[error(fmt = core::fmt::Octal::fmt)]
|
||||
I64(i64),
|
||||
#[error("...{0}")]
|
||||
Other(bool),
|
||||
}
|
||||
|
||||
assert("unit=", Error::Unit);
|
||||
assert("pair=10:0", Error::Tuple(10, 0));
|
||||
assert("pair=10:0", Error::Entry { k: 10, v: 0 });
|
||||
assert("0o777", Error::I16(0o777));
|
||||
assert("0o777", Error::I32 { n: 0o777 });
|
||||
assert("777", Error::I64(0o777));
|
||||
assert("...false", Error::Other(false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fmt_path_inherited() {
|
||||
#[derive(Error, Debug)]
|
||||
#[error(fmt = crate::util::octal)]
|
||||
pub enum Error {
|
||||
I16(i16),
|
||||
I32 {
|
||||
n: i32,
|
||||
},
|
||||
#[error(fmt = core::fmt::Octal::fmt)]
|
||||
I64(i64),
|
||||
#[error("...{0}")]
|
||||
Other(bool),
|
||||
}
|
||||
|
||||
assert("0o777", Error::I16(0o777));
|
||||
assert("0o777", Error::I32 { n: 0o777 });
|
||||
assert("777", Error::I64(0o777));
|
||||
assert("...false", Error::Other(false));
|
||||
}
|
||||
Vendored
+56
@@ -0,0 +1,56 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use core::fmt::{self, Display};
|
||||
use std::io;
|
||||
use thiserror::Error;
|
||||
|
||||
macro_rules! unimplemented_display {
|
||||
($ty:ty) => {
|
||||
impl Display for $ty {
|
||||
fn fmt(&self, _formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
struct BracedError {
|
||||
msg: String,
|
||||
pos: usize,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
struct TupleError(String, usize);
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
struct UnitError;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
struct WithSource {
|
||||
#[source]
|
||||
cause: io::Error,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
struct WithAnyhow {
|
||||
#[source]
|
||||
cause: anyhow::Error,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
enum EnumError {
|
||||
Braced {
|
||||
#[source]
|
||||
cause: io::Error,
|
||||
},
|
||||
Tuple(#[source] io::Error),
|
||||
Unit,
|
||||
}
|
||||
|
||||
unimplemented_display!(BracedError);
|
||||
unimplemented_display!(TupleError);
|
||||
unimplemented_display!(UnitError);
|
||||
unimplemented_display!(WithSource);
|
||||
unimplemented_display!(WithAnyhow);
|
||||
unimplemented_display!(EnumError);
|
||||
Vendored
+118
@@ -0,0 +1,118 @@
|
||||
#![allow(clippy::iter_cloned_collect, clippy::uninlined_format_args)]
|
||||
|
||||
use core::fmt::Display;
|
||||
#[cfg(feature = "std")]
|
||||
use std::path::PathBuf;
|
||||
use thiserror::Error;
|
||||
|
||||
// Some of the elaborate cases from the rcc codebase, which is a C compiler in
|
||||
// Rust. https://github.com/jyn514/rcc/blob/0.8.0/src/data/error.rs
|
||||
#[derive(Error, Debug)]
|
||||
pub enum CompilerError {
|
||||
#[error("cannot shift {} by {maximum} or more bits (got {current})", if *.is_left { "left" } else { "right" })]
|
||||
TooManyShiftBits {
|
||||
is_left: bool,
|
||||
maximum: u64,
|
||||
current: u64,
|
||||
},
|
||||
|
||||
#[error("#error {}", (.0).iter().copied().collect::<Vec<_>>().join(" "))]
|
||||
User(Vec<&'static str>),
|
||||
|
||||
#[error("overflow while parsing {}integer literal",
|
||||
if let Some(signed) = .is_signed {
|
||||
if *signed { "signed "} else { "unsigned "}
|
||||
} else {
|
||||
""
|
||||
}
|
||||
)]
|
||||
IntegerOverflow { is_signed: Option<bool> },
|
||||
|
||||
#[error("overflow while parsing {}integer literal", match .is_signed {
|
||||
Some(true) => "signed ",
|
||||
Some(false) => "unsigned ",
|
||||
None => "",
|
||||
})]
|
||||
IntegerOverflow2 { is_signed: Option<bool> },
|
||||
}
|
||||
|
||||
// Examples drawn from Rustup.
|
||||
#[derive(Error, Debug)]
|
||||
pub enum RustupError {
|
||||
#[error(
|
||||
"toolchain '{name}' does not contain component {component}{}",
|
||||
.suggestion
|
||||
.as_ref()
|
||||
.map_or_else(String::new, |s| format!("; did you mean '{}'?", s)),
|
||||
)]
|
||||
UnknownComponent {
|
||||
name: String,
|
||||
component: String,
|
||||
suggestion: Option<String>,
|
||||
},
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn assert<T: Display>(expected: &str, value: T) {
|
||||
assert_eq!(expected, value.to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rcc() {
|
||||
assert(
|
||||
"cannot shift left by 32 or more bits (got 50)",
|
||||
CompilerError::TooManyShiftBits {
|
||||
is_left: true,
|
||||
maximum: 32,
|
||||
current: 50,
|
||||
},
|
||||
);
|
||||
|
||||
assert("#error A B C", CompilerError::User(vec!["A", "B", "C"]));
|
||||
|
||||
assert(
|
||||
"overflow while parsing signed integer literal",
|
||||
CompilerError::IntegerOverflow {
|
||||
is_signed: Some(true),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rustup() {
|
||||
assert(
|
||||
"toolchain 'nightly' does not contain component clipy; did you mean 'clippy'?",
|
||||
RustupError::UnknownComponent {
|
||||
name: "nightly".to_owned(),
|
||||
component: "clipy".to_owned(),
|
||||
suggestion: Some("clippy".to_owned()),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Regression test for https://github.com/dtolnay/thiserror/issues/335
|
||||
#[cfg(feature = "std")]
|
||||
#[test]
|
||||
#[allow(non_snake_case)]
|
||||
fn test_assoc_type_equality_constraint() {
|
||||
pub trait Trait<T>: Display {
|
||||
type A;
|
||||
}
|
||||
|
||||
impl<T> Trait<T> for i32 {
|
||||
type A = i32;
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("{A} {b}", b = &0 as &dyn Trait<i32, A = i32>)]
|
||||
pub struct Error {
|
||||
pub A: PathBuf,
|
||||
}
|
||||
|
||||
assert(
|
||||
"... 0",
|
||||
Error {
|
||||
A: PathBuf::from("..."),
|
||||
},
|
||||
);
|
||||
}
|
||||
Vendored
+64
@@ -0,0 +1,64 @@
|
||||
#![allow(clippy::extra_unused_type_parameters)]
|
||||
|
||||
use std::io;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("...")]
|
||||
pub struct ErrorStruct {
|
||||
#[from]
|
||||
source: io::Error,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("...")]
|
||||
pub struct ErrorStructOptional {
|
||||
#[from]
|
||||
source: Option<io::Error>,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("...")]
|
||||
pub struct ErrorTuple(#[from] io::Error);
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("...")]
|
||||
pub struct ErrorTupleOptional(#[from] Option<io::Error>);
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("...")]
|
||||
pub enum ErrorEnum {
|
||||
Test {
|
||||
#[from]
|
||||
source: io::Error,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("...")]
|
||||
pub enum ErrorEnumOptional {
|
||||
Test {
|
||||
#[from]
|
||||
source: Option<io::Error>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("...")]
|
||||
pub enum Many {
|
||||
Any(#[from] anyhow::Error),
|
||||
Io(#[from] io::Error),
|
||||
}
|
||||
|
||||
fn assert_impl<T: From<io::Error>>() {}
|
||||
|
||||
#[test]
|
||||
fn test_from() {
|
||||
assert_impl::<ErrorStruct>();
|
||||
assert_impl::<ErrorStructOptional>();
|
||||
assert_impl::<ErrorTuple>();
|
||||
assert_impl::<ErrorTupleOptional>();
|
||||
assert_impl::<ErrorEnum>();
|
||||
assert_impl::<ErrorEnumOptional>();
|
||||
assert_impl::<Many>();
|
||||
}
|
||||
+205
@@ -0,0 +1,205 @@
|
||||
#![allow(clippy::needless_late_init, clippy::uninlined_format_args)]
|
||||
|
||||
use core::fmt::{self, Debug, Display};
|
||||
use core::str::FromStr;
|
||||
use thiserror::Error;
|
||||
|
||||
pub struct NoFormat;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DebugOnly;
|
||||
|
||||
pub struct DisplayOnly;
|
||||
|
||||
impl Display for DisplayOnly {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str("display only")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DebugAndDisplay;
|
||||
|
||||
impl Display for DebugAndDisplay {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str("debug and display")
|
||||
}
|
||||
}
|
||||
|
||||
// Should expand to:
|
||||
//
|
||||
// impl<E> Display for EnumDebugField<E>
|
||||
// where
|
||||
// E: Debug;
|
||||
//
|
||||
// impl<E> Error for EnumDebugField<E>
|
||||
// where
|
||||
// Self: Debug + Display;
|
||||
//
|
||||
#[derive(Error, Debug)]
|
||||
pub enum EnumDebugGeneric<E> {
|
||||
#[error("{0:?}")]
|
||||
FatalError(E),
|
||||
}
|
||||
|
||||
// Should expand to:
|
||||
//
|
||||
// impl<E> Display for EnumFromGeneric<E>;
|
||||
//
|
||||
// impl<E> Error for EnumFromGeneric<E>
|
||||
// where
|
||||
// EnumDebugGeneric<E>: Error + 'static,
|
||||
// Self: Debug + Display;
|
||||
//
|
||||
#[derive(Error, Debug)]
|
||||
pub enum EnumFromGeneric<E> {
|
||||
#[error("enum from generic")]
|
||||
Source(#[from] EnumDebugGeneric<E>),
|
||||
}
|
||||
|
||||
// Should expand to:
|
||||
//
|
||||
// impl<HasDisplay, HasDebug, HasNeither> Display
|
||||
// for EnumCompound<HasDisplay, HasDebug, HasNeither>
|
||||
// where
|
||||
// HasDisplay: Display,
|
||||
// HasDebug: Debug;
|
||||
//
|
||||
// impl<HasDisplay, HasDebug, HasNeither> Error
|
||||
// for EnumCompound<HasDisplay, HasDebug, HasNeither>
|
||||
// where
|
||||
// Self: Debug + Display;
|
||||
//
|
||||
#[derive(Error)]
|
||||
pub enum EnumCompound<HasDisplay, HasDebug, HasNeither> {
|
||||
#[error("{0} {1:?}")]
|
||||
DisplayDebug(HasDisplay, HasDebug),
|
||||
#[error("{0}")]
|
||||
Display(HasDisplay, HasNeither),
|
||||
#[error("{1:?}")]
|
||||
Debug(HasNeither, HasDebug),
|
||||
}
|
||||
|
||||
impl<HasDisplay, HasDebug, HasNeither> Debug for EnumCompound<HasDisplay, HasDebug, HasNeither> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str("EnumCompound")
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_display_enum_compound() {
|
||||
let mut instance: EnumCompound<DisplayOnly, DebugOnly, NoFormat>;
|
||||
|
||||
instance = EnumCompound::DisplayDebug(DisplayOnly, DebugOnly);
|
||||
assert_eq!(format!("{}", instance), "display only DebugOnly");
|
||||
|
||||
instance = EnumCompound::Display(DisplayOnly, NoFormat);
|
||||
assert_eq!(format!("{}", instance), "display only");
|
||||
|
||||
instance = EnumCompound::Debug(NoFormat, DebugOnly);
|
||||
assert_eq!(format!("{}", instance), "DebugOnly");
|
||||
}
|
||||
|
||||
// Should expand to:
|
||||
//
|
||||
// impl<E> Display for EnumTransparentGeneric<E>
|
||||
// where
|
||||
// E: Display;
|
||||
//
|
||||
// impl<E> Error for EnumTransparentGeneric<E>
|
||||
// where
|
||||
// E: Error,
|
||||
// Self: Debug + Display;
|
||||
//
|
||||
#[derive(Error, Debug)]
|
||||
pub enum EnumTransparentGeneric<E> {
|
||||
#[error(transparent)]
|
||||
Other(E),
|
||||
}
|
||||
|
||||
// Should expand to:
|
||||
//
|
||||
// impl<E> Display for StructDebugGeneric<E>
|
||||
// where
|
||||
// E: Debug;
|
||||
//
|
||||
// impl<E> Error for StructDebugGeneric<E>
|
||||
// where
|
||||
// Self: Debug + Display;
|
||||
//
|
||||
#[derive(Error, Debug)]
|
||||
#[error("{underlying:?}")]
|
||||
pub struct StructDebugGeneric<E> {
|
||||
pub underlying: E,
|
||||
}
|
||||
|
||||
// Should expand to:
|
||||
//
|
||||
// impl<E> Error for StructFromGeneric<E>
|
||||
// where
|
||||
// StructDebugGeneric<E>: Error + 'static,
|
||||
// Self: Debug + Display;
|
||||
//
|
||||
#[derive(Error, Debug)]
|
||||
pub struct StructFromGeneric<E> {
|
||||
#[from]
|
||||
pub source: StructDebugGeneric<E>,
|
||||
}
|
||||
|
||||
// Should expand to:
|
||||
//
|
||||
// impl<E> Display for StructTransparentGeneric<E>
|
||||
// where
|
||||
// E: Display;
|
||||
//
|
||||
// impl<E> Error for StructTransparentGeneric<E>
|
||||
// where
|
||||
// E: Error,
|
||||
// Self: Debug + Display;
|
||||
//
|
||||
#[derive(Error, Debug)]
|
||||
#[error(transparent)]
|
||||
pub struct StructTransparentGeneric<E>(pub E);
|
||||
|
||||
// Should expand to:
|
||||
//
|
||||
// impl<T: FromStr> Display for AssociatedTypeError<T>
|
||||
// where
|
||||
// T::Err: Display;
|
||||
//
|
||||
// impl<T: FromStr> Error for AssociatedTypeError<T>
|
||||
// where
|
||||
// Self: Debug + Display;
|
||||
//
|
||||
#[derive(Error, Debug)]
|
||||
pub enum AssociatedTypeError<T: FromStr> {
|
||||
#[error("couldn't parse matrix")]
|
||||
Other,
|
||||
#[error("couldn't parse entry: {0}")]
|
||||
EntryParseError(T::Err),
|
||||
}
|
||||
|
||||
// Regression test for https://github.com/dtolnay/thiserror/issues/345
|
||||
#[test]
|
||||
fn test_no_bound_on_named_fmt() {
|
||||
#[derive(Error, Debug)]
|
||||
#[error("{thing}", thing = "...")]
|
||||
struct Error<T> {
|
||||
thing: T,
|
||||
}
|
||||
|
||||
let error = Error { thing: DebugOnly };
|
||||
assert_eq!(error.to_string(), "...");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiple_bound() {
|
||||
#[derive(Error, Debug)]
|
||||
#[error("0x{thing:x} 0x{thing:X}")]
|
||||
pub struct Error<T> {
|
||||
thing: T,
|
||||
}
|
||||
|
||||
let error = Error { thing: 0xFFi32 };
|
||||
assert_eq!(error.to_string(), "0xff 0xFF");
|
||||
}
|
||||
Vendored
+109
@@ -0,0 +1,109 @@
|
||||
#![allow(clippy::mixed_attributes_style)]
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
pub use std::error::Error;
|
||||
|
||||
#[test]
|
||||
fn test_allow_attributes() {
|
||||
#![deny(clippy::allow_attributes)]
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("...")]
|
||||
pub struct MyError(#[from] anyhow::Error);
|
||||
|
||||
let _: MyError;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unused_qualifications() {
|
||||
#![deny(unused_qualifications)]
|
||||
|
||||
// Expansion of derive(Error) macro can't know whether something like
|
||||
// std::error::Error is already imported in the caller's scope so it must
|
||||
// suppress unused_qualifications.
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("...")]
|
||||
pub struct MyError;
|
||||
|
||||
let _: MyError;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_needless_lifetimes() {
|
||||
#![allow(dead_code)]
|
||||
#![deny(clippy::elidable_lifetime_names, clippy::needless_lifetimes)]
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("...")]
|
||||
pub enum MyError<'a> {
|
||||
A(#[from] std::io::Error),
|
||||
B(&'a ()),
|
||||
}
|
||||
|
||||
let _: MyError;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_forbid_needless_lifetimes() {
|
||||
#![forbid(clippy::needless_lifetimes)]
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("...")]
|
||||
pub struct MyError(#[from] std::io::Error);
|
||||
|
||||
let _: MyError;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deprecated() {
|
||||
#![deny(deprecated)]
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[deprecated]
|
||||
#[error("...")]
|
||||
pub struct DeprecatedStruct;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("{message} {}", .message)]
|
||||
pub struct DeprecatedStructField {
|
||||
#[deprecated]
|
||||
message: String,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[deprecated]
|
||||
pub enum DeprecatedEnum {
|
||||
#[error("...")]
|
||||
Variant,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum DeprecatedVariant {
|
||||
#[deprecated]
|
||||
#[error("...")]
|
||||
Variant,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum DeprecatedFrom {
|
||||
#[error(transparent)]
|
||||
Variant(
|
||||
#[from]
|
||||
#[allow(deprecated)]
|
||||
DeprecatedStruct,
|
||||
),
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
let _: DeprecatedStruct;
|
||||
#[allow(deprecated)]
|
||||
let _: DeprecatedStructField;
|
||||
#[allow(deprecated)]
|
||||
let _ = DeprecatedEnum::Variant;
|
||||
#[allow(deprecated)]
|
||||
let _ = DeprecatedVariant::Variant;
|
||||
#[allow(deprecated)]
|
||||
let _ = DeprecatedFrom::Variant(DeprecatedStruct);
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
#![cfg(feature = "std")]
|
||||
#![cfg_attr(thiserror_nightly_testing, feature(error_generic_member_access))]
|
||||
|
||||
#[cfg(thiserror_nightly_testing)]
|
||||
pub mod structs {
|
||||
use std::backtrace::Backtrace;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("...")]
|
||||
pub struct OptSourceNoBacktrace {
|
||||
#[source]
|
||||
pub source: Option<anyhow::Error>,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("...")]
|
||||
pub struct OptSourceAlwaysBacktrace {
|
||||
#[source]
|
||||
pub source: Option<anyhow::Error>,
|
||||
pub backtrace: Backtrace,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("...")]
|
||||
pub struct NoSourceOptBacktrace {
|
||||
#[backtrace]
|
||||
pub backtrace: Option<Backtrace>,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("...")]
|
||||
pub struct AlwaysSourceOptBacktrace {
|
||||
pub source: anyhow::Error,
|
||||
#[backtrace]
|
||||
pub backtrace: Option<Backtrace>,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("...")]
|
||||
pub struct OptSourceOptBacktrace {
|
||||
#[source]
|
||||
pub source: Option<anyhow::Error>,
|
||||
#[backtrace]
|
||||
pub backtrace: Option<Backtrace>,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(thiserror_nightly_testing)]
|
||||
pub mod enums {
|
||||
use std::backtrace::Backtrace;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum OptSourceNoBacktrace {
|
||||
#[error("...")]
|
||||
Test {
|
||||
#[source]
|
||||
source: Option<anyhow::Error>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum OptSourceAlwaysBacktrace {
|
||||
#[error("...")]
|
||||
Test {
|
||||
#[source]
|
||||
source: Option<anyhow::Error>,
|
||||
backtrace: Backtrace,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum NoSourceOptBacktrace {
|
||||
#[error("...")]
|
||||
Test {
|
||||
#[backtrace]
|
||||
backtrace: Option<Backtrace>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum AlwaysSourceOptBacktrace {
|
||||
#[error("...")]
|
||||
Test {
|
||||
source: anyhow::Error,
|
||||
#[backtrace]
|
||||
backtrace: Option<Backtrace>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum OptSourceOptBacktrace {
|
||||
#[error("...")]
|
||||
Test {
|
||||
#[source]
|
||||
source: Option<anyhow::Error>,
|
||||
#[backtrace]
|
||||
backtrace: Option<Backtrace>,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(
|
||||
not(thiserror_nightly_testing),
|
||||
ignore = "requires `--cfg=thiserror_nightly_testing`"
|
||||
)]
|
||||
fn test_option() {}
|
||||
Vendored
+54
@@ -0,0 +1,54 @@
|
||||
#![cfg(feature = "std")]
|
||||
|
||||
use core::fmt::Display;
|
||||
use ref_cast::RefCast;
|
||||
use std::path::{Path, PathBuf};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("failed to read '{file}'")]
|
||||
struct StructPathBuf {
|
||||
file: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug, RefCast)]
|
||||
#[repr(C)]
|
||||
#[error("failed to read '{file}'")]
|
||||
struct StructPath {
|
||||
file: Path,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
enum EnumPathBuf {
|
||||
#[error("failed to read '{0}'")]
|
||||
Read(PathBuf),
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("{tail}")]
|
||||
pub struct UnsizedError {
|
||||
pub head: i32,
|
||||
pub tail: str,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum BothError {
|
||||
#[error("display:{0} debug:{0:?}")]
|
||||
DisplayDebug(PathBuf),
|
||||
#[error("debug:{0:?} display:{0}")]
|
||||
DebugDisplay(PathBuf),
|
||||
}
|
||||
|
||||
fn assert<T: Display>(expected: &str, value: T) {
|
||||
assert_eq!(expected, value.to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_display() {
|
||||
let path = Path::new("/thiserror");
|
||||
let file = path.to_owned();
|
||||
assert("failed to read '/thiserror'", StructPathBuf { file });
|
||||
let file = path.to_owned();
|
||||
assert("failed to read '/thiserror'", EnumPathBuf::Read(file));
|
||||
assert("failed to read '/thiserror'", StructPath::ref_cast(path));
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
use std::error::Error as StdError;
|
||||
use std::io;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("implicit source")]
|
||||
pub struct ImplicitSource {
|
||||
source: io::Error,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("explicit source")]
|
||||
pub struct ExplicitSource {
|
||||
source: String,
|
||||
#[source]
|
||||
io: io::Error,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("boxed source")]
|
||||
pub struct BoxedSource {
|
||||
#[source]
|
||||
source: Box<dyn StdError + Send + 'static>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_implicit_source() {
|
||||
let io = io::Error::new(io::ErrorKind::Other, "oh no!");
|
||||
let error = ImplicitSource { source: io };
|
||||
error.source().unwrap().downcast_ref::<io::Error>().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_explicit_source() {
|
||||
let io = io::Error::new(io::ErrorKind::Other, "oh no!");
|
||||
let error = ExplicitSource {
|
||||
source: String::new(),
|
||||
io,
|
||||
};
|
||||
error.source().unwrap().downcast_ref::<io::Error>().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_boxed_source() {
|
||||
let source = Box::new(io::Error::new(io::ErrorKind::Other, "oh no!"));
|
||||
let error = BoxedSource { source };
|
||||
error.source().unwrap().downcast_ref::<io::Error>().unwrap();
|
||||
}
|
||||
|
||||
macro_rules! error_from_macro {
|
||||
($($variants:tt)*) => {
|
||||
#[derive(Error)]
|
||||
#[derive(Debug)]
|
||||
pub enum MacroSource {
|
||||
$($variants)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test that we generate impls with the proper hygiene
|
||||
#[rustfmt::skip]
|
||||
error_from_macro! {
|
||||
#[error("Something")]
|
||||
Variant(#[from] io::Error)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_not_source() {
|
||||
#[derive(Error, Debug)]
|
||||
#[error("{source} ==> {destination}")]
|
||||
pub struct NotSource {
|
||||
r#source: char,
|
||||
destination: char,
|
||||
}
|
||||
|
||||
let error = NotSource {
|
||||
source: 'S',
|
||||
destination: 'D',
|
||||
};
|
||||
assert_eq!(error.to_string(), "S ==> D");
|
||||
assert!(error.source().is_none());
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
use anyhow::anyhow;
|
||||
use std::error::Error as _;
|
||||
use std::io;
|
||||
use thiserror::Error;
|
||||
|
||||
#[test]
|
||||
fn test_transparent_struct() {
|
||||
#[derive(Error, Debug)]
|
||||
#[error(transparent)]
|
||||
struct Error(ErrorKind);
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
enum ErrorKind {
|
||||
#[error("E0")]
|
||||
E0,
|
||||
#[error("E1")]
|
||||
E1(#[from] io::Error),
|
||||
}
|
||||
|
||||
let error = Error(ErrorKind::E0);
|
||||
assert_eq!("E0", error.to_string());
|
||||
assert!(error.source().is_none());
|
||||
|
||||
let io = io::Error::new(io::ErrorKind::Other, "oh no!");
|
||||
let error = Error(ErrorKind::from(io));
|
||||
assert_eq!("E1", error.to_string());
|
||||
error.source().unwrap().downcast_ref::<io::Error>().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transparent_enum() {
|
||||
#[derive(Error, Debug)]
|
||||
enum Error {
|
||||
#[error("this failed")]
|
||||
This,
|
||||
#[error(transparent)]
|
||||
Other(anyhow::Error),
|
||||
}
|
||||
|
||||
let error = Error::This;
|
||||
assert_eq!("this failed", error.to_string());
|
||||
|
||||
let error = Error::Other(anyhow!("inner").context("outer"));
|
||||
assert_eq!("outer", error.to_string());
|
||||
assert_eq!("inner", error.source().unwrap().to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transparent_enum_with_default_message() {
|
||||
#[derive(Error, Debug)]
|
||||
#[error("this failed: {0}_{1}")]
|
||||
enum Error {
|
||||
This(i32, i32),
|
||||
#[error(transparent)]
|
||||
Other(anyhow::Error),
|
||||
}
|
||||
|
||||
let error = Error::This(-1, -1);
|
||||
assert_eq!("this failed: -1_-1", error.to_string());
|
||||
|
||||
let error = Error::Other(anyhow!("inner").context("outer"));
|
||||
assert_eq!("outer", error.to_string());
|
||||
assert_eq!("inner", error.source().unwrap().to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_anyhow() {
|
||||
#[derive(Error, Debug)]
|
||||
#[error(transparent)]
|
||||
struct Any(#[from] anyhow::Error);
|
||||
|
||||
let error = Any::from(anyhow!("inner").context("outer"));
|
||||
assert_eq!("outer", error.to_string());
|
||||
assert_eq!("inner", error.source().unwrap().to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_non_static() {
|
||||
#[derive(Error, Debug)]
|
||||
#[error(transparent)]
|
||||
struct Error<'a> {
|
||||
inner: ErrorKind<'a>,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
enum ErrorKind<'a> {
|
||||
#[error("unexpected token: {:?}", token)]
|
||||
Unexpected { token: &'a str },
|
||||
}
|
||||
|
||||
let error = Error {
|
||||
inner: ErrorKind::Unexpected { token: "error" },
|
||||
};
|
||||
assert_eq!("unexpected token: \"error\"", error.to_string());
|
||||
assert!(error.source().is_none());
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error(transparent)]
|
||||
pub struct Error(#[error(transparent)] std::io::Error);
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: #[error(transparent)] needs to go outside the enum or struct, not on an individual field
|
||||
--> tests/ui/bad-field-attr.rs:5:18
|
||||
|
|
||||
5 | pub struct Error(#[error(transparent)] std::io::Error);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
use thiserror::Error;
|
||||
|
||||
macro_rules! error_type {
|
||||
($name:ident, $what:expr) => {
|
||||
// Use #[error("invalid {}", $what)] instead.
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error(concat!("invalid ", $what))]
|
||||
pub struct $name;
|
||||
};
|
||||
}
|
||||
|
||||
error_type!(Error, "foo");
|
||||
|
||||
fn main() {}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
error: expected one of: string literal, `transparent`, `fmt`
|
||||
--> tests/ui/concat-display.rs:8:17
|
||||
|
|
||||
8 | #[error(concat!("invalid ", $what))]
|
||||
| ^^^^^^
|
||||
...
|
||||
13 | error_type!(Error, "foo");
|
||||
| ------------------------- in this macro invocation
|
||||
|
|
||||
= note: this error originates in the macro `error_type` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
@@ -0,0 +1,7 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("{_}")]
|
||||
pub struct Error;
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,7 @@
|
||||
error: invalid format string: invalid argument name `_`
|
||||
--> tests/ui/display-underscore.rs:4:11
|
||||
|
|
||||
4 | #[error("{_}")]
|
||||
| ^ invalid argument name in format string
|
||||
|
|
||||
= note: argument name cannot be a single underscore
|
||||
@@ -0,0 +1,13 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ErrorEnum {
|
||||
Confusing {
|
||||
#[source]
|
||||
a: std::io::Error,
|
||||
#[source]
|
||||
b: anyhow::Error,
|
||||
},
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: duplicate #[source] attribute
|
||||
--> tests/ui/duplicate-enum-source.rs:8:9
|
||||
|
|
||||
8 | #[source]
|
||||
| ^^^^^^^^^
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("...")]
|
||||
#[error("...")]
|
||||
pub struct Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error(fmt = core::fmt::Octal::fmt)]
|
||||
#[error(fmt = core::fmt::LowerHex::fmt)]
|
||||
pub enum FmtFmt {}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error(fmt = core::fmt::Octal::fmt)]
|
||||
#[error(transparent)]
|
||||
pub enum FmtTransparent {}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error(fmt = core::fmt::Octal::fmt)]
|
||||
#[error("...")]
|
||||
pub enum FmtDisplay {}
|
||||
|
||||
fn main() {}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
error: only one #[error(...)] attribute is allowed
|
||||
--> tests/ui/duplicate-fmt.rs:5:1
|
||||
|
|
||||
5 | #[error("...")]
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: duplicate #[error(fmt = ...)] attribute
|
||||
--> tests/ui/duplicate-fmt.rs:10:1
|
||||
|
|
||||
10 | #[error(fmt = core::fmt::LowerHex::fmt)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: cannot have both #[error(transparent)] and #[error(fmt = ...)]
|
||||
--> tests/ui/duplicate-fmt.rs:14:1
|
||||
|
|
||||
14 | #[error(fmt = core::fmt::Octal::fmt)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: cannot have both #[error(fmt = ...)] and a format arguments attribute
|
||||
--> tests/ui/duplicate-fmt.rs:20:1
|
||||
|
|
||||
20 | #[error("...")]
|
||||
| ^^^^^^^^^^^^^^^
|
||||
@@ -0,0 +1,11 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub struct ErrorStruct {
|
||||
#[source]
|
||||
a: std::io::Error,
|
||||
#[source]
|
||||
b: anyhow::Error,
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: duplicate #[source] attribute
|
||||
--> tests/ui/duplicate-struct-source.rs:7:5
|
||||
|
|
||||
7 | #[source]
|
||||
| ^^^^^^^^^
|
||||
@@ -0,0 +1,8 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error(transparent)]
|
||||
#[error(transparent)]
|
||||
pub struct Error(anyhow::Error);
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: duplicate #[error(transparent)] attribute
|
||||
--> tests/ui/duplicate-transparent.rs:5:1
|
||||
|
|
||||
5 | #[error(transparent)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
@@ -0,0 +1,7 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("".yellow)]
|
||||
pub struct ArgError;
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,24 @@
|
||||
error: expected `,`, found `.`
|
||||
--> tests/ui/expression-fallback.rs:4:11
|
||||
|
|
||||
4 | #[error("".yellow)]
|
||||
| ^ expected `,`
|
||||
|
||||
error: argument never used
|
||||
--> tests/ui/expression-fallback.rs:4:12
|
||||
|
|
||||
4 | #[error("".yellow)]
|
||||
| -- ^^^^^^ argument never used
|
||||
| |
|
||||
| formatting specifier missing
|
||||
|
|
||||
help: format specifiers use curly braces, consider adding a format specifier
|
||||
|
|
||||
4 | #[error("{}".yellow)]
|
||||
| ++
|
||||
|
||||
error[E0425]: cannot find value `yellow` in this scope
|
||||
--> tests/ui/expression-fallback.rs:4:12
|
||||
|
|
||||
4 | #[error("".yellow)]
|
||||
| ^^^^^^ not found in this scope
|
||||
@@ -0,0 +1,14 @@
|
||||
use core::fmt::{self, Display};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error]
|
||||
pub struct MyError;
|
||||
|
||||
impl Display for MyError {
|
||||
fn fmt(&self, _formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,16 @@
|
||||
error: expected attribute arguments in parentheses: #[error(...)]
|
||||
--> tests/ui/fallback-impl-with-display.rs:5:3
|
||||
|
|
||||
5 | #[error]
|
||||
| ^^^^^
|
||||
|
||||
error[E0119]: conflicting implementations of trait `std::fmt::Display` for type `MyError`
|
||||
--> tests/ui/fallback-impl-with-display.rs:4:10
|
||||
|
|
||||
4 | #[derive(Error, Debug)]
|
||||
| ^^^^^ conflicting implementation for `MyError`
|
||||
...
|
||||
8 | impl Display for MyError {
|
||||
| ------------------------ first implementation here
|
||||
|
|
||||
= note: this error originates in the derive macro `Error` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
@@ -0,0 +1,15 @@
|
||||
// https://github.com/dtolnay/thiserror/issues/163
|
||||
|
||||
use std::backtrace::Backtrace;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("...")]
|
||||
pub struct Error(
|
||||
#[from]
|
||||
#[backtrace]
|
||||
std::io::Error,
|
||||
Backtrace,
|
||||
);
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: deriving From requires no fields other than source and backtrace
|
||||
--> tests/ui/from-backtrace-backtrace.rs:9:5
|
||||
|
|
||||
9 | #[from]
|
||||
| ^^^^^^^
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub struct Error {
|
||||
#[source]
|
||||
source: std::io::Error,
|
||||
#[from]
|
||||
other: anyhow::Error,
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: #[from] is only supported on the source field, not any other field
|
||||
--> tests/ui/from-not-source.rs:7:5
|
||||
|
|
||||
7 | #[from]
|
||||
| ^^^^^^^
|
||||
@@ -0,0 +1,11 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error]
|
||||
pub struct MyError;
|
||||
|
||||
fn main() {
|
||||
// No error on the following line. Thiserror emits an Error impl despite the
|
||||
// bad attribute.
|
||||
_ = &MyError as &dyn std::error::Error;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: expected attribute arguments in parentheses: #[error(...)]
|
||||
--> tests/ui/invalid-input-impl-anyway.rs:4:3
|
||||
|
|
||||
4 | #[error]
|
||||
| ^^^^^
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
use core::fmt::Debug;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("error")]
|
||||
struct Error<'a>(#[from] Inner<'a>);
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("{0}")]
|
||||
struct Inner<'a>(&'a str);
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
enum Enum<'a> {
|
||||
#[error("error")]
|
||||
Foo(#[from] Generic<&'a str>),
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("{0:?}")]
|
||||
struct Generic<T: Debug>(T);
|
||||
|
||||
fn main() -> Result<(), Error<'static>> {
|
||||
Err(Error(Inner("some text")))
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
error: non-static lifetimes are not allowed in the source of an error, because std::error::Error requires the source is dyn Error + 'static
|
||||
--> tests/ui/lifetime.rs:6:26
|
||||
|
|
||||
6 | struct Error<'a>(#[from] Inner<'a>);
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: non-static lifetimes are not allowed in the source of an error, because std::error::Error requires the source is dyn Error + 'static
|
||||
--> tests/ui/lifetime.rs:15:17
|
||||
|
|
||||
15 | Foo(#[from] Generic<&'a str>),
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
@@ -0,0 +1,9 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum MyError {
|
||||
First,
|
||||
Second,
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,19 @@
|
||||
error[E0277]: `MyError` doesn't implement `std::fmt::Display`
|
||||
--> tests/ui/missing-display.rs:4:10
|
||||
|
|
||||
3 | #[derive(Error, Debug)]
|
||||
| ----- in this derive macro expansion
|
||||
4 | pub enum MyError {
|
||||
| ^^^^^^^ unsatisfied trait bound
|
||||
|
|
||||
help: the trait `std::fmt::Display` is not implemented for `MyError`
|
||||
--> tests/ui/missing-display.rs:4:1
|
||||
|
|
||||
4 | pub enum MyError {
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
note: required by a bound in `std::error::Error`
|
||||
--> $RUST/core/src/error.rs
|
||||
|
|
||||
| pub trait Error: Debug + Display {
|
||||
| ^^^^^^^ required by this bound in `Error`
|
||||
= note: this error originates in the derive macro `Error` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("...")]
|
||||
A(usize),
|
||||
B(usize),
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: missing #[error("...")] display attribute
|
||||
--> tests/ui/missing-fmt.rs:7:5
|
||||
|
|
||||
7 | B(usize),
|
||||
| ^^^^^^^^
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct NoDisplay;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("thread: {thread}")]
|
||||
pub struct Error {
|
||||
thread: NoDisplay,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("thread: {thread:o}")]
|
||||
pub struct ErrorOctal {
|
||||
thread: NoDisplay,
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
error[E0599]: the method `as_display` exists for reference `&NoDisplay`, but its trait bounds were not satisfied
|
||||
--> tests/ui/no-display.rs:7:9
|
||||
|
|
||||
4 | struct NoDisplay;
|
||||
| ---------------- doesn't satisfy `NoDisplay: std::fmt::Display`
|
||||
...
|
||||
7 | #[error("thread: {thread}")]
|
||||
| ^^^^^^^^^^^^^^^^^^ method cannot be called on `&NoDisplay` due to unsatisfied trait bounds
|
||||
|
|
||||
= note: the following trait bounds were not satisfied:
|
||||
`NoDisplay: std::fmt::Display`
|
||||
which is required by `&NoDisplay: AsDisplay<'_>`
|
||||
note: the trait `std::fmt::Display` must be implemented
|
||||
--> $RUST/core/src/fmt/mod.rs
|
||||
|
|
||||
| pub trait Display: PointeeSized {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
= help: items from traits can only be used if the trait is implemented and in scope
|
||||
= note: the following trait defines an item `as_display`, perhaps you need to implement it:
|
||||
candidate #1: `AsDisplay`
|
||||
|
||||
error[E0277]: the trait bound `NoDisplay: Octal` is not satisfied
|
||||
--> tests/ui/no-display.rs:13:9
|
||||
|
|
||||
12 | #[derive(Error, Debug)]
|
||||
| ----- in this derive macro expansion
|
||||
13 | #[error("thread: {thread:o}")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound
|
||||
|
|
||||
help: the trait `Octal` is not implemented for `NoDisplay`
|
||||
--> tests/ui/no-display.rs:4:1
|
||||
|
|
||||
4 | struct NoDisplay;
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
= help: the following other types implement trait `Octal`:
|
||||
&T
|
||||
&mut T
|
||||
NonZero<T>
|
||||
Saturating<T>
|
||||
Wrapping<T>
|
||||
i128
|
||||
i16
|
||||
i32
|
||||
and $N others
|
||||
= note: required for `&NoDisplay` to implement `Octal`
|
||||
= note: this error originates in the macro `$crate::format_args` which comes from the expansion of the derive macro `Error` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
@@ -0,0 +1,7 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("invalid rdo_lookahead_frames {0} (expected < {})", i32::MAX)]
|
||||
pub struct Error(u32);
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: ambiguous reference to positional arguments by number in a tuple struct; change this to a named argument
|
||||
--> tests/ui/numbered-positional-tuple.rs:4:61
|
||||
|
|
||||
4 | #[error("invalid rdo_lookahead_frames {0} (expected < {})", i32::MAX)]
|
||||
| ^^^^^^^^
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("error: {r#fn}")]
|
||||
pub struct Error {
|
||||
r#fn: &'static str,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let r#fn = "...";
|
||||
let _ = format!("error: {r#fn}");
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
error: invalid format string: raw identifiers are not supported
|
||||
--> tests/ui/raw-identifier.rs:4:18
|
||||
|
|
||||
4 | #[error("error: {r#fn}")]
|
||||
| --^^
|
||||
| |
|
||||
| raw identifier used here in format string
|
||||
| help: remove the `r#`
|
||||
|
|
||||
= note: identifiers in format strings can be keywords and don't need to be prefixed with `r#`
|
||||
|
||||
error: invalid format string: raw identifiers are not supported
|
||||
--> tests/ui/raw-identifier.rs:11:30
|
||||
|
|
||||
11 | let _ = format!("error: {r#fn}");
|
||||
| --^^
|
||||
| |
|
||||
| raw identifier used here in format string
|
||||
| help: remove the `r#`
|
||||
|
|
||||
= note: identifiers in format strings can be keywords and don't need to be prefixed with `r#`
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("failed to open")]
|
||||
OpenFile(#[from] std::io::Error),
|
||||
#[error("failed to close")]
|
||||
CloseFile(#[from] std::io::Error),
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,8 @@
|
||||
error[E0119]: conflicting implementations of trait `From<std::io::Error>` for type `Error`
|
||||
--> tests/ui/same-from-type.rs:8:15
|
||||
|
|
||||
6 | OpenFile(#[from] std::io::Error),
|
||||
| ------- first implementation here
|
||||
7 | #[error("failed to close")]
|
||||
8 | CloseFile(#[from] std::io::Error),
|
||||
| ^^^^^^^ conflicting implementation for `Error`
|
||||
@@ -0,0 +1,12 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NotError;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("...")]
|
||||
pub enum ErrorEnum {
|
||||
Broken { source: NotError },
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,22 @@
|
||||
error[E0599]: the method `as_dyn_error` exists for reference `&NotError`, but its trait bounds were not satisfied
|
||||
--> tests/ui/source-enum-not-error.rs:9:14
|
||||
|
|
||||
4 | pub struct NotError;
|
||||
| ------------------- doesn't satisfy `NotError: AsDynError<'_>` or `NotError: std::error::Error`
|
||||
...
|
||||
9 | Broken { source: NotError },
|
||||
| ^^^^^^ method cannot be called on `&NotError` due to unsatisfied trait bounds
|
||||
|
|
||||
= note: the following trait bounds were not satisfied:
|
||||
`NotError: std::error::Error`
|
||||
which is required by `NotError: AsDynError<'_>`
|
||||
`&NotError: std::error::Error`
|
||||
which is required by `&NotError: AsDynError<'_>`
|
||||
note: the trait `std::error::Error` must be implemented
|
||||
--> $RUST/core/src/error.rs
|
||||
|
|
||||
| pub trait Error: Debug + Display {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
= help: items from traits can only be used if the trait is implemented and in scope
|
||||
= note: the following trait defines an item `as_dyn_error`, perhaps you need to implement it:
|
||||
candidate #1: `AsDynError`
|
||||
@@ -0,0 +1,12 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NotError;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("...")]
|
||||
pub enum ErrorEnum {
|
||||
Broken(#[source] NotError),
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,22 @@
|
||||
error[E0599]: the method `as_dyn_error` exists for reference `&NotError`, but its trait bounds were not satisfied
|
||||
--> tests/ui/source-enum-unnamed-field-not-error.rs:9:12
|
||||
|
|
||||
4 | pub struct NotError;
|
||||
| ------------------- doesn't satisfy `NotError: AsDynError<'_>` or `NotError: std::error::Error`
|
||||
...
|
||||
9 | Broken(#[source] NotError),
|
||||
| ^^^^^^^^^ method cannot be called on `&NotError` due to unsatisfied trait bounds
|
||||
|
|
||||
= note: the following trait bounds were not satisfied:
|
||||
`NotError: std::error::Error`
|
||||
which is required by `NotError: AsDynError<'_>`
|
||||
`&NotError: std::error::Error`
|
||||
which is required by `&NotError: AsDynError<'_>`
|
||||
note: the trait `std::error::Error` must be implemented
|
||||
--> $RUST/core/src/error.rs
|
||||
|
|
||||
| pub trait Error: Debug + Display {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
= help: items from traits can only be used if the trait is implemented and in scope
|
||||
= note: the following trait defines an item `as_dyn_error`, perhaps you need to implement it:
|
||||
candidate #1: `AsDynError`
|
||||
@@ -0,0 +1,12 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct NotError;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("...")]
|
||||
pub struct ErrorStruct {
|
||||
source: NotError,
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,20 @@
|
||||
error[E0599]: the method `as_dyn_error` exists for struct `NotError`, but its trait bounds were not satisfied
|
||||
--> tests/ui/source-struct-not-error.rs:9:5
|
||||
|
|
||||
4 | struct NotError;
|
||||
| --------------- method `as_dyn_error` not found for this struct because it doesn't satisfy `NotError: AsDynError<'_>` or `NotError: std::error::Error`
|
||||
...
|
||||
9 | source: NotError,
|
||||
| ^^^^^^ method cannot be called on `NotError` due to unsatisfied trait bounds
|
||||
|
|
||||
= note: the following trait bounds were not satisfied:
|
||||
`NotError: std::error::Error`
|
||||
which is required by `NotError: AsDynError<'_>`
|
||||
note: the trait `std::error::Error` must be implemented
|
||||
--> $RUST/core/src/error.rs
|
||||
|
|
||||
| pub trait Error: Debug + Display {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
= help: items from traits can only be used if the trait is implemented and in scope
|
||||
= note: the following trait defines an item `as_dyn_error`, perhaps you need to implement it:
|
||||
candidate #1: `AsDynError`
|
||||
@@ -0,0 +1,10 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct NotError;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("...")]
|
||||
pub struct ErrorStruct(#[source] NotError);
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,20 @@
|
||||
error[E0599]: the method `as_dyn_error` exists for struct `NotError`, but its trait bounds were not satisfied
|
||||
--> tests/ui/source-struct-unnamed-field-not-error.rs:8:24
|
||||
|
|
||||
4 | struct NotError;
|
||||
| --------------- method `as_dyn_error` not found for this struct because it doesn't satisfy `NotError: AsDynError<'_>` or `NotError: std::error::Error`
|
||||
...
|
||||
8 | pub struct ErrorStruct(#[source] NotError);
|
||||
| ^^^^^^^^^ method cannot be called on `NotError` due to unsatisfied trait bounds
|
||||
|
|
||||
= note: the following trait bounds were not satisfied:
|
||||
`NotError: std::error::Error`
|
||||
which is required by `NotError: AsDynError<'_>`
|
||||
note: the trait `std::error::Error` must be implemented
|
||||
--> $RUST/core/src/error.rs
|
||||
|
|
||||
| pub trait Error: Debug + Display {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
= help: items from traits can only be used if the trait is implemented and in scope
|
||||
= note: the following trait defines an item `as_dyn_error`, perhaps you need to implement it:
|
||||
candidate #1: `AsDynError`
|
||||
@@ -0,0 +1,7 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error(fmt = core::fmt::Octal::fmt)]
|
||||
pub struct Error(i32);
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: #[error(fmt = ...)] is only supported in enums; for a struct, handwrite your own Display impl
|
||||
--> tests/ui/struct-with-fmt.rs:4:1
|
||||
|
|
||||
4 | #[error(fmt = core::fmt::Octal::fmt)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@@ -0,0 +1,8 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error(transparent)]
|
||||
#[error("...")]
|
||||
pub struct Error(anyhow::Error);
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: cannot have both #[error(transparent)] and a display attribute
|
||||
--> tests/ui/transparent-display.rs:5:1
|
||||
|
|
||||
5 | #[error("...")]
|
||||
| ^^^^^^^^^^^^^^^
|
||||
@@ -0,0 +1,9 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error(transparent)]
|
||||
Other(anyhow::Error, String),
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,6 @@
|
||||
error: #[error(transparent)] requires exactly one field
|
||||
--> tests/ui/transparent-enum-many.rs:5:5
|
||||
|
|
||||
5 | / #[error(transparent)]
|
||||
6 | | Other(anyhow::Error, String),
|
||||
| |________________________________^
|
||||
@@ -0,0 +1,9 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error(transparent)]
|
||||
Other { message: String },
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,20 @@
|
||||
error[E0599]: the method `as_dyn_error` exists for reference `&String`, but its trait bounds were not satisfied
|
||||
--> tests/ui/transparent-enum-not-error.rs:5:13
|
||||
|
|
||||
5 | #[error(transparent)]
|
||||
| ^^^^^^^^^^^ method cannot be called on `&String` due to unsatisfied trait bounds
|
||||
|
|
||||
::: $RUST/alloc/src/string.rs
|
||||
|
|
||||
| pub struct String {
|
||||
| ----------------- doesn't satisfy `String: AsDynError<'_>` or `String: std::error::Error`
|
||||
|
|
||||
= note: the following trait bounds were not satisfied:
|
||||
`String: std::error::Error`
|
||||
which is required by `String: AsDynError<'_>`
|
||||
`&String: std::error::Error`
|
||||
which is required by `&String: AsDynError<'_>`
|
||||
`str: Sized`
|
||||
which is required by `str: AsDynError<'_>`
|
||||
`str: std::error::Error`
|
||||
which is required by `str: AsDynError<'_>`
|
||||
@@ -0,0 +1,9 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error(transparent)]
|
||||
Other(#[source] anyhow::Error),
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: transparent variant can't contain #[source]
|
||||
--> tests/ui/transparent-enum-source.rs:6:11
|
||||
|
|
||||
6 | Other(#[source] anyhow::Error),
|
||||
| ^^^^^^^^^
|
||||
@@ -0,0 +1,9 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error(transparent)]
|
||||
Other(String),
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,20 @@
|
||||
error[E0599]: the method `as_dyn_error` exists for reference `&String`, but its trait bounds were not satisfied
|
||||
--> tests/ui/transparent-enum-unnamed-field-not-error.rs:5:13
|
||||
|
|
||||
5 | #[error(transparent)]
|
||||
| ^^^^^^^^^^^ method cannot be called on `&String` due to unsatisfied trait bounds
|
||||
|
|
||||
::: $RUST/alloc/src/string.rs
|
||||
|
|
||||
| pub struct String {
|
||||
| ----------------- doesn't satisfy `String: AsDynError<'_>` or `String: std::error::Error`
|
||||
|
|
||||
= note: the following trait bounds were not satisfied:
|
||||
`String: std::error::Error`
|
||||
which is required by `String: AsDynError<'_>`
|
||||
`&String: std::error::Error`
|
||||
which is required by `&String: AsDynError<'_>`
|
||||
`str: Sized`
|
||||
which is required by `str: AsDynError<'_>`
|
||||
`str: std::error::Error`
|
||||
which is required by `str: AsDynError<'_>`
|
||||
@@ -0,0 +1,10 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error(transparent)]
|
||||
pub struct Error {
|
||||
inner: anyhow::Error,
|
||||
what: String,
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: #[error(transparent)] requires exactly one field
|
||||
--> tests/ui/transparent-struct-many.rs:4:1
|
||||
|
|
||||
4 | #[error(transparent)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
@@ -0,0 +1,9 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error(transparent)]
|
||||
pub struct Error {
|
||||
message: String,
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,18 @@
|
||||
error[E0599]: the method `as_dyn_error` exists for struct `String`, but its trait bounds were not satisfied
|
||||
--> tests/ui/transparent-struct-not-error.rs:4:9
|
||||
|
|
||||
4 | #[error(transparent)]
|
||||
| ^^^^^^^^^^^ method cannot be called on `String` due to unsatisfied trait bounds
|
||||
|
|
||||
::: $RUST/alloc/src/string.rs
|
||||
|
|
||||
| pub struct String {
|
||||
| ----------------- doesn't satisfy `String: AsDynError<'_>` or `String: std::error::Error`
|
||||
|
|
||||
= note: the following trait bounds were not satisfied:
|
||||
`String: std::error::Error`
|
||||
which is required by `String: AsDynError<'_>`
|
||||
`str: Sized`
|
||||
which is required by `str: AsDynError<'_>`
|
||||
`str: std::error::Error`
|
||||
which is required by `str: AsDynError<'_>`
|
||||
@@ -0,0 +1,7 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error(transparent)]
|
||||
pub struct Error(#[source] anyhow::Error);
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: transparent error struct can't contain #[source]
|
||||
--> tests/ui/transparent-struct-source.rs:5:18
|
||||
|
|
||||
5 | pub struct Error(#[source] anyhow::Error);
|
||||
| ^^^^^^^^^
|
||||
@@ -0,0 +1,7 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error(transparent)]
|
||||
pub struct Error(String);
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,18 @@
|
||||
error[E0599]: the method `as_dyn_error` exists for struct `String`, but its trait bounds were not satisfied
|
||||
--> tests/ui/transparent-struct-unnamed-field-not-error.rs:4:9
|
||||
|
|
||||
4 | #[error(transparent)]
|
||||
| ^^^^^^^^^^^ method cannot be called on `String` due to unsatisfied trait bounds
|
||||
|
|
||||
::: $RUST/alloc/src/string.rs
|
||||
|
|
||||
| pub struct String {
|
||||
| ----------------- doesn't satisfy `String: AsDynError<'_>` or `String: std::error::Error`
|
||||
|
|
||||
= note: the following trait bounds were not satisfied:
|
||||
`String: std::error::Error`
|
||||
which is required by `String: AsDynError<'_>`
|
||||
`str: Sized`
|
||||
which is required by `str: AsDynError<'_>`
|
||||
`str: std::error::Error`
|
||||
which is required by `str: AsDynError<'_>`
|
||||
@@ -0,0 +1,9 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("{self}")]
|
||||
pub struct Error;
|
||||
|
||||
fn main() {
|
||||
__FAIL__;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
error[E0425]: cannot find value `__FAIL__` in this scope
|
||||
--> tests/ui/unconditional-recursion.rs:8:5
|
||||
|
|
||||
8 | __FAIL__;
|
||||
| ^^^^^^^^ not found in this scope
|
||||
|
||||
warning: function cannot return without recursing
|
||||
--> tests/ui/unconditional-recursion.rs:4:9
|
||||
|
|
||||
4 | #[error("{self}")]
|
||||
| ^^^^^^^^
|
||||
| |
|
||||
| cannot return without recursing
|
||||
| recursive call site
|
||||
|
|
||||
= help: a `loop` may express intention better if this is on purpose
|
||||
note: the lint level is defined here
|
||||
--> tests/ui/unconditional-recursion.rs:4:9
|
||||
|
|
||||
4 | #[error("{self}")]
|
||||
| ^^^^^^^^
|
||||
@@ -0,0 +1,11 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
What {
|
||||
#[error("...")]
|
||||
io: std::io::Error,
|
||||
},
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: not expected here; the #[error(...)] attribute belongs on top of a struct or an enum variant
|
||||
--> tests/ui/unexpected-field-fmt.rs:6:9
|
||||
|
|
||||
6 | #[error("...")]
|
||||
| ^^^^^^^^^^^^^^^
|
||||
Loaded 100 of 17624 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user