├── .github ├── FUNDING.yml ├── renovate.json ├── settings │ ├── typos.yml │ └── yamllint.yml └── workflows │ ├── actions.yml │ ├── audit.yml │ ├── coverage.yml │ ├── pull-request.yml │ ├── release.yml │ └── spellcheck.yml ├── .gitignore ├── Cargo.toml ├── Justfile ├── LICENSE.md ├── README.md ├── build.rs ├── codecov.yml ├── docs ├── features.md ├── state-escalation.md └── why-augment-result.md ├── rustfmt.toml └── src ├── aberration.rs ├── concern.rs ├── convert.rs ├── diagnostic.rs ├── iter.rs ├── lib.rs ├── nightly.rs ├── outcome.rs ├── prelude.rs ├── private.rs ├── report.rs ├── unstable.rs └── wrap.rs /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: bruxisma 2 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["bruxisma/renovate"], 3 | "cargo": { 4 | "rangeStrategy": "auto" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.github/settings/typos.yml: -------------------------------------------------------------------------------- 1 | [default] 2 | locale = 'en-us' 3 | -------------------------------------------------------------------------------- /.github/settings/yamllint.yml: -------------------------------------------------------------------------------- 1 | extends: default 2 | rules: 3 | comments-indentation: {} 4 | document-start: 5 | present: false 6 | indentation: 7 | indent-sequences: true 8 | spaces: 2 9 | line-length: 10 | allow-non-breakable-inline-mappings: true 11 | -------------------------------------------------------------------------------- /.github/workflows/actions.yml: -------------------------------------------------------------------------------- 1 | name: Lint Actions 2 | on: 3 | pull_request: 4 | paths: [.github/**.yml] 5 | types: [synchronize, edited, opened] 6 | push: 7 | paths: [.github/**.yml] 8 | jobs: 9 | skip: 10 | name: Check Duplicate Actions 11 | #continue-on-error: true 12 | runs-on: ubuntu-latest 13 | outputs: 14 | should-skip: ${{ steps.skip-check.outputs.should_skip }} 15 | steps: 16 | - name: Skip Duplicate Actions (Lint Actions) 17 | uses: fkirc/skip-duplicate-actions@f75f66ce1886f00957d99748a42c724f4330bdcf # v5 18 | id: skip-check 19 | actions: 20 | name: Action Workflows 21 | runs-on: ubuntu-latest 22 | needs: skip 23 | if: needs.skip.outputs.should-skip != 'true' 24 | steps: 25 | - name: Checkout 26 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 27 | - name: Review Dog - actionlint 28 | uses: reviewdog/action-actionlint@a5524e1c19e62881d79c1f1b9b6f09f16356e281 # v1 29 | yaml: 30 | name: YAML 31 | runs-on: ubuntu-latest 32 | needs: skip 33 | if: needs.skip.outputs.should-skip != 'true' 34 | steps: 35 | - name: Checkout 36 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 37 | - name: Review Dog - yamllint 38 | uses: reviewdog/action-yamllint@f01d8a48fd8d89f89895499fca2cff09f9e9e8c0 # v1 39 | with: 40 | yamllint_flags: >- 41 | --config-file ${{github.workspace}}/.github/settings/yamllint.yml 42 | ${{github.workspace}} 43 | -------------------------------------------------------------------------------- /.github/workflows/audit.yml: -------------------------------------------------------------------------------- 1 | name: Audit 2 | on: 3 | pull_request: 4 | paths: [Cargo.toml, Cargo.lock] 5 | types: [synchronize, opened, edited] 6 | push: 7 | paths: [Cargo.toml, Cargo.lock] 8 | jobs: 9 | skip: 10 | name: Check Duplicate Actions 11 | # continue-on-error: true 12 | runs-on: ubuntu-latest 13 | outputs: 14 | should-skip: ${{ steps.skip-check.outputs.should_skip }} 15 | steps: 16 | - name: Skip Duplicate Actions (Audit) 17 | uses: fkirc/skip-duplicate-actions@f75f66ce1886f00957d99748a42c724f4330bdcf # v5 18 | id: skip-check 19 | audit: 20 | name: Audit 21 | runs-on: ubuntu-latest 22 | needs: skip 23 | if: needs.skip.outputs.should-skip != 'true' 24 | steps: 25 | - name: Checkout 26 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 27 | - name: Audit Check 28 | uses: actions-rs/audit-check@35b7b53b1e25b55642157ac01b4adceb5b9ebef3 # renovate: tag=v1 29 | with: 30 | token: ${{github.token}} 31 | -------------------------------------------------------------------------------- /.github/workflows/coverage.yml: -------------------------------------------------------------------------------- 1 | name: Code Coverage 2 | on: 3 | pull_request: 4 | types: [opened, edited, synchronize] 5 | push: 6 | branches: main 7 | jobs: 8 | coverage: 9 | name: Code Coverage 10 | runs-on: ${{matrix.os}}-latest 11 | strategy: 12 | matrix: 13 | os: [ubuntu, windows, macos] 14 | permissions: 15 | id-token: write 16 | checks: read 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 20 | - name: Authenticate GitHub CLI 21 | run: echo ${{github.token}} | gh auth login --with-token 22 | - name: Setup Rust Toolchain and Tools 23 | uses: moonrepo/setup-rust@ede6de059f8046a5e236c94046823e2af11ca670 # v1 24 | with: 25 | bins: sccache, grcov, cargo-hack 26 | channel: nightly 27 | components: llvm-tools-preview 28 | cache-target: coverage 29 | - name: Setup Shared Cache Directory 30 | shell: bash 31 | run: | 32 | declare path="${HOME}/.cache/sccache" 33 | if test "${RUNNER_OS}" = "Windows"; then 34 | path=$(cygpath --windows "${path}") 35 | fi 36 | echo "SCCACHE_DIR=${path}" >> "${GITHUB_ENV}" 37 | - name: Restore sccache build artifacts 38 | uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4 39 | with: 40 | restore-keys: ${{runner.os}}-nightly-coverage- 41 | key: ${{runner.os}}-nightly-coverage-${{hashFiles('**/Cargo.lock')}} 42 | path: ${{env.SCCACHE_DIR}} 43 | - name: Run Tests (coverage) 44 | run: cargo hack test --profile=coverage --feature-powerset 45 | env: 46 | LLVM_PROFILE_FILE: target/coverage/outcome-%p-%m.profraw 47 | RUSTC_WRAPPER: sccache 48 | RUSTDOCFLAGS: >- 49 | -C instrument-coverage 50 | -Zunstable-options 51 | --persist-doctests ${{github.workspace}}/target/coverage 52 | RUSTFLAGS: -C instrument-coverage 53 | - name: Collect Coverage 54 | run: >- 55 | grcov ${{github.workspace}} 56 | --binary-path ${{github.workspace}}/target/coverage 57 | --output-path ${{github.workspace}}/coverage.info 58 | --output-type lcov 59 | --source-dir ${{github.workspace}} 60 | --ignore "/*" 61 | --ignore "C:/*" 62 | --ignore "../*" 63 | --ignore-not-existing 64 | --branch 65 | - name: Check coverage.info exists 66 | run: test -f ${{github.workspace}}/coverage.info 67 | - name: Upload Code Coverage 68 | uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5 69 | with: 70 | fail_ci_if_error: true 71 | use_oidc: true 72 | directory: ${{github.workspace}} 73 | files: coverage.info 74 | - name: Upload Code Coverage Artifact 75 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 76 | with: 77 | name: ${{matrix.os}}-coverage.info 78 | path: coverage.info 79 | if-no-files-found: error 80 | -------------------------------------------------------------------------------- /.github/workflows/pull-request.yml: -------------------------------------------------------------------------------- 1 | name: Pull Request 2 | on: 3 | pull_request: 4 | types: [opened, edited, synchronize] 5 | paths-ignore: 6 | - LICENSE.md 7 | - .gitignore 8 | - Justfile 9 | jobs: 10 | format: 11 | name: Formatting 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 16 | - name: Setup Rust Toolchain 17 | uses: moonrepo/setup-rust@ede6de059f8046a5e236c94046823e2af11ca670 # v1 18 | with: 19 | components: rustfmt 20 | - name: Check Formatting 21 | run: cargo fmt -- --check 22 | lint: 23 | name: Lint Source 24 | runs-on: ubuntu-latest 25 | strategy: 26 | matrix: 27 | features: 28 | - name: Default 29 | args: '' 30 | - name: No Standard 31 | args: --no-default-features 32 | - name: All Features 33 | args: --all-features 34 | - name: Report 35 | args: --features report 36 | - name: Unstable 37 | args: --features unstable 38 | - name: Nightly 39 | args: --features nightly 40 | steps: 41 | - name: Checkout 42 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 43 | - name: Setup Rust Toolchain 44 | uses: moonrepo/setup-rust@ede6de059f8046a5e236c94046823e2af11ca670 # v1 45 | with: 46 | components: clippy 47 | - name: Clippy 48 | uses: giraffate/clippy-action@13b9d32482f25d29ead141b79e7e04e7900281e0 # v1 49 | test: 50 | name: Test Suite 51 | runs-on: ${{matrix.os}}-latest 52 | needs: [lint, format] 53 | defaults: 54 | run: 55 | shell: bash 56 | strategy: 57 | matrix: 58 | toolchain: [stable, beta, nightly] 59 | profile: [release, dev] 60 | # One day we'll be able to just use `--profile`, but until then we need 61 | # to do this workaround to simplify the build steps 62 | os: [ubuntu, windows, macos] 63 | env: 64 | SCCACHE_CACHE_SIZE: 5G 65 | steps: 66 | - name: Checkout 67 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 68 | - name: Authenticate GitHub CLI 69 | run: echo ${{github.token}} | gh auth login --with-token 70 | - name: Setup Rust Toolchain 71 | uses: moonrepo/setup-rust@ede6de059f8046a5e236c94046823e2af11ca670 # v1 72 | with: 73 | bins: sccache, cargo-hack 74 | channel: ${{matrix.toolchain}} 75 | cache-target: ${{matrix.profile}} 76 | - name: Setup Shared Cache Directory 77 | run: | 78 | declare path="${HOME}/.cache/sccache" 79 | if test "${RUNNER_OS}" = "Windows"; then 80 | path=$(cygpath --windows "${path}") 81 | fi 82 | echo "SCCACHE_DIR=${path}" >> "${GITHUB_ENV}" 83 | - name: Restore sccache build artifacts 84 | uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4 85 | with: 86 | restore-keys: ${{runner.os}}-${{matrix.toolchain}}-${{matrix.profile}}- 87 | key: ${{runner.os}}-${{matrix.toolchain}}-${{matrix.profile}}-${{hashFiles('**/Cargo.lock')}} 88 | path: ${{env.SCCACHE_DIR}} 89 | - name: Run Tests (${{matrix.profile}}) 90 | run: cargo hack test --profile ${{matrix.profile}} --feature-powerset 91 | env: 92 | RUSTC_WRAPPER: sccache 93 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | push: 4 | tags: ['v*'] 5 | jobs: 6 | validate: 7 | name: Validate Release 8 | strategy: 9 | matrix: 10 | os: [ubuntu, windows, macos] 11 | runs-on: ${{matrix.os}}-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 15 | - name: Setup Rust Toolchain 16 | uses: moonrepo/setup-rust@ede6de059f8046a5e236c94046823e2af11ca670 # v1 17 | with: 18 | bins: cargo-hack 19 | - name: Validate Publish 20 | run: cargo hack publish --dry-run --each-feature --locked 21 | publish: 22 | needs: [validate] 23 | name: Publish Crate 24 | runs-on: ubuntu-latest 25 | environment: release 26 | steps: 27 | - name: Checkout 28 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 29 | - name: Set Git Branch 30 | run: git switch -c ${{github.ref_name}} 31 | - name: Install Cargo Release 32 | uses: baptiste0928/cargo-install@91c5da15570085bcde6f4d7aed98cb82d6769fd3 # v3 33 | with: 34 | crate: cargo-release 35 | - name: Get Crate Version 36 | shell: bash 37 | id: metadata 38 | run: | 39 | declare query='.packages[] | select(.name | contains("outcome")).version' 40 | echo "::set-output name=version::$(cargo metadata --format-version=1 | jq -r "${query}")" 41 | - name: Verify Version 42 | run: test "v${{steps.metadata.outputs.version}}" = "${{github.ref_name}}" 43 | - name: Configure Git User 44 | run: | 45 | git config user.email "41898282+github-actions[bot]@users.noreply.github.com" 46 | git config user.name "github-actions[bot]" 47 | - name: Publish Crate 48 | run: >- 49 | cargo release 50 | --token ${{secrets.CARGO_REGISTRY_TOKEN}} 51 | --tag-name ${{github.ref_name}} 52 | --no-confirm 53 | --no-verify 54 | --no-push 55 | --no-tag 56 | --execute 57 | - name: Yank On Failure 58 | shell: bash 59 | if: failure() 60 | run: >- 61 | cargo yank 62 | --vers ${{steps.metadata.outputs.version}} 63 | --token ${{secrets.CARGO_REGISTRY_TOKEN}} 64 | -------------------------------------------------------------------------------- /.github/workflows/spellcheck.yml: -------------------------------------------------------------------------------- 1 | name: Spellcheck 2 | on: 3 | pull_request: 4 | types: [synchronize, edited, opened] 5 | jobs: 6 | skip: 7 | name: Check Duplicate Actions 8 | runs-on: ubuntu-latest 9 | outputs: 10 | should-skip: ${{steps.skip-check.outputs.should_skip}} 11 | steps: 12 | - name: Skip Duplicate Actions (Spellcheck) 13 | uses: fkirc/skip-duplicate-actions@f75f66ce1886f00957d99748a42c724f4330bdcf # v5 14 | id: skip-check 15 | spellcheck: 16 | name: Spellcheck 17 | runs-on: ubuntu-latest 18 | needs: skip 19 | if: needs.skip.outputs.should-skip != 'true' 20 | steps: 21 | - name: Checkout 22 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 23 | - name: Checking Spelling 24 | uses: crate-ci/typos@0f0ccba9ed1df83948f0c15026e4f5ccfce46109 # v1.32.0 25 | with: 26 | config: ${{github.workspace}}/.github/settings/typos.yml 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.cargo 2 | /target 3 | Cargo.lock 4 | node_modules 5 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "outcome-46f94afc-026f-5511-9d7e-7d1fd495fb5c" 3 | description = "Augmentations for error propagation" 4 | repository = "https://github.com/bruxisma/outcome" 5 | version = "0.2.0-dev" 6 | edition = "2021" 7 | license = "MIT" 8 | readme = "README.md" 9 | categories = ["no-std", "rust-patterns"] 10 | keywords = ["outcome", "result", "failure", "eyre"] 11 | exclude = [".github", ".gitignore", "Justfile", "rustfmt.toml", "codecov.yml"] 12 | build = "build.rs" 13 | 14 | [lib] 15 | name = "outcome" 16 | path = "src/lib.rs" 17 | 18 | [dependencies] 19 | eyre = { version = "0.6.12", optional = true } 20 | miette = { version = ">=3.0.0", optional = true } 21 | 22 | [build-dependencies] 23 | rustversion = "1.0.15" 24 | 25 | [features] 26 | default = ["std"] 27 | diagnostic = ["miette", "std"] 28 | nightly = ["unstable"] 29 | report = ["eyre", "std"] 30 | std = [] 31 | unstable = [] 32 | 33 | [package.metadata.docs.rs] 34 | all-features = true 35 | rustdoc-args = ["--cfg", "docsrs"] 36 | rustc-args = ["--cfg", "docsrs"] 37 | 38 | [profile.coverage] 39 | codegen-units = 1 40 | incremental = false 41 | inherits = "test" 42 | opt-level = 0 43 | overflow-checks = false 44 | panic = "abort" 45 | -------------------------------------------------------------------------------- /Justfile: -------------------------------------------------------------------------------- 1 | set windows-shell := ["pwsh", "-NoProfile", "-NoLogo", "-Command"] 2 | 3 | rustflags := "-C instrument-coverage" 4 | rustdocflags := rustflags + " -Z unstable-options --persist-doctests target/coverage" 5 | coverage := join(justfile_directory(), "target/coverage/outcome-%p-%m.profraw") 6 | pwd := justfile_directory() 7 | libdir := `rustc --print target-libdir` 8 | bindir := join(parent_directory(libdir), "bin") 9 | rmflags := if os() == "windows" { 10 | "-Recurse -Force -ErrorAction SilentlyContinue" 11 | } else { 12 | "-rf" 13 | } 14 | 15 | default: fmt test 16 | 17 | @build: 18 | cargo build --no-default-features 19 | cargo build --all-features 20 | 21 | @bootstrap: 22 | rustup component add llvm-tools-preview 23 | cargo install cargo-hack cargo-llvm-cov 24 | 25 | @clear-reports: 26 | rm {{rmflags}} {{join(justfile_directory(), "target/coverage")}} 27 | 28 | @collect type="lcov" $RUSTUP_TOOLCHAIN="nightly": clear-reports coverage 29 | grcov ${PWD}/target/coverage/ \ 30 | --source-dir {{pwd}} \ 31 | --output-type {{type}} \ 32 | --output-path {{ if type == "lcov" { "coverage.info" } else { "target/collected" } }} \ 33 | --commit-sha $(git rev-parse HEAD) \ 34 | --binary-path {{pwd}}/target/coverage \ 35 | --prefix-dir {{pwd}} \ 36 | --filter covered \ 37 | --branch \ 38 | --llvm \ 39 | --ignore-not-existing \ 40 | --guess-directory-when-missing 41 | 42 | coverage $RUSTFLAGS=rustflags $RUSTDOCFLAGS=rustdocflags $LLVM_PROFILE_FILE=coverage: 43 | cargo +nightly test --no-default-features --doc --quiet --profile=coverage 44 | cargo +nightly test --no-default-features --lib --quiet --profile=coverage 45 | cargo +nightly test --all-features --doc --quiet --profile=coverage 46 | cargo +nightly test --all-features --lib --quiet --profile=coverage 47 | 48 | @test: coverage 49 | cargo test --no-default-features --doc --quiet 50 | cargo test --no-default-features --lib --quiet 51 | cargo test --all-features --doc --quiet 52 | cargo test --all-features --lib --quiet 53 | 54 | @fmt: 55 | @cargo fmt 56 | 57 | docs *ARGS: 58 | @cargo +nightly doc {{ARGS}} --all-features 59 | 60 | @check: 61 | cargo +nightly clippy --all-features -- -D warnings 62 | @cargo clippy --all-features -- -D warnings 63 | 64 | @commit: fmt check docs test 65 | git commit -v --signoff 66 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright © 2021 Isabella Muerte 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | [![Audit][audit-badge-svg]][audit-badge-yml] 4 | [![Pull Request][pull-request-badge-svg]][pull-request-badge-yml] 5 | [![codecov][codecov-badge-svg]][codecov-badge-lnk] 6 | [![Documentation][documentation-badge-svg]][documentation-badge-lnk] 7 | 8 | [`Outcome`][`Outcome`] is an augmentation of the [`Result`] type 9 | found in the Rust standard library. 10 | 11 | It is an enum with the variants 12 | - [`Success(S)`], representing success and containing a value 13 | - [`Mistake(M)`], representing an optionally *retryable error* and 14 | containing a value 15 | - [`Failure(F)`], representing failure and containing a value. 16 | 17 | ```rust 18 | enum Outcome { 19 | Success(S), 20 | Mistake(M), 21 | Failure(F), 22 | } 23 | ``` 24 | 25 | [`Outcome`] is an *augmentation* to [`Result`]. It adds a third state to 26 | the "success or failure" dichotomy that [`Result`][`Result`] models. 27 | This third state is that of a *soft* or *retryable* error. A *retryable* 28 | error is one where an operation might not have succeeded, either due to 29 | other operations (e.g., a disk read or write not completing), 30 | misconfiguration (e.g., forgetting to set a specific flag before calling a 31 | function), or busy resources (e.g., attempting to lock an audio, video, or 32 | database resource). 33 | 34 | ```rust 35 | use outcome::prelude::*; 36 | 37 | #[derive(Debug, PartialEq)] 38 | enum Version { V1, V2 } 39 | 40 | #[derive(Debug, PartialEq)] 41 | struct EmptyInput; 42 | 43 | fn parse_version(header: &[u8]) -> Outcome { 44 | match header.get(0) { 45 | None => Mistake(EmptyInput), 46 | Some(&1) => Success(Version::V1), 47 | Some(&2) => Success(Version::V2), 48 | Some(_) => Failure("invalid or unknown version"), 49 | } 50 | } 51 | 52 | let version = parse_version(&[]); 53 | assert_eq!(version, Mistake(EmptyInput)); 54 | ``` 55 | 56 | # Usage 57 | 58 | At this time, the name `outcome` is already taken on [crates.io]. As 59 | [crates.io] does not yet support namespaces or collections, we've had to 60 | take a *unique* approach to still publish the crate. To do this, we've 61 | generated a `UUIDv5` string via python: 62 | 63 | ```python 64 | from uuid import * 65 | print(uuid5(uuid5(NAMESPACE_DNS, "occult.work"), "outcome")) 66 | ``` 67 | 68 | This *should* generate the string `46f94afc-026f-5511-9d7e-7d1fd495fb5c`. 69 | Thus the dependency in your `Cargo.toml` will look something like: 70 | 71 | ```toml 72 | [dependencies] 73 | outcome-46f94afc-026f-5511-9d7e-7d1fd495fb5c = "*" 74 | ``` 75 | 76 | However, the exported library is still named `outcome`, so importing it is 77 | treated the same: 78 | 79 | ```rust 80 | use outcome::prelude::*; 81 | ``` 82 | 83 | Users can also work around this by using the `package` key in their 84 | dependency declaration: 85 | 86 | ```toml 87 | [dependencies.outcome] 88 | version = "*" 89 | package = "outcome-46f94afc-026f-5511-9d7e-7d1fd495fb5c" 90 | ``` 91 | 92 | Is this solution friendly to users? No, but neither is the lack of 93 | namespacing nor a squatting policy on [crates.io]. If/when this problem is 94 | resolved, this crate's documentation (and name!) will be changed and all 95 | versions will be yanked. 96 | 97 | [`Result`]: core::result::Result 98 | 99 | [`Outcome`]: crate::prelude::Outcome 100 | 101 | [`Success(S)`]: crate::prelude::Success 102 | [`Mistake(M)`]: crate::prelude::Mistake 103 | [`Failure(F)`]: crate::prelude::Failure 104 | 105 | [crates.io]: https://crates.io 106 | [`eyre`]: https://crates.io/crates/eyre 107 | 108 | [documentation-badge-svg]: https://img.shields.io/docsrs/outcome-46f94afc-026f-5511-9d7e-7d1fd495fb5c.svg?label=Documentation 109 | [documentation-badge-lnk]: https://docs.rs/outcome-46f94afc-026f-5511-9d7e-7d1fd495fb5c/latest/outcome/ 110 | 111 | [pull-request-badge-svg]: https://github.com/bruxisma/outcome/actions/workflows/pull-request.yml/badge.svg 112 | [pull-request-badge-yml]: https://github.com/bruxisma/outcome/actions/workflows/pull-request.yml 113 | [codecov-badge-svg]: https://codecov.io/gh/bruxisma/outcome/branch/main/graph/badge.svg 114 | [codecov-badge-lnk]: https://codecov.io/gh/bruxisma/outcome 115 | [audit-badge-svg]: https://github.com/bruxisma/outcome/actions/workflows/audit.yml/badge.svg 116 | [audit-badge-yml]: https://github.com/bruxisma/outcome/actions/workflows/audit.yml 117 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | #[rustversion::nightly] 2 | fn main() { 3 | println!("cargo:rustc-cfg=nightly"); 4 | } 5 | 6 | #[rustversion::not(nightly)] 7 | fn main() {} 8 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | branch: main 3 | coverage: 4 | status: 5 | project: 6 | default: 7 | threshold: 10% 8 | informational: true 9 | comment: 10 | layout: diff, flags, files 11 | require_changes: true 12 | -------------------------------------------------------------------------------- /docs/features.md: -------------------------------------------------------------------------------- 1 | # Features 2 | 3 | There are several features available to the crate that are disabled by 4 | default. These include: 5 | 6 | - `unstable` (Enable "unstable" functions that mirror unstable functions 7 | found in [`Result`]. Unlike [`Result`], however, a nightly compiler is 8 | not required.) 9 | - `nightly` (Enable features that require the nightly rust compiler to be 10 | used, such as [`Try`]) 11 | - `report` (Enable conversion from [`Aberration`] to an 12 | [`eyre::Report`][`Report`]) 13 | - `diagnostic` (Enable conversion from [`Aberration`] to a 14 | [`miette::Report`]) 15 | 16 | Users can also enable `no_std` support by either setting `default-features` to 17 | `false` or simply not listing `std` in the list of features. Lastly, the 18 | `report` and `diagnostic` features should be treated as mutually exclusive as 19 | they both export a similar interface that is similar enough to cause 20 | collisions. If the author of `eyre` ever manages to make it `no_std` (there are 21 | limitations preventing [`Error`] from being `no_std`), these features will be 22 | redesigned such that `report` + `std` uses `miette`, and `report` + `no_std` 23 | uses `eyre`. 24 | 25 | - `nightly` will enable `unstable`. 26 | - `report` will enable `std`. 27 | - `diagnostic` will enable `std`. 28 | 29 | **NOTE**: Due to limitations with cargo features, we cannot actually enforce 30 | mutual exclusivity, and support building the crate with documentation and tests 31 | with all features enabled. 32 | 33 | ### `no_std` 34 | 35 | Nearly every single feature in `outcome` supports working with `#![no_std]` 36 | support, however currently `eyre` *does* require `std` support (Attempts 37 | were made at making `no_std` work, but this was removed and has not been 38 | available for some time). 39 | 40 | ```toml 41 | [dependencies.outcome] 42 | package = "outcome-46f94afc-026f-5511-9d7e-7d1fd495fb5c" 43 | version = "..." 44 | features = ["nightly"] 45 | ``` 46 | 47 | ### `unstable` 48 | 49 | When enabled, the `unstable` feature provides several associated methods for 50 | [`Outcome`] that mirror unstable APIs found in [`Result`][`Result`]. If 51 | the methods mirrored are changed in any future releases of stable rust, these 52 | will as well. Additionally, if any of the APIs are stabilized, they will be 53 | moved out of this feature and into the default feature set. Unlike the 54 | `nightly` feature, these APIs can be implemented in *stable* rust. 55 | 56 | ### `nightly` 57 | 58 | The `nightly` feature set also requires a nightly toolchain. This is detected 59 | in outcome's `build.rs` script via the 60 | [rustversion](https://crates.io/crates/rustversion) crate. While users can 61 | enable the nightly feature on a stable toolchain, nothing additional will 62 | be compiled. 63 | 64 | Once available, users will have to enable specific nightly features for 65 | each API set mentioned. These are listed below. 66 | 67 | - `#![feature(try_trait_v2)]` — operator `?` support 68 | - [`Outcome`] may be used with operator `?`, including from functions 69 | that return a [`Result`], as long as: 70 | - `E: From` 71 | - `#![feature(never_type)]` — APIs that return `!` 72 | - [`Outcome`] will have several functions where the `!` type is used in 73 | the function signature. These include `into_success`, and others. 74 | - Several stable functions that return an [`Aberration`] will instead return 75 | an `Outcome`. 76 | - `#![feature(termination_trait_lib)]` — Exit process with an 77 | [`Outcome`] 78 | - **NOTE**: This requires the `std` feature to be enabled as well. 79 | - In addition to being usable with `fn main()`, *any unit test* may 80 | return an [`Outcome`] directly. This works in the same way as returning a 81 | [`Result`] 82 | 83 | ### `report` 84 | 85 | The `report` feature adds the [`WrapFailure`] trait to both [`Outcome`] and 86 | [`Aberration`]. This trait is meant to mimic the [`WrapErr`] trait found on 87 | [`Result`][`Result`] that is provided by [`eyre`]. Therefore, a blanket 88 | implementation is provided for all types that implement [`WrapErr`]. However, 89 | to stay in line with `outcome`'s naming convention, instances of `err` have 90 | been replaced with `failure`. 91 | 92 | [`Result`]: core::result::Result 93 | [`Try`]: core::ops::Try 94 | 95 | [`Error`]: std::error::Error 96 | 97 | [`WrapErr`]: eyre::WrapErr 98 | [`Report`]: eyre::Report 99 | 100 | [`miette::Report`]: miette::Report 101 | 102 | [`WrapFailure`]: crate::report::WrapFailure 103 | [`Aberration`]: crate::prelude::Aberration 104 | [`Outcome`]: crate::prelude::Outcome 105 | 106 | [`eyre`]: https://crates.io/crates/eyre 107 | -------------------------------------------------------------------------------- /docs/state-escalation.md: -------------------------------------------------------------------------------- 1 | # State Escalation 2 | 3 | Using the `parse_version` example seen earlier in our documentation, we can see 4 | that the `Version` enum can return either `V1` or `V2`. At some point, a tool 5 | parsing some version number might choose to deprecate support for `V1`. In 6 | these instances, a developer might choose to turn `V1` into a `Mistake`. 7 | However, `parse_version` returns a `Success(V1)`. How, then, can we turn this 8 | into a `Mistake` *in-place*? 9 | 10 | This is where *state escalation* comes into play. It is one of the more 11 | experimental features that `outcome` is trying to experiment with. The basic 12 | idea of this concept is a successful outcome for *one function* does not imply 13 | that the caller considers the successful value valid enough to continue. 14 | 15 | With state escalation, we not only *move* the possible state of an `Outcome` 16 | from `Success` to `Mistake` or `Mistake` to `Failure`, but we are also able to 17 | eliminate possible states from the `Outcome`. That is, given an `Outcome`, the first state escalation would return an `Outcome` (on 19 | stable, this returns an `Outcome`. A second state 20 | escalation would result in an `Outcome`. This allows for *fast* state 21 | transitions with little to no effort, while keeping this state transition 22 | separate from mapping operations. The benefit of reducing the possible set of 23 | states that the `Outcome` can represent is simply a *side effect* of Rust's 24 | powerful type system with regards to `enum`s and their variants. 25 | 26 | It is important to note that there is no *de-escalation* of state possible 27 | without explicit operations from users. In other words, a new Outcome must be 28 | generated when one or more fields are `Infallible`. This is difficult to 29 | enforce without certain features missing from Rust, specifically partial 30 | specialization and `impl` overloading (e.g., an `Outcome` should *not* 31 | be allowed to have `and_then` called on it). However this same limitation is 32 | found in `Result` and other variants, and even Finite State Machines 33 | implemented with Rust `enum`s will suffer the same fate. In an ideal world, 34 | Outcome's state escalation would only permit unidirectional transitions, and 35 | would require the creation of a completely *new* `Outcome` if a user wanted to 36 | reset or *de-escalate* the `Outcome`. This might not ever be possible to do or 37 | enforce in Rust, so in the meantime it is up to users to behave themselves with 38 | regards to escalating state. 39 | -------------------------------------------------------------------------------- /docs/why-augment-result.md: -------------------------------------------------------------------------------- 1 | # Why Augment `Result`? 2 | 3 | [`Outcome`] is *not* intended to fully replace [`Result`], especially at 4 | the API boundary (i.e., the API used by clients) when there is a clear 5 | success or failure state that can be transferred to users. Instead, it 6 | provides the ability to quickly expand the surface area of consumed APIs 7 | with finer grained control over errors so that library writers can write 8 | *correct* behavior and then return at a later time to compose results, 9 | expand error definitions, or to represent different error severities. 10 | 11 | As an example, the section [making unhandled errors unrepresentable][1] in 12 | the post *Error Handling in a Correctness-Critical Rust Project*, the 13 | author states: 14 | 15 | > this led me to go for what felt like the nuclear solution, but after 16 | > seeing how many bugs it immediately rooted out by simply refactoring the 17 | > codebase, I’m convinced that this is the only way to do error handling in 18 | > systems where we have multiple error handling concerns in Rust today. 19 | 20 | The solution, as they explain in the next paragraph, is 21 | 22 | > make the global `Error` enum specifically only hold errors that should 23 | > cause the overall system to halt - reserved for situations that require 24 | > human intervention. Keep errors which relate to separate concerns in 25 | > totally separate error types. By keeping errors that must be handled 26 | > separately in their own types, we reduce the chance that the try `?` 27 | > operator will accidentally push a local concern into a caller that can’t 28 | > deal with it. 29 | 30 | As the author of this post later shows, the `sled::Tree::compare_and_swap` 31 | function returns a `Result, sled::Error>`. 32 | They state this looks "*way less cute*", but will 33 | 34 | > improve chances that users will properly handle their compare and 35 | > swap-related errors properly\[sic] 36 | > 37 | > ```rust 38 | > # const IGNORE: &str = stringify! { 39 | > // we can actually use try `?` now 40 | > let cas_result = sled.compare_and_swap( 41 | > "dogs", 42 | > "pickles", 43 | > "catfood" 44 | > )?; 45 | > 46 | > if let Err(cas_error) = cas_result { 47 | > // handle expected issue 48 | > } 49 | > # }; 50 | > ``` 51 | 52 | The issue with this return type is that there is *technically nothing* to 53 | stop a user from using what the creator of the `outcome` crate calls the 54 | WTF operator (`??`) to ignore these intermediate errors. 55 | 56 | ```rust 57 | # const IGNORE: &str = stringify! { 58 | let cas = sled.compare_and_swap("dogs", "pickles", "catfood")??; 59 | # }; 60 | ``` 61 | 62 | It would be hard to *forbid* this kind of usage with tools like clippy due 63 | to libraries such as [nom][2] relying on nested results and expecting 64 | moderately complex pattern matching to extract relevant information. 65 | 66 | Luckily, it *is* easier to prevent this issue in the first place if: 67 | 68 | - An explicit call to extract an inner `Result`-like type must be made 69 | - The call of an easily greppable/searchable function before using the 70 | "WTF" (`??`) operator is permitted. 71 | - The [`Try`] trait returns a type that *must* be decomposed explicitly 72 | and *does not support* the try `?` operator itself. 73 | 74 | Thanks to [clippy](https://github.com/rust-lang/rust-clippy)'s 75 | `disallowed_method` lint, users can rely on the first two options until 76 | [`Try`] has been stabilized. 77 | 78 | `outcome` provides this in the form of its [`Concern`] type, whose variants 79 | match the [`Success`] and [`Failure`] of [`Outcome`], as well as the associated 80 | function [`Outcome::acclimate`], which returns a `Result, F>`. 81 | 82 | **NOTE**: This associated function will be deprecated once [`Try`] has been 83 | stabilized. 84 | 85 | [`Result`]: core::result::Result 86 | [`Try`]: core::ops::Try 87 | 88 | [`Outcome::acclimate`]: crate::prelude::Outcome::acclimate 89 | [`Concern`]: crate::prelude::Concern 90 | [`Success`]: crate::prelude::Success 91 | [`Failure`]: crate::prelude::Failure 92 | [`Outcome`]: crate::prelude::Outcome 93 | 94 | [1]: https://sled.rs/errors.html#making-unhandled-errors-unrepresentable 95 | [2]: https://crates.io/crates/nom 96 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 80 2 | tab_spaces = 2 3 | newline_style = "Unix" 4 | -------------------------------------------------------------------------------- /src/aberration.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "std")] 2 | extern crate std; 3 | 4 | #[cfg(feature = "std")] 5 | use std::{ 6 | eprintln, 7 | process::{ExitCode, Termination}, 8 | }; 9 | 10 | #[cfg(not(feature = "nightly"))] 11 | use core::convert::Infallible; 12 | use core::fmt::Debug; 13 | 14 | #[cfg(not(feature = "nightly"))] 15 | use crate::outcome::Outcome; 16 | use crate::private::panic; 17 | 18 | /// `Aberration` is a type that can represent a [`Mistake`], or [`Failure`]. 19 | /// 20 | /// **NOTE**: This type will become a alias once `!` is stabilized. 21 | /// 22 | /// See the [module documentation](crate) for details. 23 | /// 24 | /// [`Mistake`]: Aberration::Mistake 25 | /// [`Failure`]: Aberration::Failure 26 | #[must_use = "This Aberration might be a `Mistake`, which should be handled"] 27 | #[derive(Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)] 28 | pub enum Aberration { 29 | /// Contains the mistake value. Analogous to 30 | /// [`Outcome::Mistake`](crate::prelude::Outcome::Mistake) 31 | Mistake(M), 32 | /// Contains the failure value. Analogous to 33 | /// [`Outcome::Failure`](crate::prelude::Outcome::Failure) 34 | Failure(F), 35 | } 36 | 37 | impl Aberration { 38 | /// Converts from `&Aberration` to `Aberration<&M, &F>`. 39 | /// 40 | /// Produces a new `Aberration`, containing a reference into the original, 41 | /// leaving it in place. 42 | /// # Examples 43 | /// 44 | /// ``` 45 | /// # use outcome::prelude::*; 46 | /// let x: Aberration = Aberration::Mistake(42); 47 | /// assert_eq!(x.as_ref(), Aberration::Mistake(&42)); 48 | /// 49 | /// let x: Aberration<&str, u32> = Aberration::Failure(47); 50 | /// assert_eq!(x.as_ref(), Aberration::Failure(&47)); 51 | #[inline] 52 | pub fn as_ref(&self) -> Aberration<&M, &F> { 53 | match *self { 54 | Self::Mistake(ref value) => Aberration::Mistake(value), 55 | Self::Failure(ref value) => Aberration::Failure(value), 56 | } 57 | } 58 | 59 | /// Converts from `&mut Aberration` to `Aberration<&mut M, &mut F>` 60 | /// 61 | /// # Examples 62 | /// 63 | /// ``` 64 | /// # use outcome::prelude::*; 65 | /// fn mutate(x: &mut Aberration) { 66 | /// match x.as_mut() { 67 | /// Aberration::Mistake(m) => *m = 42, 68 | /// Aberration::Failure(f) => *f = 47, 69 | /// } 70 | /// } 71 | /// 72 | /// let mut x: Aberration = Aberration::Mistake(1); 73 | /// mutate(&mut x); 74 | /// assert_eq!(x.unwrap_mistake(), 42); 75 | /// 76 | /// let mut x: Aberration = Aberration::Failure(1); 77 | /// mutate(&mut x); 78 | /// assert_eq!(x.unwrap_failure(), 47); 79 | /// ``` 80 | #[inline] 81 | pub fn as_mut(&mut self) -> Aberration<&mut M, &mut F> { 82 | match *self { 83 | Self::Mistake(ref mut value) => Aberration::Mistake(value), 84 | Self::Failure(ref mut value) => Aberration::Failure(value), 85 | } 86 | } 87 | 88 | /// Returns `true` if the aberration is a [`Mistake`] 89 | /// 90 | /// # Examples 91 | /// 92 | /// # Basic usage: 93 | /// 94 | /// ``` 95 | /// # use outcome::prelude::*; 96 | /// let x: Aberration = Aberration::Mistake(42); 97 | /// assert!(x.is_mistake()); 98 | /// 99 | /// let x: Aberration = Aberration::Failure(47); 100 | /// assert!(!x.is_mistake()); 101 | /// ``` 102 | /// 103 | /// [`Mistake`]: Aberration::Mistake 104 | #[must_use = "if you intended to assert a mistake, consider `.unwrap_mistake()` instead"] 105 | #[inline] 106 | pub fn is_mistake(&self) -> bool { 107 | if let Self::Mistake(_) = self { 108 | return true; 109 | } 110 | false 111 | } 112 | 113 | /// Returns `true` if the aberration is a [`Failure`] 114 | /// 115 | /// # Examples 116 | /// 117 | /// # Basic usage: 118 | /// 119 | /// ``` 120 | /// # use outcome::prelude::*; 121 | /// let x: Aberration = Aberration::Failure(1); 122 | /// assert!(x.is_failure()); 123 | /// 124 | /// let x: Aberration = Aberration::Mistake(1); 125 | /// assert!(!x.is_failure()); 126 | /// ``` 127 | /// [`Failure`]: Aberration::Failure 128 | #[must_use = "if you intended to assert a failure, consider `.unwrap_failure()` instead"] 129 | #[inline] 130 | pub fn is_failure(&self) -> bool { 131 | if let Self::Failure(_) = self { 132 | return true; 133 | } 134 | false 135 | } 136 | 137 | /// Converts from `Aberration` to [`Option`] 138 | /// 139 | /// Converts `self` into an [`Option`], consuming `self`, and discarding 140 | /// the failure value if any. 141 | /// 142 | /// # Examples 143 | /// 144 | /// ``` 145 | /// # use outcome::prelude::*; 146 | /// let x: Aberration = Aberration::Failure(42); 147 | /// assert_eq!(x.mistake(), None); 148 | /// 149 | /// let x: Aberration = Aberration::Mistake(42); 150 | /// assert_eq!(x.mistake(), Some(42)); 151 | /// ``` 152 | #[inline] 153 | pub fn mistake(self) -> Option { 154 | if let Self::Mistake(value) = self { 155 | return Some(value); 156 | } 157 | None 158 | } 159 | 160 | /// Converts from `Aberration` to [`Option`] 161 | /// 162 | /// Converts `self` into an [`Option`], consuming `self`, and discarding the 163 | /// mistake value if any. 164 | /// 165 | /// # Examples 166 | /// 167 | /// ``` 168 | /// # use outcome::prelude::*; 169 | /// let x: Aberration = Aberration::Failure(42); 170 | /// assert_eq!(x.failure(), Some(42)); 171 | /// 172 | /// let x: Aberration = Aberration::Mistake(42); 173 | /// assert_eq!(x.failure(), None); 174 | /// ``` 175 | #[inline] 176 | pub fn failure(self) -> Option { 177 | if let Self::Failure(value) = self { 178 | return Some(value); 179 | } 180 | None 181 | } 182 | 183 | /// Maps an `Aberration` to `Aberration` by applying a function 184 | /// to a contained [`Mistake`] value, leaving any [`Failure`] value 185 | /// untouched. 186 | /// 187 | /// # Examples 188 | /// 189 | /// ``` 190 | /// # use outcome::prelude::*; 191 | /// let x: Aberration<&str, &str> = Aberration::Mistake("foo"); 192 | /// assert_eq!(x.map_mistake(|v| v.len()), Aberration::Mistake(3)); 193 | /// 194 | /// let x: Aberration<&str, &str> = Aberration::Failure("bar"); 195 | /// assert_eq!(x.map_mistake(|v| v.len()), Aberration::Failure("bar")); 196 | /// ``` 197 | /// 198 | /// [`Mistake`]: Aberration::Mistake 199 | /// [`Failure`]: Aberration::Failure 200 | #[inline] 201 | pub fn map_mistake(self, callable: C) -> Aberration 202 | where 203 | C: FnOnce(M) -> N, 204 | { 205 | match self { 206 | Self::Mistake(value) => Aberration::Mistake(callable(value)), 207 | Self::Failure(value) => Aberration::Failure(value), 208 | } 209 | } 210 | 211 | /// Maps an `Aberration` to `Aberration` by applying a function 212 | /// to a contained [`Failure`] value, leaving any [`Mistake`] value 213 | /// untouched. 214 | /// 215 | /// [`Mistake`]: Aberration::Mistake 216 | /// [`Failure`]: Aberration::Failure 217 | #[inline] 218 | pub fn map_failure(self, callable: C) -> Aberration 219 | where 220 | C: FnOnce(F) -> G, 221 | { 222 | match self { 223 | Self::Mistake(value) => Aberration::Mistake(value), 224 | Self::Failure(value) => Aberration::Failure(callable(value)), 225 | } 226 | } 227 | } 228 | 229 | #[cfg(not(feature = "nightly"))] 230 | impl Aberration 231 | where 232 | M: Into, 233 | { 234 | /// **TODO**: Write documentation 235 | pub fn escalate(self) -> Outcome { 236 | match self { 237 | Self::Mistake(m) => Outcome::Failure(m.into()), 238 | Self::Failure(f) => Outcome::Failure(f), 239 | } 240 | } 241 | } 242 | 243 | impl Aberration { 244 | /// Returns the contained [`Mistake`] value, consuming the `self` value. 245 | /// 246 | /// # Panics 247 | /// 248 | /// Panics if the value is a [`Failure`], with a custom panic message 249 | /// provided by the failure. 250 | /// 251 | /// # Examples 252 | /// 253 | /// ```should_panic 254 | /// # use outcome::prelude::*; 255 | /// let x: Aberration<&str, i32> = Aberration::Failure(47); 256 | /// x.unwrap_mistake(); // panics with '47' 257 | /// ``` 258 | /// 259 | /// ``` 260 | /// # use outcome::prelude::*; 261 | /// let x: Aberration<&str, i32> = Aberration::Mistake("try again!"); 262 | /// assert_eq!(x.unwrap_mistake(), "try again!"); 263 | /// ``` 264 | /// 265 | /// [`Mistake`]: Aberration::Mistake 266 | /// [`Failure`]: Aberration::Failure 267 | #[track_caller] 268 | #[inline] 269 | pub fn unwrap_mistake(self) -> M { 270 | match self { 271 | Self::Mistake(m) => m, 272 | Self::Failure(f) => panic("Aberration::unwrap_mistake()", "Failure", &f), 273 | } 274 | } 275 | } 276 | 277 | impl Aberration { 278 | /// Returns the contained [`Failure`] value, consuming the `self` value. 279 | /// 280 | /// # Panics 281 | /// 282 | /// Panics if the value is a [`Mistake`], with a custom panic message 283 | /// provided by the mistake. 284 | /// 285 | /// # Examples 286 | /// 287 | /// ```should_panic 288 | /// # use outcome::prelude::*; 289 | /// let x: Aberration = Aberration::Mistake(47); 290 | /// x.unwrap_failure(); // panics with '47' 291 | /// ``` 292 | /// 293 | /// ``` 294 | /// # use outcome::prelude::*; 295 | /// let x: Aberration = Aberration::Failure("error!"); 296 | /// assert_eq!(x.unwrap_failure(), "error!"); 297 | /// ``` 298 | /// 299 | /// [`Mistake`]: Aberration::Mistake 300 | /// [`Failure`]: Aberration::Failure 301 | #[track_caller] 302 | #[inline] 303 | pub fn unwrap_failure(self) -> F { 304 | match self { 305 | Self::Mistake(m) => panic("Aberration::unwrap_failure()", "Mistake", &m), 306 | Self::Failure(f) => f, 307 | } 308 | } 309 | } 310 | 311 | impl Clone for Aberration { 312 | #[inline] 313 | fn clone(&self) -> Self { 314 | match self { 315 | Self::Mistake(value) => Self::Mistake(value.clone()), 316 | Self::Failure(value) => Self::Failure(value.clone()), 317 | } 318 | } 319 | 320 | #[inline] 321 | fn clone_from(&mut self, source: &Self) { 322 | match (self, source) { 323 | (Self::Mistake(to), Self::Mistake(from)) => to.clone_from(from), 324 | (Self::Failure(to), Self::Failure(from)) => to.clone_from(from), 325 | (to, from) => *to = from.clone(), 326 | } 327 | } 328 | } 329 | 330 | #[cfg(feature = "std")] 331 | impl Termination for Aberration { 332 | #[inline] 333 | fn report(self) -> ExitCode { 334 | #[allow(clippy::print_stderr)] 335 | match self { 336 | Self::Mistake(m) => eprintln!("Mistake: {m:?}"), 337 | Self::Failure(f) => eprintln!("Failure: {f:?}"), 338 | }; 339 | ExitCode::FAILURE 340 | } 341 | } 342 | -------------------------------------------------------------------------------- /src/concern.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | fmt::Debug, 3 | ops::{Deref, DerefMut}, 4 | }; 5 | 6 | use crate::{iter::*, private::panic}; 7 | 8 | /// `Concern` is a type that can represent a [`Success`], or [`Mistake`]. 9 | /// 10 | /// **NOTE**: This type will become a type alias once `!` is stabilized. 11 | /// 12 | /// See the [module documentation](crate) for more usage details. 13 | /// 14 | /// [`Success`]: Concern::Success 15 | /// [`Mistake`]: Concern::Mistake 16 | /// [`Try`]: core::ops::Try 17 | #[must_use = "This Concern might be a `Mistake`, which should be handled"] 18 | #[derive(Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)] 19 | pub enum Concern { 20 | /// Contains the success value 21 | Success(S), 22 | /// Contains the mistake value 23 | Mistake(M), 24 | } 25 | 26 | impl Concern { 27 | /// Converts from `&Concern` to `Concern<&S, &M>`. 28 | /// 29 | /// Produces a new `Concern`, containing a reference into the original, 30 | /// leaving it in place. 31 | /// 32 | /// # Examples 33 | /// 34 | /// ``` 35 | /// # use outcome::prelude::*; 36 | /// let x: Concern = Concern::Success(42); 37 | /// assert_eq!(x.as_ref(), Concern::Success(&42)); 38 | /// 39 | /// let x: Concern = Concern::Mistake(47); 40 | /// assert_eq!(x.as_ref(), Concern::Mistake(&47)); 41 | /// ``` 42 | #[inline] 43 | pub fn as_ref(&self) -> Concern<&S, &M> { 44 | match *self { 45 | Self::Success(ref value) => Concern::Success(value), 46 | Self::Mistake(ref value) => Concern::Mistake(value), 47 | } 48 | } 49 | 50 | /// Converts from `&mut Concern` to `Concern<&mut S, &mut F>` 51 | /// 52 | /// # Examples 53 | /// 54 | /// ``` 55 | /// # use outcome::prelude::*; 56 | /// fn mutate(c: &mut Concern) { 57 | /// match c.as_mut() { 58 | /// Concern::Success(s) => *s = 47, 59 | /// Concern::Mistake(m) => *m = 19, 60 | /// } 61 | /// } 62 | /// 63 | /// let mut x: Concern = Concern::Success(42); 64 | /// mutate(&mut x); 65 | /// assert_eq!(x.unwrap(), 47); 66 | /// 67 | /// let mut x: Concern = Concern::Mistake(47); 68 | /// mutate(&mut x); 69 | /// assert_eq!(x.unwrap_mistake(), 19); 70 | /// ``` 71 | #[inline] 72 | pub fn as_mut(&mut self) -> Concern<&mut S, &mut M> { 73 | match *self { 74 | Self::Success(ref mut value) => Concern::Success(value), 75 | Self::Mistake(ref mut value) => Concern::Mistake(value), 76 | } 77 | } 78 | 79 | /// Returns an iterator over the possibly contained value. 80 | /// 81 | /// The iterator yields one value if the outcome is [`Success`], otherwise 82 | /// none. 83 | /// 84 | /// # Examples 85 | /// 86 | /// Basic usage: 87 | /// 88 | /// ``` 89 | /// # use outcome::prelude::*; 90 | /// let x: Concern = Concern::Success(42); 91 | /// assert_eq!(x.iter().next(), Some(&42)); 92 | /// 93 | /// let x: Concern = Concern::Mistake(47); 94 | /// assert_eq!(x.iter().next(), None); 95 | /// ``` 96 | /// 97 | /// [`Success`]: Concern::Success 98 | #[inline] 99 | pub fn iter(&self) -> Iter<'_, S> { 100 | Iter { 101 | inner: self.as_ref().success(), 102 | } 103 | } 104 | 105 | /// Returns a mutable iterator over the possibly contained value. 106 | /// 107 | /// The iterator yields one value if the result is [`Success`], otherwise 108 | /// none. 109 | /// 110 | /// # Examples 111 | /// 112 | /// ``` 113 | /// # use outcome::prelude::*; 114 | /// let mut x: Concern = Concern::Success(7); 115 | /// match x.iter_mut().next() { 116 | /// Some(v) => *v += 40, 117 | /// None => {} 118 | /// } 119 | /// assert_eq!(x, Concern::Success(47)); 120 | /// ``` 121 | /// 122 | /// [`Success`]: Concern::Success 123 | #[inline] 124 | pub fn iter_mut(&mut self) -> IterMut<'_, S> { 125 | IterMut { 126 | inner: self.as_mut().success(), 127 | } 128 | } 129 | 130 | /// Returns `true` if the concern is a [`Success`] 131 | /// 132 | /// # Examples 133 | /// 134 | /// Basic usage: 135 | /// 136 | /// ``` 137 | /// # use outcome::prelude::*; 138 | /// let x: Concern = Concern::Success(42); 139 | /// assert!(x.is_success()); 140 | /// 141 | /// let x: Concern = Concern::Mistake("hello"); 142 | /// assert!(!x.is_success()); 143 | /// ``` 144 | /// 145 | /// [`Success`]: Concern::Success 146 | #[must_use = "if you intended to assert a success, consider `.unwrap()` instead"] 147 | #[inline] 148 | pub fn is_success(&self) -> bool { 149 | if let Self::Success(_) = self { 150 | return true; 151 | } 152 | false 153 | } 154 | 155 | /// Returns `true` if the concern is a [`Mistake`] 156 | /// 157 | /// # Examples 158 | /// 159 | /// ``` 160 | /// # use outcome::prelude::*; 161 | /// let x: Concern = Concern::Success(42); 162 | /// assert!(!x.is_mistake()); 163 | /// 164 | /// let x: Concern = Concern::Mistake("hello"); 165 | /// assert!(x.is_mistake()); 166 | /// ``` 167 | /// 168 | /// [`Mistake`]: Concern::Mistake 169 | #[must_use = "if you intended to assert a mistake, consider `.unwrap_mistake()` instead"] 170 | #[inline] 171 | pub fn is_mistake(&self) -> bool { 172 | if let Self::Mistake(_) = self { 173 | return true; 174 | } 175 | false 176 | } 177 | 178 | /// Converts from `Concern` to [`Option`] 179 | /// 180 | /// Converts `self` into an [`Option`], consuming `self`, and discarding 181 | /// the mistake, if any. 182 | /// 183 | /// # Examples 184 | /// 185 | /// ``` 186 | /// # use outcome::prelude::*; 187 | /// let x: Concern = Concern::Success(42); 188 | /// assert_eq!(x.success(), Some(42)); 189 | /// 190 | /// let x: Concern = Concern::Mistake("hello"); 191 | /// assert_eq!(x.success(), None); 192 | /// ``` 193 | #[inline] 194 | pub fn success(self) -> Option { 195 | if let Self::Success(value) = self { 196 | return Some(value); 197 | } 198 | None 199 | } 200 | 201 | /// Converts from `Concern` to [`Option`] 202 | /// 203 | /// Converts `self` into an [`Option`], consuming `self`, and discarding 204 | /// the success, if any. 205 | /// 206 | /// # Examples 207 | /// 208 | /// ``` 209 | /// # use outcome::prelude::*; 210 | /// let x: Concern = Concern::Success(42); 211 | /// assert_eq!(x.mistake(), None); 212 | /// 213 | /// let x: Concern = Concern::Mistake("hello"); 214 | /// assert_eq!(x.mistake(), Some("hello")); 215 | /// ``` 216 | #[inline] 217 | pub fn mistake(self) -> Option { 218 | if let Self::Mistake(value) = self { 219 | return Some(value); 220 | } 221 | None 222 | } 223 | 224 | /// Maps a `Concern` to `Concern` by applying a function to a 225 | /// contained [`Success`] value, leaving any [`Mistake`] value untouched. 226 | /// 227 | /// # Examples 228 | /// 229 | /// ``` 230 | /// # use outcome::prelude::*; 231 | /// let x: Concern = Concern::Success(42); 232 | /// assert_eq!(x.map(|x| x + 1), Concern::Success(43)); 233 | /// 234 | /// let x: Concern = Concern::Mistake("hello"); 235 | /// assert_eq!(x.map(|x| x + 1), Concern::Mistake("hello")); 236 | /// ``` 237 | /// 238 | /// [`Success`]: Concern::Success 239 | /// [`Mistake`]: Concern::Mistake 240 | #[inline] 241 | pub fn map(self, callable: C) -> Concern 242 | where 243 | C: FnOnce(S) -> T, 244 | { 245 | match self { 246 | Self::Success(value) => Concern::Success(callable(value)), 247 | Self::Mistake(value) => Concern::Mistake(value), 248 | } 249 | } 250 | 251 | /// Maps a `Concern` to `Concern` by applying a function to a 252 | /// contained [`Mistake`] value, leaving any [`Success`] value untouched. 253 | /// 254 | /// 255 | /// # Examples 256 | /// 257 | /// ``` 258 | /// # use outcome::prelude::*; 259 | /// let x: Concern = Concern::Success(42); 260 | /// assert_eq!(x.map_mistake(|x| x + 1), Concern::Success(42)); 261 | /// 262 | /// let x: Concern = Concern::Mistake(46); 263 | /// assert_eq!(x.map_mistake(|x| x + 1), Concern::Mistake(47)); 264 | /// ``` 265 | /// 266 | /// [`Success`]: Concern::Success 267 | /// [`Mistake`]: Concern::Mistake 268 | #[inline] 269 | pub fn map_mistake(self, callable: C) -> Concern 270 | where 271 | C: FnOnce(M) -> N, 272 | { 273 | match self { 274 | Self::Success(value) => Concern::Success(value), 275 | Self::Mistake(value) => Concern::Mistake(callable(value)), 276 | } 277 | } 278 | } 279 | 280 | impl Concern { 281 | /// Returns the contained [`Success`] value, consuming the `self` value. 282 | /// 283 | /// # Panics 284 | /// 285 | /// Panics if the value is a [`Mistake`] with a panic message provided by 286 | /// their value. 287 | /// 288 | /// # Examples 289 | /// 290 | /// ``` 291 | /// # use outcome::prelude::*; 292 | /// let x: Concern = Concern::Success(42); 293 | /// assert_eq!(x.unwrap(), 42); 294 | /// ``` 295 | /// 296 | /// ```should_panic 297 | /// # use outcome::prelude::*; 298 | /// let x: Concern = Concern::Mistake("hello"); 299 | /// x.unwrap(); // panics with "hello" 300 | /// ``` 301 | /// 302 | /// [`Success`]: Concern::Success 303 | /// [`Mistake`]: Concern::Mistake 304 | #[track_caller] 305 | #[inline] 306 | pub fn unwrap(self) -> S { 307 | match self { 308 | Self::Success(s) => s, 309 | Self::Mistake(m) => panic("Concern::unwrap()", "Mistake", &m), 310 | } 311 | } 312 | } 313 | 314 | impl Concern { 315 | /// Returns the contained [`Mistake`] value, consuming the `self` value. 316 | /// 317 | /// # Panics 318 | /// 319 | /// Panics if the value is a [`Success`] with a panic message provided by 320 | /// their value. 321 | /// 322 | /// # Examples 323 | /// 324 | /// ``` 325 | /// # use outcome::prelude::*; 326 | /// let x: Concern = Concern::Mistake("hello"); 327 | /// assert_eq!(x.unwrap_mistake(), "hello"); 328 | /// ``` 329 | /// 330 | /// ```should_panic 331 | /// # use outcome::prelude::*; 332 | /// let x: Concern = Concern::Success(42); 333 | /// x.unwrap_mistake(); // panics with "42" 334 | /// ``` 335 | /// 336 | /// [`Success`]: Concern::Success 337 | /// [`Mistake`]: Concern::Mistake 338 | #[track_caller] 339 | #[inline] 340 | pub fn unwrap_mistake(self) -> M { 341 | match self { 342 | Self::Success(s) => panic("Concern::unwrap_mistake()", "Success", &s), 343 | Self::Mistake(m) => m, 344 | } 345 | } 346 | } 347 | 348 | impl Concern { 349 | /// Converts from `Concern` (or `&Concern`) to `Concern<&::Target, M>`. 351 | /// 352 | /// Coerces the [`Success`] variant of the original [`Concern`] via [`Deref`] 353 | /// and returns the new [`Concern`]. 354 | /// 355 | /// # Examples 356 | /// 357 | /// ``` 358 | /// # use outcome::prelude::*; 359 | /// let x: Concern = Concern::Success("hello".to_string()); 360 | /// let y: Concern<&str, &u32> = Concern::Success("hello"); 361 | /// assert_eq!(x.as_deref(), y); 362 | /// ``` 363 | /// 364 | /// [`Success`]: Concern::Success 365 | /// [`Deref`]: core::ops::Deref 366 | pub fn as_deref(&self) -> Concern<&S::Target, &M> { 367 | self.as_ref().map(Deref::deref) 368 | } 369 | } 370 | 371 | impl Concern { 372 | /// Converts from `Concern` (or `&mut Concern`) to 373 | /// `Concern<&mut ::Target, &mut M>`. 374 | /// 375 | /// Coerces the [`Success`] variant of the original [`Concern`] via 376 | /// [`DerefMut`] and returns the new [`Concern`] 377 | /// 378 | /// # Examples 379 | /// 380 | /// ``` 381 | /// # use outcome::prelude::*; 382 | /// let mut s = "HELLO".to_string(); 383 | /// let mut x: Concern = Concern::Success("hello".to_string()); 384 | /// let y: Concern<&mut str, &mut u32> = Concern::Success(&mut s); 385 | /// assert_eq!(x.as_deref_mut().map(|x| { x.make_ascii_uppercase(); x }), y); 386 | /// ``` 387 | /// 388 | /// [`DerefMut`]: core::ops::DerefMut 389 | /// [`Success`]: Concern::Success 390 | pub fn as_deref_mut(&mut self) -> Concern<&mut S::Target, &mut M> { 391 | self.as_mut().map(DerefMut::deref_mut) 392 | } 393 | } 394 | 395 | impl Clone for Concern { 396 | #[inline] 397 | fn clone(&self) -> Self { 398 | match self { 399 | Self::Success(value) => Self::Success(value.clone()), 400 | Self::Mistake(value) => Self::Mistake(value.clone()), 401 | } 402 | } 403 | 404 | #[inline] 405 | fn clone_from(&mut self, source: &Self) { 406 | match (self, source) { 407 | (Self::Success(to), Self::Success(from)) => to.clone_from(from), 408 | (Self::Mistake(to), Self::Mistake(from)) => to.clone_from(from), 409 | (to, from) => *to = from.clone(), 410 | } 411 | } 412 | } 413 | -------------------------------------------------------------------------------- /src/convert.rs: -------------------------------------------------------------------------------- 1 | //! Traits for retryable conversions between types. 2 | //! 3 | //! Much like the rust standard library, the traits in this module provide a 4 | //! way to convert from one type to another type, albeit with a focus on using 5 | //! the [`Outcome`](crate::prelude::Outcome). In `outcome`'s case, only two 6 | //! traits are provided, [`AttemptFrom`] and [`AttemptInto`], which mirror 7 | //! [`TryFrom`] and [`TryInto`] respectively. 8 | //! 9 | //! As a library author, you should always prefer implementing [`AttemptFrom`] 10 | //! over [`AttemptInto`], as [`AttemptFrom`] offers greater flexibility and 11 | //! offers an equivalent [`AttemptInto`] implementation for free, thanks to a 12 | //! blanket implementation in the `outcome` crate. 13 | //! 14 | //! # Generic Implementations 15 | //! 16 | //! - [`AttemptFrom`]` for T` implies [`AttemptInto`]` for U` 17 | //! 18 | //! [`AttemptFrom`]: crate::convert::AttemptFrom 19 | //! [`AttemptInto`]: crate::convert::AttemptInto 20 | //! [`TryFrom`]: core::convert::TryFrom 21 | //! [`TryInto`]: core::convert::TryInto 22 | 23 | use core::convert::Infallible; 24 | 25 | use crate::prelude::{Outcome, Success}; 26 | 27 | /// Outcome's analogue to [`TryFrom`], and the reciprocal of [`TryInto`]. 28 | /// 29 | /// This is useful when doing a type conversion that *might* trivially succeed, 30 | /// but also might need special error handling. `AttemptFrom` adds the 31 | /// additional ability to inform the caller that they are free to *retry* the 32 | /// conversion event. This is extremely useful in cases where non-[`Copy`]able 33 | /// types are consumed during the conversion, but users 34 | /// 35 | /// 1. Cannot control the error type returned to give useful diagnostics, 36 | /// either to a caller further up the chain OR to logging information 37 | /// 2. May want to try a *different* conversion operation instead (e.g., 38 | /// trying to treat a string as a filepath *without* having to convert it 39 | /// to the underlying native storage format first) 40 | /// 41 | /// # Examples 42 | /// 43 | /// ``` 44 | /// use outcome::convert::*; 45 | /// use outcome::prelude::*; 46 | /// 47 | /// #[derive(Debug, PartialOrd, PartialEq, Ord, Eq, Hash)] 48 | /// enum Version { V1, V2 } 49 | /// 50 | /// #[derive(Debug, PartialOrd, PartialEq, Ord, Eq, Hash)] 51 | /// struct EmptyInput; 52 | /// 53 | /// #[derive(Debug, PartialOrd, PartialEq, Ord, Eq, Hash)] 54 | /// enum ParseError { 55 | /// InvalidVersion(u8), 56 | /// } 57 | /// 58 | /// impl std::fmt::Display for ParseError { 59 | /// fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 60 | /// match self { 61 | /// Self::InvalidVersion(v) => write!(f, "Expected a valid version, received: {:x?}", v) 62 | /// } 63 | /// } 64 | /// } 65 | /// 66 | /// impl AttemptFrom<&[u8; N]> for Version { 67 | /// type Mistake = EmptyInput; 68 | /// type Failure = ParseError; 69 | /// 70 | /// fn attempt_from (value: &[u8; N]) -> Outcome { 71 | /// match value.get(0) { 72 | /// None => Mistake(EmptyInput), 73 | /// Some(&1) => Success(Version::V1), 74 | /// Some(&2) => Success(Version::V2), 75 | /// Some(&value) => Failure(ParseError::InvalidVersion(value)), 76 | /// } 77 | /// } 78 | /// } 79 | /// 80 | /// let empty = Version::attempt_from(&[]); 81 | /// let v1 = Version::attempt_from(&[1u8]); 82 | /// let v2 = Version::attempt_from(&[2u8]); 83 | /// let v3 = Version::attempt_from(&[3u8]); 84 | /// assert_eq!(empty, Mistake(EmptyInput)); 85 | /// assert_eq!(v1, Success(Version::V1)); 86 | /// assert_eq!(v2, Success(Version::V2)); 87 | /// assert_eq!(v3, Failure(ParseError::InvalidVersion(3))); 88 | /// ``` 89 | /// 90 | /// [`TryFrom`]: core::convert::TryFrom 91 | /// [`TryInto`]: core::convert::TryInto 92 | /// [`Copy`]: core::marker::Copy 93 | pub trait AttemptFrom: Sized { 94 | /// The *retryable* error type 95 | type Mistake; 96 | /// The *failure* error type 97 | type Failure; 98 | 99 | /// Performs the conversion 100 | fn attempt_from(value: T) -> Outcome; 101 | } 102 | 103 | /// An attempted conversion that consumes `self`, which may or may not be 104 | /// expensive. Outcome's analogue to [`TryInto`]. 105 | /// 106 | /// Library writers should *usually* not implement this trait directly, but 107 | /// should prefer implementing the [`AttemptFrom`] trait, which offers more 108 | /// flexibility and provides an equivalent `AttemptInto` implementation for 109 | /// free, thanks to the blanket implementation provided by the `outcome` crate. 110 | /// 111 | /// Unlike [`TryInto`], users are free to return a *retryable* error, which 112 | /// *should* return the data consumed (however this cannot be enforced in 113 | /// practice). 114 | /// 115 | /// For more information on this, see the documentation for [`Into`]. 116 | /// 117 | /// # Examples 118 | /// 119 | /// The following example uses the same code from [`AttemptFrom`], but calls 120 | /// `attempt_into` on each object instead. 121 | /// 122 | /// ``` 123 | /// use outcome::convert::*; 124 | /// use outcome::prelude::*; 125 | /// 126 | /// #[derive(Debug, PartialOrd, PartialEq, Ord, Eq, Hash)] 127 | /// enum Version { V1, V2 } 128 | /// 129 | /// #[derive(Debug, PartialOrd, PartialEq, Ord, Eq, Hash)] 130 | /// struct EmptyInput; 131 | /// 132 | /// #[derive(Debug, PartialOrd, PartialEq, Ord, Eq, Hash)] 133 | /// enum ParseError { 134 | /// InvalidVersion(u8), 135 | /// } 136 | /// 137 | /// impl std::fmt::Display for ParseError { 138 | /// fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 139 | /// match self { 140 | /// Self::InvalidVersion(v) => write!(f, "Expected a valid version, received: {:x?}", v) 141 | /// } 142 | /// } 143 | /// } 144 | /// 145 | /// impl AttemptFrom<&[u8; N]> for Version { 146 | /// type Mistake = EmptyInput; 147 | /// type Failure = ParseError; 148 | /// 149 | /// fn attempt_from (value: &[u8; N]) -> Outcome { 150 | /// match value.get(0) { 151 | /// None => Mistake(EmptyInput), 152 | /// Some(&1) => Success(Version::V1), 153 | /// Some(&2) => Success(Version::V2), 154 | /// Some(&value) => Failure(ParseError::InvalidVersion(value)), 155 | /// } 156 | /// } 157 | /// } 158 | /// 159 | /// type ParseOutcome = Outcome; 160 | /// 161 | /// let empty: ParseOutcome = (&[]).attempt_into(); 162 | /// let v1: ParseOutcome = (&[1u8]).attempt_into(); 163 | /// let v2: ParseOutcome = (&[2u8]).attempt_into(); 164 | /// let v3: ParseOutcome = (&[3u8]).attempt_into(); 165 | /// assert_eq!(empty, Mistake(EmptyInput)); 166 | /// assert_eq!(v1, Success(Version::V1)); 167 | /// assert_eq!(v2, Success(Version::V2)); 168 | /// assert_eq!(v3, Failure(ParseError::InvalidVersion(3))); 169 | /// ``` 170 | /// 171 | /// 172 | /// [`TryInto`]: core::convert::TryInto 173 | /// [`Mutex`]: core::sync::Mutex 174 | /// [`Into`]: core::convert::Into 175 | pub trait AttemptInto: Sized { 176 | /// The type returned in the event of a conversion error where the caller 177 | /// *may* retry the conversion. 178 | type Mistake; 179 | /// The type returned in the event of a conversion error where the caller 180 | /// *may not* retry the conversion. 181 | type Failure; 182 | 183 | /// Performs the conversion. 184 | fn attempt_into(self) -> Outcome; 185 | } 186 | 187 | /* Blanket Trait Implementations */ 188 | impl AttemptInto for T 189 | where 190 | U: AttemptFrom, 191 | { 192 | type Mistake = U::Mistake; 193 | type Failure = U::Failure; 194 | 195 | fn attempt_into(self) -> Outcome { 196 | U::attempt_from(self) 197 | } 198 | } 199 | 200 | impl AttemptFrom for T 201 | where 202 | U: Into, 203 | { 204 | type Mistake = Infallible; 205 | type Failure = Infallible; 206 | 207 | fn attempt_from(value: U) -> Outcome { 208 | Success(value.into()) 209 | } 210 | } 211 | 212 | // Reflexive implementation for all [`TryInto`] implementations. 213 | // 214 | // # Notes 215 | // 216 | // If a [`TryInto`] implementation exists because of an [`Into`] 217 | // implementation, the type returned by [`AttemptFrom`] will be an `Outcome`. If the [`unstable` feature](crate#features) is enabled, users can 219 | // then call [`Outcome::into_success`], which will never panic. 220 | // 221 | // ```compile_fail 222 | // # use outcome::prelude::*; 223 | // # use core::convert::Infallible; 224 | // let x: Outcome = 1u8.attempt_into(); 225 | // assert_eq!(x.into_success(), 1); 226 | // ``` 227 | //impl AttemptFrom for T 228 | //where 229 | // U: TryInto, 230 | //{ 231 | // type Mistake = Infallible; 232 | // type Failure = >::Error; 233 | // 234 | // fn attempt_from(value: U) -> Outcome { 235 | // match value.try_into() { 236 | // Ok(s) => Success(s), 237 | // Err(f) => Failure(f), 238 | // } 239 | // } 240 | //} 241 | -------------------------------------------------------------------------------- /src/diagnostic.rs: -------------------------------------------------------------------------------- 1 | //! Support for the [`miette`] crate. 2 | //! 3 | //! This module re-exports `miette`'s [`Report`] as well as [`Result`], and 4 | //! provides a trait, [`WrapFailure`], as a mirror to the [`WrapErr`] trait. 5 | //! Additionally, [`WrapFailure`] is also implemented for [`Result`]. 6 | //! Lastly, to stay in line with behavior from [`miette`], the [`WrapFailure`] 7 | //! trait is *also* sealed. 8 | //! 9 | //! [`WrapErr`]: miette::WrapErr 10 | //! [`miette`]: https://crates.io/crates/miette 11 | extern crate std; 12 | 13 | use crate::prelude::*; 14 | use miette::Diagnostic; 15 | use std::fmt::Display; 16 | 17 | #[doc(no_inline)] 18 | pub use miette::{Report, Result}; 19 | 20 | crate::wrap::r#trait!(Diagnostic); 21 | crate::wrap::r#impl!(Diagnostic); 22 | crate::wrap::result!(miette); 23 | -------------------------------------------------------------------------------- /src/iter.rs: -------------------------------------------------------------------------------- 1 | use core::iter::FusedIterator; 2 | 3 | use crate::prelude::*; 4 | 5 | /// An iterator over the value in a [`Success`] variant of an [`Outcome`]. 6 | /// 7 | /// The iterator yields one value if the result is [`Success`], otherwise none. 8 | /// 9 | /// This struct is created by the [`into_iter`] method on [`Outcome`] (via the 10 | /// [`IntoIterator`] trait). 11 | /// 12 | /// [`Success`]: crate::prelude::Success 13 | /// [`Outcome`]: crate::prelude::Outcome 14 | /// [`into_iter`]: crate::prelude::Outcome::into_iter 15 | #[derive(Clone, Debug)] 16 | pub struct IntoIter { 17 | pub(crate) inner: Option, 18 | } 19 | 20 | /// An iterator over a mutable reference to the [`Success`] variant of an 21 | /// [`Outcome`]. 22 | /// 23 | /// Created by [`Outcome::iter_mut`] 24 | /// 25 | /// [`Success`]: crate::prelude::Success 26 | /// [`Outcome`]: crate::prelude::Outcome 27 | /// [`Outcome::iter_mut`]: crate::prelude::Outcome::iter_mut 28 | #[derive(Debug)] 29 | pub struct IterMut<'a, T: 'a> { 30 | pub(crate) inner: Option<&'a mut T>, 31 | } 32 | 33 | /// An iterator over a reference to the [`Success`] variant of an [`Outcome`]. 34 | /// 35 | /// The iterator yields one value if the result is [`Success`], otherwise none. 36 | /// 37 | /// Created by [`Outcome::iter`]. 38 | /// 39 | /// [`Success`]: crate::prelude::Success 40 | /// [`Outcome`]: crate::prelude::Outcome 41 | /// [`Outcome::iter`]: crate::prelude::Outcome::iter 42 | #[derive(Debug)] 43 | pub struct Iter<'a, T: 'a> { 44 | pub(crate) inner: Option<&'a T>, 45 | } 46 | 47 | /// An iterator adapter that produces output as long as the underlying iterator 48 | /// produces [`Outcome::Success`] values. 49 | /// 50 | /// If an error is encountered, the iterator stops and the error is stored. 51 | //struct OutcomeShunt<'a, I, M, F> { 52 | // error: &'a mut Outcome<(), M, F>, 53 | // iter: I, 54 | //} 55 | 56 | impl<'a, S, M, F> IntoIterator for &'a mut Outcome { 57 | type IntoIter = IterMut<'a, S>; 58 | type Item = &'a mut S; 59 | 60 | fn into_iter(self) -> Self::IntoIter { 61 | self.iter_mut() 62 | } 63 | } 64 | 65 | impl<'a, S, M, F> IntoIterator for &'a Outcome { 66 | type IntoIter = Iter<'a, S>; 67 | type Item = &'a S; 68 | 69 | fn into_iter(self) -> Self::IntoIter { 70 | self.iter() 71 | } 72 | } 73 | 74 | impl IntoIterator for Outcome { 75 | type IntoIter = IntoIter; 76 | type Item = S; 77 | 78 | #[inline] 79 | fn into_iter(self) -> Self::IntoIter { 80 | IntoIter { 81 | inner: self.success(), 82 | } 83 | } 84 | } 85 | 86 | /* Iterator Trait Implementations */ 87 | //impl> FromIterator> 88 | // for Outcome 89 | //{ 90 | // #[inline] 91 | // fn from_iter(iter: I) -> Outcome 92 | // where 93 | // I: IntoIterator>, 94 | // { 95 | // process_outcomes(iter.into_iter(), Iterator::collect) 96 | // } 97 | //} 98 | 99 | impl Iterator for IntoIter { 100 | type Item = T; 101 | 102 | #[inline] 103 | fn next(&mut self) -> Option { 104 | self.inner.take() 105 | } 106 | 107 | #[inline] 108 | fn size_hint(&self) -> (usize, Option) { 109 | let n = usize::from(self.inner.is_some()); 110 | (n, Some(n)) 111 | } 112 | } 113 | 114 | impl<'a, T> Iterator for IterMut<'a, T> { 115 | type Item = &'a mut T; 116 | 117 | #[inline] 118 | fn next(&mut self) -> Option<&'a mut T> { 119 | self.inner.take() 120 | } 121 | 122 | #[inline] 123 | fn size_hint(&self) -> (usize, Option) { 124 | let n = usize::from(self.inner.is_some()); 125 | (n, Some(n)) 126 | } 127 | } 128 | 129 | impl<'a, T> Iterator for Iter<'a, T> { 130 | type Item = &'a T; 131 | 132 | #[inline] 133 | fn next(&mut self) -> Option<&'a T> { 134 | self.inner.take() 135 | } 136 | 137 | #[inline] 138 | fn size_hint(&self) -> (usize, Option) { 139 | let n = usize::from(self.inner.is_some()); 140 | (n, Some(n)) 141 | } 142 | } 143 | 144 | //impl Iterator for OutcomeShunt<'_, I, M, F> 145 | //where 146 | // I: Iterator>, 147 | //{ 148 | // type Item = S; 149 | // 150 | // fn next(&mut self) -> Option { 151 | // self.find(|_| true) 152 | // } 153 | // 154 | // fn size_hint(&self) -> (usize, Option) { 155 | // if self.error.is_error() { 156 | // (0, Some(0)) 157 | // } else { 158 | // let (_, upper) = self.iter.size_hint(); 159 | // (0, upper) 160 | // } 161 | // } 162 | //} 163 | 164 | impl DoubleEndedIterator for IntoIter { 165 | #[inline] 166 | fn next_back(&mut self) -> Option { 167 | self.inner.take() 168 | } 169 | } 170 | 171 | impl<'a, T> DoubleEndedIterator for IterMut<'a, T> { 172 | #[inline] 173 | fn next_back(&mut self) -> Option<&'a mut T> { 174 | self.inner.take() 175 | } 176 | } 177 | 178 | impl<'a, T> DoubleEndedIterator for Iter<'a, T> { 179 | #[inline] 180 | fn next_back(&mut self) -> Option<&'a T> { 181 | self.inner.take() 182 | } 183 | } 184 | 185 | impl ExactSizeIterator for IntoIter {} 186 | impl ExactSizeIterator for IterMut<'_, T> {} 187 | impl ExactSizeIterator for Iter<'_, T> {} 188 | 189 | impl FusedIterator for IntoIter {} 190 | impl FusedIterator for IterMut<'_, T> {} 191 | impl FusedIterator for Iter<'_, T> {} 192 | 193 | #[cfg(test)] 194 | mod tests { 195 | #[cfg(feature = "std")] 196 | extern crate std; 197 | #[cfg(feature = "std")] 198 | use std::vec::Vec; 199 | 200 | use super::*; 201 | 202 | #[cfg(feature = "std")] 203 | #[test] 204 | fn into_iter_with_collect() { 205 | let success: Vec = Success::(1).into_iter().collect(); 206 | let mistake: Vec = Mistake::(()).into_iter().collect(); 207 | let failure: Vec = Failure::(()).into_iter().collect(); 208 | assert_eq!(success, [1]); 209 | assert_eq!(mistake, []); 210 | assert_eq!(failure, []); 211 | } 212 | 213 | #[test] 214 | fn size_hint() { 215 | assert_eq!((1, Some(1)), Success::(1).iter().size_hint()); 216 | assert_eq!((0, Some(0)), Mistake::<(), i32, ()>(1).iter().size_hint()); 217 | assert_eq!((0, Some(0)), Failure::<(), (), i32>(1).iter().size_hint()); 218 | assert_eq!( 219 | (1, Some(1)), 220 | Success::(1).iter_mut().size_hint() 221 | ); 222 | assert_eq!( 223 | (0, Some(0)), 224 | Mistake::<(), i32, ()>(1).iter_mut().size_hint() 225 | ); 226 | assert_eq!( 227 | (0, Some(0)), 228 | Failure::<(), (), i32>(1).iter_mut().size_hint() 229 | ); 230 | } 231 | 232 | #[test] 233 | fn next_back() { 234 | assert!(Success::(1).iter().next_back().is_some()); 235 | assert!(Mistake::<(), i32, ()>(1).iter().next_back().is_none()); 236 | assert!(Failure::<(), (), i32>(1).iter().next_back().is_none()); 237 | assert!(Success::(1).into_iter().next_back().is_some()); 238 | assert!(Mistake::<(), i32, ()>(1).into_iter().next_back().is_none()); 239 | assert!(Failure::<(), (), i32>(1).into_iter().next_back().is_none()); 240 | assert!(Success::(1).iter_mut().next_back().is_some()); 241 | assert!(Mistake::<(), i32, ()>(1).iter_mut().next_back().is_none()); 242 | assert!(Failure::<(), (), i32>(1).iter_mut().next_back().is_none()); 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md"))] 2 | #![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/features.md"))] 3 | #![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/why-augment-result.md"))] 4 | #![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/state-escalation.md"))] 5 | #![doc(test(attr(allow(unused_imports))))] 6 | #![doc(test(attr(allow(dead_code))))] 7 | #![doc(test(attr(deny(warnings))))] 8 | #![warn(clippy::cargo_common_metadata)] 9 | #![warn(clippy::doc_markdown)] 10 | #![warn(clippy::fallible_impl_from)] 11 | #![warn(clippy::large_digit_groups)] 12 | #![warn(clippy::manual_ok_or)] 13 | #![warn(clippy::print_stderr)] 14 | #![warn(clippy::print_stdout)] 15 | #![warn(clippy::redundant_pub_crate)] 16 | #![warn(clippy::redundant_else)] 17 | #![warn(clippy::single_match_else)] 18 | #![warn(clippy::trait_duplication_in_bounds)] 19 | #![warn(clippy::type_repetition_in_bounds)] 20 | #![warn(clippy::unneeded_field_pattern)] 21 | #![warn(clippy::unnested_or_patterns)] 22 | #![warn(clippy::unused_self)] 23 | #![warn(clippy::use_self)] 24 | #![warn(clippy::missing_panics_doc)] 25 | #![warn(clippy::missing_safety_doc)] 26 | //#![warn(missing_doc_code_examples)] 27 | #![warn(let_underscore_drop)] 28 | #![warn(missing_docs)] 29 | #![warn(unsafe_code)] 30 | #![cfg_attr( 31 | all(nightly, feature = "nightly"), 32 | feature(try_trait_v2), 33 | feature(never_type), 34 | feature(exhaustive_patterns) 35 | )] 36 | #![cfg_attr(any(docsrs, nightly), feature(doc_cfg))] 37 | #![no_std] 38 | 39 | #[cfg(doc)] 40 | extern crate std; 41 | 42 | #[cfg_attr(any(docsrs, nightly), doc(cfg(feature = "unstable")))] 43 | #[cfg(feature = "unstable")] 44 | mod unstable; 45 | 46 | #[cfg_attr(any(docsrs, nightly), doc(cfg(feature = "nightly")))] 47 | #[cfg(all(nightly, feature = "nightly"))] 48 | mod nightly; 49 | 50 | mod aberration; 51 | mod concern; 52 | mod outcome; 53 | mod private; 54 | 55 | mod iter; 56 | 57 | #[cfg(any(feature = "report", feature = "diagnostic"))] 58 | mod wrap; 59 | 60 | pub mod convert; 61 | pub mod prelude; 62 | 63 | #[cfg_attr(any(docsrs, nightly), doc(cfg(feature = "report")))] 64 | #[cfg(feature = "report")] 65 | pub mod report; 66 | 67 | #[cfg_attr(any(docsrs, nightly), doc(cfg(feature = "diagnostic")))] 68 | #[cfg(feature = "diagnostic")] 69 | pub mod diagnostic; 70 | 71 | #[cfg_attr(doc, doc(inline))] 72 | pub use crate::{aberration::*, concern::*, convert::*, iter::*, outcome::*}; 73 | -------------------------------------------------------------------------------- /src/nightly.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "std")] 2 | extern crate std; 3 | use core::{ 4 | convert::Infallible, 5 | ops::{ControlFlow, FromResidual, Try}, 6 | }; 7 | #[cfg(feature = "std")] 8 | use std::{ 9 | eprintln, 10 | fmt::Debug, 11 | process::{ExitCode, Termination}, 12 | }; 13 | 14 | use crate::prelude::*; 15 | 16 | /* feature(never_type) */ 17 | impl Outcome { 18 | /// **`TODO`**: write documentation 19 | pub fn escalate_with(self, closure: C) -> Outcome 20 | where 21 | T: Into, 22 | C: FnOnce(S) -> T, 23 | { 24 | match self { 25 | Success(s) => Mistake(closure(s).into()), 26 | Mistake(m) => Mistake(m), 27 | Failure(f) => Failure(f), 28 | } 29 | } 30 | } 31 | 32 | impl, M: Into, F> Outcome { 33 | /// Escalates an [`Outcome`] from a [`Mistake`] to a [`Failure`] 34 | pub fn escalate_mistake(self) -> Outcome { 35 | match self { 36 | Success(s) => s.into(), 37 | Mistake(m) => Failure(m.into()), 38 | Failure(f) => Failure(f), 39 | } 40 | } 41 | } 42 | 43 | impl, M, F> Outcome { 44 | /// Escalates an [`Outcome`] from a [`Mistake`] to a [`Failure`] using the 45 | /// given closure. 46 | /// 47 | pub fn escalate_mistake_with(self, closure: C) -> Outcome 48 | where 49 | G: Into, 50 | C: FnOnce(M) -> G, 51 | { 52 | match self { 53 | Success(s) => s.into(), 54 | Mistake(m) => Failure(closure(m).into()), 55 | Failure(f) => Failure(f), 56 | } 57 | } 58 | } 59 | 60 | impl, F: Into> Outcome { 61 | /// Returns the contained [`Success`] value, but never panics. 62 | /// 63 | /// Unlike [`unwrap`], this method is known to never panic on the outcome 64 | /// types it is implemented for. Therefore, it can be used instead of 65 | /// `unwrap` as a maintainability safeguard that will fail to compile if the 66 | /// mistake or failure type of the `Outcome` is later changed to mistake or 67 | /// failure that can actually occur. 68 | /// 69 | /// # Examples 70 | /// 71 | /// ``` 72 | /// # #![feature(never_type)] 73 | /// # use outcome::prelude::*; 74 | /// fn only_success() -> Outcome { 75 | /// Success("This is fine 🐶☕🔥".into()) 76 | /// } 77 | /// 78 | /// let s: String = only_success().into_success(); 79 | /// assert!(s.contains("This is fine")); 80 | /// ``` 81 | /// 82 | /// [`unwrap`]: crate::prelude::Outcome::unwrap 83 | pub fn into_success(self) -> S { 84 | match self { 85 | Success(s) => s, 86 | Mistake(m) => m.into(), 87 | Failure(f) => f.into(), 88 | } 89 | } 90 | } 91 | 92 | impl, M, F: Into> Outcome { 93 | /// Returns the contained [`Mistake`] value, but never panics. 94 | /// 95 | /// Unlike [`unwrap_mistake`], this method is known to never panic on the 96 | /// outcome types it is implemented for. Therefore it can be used instead of 97 | /// `unwrap_mistake` as a maintainibility safeguard that will fail to compile 98 | /// if the success or failure type of the `Outcome` is later changed to a 99 | /// success or failure that can actually occur. 100 | /// 101 | /// # Examples 102 | /// 103 | /// ``` 104 | /// # #![feature(never_type)] 105 | /// # use outcome::prelude::*; 106 | /// fn only_mistake() -> Outcome { 107 | /// Mistake("Try another! 🍾🔫🤠".into()) 108 | /// } 109 | /// 110 | /// let s: String = only_mistake().into_mistake(); 111 | /// assert!(s.contains("Try another!")); 112 | /// ``` 113 | /// 114 | /// [`unwrap_mistake`]: crate::prelude::Outcome::unwrap_mistake 115 | pub fn into_mistake(self) -> M { 116 | match self { 117 | Success(s) => s.into(), 118 | Mistake(m) => m, 119 | Failure(f) => f.into(), 120 | } 121 | } 122 | } 123 | 124 | impl, M: Into, F> Outcome { 125 | /// Returns the contained [`Failure`] value, but never panics. 126 | /// 127 | /// Unlike [`unwrap_failure`], this method is known to never panic on the 128 | /// outcome types it is implemented for. Therefore, it can be used instead of 129 | /// `unwrap_failure` as a maintainibility safeguard that will fail to compile 130 | /// if the success or mistake type of the `Outcome` is later changed to a 131 | /// success or mistake that can actually occur. 132 | /// 133 | /// ``` 134 | /// # #![feature(never_type)] 135 | /// # use outcome::prelude::*; 136 | /// fn only_failure() -> Outcome { 137 | /// Failure("Catarina! 👦🤚🪑👧".into()) 138 | /// } 139 | /// 140 | /// let s: String = only_failure().into_failure(); 141 | /// assert!(s.contains("Catarina!")); 142 | /// ``` 143 | /// 144 | /// [`unwrap_failure`]: crate::prelude::Outcome::unwrap_failure 145 | pub fn into_failure(self) -> F { 146 | match self { 147 | Success(s) => s.into(), 148 | Mistake(m) => m.into(), 149 | Failure(f) => f, 150 | } 151 | } 152 | } 153 | 154 | #[cfg(feature = "std")] 155 | impl Termination for Outcome { 156 | fn report(self) -> ExitCode { 157 | #[allow(clippy::print_stderr)] 158 | match self { 159 | Mistake(m) => eprintln!("Mistake: {:?}", m), 160 | Failure(f) => eprintln!("Failure: {:?}", f), 161 | }; 162 | ExitCode::FAILURE 163 | } 164 | } 165 | 166 | /* feature(try_trait_v2) */ 167 | impl Try for Outcome { 168 | type Output = Concern; 169 | type Residual = Outcome; 170 | 171 | #[inline] 172 | fn from_output(output: Self::Output) -> Self { 173 | match output { 174 | Concern::Success(s) => Success(s), 175 | Concern::Mistake(m) => Mistake(m), 176 | } 177 | } 178 | 179 | #[inline] 180 | fn branch(self) -> ControlFlow { 181 | match self { 182 | Success(s) => ControlFlow::Continue(Concern::Success(s)), 183 | Mistake(m) => ControlFlow::Continue(Concern::Mistake(m)), 184 | Failure(f) => ControlFlow::Break(Failure(f)), 185 | } 186 | } 187 | } 188 | 189 | impl Try for Aberration { 190 | type Output = M; 191 | type Residual = Result; 192 | 193 | #[inline] 194 | fn from_output(output: Self::Output) -> Self { 195 | Self::Mistake(output) 196 | } 197 | 198 | #[inline] 199 | fn branch(self) -> ControlFlow { 200 | match self { 201 | Self::Mistake(m) => ControlFlow::Continue(m), 202 | Self::Failure(f) => ControlFlow::Break(Err(f)), 203 | } 204 | } 205 | } 206 | 207 | impl> FromResidual> 208 | for Outcome 209 | { 210 | #[inline] 211 | fn from_residual(residual: Outcome) -> Self { 212 | match residual { 213 | Failure(f) => Failure(From::from(f)), 214 | } 215 | } 216 | } 217 | 218 | impl, G: From> FromResidual> 219 | for Outcome 220 | { 221 | #[inline] 222 | fn from_residual(residual: Aberration) -> Self { 223 | match residual { 224 | Aberration::Mistake(m) => Mistake(From::from(m)), 225 | Aberration::Failure(f) => Failure(From::from(f)), 226 | } 227 | } 228 | } 229 | 230 | impl> FromResidual> 231 | for Result 232 | { 233 | #[inline] 234 | fn from_residual(residual: Outcome) -> Self { 235 | match residual { 236 | Failure(f) => Err(From::from(f)), 237 | } 238 | } 239 | } 240 | 241 | impl> FromResidual> 242 | for Outcome 243 | { 244 | #[inline] 245 | fn from_residual(residual: Result) -> Self { 246 | match residual { 247 | Err(e) => Failure(From::from(e)), 248 | } 249 | } 250 | } 251 | 252 | impl> FromResidual> 253 | for Aberration 254 | { 255 | #[inline] 256 | fn from_residual(residual: Result) -> Self { 257 | match residual { 258 | Err(e) => Self::Failure(From::from(e)), 259 | } 260 | } 261 | } 262 | 263 | #[cfg(test)] 264 | mod tests { 265 | use super::*; 266 | 267 | mod try_trait { 268 | use super::*; 269 | 270 | #[test] 271 | fn aberration() -> Result<(), &'static str> { 272 | let aberration: Aberration = Aberration::Mistake(0u32); 273 | let value = aberration?; 274 | assert_eq!(value, 0u32); 275 | Ok(()) 276 | } 277 | 278 | #[test] 279 | fn outcome() -> Result<(), &'static str> { 280 | let outcome: Outcome = Mistake(0u32); 281 | let concern = outcome?; 282 | assert_eq!(concern, Concern::Mistake(0u32)); 283 | Ok(()) 284 | } 285 | } 286 | 287 | #[cfg(feature = "std")] 288 | mod termination { 289 | use super::*; 290 | 291 | #[test] 292 | fn aberration() -> Outcome<(), (), &'static str> { 293 | let aberration: Aberration = Aberration::Mistake(0u32); 294 | let value = aberration?; 295 | assert_eq!(value, 0u32); 296 | Success(()) 297 | } 298 | 299 | #[test] 300 | fn outcome() -> Outcome<(), (), &'static str> { 301 | let outcome: Outcome = Mistake(0u32); 302 | let concern = outcome?; 303 | assert_eq!(concern, Concern::Mistake(0u32)); 304 | Success(()) 305 | } 306 | 307 | #[test] 308 | fn result() -> Outcome<(), (), &'static str> { 309 | let result: Result = Ok(0u32); 310 | let value = result?; 311 | assert_eq!(value, 0u32); 312 | Success(()) 313 | } 314 | } 315 | } 316 | -------------------------------------------------------------------------------- /src/outcome.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "std")] 2 | extern crate std; 3 | 4 | use core::{ 5 | fmt::Debug, 6 | ops::{Deref, DerefMut}, 7 | }; 8 | 9 | #[doc(hidden)] 10 | pub use crate::iter::*; 11 | use crate::{aberration::*, concern::*, private::*}; 12 | 13 | #[cfg(feature = "std")] 14 | use std::{ 15 | eprintln, 16 | process::{ExitCode, Termination}, 17 | }; 18 | 19 | // TODO: Add an 'aggregate' set of functions (aggregate(_(mistake|failure))?) 20 | // to collect all success, mistake or failure into iterators/partition an 21 | // iterable of failures, concerns, mistakes, etc. 22 | // 23 | // TODO: Add an aggregate_reports function in crate::report 24 | 25 | /// `Outcome` is a type that represents a [`Success`], [`Mistake`], or 26 | /// [`Failure`]. 27 | /// 28 | /// See the [module documentation](crate) for more details. 29 | /// 30 | /// # Example 31 | /// 32 | /// The following example shows using Outcome to wrap [`Mutex`] to create a 33 | /// spin lock with [exponential backoff][1], that will not block and is adapted 34 | /// from the C++ code in the blog post [*Using locks in real-time audio 35 | /// processing, safely*][2]. 36 | /// 37 | /// This is *not* meant to be an example of good API design, but to show how 38 | /// [`Outcome`] can be used to make retryable APIs easier to work with. 39 | /// 40 | /// ``` 41 | /// # use outcome::prelude::*; 42 | /// use std::sync::{Mutex, MutexGuard, PoisonError, LockResult, TryLockError}; 43 | /// #[cfg(target_arch = "x86_64")] 44 | /// use std::arch::x86_64::_mm_pause; 45 | /// #[cfg(target_arch = "x86")] 46 | /// use std::arch::x86::_mm_pause; 47 | /// 48 | /// #[cfg(not(any(target_arch = "x86_64", target_arch="x86")))] 49 | /// #[inline(never)] 50 | /// unsafe fn _mm_pause() { } 51 | /// 52 | /// struct WouldBlock; 53 | /// 54 | /// struct SpinMutex { 55 | /// inner: Mutex, 56 | /// } 57 | /// 58 | /// type TryLockOutcome<'a, T> = Outcome< 59 | /// MutexGuard<'a, T>, 60 | /// WouldBlock, 61 | /// PoisonError> 62 | /// >; 63 | /// 64 | /// impl SpinMutex { 65 | /// pub fn try_lock(&self) -> TryLockOutcome { 66 | /// match self.inner.try_lock() { 67 | /// Err(TryLockError::Poisoned(f)) => Failure(f), 68 | /// Err(TryLockError::WouldBlock) => Mistake(WouldBlock), 69 | /// Ok(s) => Success(s), 70 | /// } 71 | /// } 72 | /// 73 | /// pub fn lock(&self) -> LockResult> { 74 | /// for _ in 0..5 { 75 | /// match self.try_lock() { 76 | /// Success(s) => { return Ok(s); } 77 | /// Mistake(_) => { continue; } 78 | /// Failure(f) => { return Err(f); } 79 | /// } 80 | /// } 81 | /// 82 | /// for _ in 0..10 { 83 | /// match self.try_lock() { 84 | /// Success(s) => { return Ok(s); } 85 | /// Mistake(_) => { unsafe { _mm_pause(); } } 86 | /// Failure(f) => { return Err(f); } 87 | /// } 88 | /// } 89 | /// 90 | /// let mut n = 0; 91 | /// loop { 92 | /// for _ in 0..3000 { 93 | /// match self.try_lock() { 94 | /// Success(s) => { return Ok(s); } 95 | /// Mistake(_) => { 96 | /// for _ in 0..10 { unsafe { _mm_pause(); } } 97 | /// continue; 98 | /// } 99 | /// Failure(f) => { return Err(f); } 100 | /// } 101 | /// } 102 | /// std::thread::yield_now(); 103 | /// n += 1; 104 | /// if n >= 2 { break self.inner.lock(); } 105 | /// } 106 | /// } 107 | /// } 108 | /// ``` 109 | /// [`Mutex`]: std::sync::Mutex 110 | /// 111 | /// [1]: https://en.wikipedia.org/wiki/Exponential_backoff 112 | /// [2]: https://timur.audio/using-locks-in-real-time-audio-processing-safely 113 | #[must_use = "This `Outcome` might not be a `Success`, which should be handled"] 114 | #[derive(Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)] 115 | pub enum Outcome { 116 | /// Contains the success value 117 | Success(S), 118 | /// Contains the mistake value 119 | Mistake(M), 120 | /// Contains the failure value 121 | Failure(F), 122 | } 123 | 124 | use Outcome::{Failure, Mistake, Success}; 125 | 126 | impl Outcome { 127 | /// Converts from `&Outcome` to `Outcome<&S, &M, &F>`. 128 | /// 129 | /// Produces a new `Outcome`, containing a reference into the original, 130 | /// leaving the original in place. 131 | /// 132 | /// # Examples 133 | /// 134 | /// ``` 135 | /// # use outcome::prelude::*; 136 | /// let x: Outcome = Success(2); 137 | /// assert_eq!(x.as_ref(), Success(&2)); 138 | /// 139 | /// let x: Outcome = Mistake(47); 140 | /// assert_eq!(x.as_ref(), Mistake(&47)); 141 | /// 142 | /// let x: Outcome = Failure(42); 143 | /// assert_eq!(x.as_ref(), Failure(&42)); 144 | /// ``` 145 | #[inline] 146 | pub fn as_ref(&self) -> Outcome<&S, &M, &F> { 147 | match *self { 148 | Success(ref value) => Success(value), 149 | Mistake(ref value) => Mistake(value), 150 | Failure(ref value) => Failure(value), 151 | } 152 | } 153 | 154 | /// Converts from `&mut Outcome` to `Outcome<&mut S, &mut M, &mut 155 | /// F>`. 156 | /// 157 | /// # Examples 158 | /// 159 | /// ``` 160 | /// # use outcome::prelude::*; 161 | /// fn mutate(o: &mut Outcome) { 162 | /// match o.as_mut() { 163 | /// Success(s) => *s = 47, 164 | /// Mistake(m) => *m = 19, 165 | /// Failure(f) => *f = 0, 166 | /// } 167 | /// } 168 | /// 169 | /// let mut x: Outcome = Success(2); 170 | /// mutate(&mut x); 171 | /// assert_eq!(x.unwrap(), 47); 172 | /// 173 | /// let mut x: Outcome = Mistake(47); 174 | /// mutate(&mut x); 175 | /// assert_eq!(x.unwrap_mistake(), 19); 176 | /// 177 | /// let mut x: Outcome = Failure(47); 178 | /// mutate(&mut x); 179 | /// assert_eq!(x.unwrap_failure(), 0); 180 | /// ``` 181 | #[inline] 182 | pub fn as_mut(&mut self) -> Outcome<&mut S, &mut M, &mut F> { 183 | match *self { 184 | Success(ref mut value) => Success(value), 185 | Mistake(ref mut value) => Mistake(value), 186 | Failure(ref mut value) => Failure(value), 187 | } 188 | } 189 | 190 | /// Returns a `Result, F>`, which allows a user to still rely 191 | /// on the `?` operator until [`Try`] has been stabilized. 192 | /// 193 | /// # Examples 194 | /// 195 | /// ``` 196 | /// # use outcome::prelude::*; 197 | /// 198 | /// pub fn invoke() -> Result { 199 | /// let x: Outcome = Success(2); 200 | /// let c = x.acclimate()?; 201 | /// Ok(match c { 202 | /// Concern::Success(s) => s, 203 | /// Concern::Mistake(m) => m as u32, 204 | /// }) 205 | /// } 206 | /// ``` 207 | /// 208 | /// 209 | /// [`Try`]: core::ops::Try 210 | #[inline] 211 | pub fn acclimate(self) -> Result, F> { 212 | match self { 213 | Success(value) => Ok(Concern::Success(value)), 214 | Mistake(value) => Ok(Concern::Mistake(value)), 215 | Failure(value) => Err(value), 216 | } 217 | } 218 | 219 | /// Returns an iterator over the possibly contained value. 220 | /// 221 | /// The iterators yields one value if the outcome is [`Success`], otherwise 222 | /// none. 223 | /// 224 | /// # Examples 225 | /// 226 | /// Basic usage: 227 | /// 228 | /// ``` 229 | /// # use outcome::prelude::*; 230 | /// let x: Outcome = Success(47); 231 | /// assert_eq!(x.iter().next(), Some(&47)); 232 | /// 233 | /// let x: Outcome = Mistake(0.0f32); 234 | /// assert_eq!(x.iter().next(), None); 235 | /// 236 | /// let x: Outcome = Failure("nope!"); 237 | /// assert_eq!(x.iter().next(), None); 238 | /// ``` 239 | #[inline] 240 | pub fn iter(&self) -> Iter<'_, S> { 241 | Iter { 242 | inner: self.as_ref().success(), 243 | } 244 | } 245 | 246 | /// Returns a mutable iterator over the possibly contained value. 247 | /// 248 | /// The iterator yields one value if the result is [`Success`], otherwise 249 | /// none. 250 | /// 251 | /// # Examples 252 | /// 253 | /// ``` 254 | /// # use outcome::prelude::*; 255 | /// let mut x: Outcome = Success(7); 256 | /// match x.iter_mut().next() { 257 | /// Some(v) => *v += 40, 258 | /// None => {} 259 | /// } 260 | /// assert_eq!(x, Success(47)); 261 | /// ``` 262 | #[inline] 263 | pub fn iter_mut(&mut self) -> IterMut<'_, S> { 264 | IterMut { 265 | inner: self.as_mut().success(), 266 | } 267 | } 268 | 269 | /// Returns `true` if the outcome is [`Success`]. 270 | /// 271 | /// # Examples 272 | /// 273 | /// Basic usage: 274 | /// 275 | /// ``` 276 | /// # use outcome::prelude::*; 277 | /// let x: Outcome = Success(-1); 278 | /// assert!(x.is_success()); 279 | /// 280 | /// let x: Outcome = Mistake(()); 281 | /// assert!(!x.is_success()); 282 | /// 283 | /// let x: Outcome = Failure("Some failure message"); 284 | /// assert!(!x.is_success()); 285 | /// ``` 286 | /// 287 | #[must_use = "if you intended to assert a success, consider `.unwrap()` instead"] 288 | #[inline] 289 | pub fn is_success(&self) -> bool { 290 | if let Success(_) = self { 291 | return true; 292 | } 293 | false 294 | } 295 | 296 | /// Returns `true` if the outcome is [`Mistake`]. 297 | /// 298 | /// # Examples 299 | /// 300 | /// ``` 301 | /// # use outcome::prelude::*; 302 | /// let x: Outcome<(), i32, &str> = Mistake(-1); 303 | /// assert!(x.is_mistake()); 304 | /// 305 | /// let x: Outcome<(), i32, &str> = Success(()); 306 | /// assert!(!x.is_mistake()); 307 | /// 308 | /// let x: Outcome<(), i32, &str> = Failure("Some failure message"); 309 | /// assert!(!x.is_mistake()); 310 | /// ``` 311 | #[must_use = "if you intended to assert a mistake, consider `.unwrap_mistake()` instead"] 312 | #[inline] 313 | pub fn is_mistake(&self) -> bool { 314 | if let Mistake(_) = self { 315 | return true; 316 | } 317 | false 318 | } 319 | 320 | /// Returns `true` if the outcome is [`Failure`]. 321 | /// 322 | /// # Examples 323 | /// 324 | /// ``` 325 | /// # use outcome::prelude::*; 326 | /// let x: Outcome = Failure("some failure message"); 327 | /// assert!(x.is_failure()); 328 | /// 329 | /// let x: Outcome = Mistake(0.0f32); 330 | /// assert!(!x.is_failure()); 331 | /// 332 | /// let x: Outcome = Success(-1); 333 | /// assert!(!x.is_failure()); 334 | /// ``` 335 | #[must_use = "if you intended to assert a failure, consider `.unwrap_failure()` instead"] 336 | #[inline] 337 | pub fn is_failure(&self) -> bool { 338 | if let Failure(_) = self { 339 | return true; 340 | } 341 | false 342 | } 343 | 344 | /// Returns `true` if the outcome is *not* [`Success`] 345 | /// 346 | /// # Examples 347 | /// 348 | /// ``` 349 | /// # use outcome::prelude::*; 350 | /// let x: Outcome = Failure("some failure message"); 351 | /// assert!(x.is_error()); 352 | /// 353 | /// let x: Outcome = Mistake(0.0f32); 354 | /// assert!(x.is_error()); 355 | /// 356 | /// let x: Outcome = Success(-1); 357 | /// assert!(!x.is_error()); 358 | /// ``` 359 | #[must_use = "If you intended to assert an error, consider `.unwrap_error()` instead"] 360 | #[inline] 361 | pub fn is_error(&self) -> bool { 362 | !self.is_success() 363 | } 364 | 365 | /// Converts from `Outcome` to [`Option`]. 366 | /// 367 | /// Converts `self` into an [`Option`], consuming `self`, and discarding 368 | /// the mistake or failure, if any. 369 | /// 370 | /// # Examples 371 | /// 372 | /// ``` 373 | /// # use outcome::prelude::*; 374 | /// let outcome: Outcome = Success(4); 375 | /// assert_eq!(outcome.success(), Some(4)); 376 | /// 377 | /// let outcome: Outcome = Mistake(0.0); 378 | /// assert_eq!(outcome.success(), None); 379 | /// 380 | /// let outcome: Outcome = Failure("failure"); 381 | /// assert_eq!(outcome.success(), None); 382 | /// ``` 383 | #[inline] 384 | pub fn success(self) -> Option { 385 | if let Success(value) = self { 386 | return Some(value); 387 | } 388 | None 389 | } 390 | 391 | /// Converts from `Outcome` to [`Option`]. 392 | /// 393 | /// Converts `self` into an [`Option`], consuming `self`, and discarding 394 | /// the success or failure, if any. 395 | /// 396 | /// # Examples 397 | /// 398 | /// ``` 399 | /// # use outcome::prelude::*; 400 | /// let outcome: Outcome = Mistake(47); 401 | /// assert_eq!(outcome.mistake(), Some(47)); 402 | /// 403 | /// let outcome: Outcome = Success(0.0); 404 | /// assert_eq!(outcome.mistake(), None); 405 | /// 406 | /// let outcome: Outcome = Failure("failure"); 407 | /// assert_eq!(outcome.mistake(), None); 408 | /// ``` 409 | #[inline] 410 | pub fn mistake(self) -> Option { 411 | if let Mistake(value) = self { 412 | return Some(value); 413 | } 414 | None 415 | } 416 | 417 | /// Converts from `Outcome` to [`Option`]. 418 | /// 419 | /// Converts `self` into an [`Option`], consuming `self`, and discarding 420 | /// the success or mistake, if any. 421 | /// 422 | /// # Examples 423 | /// 424 | /// ``` 425 | /// # use outcome::prelude::*; 426 | /// let outcome: Outcome = Success(0.0); 427 | /// assert_eq!(outcome.failure(), None); 428 | /// 429 | /// let outcome: Outcome = Mistake(()); 430 | /// assert_eq!(outcome.failure(), None); 431 | /// 432 | /// let outcome: Outcome = Failure(-1); 433 | /// assert_eq!(outcome.failure(), Some(-1)); 434 | /// ``` 435 | #[inline] 436 | pub fn failure(self) -> Option { 437 | if let Failure(value) = self { 438 | return Some(value); 439 | } 440 | None 441 | } 442 | 443 | /// Returns the contained [`Success`] value, consuming the `self` value, 444 | /// without checking that the value is not a [`Mistake`] or [`Failure`]. 445 | /// 446 | /// # Safety 447 | /// 448 | /// Calling this method on a [`Mistake`] or [`Failure`] is *[undefined 449 | /// behavior]* 450 | /// 451 | /// # Examples 452 | /// 453 | /// ``` 454 | /// # use outcome::prelude::*; 455 | /// let x: Outcome = Success(47); 456 | /// assert_eq!(unsafe { x.unwrap_unchecked() }, 47); 457 | /// ``` 458 | /// 459 | /// ```no_run 460 | /// # use outcome::prelude::*; 461 | /// let x: Outcome = Mistake(0.0f32); 462 | /// unsafe { x.unwrap_unchecked(); } // Undefined Behavior! 463 | /// ``` 464 | /// 465 | /// ```no_run 466 | /// # use outcome::prelude::*; 467 | /// let x: Outcome = Failure("emergency!"); 468 | /// unsafe { x.unwrap_unchecked(); } // Undefined Behavior! 469 | /// ``` 470 | /// 471 | /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html 472 | #[inline] 473 | #[track_caller] 474 | #[allow(unsafe_code)] 475 | pub unsafe fn unwrap_unchecked(self) -> S { 476 | debug_assert!(self.is_success()); 477 | if let Success(value) = self { 478 | return value; 479 | } 480 | core::hint::unreachable_unchecked(); 481 | } 482 | 483 | /// Returns the contained [`Mistake`] value, consuming the `self` value, 484 | /// without checking that the value is not a [`Success`] or [`Failure`]. 485 | /// 486 | /// # Safety 487 | /// 488 | /// Calling this method on a [`Success`] or [`Failure`] is *[undefined 489 | /// behavior]* 490 | /// 491 | /// # Examples 492 | /// 493 | /// ```no_run 494 | /// # use outcome::prelude::*; 495 | /// let x: Outcome = Success(47); 496 | /// unsafe { x.unwrap_mistake_unchecked(); } // Undefined Behavior! 497 | /// ``` 498 | /// 499 | /// ``` 500 | /// # use outcome::prelude::*; 501 | /// let x: Outcome = Mistake(47); 502 | /// assert_eq!(unsafe { x.unwrap_mistake_unchecked() }, 47); 503 | /// ``` 504 | /// 505 | /// ```no_run 506 | /// # use outcome::prelude::*; 507 | /// let x: Outcome = Failure("emergency!"); 508 | /// unsafe { x.unwrap_mistake_unchecked(); } // Undefined Behavior!! 509 | /// ``` 510 | /// 511 | /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html 512 | #[inline] 513 | #[track_caller] 514 | #[allow(unsafe_code)] 515 | pub unsafe fn unwrap_mistake_unchecked(self) -> M { 516 | debug_assert!(self.is_mistake()); 517 | if let Mistake(value) = self { 518 | return value; 519 | } 520 | core::hint::unreachable_unchecked(); 521 | } 522 | 523 | /// Returns the contained [`Failure`] value, consuming the `self` value 524 | /// without checking that the value is not a [`Success`] or [`Mistake`]. 525 | /// 526 | /// # Safety 527 | /// 528 | /// Calling this method on a [`Success`] or [`Mistake`] is *[undefined 529 | /// behavior]* 530 | /// 531 | /// # Examples 532 | /// 533 | /// ```no_run 534 | /// # use outcome::prelude::*; 535 | /// let x: Outcome = Success(47); 536 | /// unsafe { x.unwrap_failure_unchecked(); } // Undefined Behavior! 537 | /// ``` 538 | /// 539 | /// ```no_run 540 | /// # use outcome::prelude::*; 541 | /// let x: Outcome = Mistake(47); 542 | /// unsafe { x.unwrap_failure_unchecked() }; // Undefined Behavior! 543 | /// ``` 544 | /// 545 | /// ``` 546 | /// # use outcome::prelude::*; 547 | /// let x: Outcome = Failure("emergency!"); 548 | /// assert_eq!(unsafe { x.unwrap_failure_unchecked() }, "emergency!"); 549 | /// ``` 550 | /// 551 | /// 552 | /// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html 553 | #[inline] 554 | #[track_caller] 555 | #[allow(unsafe_code)] 556 | pub unsafe fn unwrap_failure_unchecked(self) -> F { 557 | debug_assert!(self.is_failure()); 558 | if let Failure(value) = self { 559 | return value; 560 | } 561 | core::hint::unreachable_unchecked(); 562 | } 563 | 564 | /// Calls `op` if the result is [`Success`], otherwise returns the 565 | /// [`Mistake`] or [`Failure`] value of `self`. 566 | /// 567 | /// This function can be used for control flow based on `Outcome` values. 568 | /// 569 | /// # Examples 570 | /// 571 | /// ``` 572 | /// # #![allow(unused_variables)] 573 | /// # use outcome::prelude::*; 574 | /// 575 | /// fn square(x: u32) -> Outcome { Success(x * x) } 576 | /// fn mistake(x: u32) -> Outcome { Mistake(x) } 577 | /// fn failure(x: u32) -> Outcome { Failure(0) } 578 | /// 579 | /// assert_eq!(Success(2).and_then(square).and_then(square), Success(16)); 580 | /// assert_eq!(Success(2).and_then(square).and_then(failure), Failure(0)); 581 | /// assert_eq!(Success(2).and_then(square).and_then(mistake), Mistake(4)); 582 | /// assert_eq!(Failure(2).and_then(square).and_then(square), Failure(2)); 583 | /// ``` 584 | #[inline] 585 | pub fn and_then(self, callable: C) -> Outcome 586 | where 587 | C: FnOnce(S) -> Outcome, 588 | { 589 | match self { 590 | Success(value) => callable(value), 591 | Mistake(value) => Mistake(value), 592 | Failure(value) => Failure(value), 593 | } 594 | } 595 | 596 | /// Maps an `Outcome` to `Outcome` by applying a function 597 | /// to a contained [`Success`] value, leaving any [`Mistake`] or [`Failure`] 598 | /// value untouched. 599 | /// 600 | /// This function can be used to compose the results of two functions. 601 | #[inline] 602 | pub fn map(self, callable: C) -> Outcome 603 | where 604 | C: FnOnce(S) -> T, 605 | { 606 | match self { 607 | Success(s) => Success(callable(s)), 608 | Mistake(m) => Mistake(m), 609 | Failure(f) => Failure(f), 610 | } 611 | } 612 | 613 | /// Returns the provided default (if [`Mistake`] or [`Failure`]), or applies 614 | /// a function to the contained value (if [`Success`]). 615 | /// 616 | /// Arguments passed to `map_or` are eagerly evaluated; if you are passing 617 | /// the result of a function call, it is recommended to use [`map_or_else`], 618 | /// which is lazily evaluated. 619 | /// 620 | /// # Examples 621 | /// 622 | ///``` 623 | /// # use outcome::prelude::*; 624 | /// let x: Outcome<_, &str, &str> = Success("foo"); 625 | /// assert_eq!(x.map_or(47, |v| v.len()), 3); 626 | /// 627 | /// let x: Outcome<&str, _, &str> = Mistake("bar"); 628 | /// assert_eq!(x.map_or(47, |v| v.len()), 47); 629 | /// 630 | /// let x: Outcome<&str, &str, _> = Failure("baz"); 631 | /// assert_eq!(x.map_or(47, |v| v.len()), 47); 632 | ///``` 633 | /// 634 | /// [`map_or_else`]: Outcome::map_or_else 635 | #[inline] 636 | pub fn map_or(self, default: T, callable: C) -> T 637 | where 638 | C: FnOnce(S) -> T, 639 | { 640 | match self { 641 | Success(value) => callable(value), 642 | _ => default, 643 | } 644 | } 645 | 646 | /// Maps an `Outcome` to `T` by applying a fallback function to a 647 | /// contained [`Mistake`] or [`Failure`] value (by way of an [`Aberration`]), 648 | /// or a default function to a contained [`Success`] value. 649 | /// 650 | /// This function can be used to unpack a successful outcome while handling 651 | /// mistakes or failures. 652 | #[inline] 653 | pub fn map_or_else(self, default: D, callable: C) -> T 654 | where 655 | D: FnOnce(Aberration) -> T, 656 | C: FnOnce(S) -> T, 657 | { 658 | match self { 659 | Success(value) => callable(value), 660 | Mistake(value) => default(Aberration::Mistake(value)), 661 | Failure(value) => default(Aberration::Failure(value)), 662 | } 663 | } 664 | 665 | /// Maps an `Outcome` to `Outcome` by applying a function to 666 | /// a contained [`Mistake`] value, leaving a [`Success`] or [`Failure`] value 667 | /// untouched. 668 | /// 669 | /// This function can be used to pass through a successful outcome while 670 | /// handling a mistake. 671 | /// 672 | /// # Examples 673 | /// 674 | /// ``` 675 | /// # use outcome::prelude::*; 676 | /// let x: Outcome<&str, _, &str> = Mistake("foo"); 677 | /// assert_eq!(x.map_mistake(|v| v.len()), Mistake(3)); 678 | /// 679 | /// let x: Outcome<&str, &str, _> = Failure("bar"); 680 | /// assert_eq!(x.map_mistake(|v| v.len()), Failure("bar")); 681 | /// 682 | /// let x: Outcome<_, &str, &str> = Success("baz"); 683 | /// assert_eq!(x.map_mistake(|v| v.len()), Success("baz")); 684 | /// ``` 685 | /// 686 | #[inline] 687 | pub fn map_mistake(self, callable: C) -> Outcome 688 | where 689 | C: FnOnce(M) -> N, 690 | { 691 | match self { 692 | Success(value) => Success(value), 693 | Mistake(value) => Mistake(callable(value)), 694 | Failure(value) => Failure(value), 695 | } 696 | } 697 | 698 | /// Maps an `Outcome` to `Outcome` by applying a function 699 | /// to a contained [`Failure`] value, leaving a [`Success`] or [`Failure`] 700 | /// value untouched. 701 | /// 702 | /// This function can be used to pass through a successful or mistaken 703 | /// outcome while handling a failure. 704 | #[inline] 705 | pub fn map_failure(self, callable: C) -> Outcome 706 | where 707 | C: FnOnce(F) -> G, 708 | { 709 | match self { 710 | Success(value) => Success(value), 711 | Mistake(value) => Mistake(value), 712 | Failure(value) => Failure(callable(value)), 713 | } 714 | } 715 | } 716 | 717 | impl Outcome<&S, M, F> { 718 | /// Maps an `Outcome<&S, M, F>` to an `Outcome` by cloning the 719 | /// contents of the `Success` value. 720 | /// 721 | /// # Examples 722 | /// 723 | /// ``` 724 | /// # use outcome::prelude::*; 725 | /// let val = 47; 726 | /// let x: Outcome<&i32, u32, f32> = Success(&val); 727 | /// assert_eq!(x, Success(&47)); 728 | /// let cloned = x.cloned(); 729 | /// assert_eq!(cloned, Success(47)); 730 | /// ``` 731 | pub fn cloned(self) -> Outcome { 732 | self.map(Clone::clone) 733 | } 734 | } 735 | 736 | impl Outcome<&mut S, M, F> { 737 | /// Maps an `Outcome<&mut S, M, F>` to an `Outcome` by cloning the 738 | /// contents of the `Success` value. 739 | /// 740 | /// # Examples 741 | /// 742 | /// ``` 743 | /// # use outcome::prelude::*; 744 | /// let mut val = 47; 745 | /// let x: Outcome<&mut i32, u32, i32> = Success(&mut val); 746 | /// assert_eq!(x, Success(&mut 47)); 747 | /// let cloned = x.cloned(); 748 | /// assert_eq!(cloned, Success(47)); 749 | /// ``` 750 | pub fn cloned(self) -> Outcome { 751 | self.map(|s| s.clone()) 752 | } 753 | } 754 | 755 | impl Outcome<&S, M, F> { 756 | /// Maps an `Outcome<&S, M, F>` to an `Outcome` by copying the 757 | /// contents of the `Success` value. 758 | /// 759 | /// # Examples 760 | /// 761 | /// ``` 762 | /// # use outcome::prelude::*; 763 | /// let value = 47; 764 | /// let x: Outcome<&i32, i32, i32> = Success(&value); 765 | /// assert_eq!(x, Success(&47)); 766 | /// let copied = x.copied(); 767 | /// assert_eq!(copied, Success(47)); 768 | /// ``` 769 | pub fn copied(self) -> Outcome { 770 | self.map(|&s| s) 771 | } 772 | } 773 | 774 | impl Outcome<&mut S, M, F> { 775 | /// Maps an `Outcome<&mut S, M, F>` to an `Outcome` by copying the 776 | /// contents of the `Success` value. 777 | /// 778 | /// # Examples 779 | /// 780 | /// ``` 781 | /// # use outcome::prelude::*; 782 | /// let mut value = 47; 783 | /// let x: Outcome<&mut i32, i32, i32> = Success(&mut value); 784 | /// assert_eq!(x, Success(&mut 47)); 785 | /// let copied = x.copied(); 786 | /// assert_eq!(copied, Success(47)); 787 | /// ``` 788 | pub fn copied(self) -> Outcome { 789 | self.map(|&mut s| s) 790 | } 791 | } 792 | 793 | /* special interfaces */ 794 | #[cfg(not(feature = "nightly"))] 795 | impl Outcome { 796 | /// **`TODO`**: Write documentation 797 | pub fn escalate_with(self, closure: C) -> Aberration 798 | where 799 | T: Into, 800 | C: FnOnce(S) -> T, 801 | { 802 | match self { 803 | Success(s) => Aberration::Mistake(closure(s).into()), 804 | Mistake(m) => Aberration::Mistake(m), 805 | Failure(f) => Aberration::Failure(f), 806 | } 807 | } 808 | } 809 | 810 | #[cfg(not(feature = "nightly"))] 811 | impl Outcome 812 | where 813 | S: Into, 814 | M: Into, 815 | { 816 | /// Escalates the state of the Outcome from Success, to Mistake, to Failure 817 | /// on each call. 818 | /// 819 | /// Once an Outcome is in a failure state, it cannot escalate any further. 820 | pub fn escalate(self) -> Aberration { 821 | match self { 822 | Success(s) => Aberration::Mistake(s.into()), 823 | Mistake(m) => Aberration::Failure(m.into()), 824 | Failure(f) => Aberration::Failure(f), 825 | } 826 | } 827 | } 828 | 829 | impl Outcome { 830 | /// Converts from `Outcome` (or `&Outcome`) to `Outcome<&::Target, M, F>`. 832 | /// 833 | /// Coerces the [`Success`] variant of the original [`Outcome`] via [`Deref`] 834 | /// and returns the new [`Outcome`]. 835 | /// 836 | /// # Examples 837 | /// 838 | /// ``` 839 | /// # use outcome::prelude::*; 840 | /// let x: Outcome = Success("hello".to_string()); 841 | /// let y: Outcome<&str, &u32, &u32> = Success("hello"); 842 | /// assert_eq!(x.as_deref(), y); 843 | /// ``` 844 | /// 845 | /// [`Deref`]: core::ops::Deref 846 | pub fn as_deref(&self) -> Outcome<&S::Target, &M, &F> { 847 | self.as_ref().map(Deref::deref) 848 | } 849 | } 850 | 851 | impl Outcome { 852 | /// Converts from `Outcome` (or `&mut Outcome`) to 853 | /// `Outcome<&mut ::Target, &mut M, &mut F>`. 854 | /// 855 | /// Coerces the [`Success`] variant of the original [`Outcome`] via 856 | /// [`DerefMut`] and returns the new [`Outcome`]. 857 | /// 858 | /// # Examples 859 | /// 860 | /// ``` 861 | /// # use outcome::prelude::*; 862 | /// let mut s = "HELLO".to_string(); 863 | /// let mut x: Outcome = Success("hello".to_string()); 864 | /// let y: Outcome<&mut str, &mut u32, &mut u32> = Success(&mut s); 865 | /// assert_eq!(x.as_deref_mut().map(|x| { x.make_ascii_uppercase(); x }), y); 866 | /// ``` 867 | /// 868 | /// [`DerefMut`]: core::ops::DerefMut 869 | pub fn as_deref_mut(&mut self) -> Outcome<&mut S::Target, &mut M, &mut F> { 870 | self.as_mut().map(DerefMut::deref_mut) 871 | } 872 | } 873 | 874 | impl Outcome { 875 | /// Returns the contained [`Success`] value, consuming the `self` value. 876 | /// 877 | /// Because this function may panic, its use is generally discouraged. 878 | /// Instead, prefer to use pattern matching and handle the [`Mistake`] or 879 | /// [`Failure`] case explicitly, or call [`unwrap_or`], [`unwrap_or_else`], 880 | /// or [`unwrap_or_default`]. 881 | /// 882 | /// # Panics 883 | /// 884 | /// Panics if the value is a [`Mistake`] or [`Failure`], with a panic message 885 | /// provided by their value. 886 | /// 887 | /// # Examples 888 | /// 889 | /// ``` 890 | /// # use outcome::prelude::*; 891 | /// let x: Outcome = Success(2); 892 | /// assert_eq!(x.unwrap(), 2); 893 | /// ``` 894 | /// 895 | /// ```should_panic 896 | /// # use outcome::prelude::*; 897 | /// let x: Outcome = Mistake("mistake"); 898 | /// x.unwrap(); // panics with `"mistake"` 899 | /// ``` 900 | /// 901 | /// ```should_panic 902 | /// # use outcome::prelude::*; 903 | /// let x: Outcome = Failure("emergency failure"); 904 | /// x.unwrap(); // panics with "emergency failure" 905 | /// ``` 906 | /// 907 | /// [`unwrap_or_default`]: Outcome::unwrap_or_default 908 | /// [`unwrap_or_else`]: Outcome::unwrap_or_else 909 | /// [`unwrap_or`]: Outcome::unwrap_or 910 | #[track_caller] 911 | #[inline] 912 | pub fn unwrap(self) -> S { 913 | match self { 914 | Success(s) => s, 915 | Mistake(m) => panic("Outcome::unwrap()", "Mistake", &m), 916 | Failure(f) => panic("Outcome::unwrap()", "Failure", &f), 917 | } 918 | } 919 | 920 | /// Returns the [`Success`] value or a provided default. 921 | /// 922 | /// Arguments passed to `unwrap_or` are eagerly evaluated; if you are passing 923 | /// the result of a function call, it is recommended to use 924 | /// [`unwrap_or_else`], which is lazily evaluated. 925 | /// 926 | /// 927 | /// # Examples 928 | /// 929 | /// ``` 930 | /// # use outcome::prelude::*; 931 | /// let x: Outcome = Success(2); 932 | /// assert_eq!(x.unwrap_or(3), 2); 933 | /// 934 | /// let x: Outcome = Mistake("mistaken"); 935 | /// assert_eq!(x.unwrap_or(3), 3); 936 | /// 937 | /// let x: Outcome = Failure("emergency failure"); 938 | /// assert_eq!(x.unwrap_or(3), 3); 939 | /// ``` 940 | /// 941 | /// [`unwrap_or_else`]: Outcome::unwrap_or_else 942 | #[track_caller] 943 | #[inline] 944 | pub fn unwrap_or(self, default: S) -> S { 945 | if let Success(success) = self { 946 | return success; 947 | } 948 | default 949 | } 950 | 951 | /// Returns the contained [`Success`] value or computes it from the closure. 952 | /// 953 | /// # Examples 954 | /// 955 | /// ``` 956 | /// # use outcome::prelude::*; 957 | /// let x: Outcome = Success(2); 958 | /// assert_eq!(x.unwrap_or_else(|_| 3), 2); 959 | /// 960 | /// let x: Outcome = Mistake("mistaken"); 961 | /// assert_eq!(x.unwrap_or_else(|_| 3), 3); 962 | /// 963 | /// let x: Outcome = Failure("emergency failure"); 964 | /// assert_eq!(x.unwrap_or_else(|_| 3), 3); 965 | /// ``` 966 | pub fn unwrap_or_else(self, op: impl FnOnce(Aberration) -> S) -> S { 967 | match self { 968 | Success(value) => value, 969 | Mistake(value) => op(Aberration::Mistake(value)), 970 | Failure(value) => op(Aberration::Failure(value)), 971 | } 972 | } 973 | } 974 | 975 | impl Outcome { 976 | /// Returns the contained [`Mistake`] value, consuming the `self` value. 977 | /// 978 | /// # Panics 979 | /// 980 | /// Panics if the value is either a [`Success`] or [`Failure`], with a custom 981 | /// panic message provided by either value. 982 | /// 983 | /// # Examples 984 | /// 985 | /// ```should_panic 986 | /// # use outcome::prelude::*; 987 | /// let x: Outcome = Success(47); 988 | /// x.unwrap_mistake(); // panics with '47' 989 | /// ``` 990 | /// 991 | /// ``` 992 | /// # use outcome::prelude::*; 993 | /// let x: Outcome = Mistake("try again!"); 994 | /// assert_eq!(x.unwrap_mistake(), "try again!"); 995 | /// ``` 996 | /// 997 | /// ```should_panic 998 | /// # use outcome::prelude::*; 999 | /// let x: Outcome = Failure("emergency failure"); 1000 | /// x.unwrap_mistake(); // panics with 'emergency failure' 1001 | /// ``` 1002 | #[track_caller] 1003 | #[inline] 1004 | pub fn unwrap_mistake(self) -> M { 1005 | match self { 1006 | Success(s) => panic("Outcome::unwrap_mistake()", "Success", &s), 1007 | Mistake(m) => m, 1008 | Failure(f) => panic("Outcome::unwrap_mistake()", "Failure", &f), 1009 | } 1010 | } 1011 | } 1012 | 1013 | impl Outcome { 1014 | /// Returns the contained [`Failure`] value, consuming the `self` value. 1015 | /// 1016 | /// # Panics 1017 | /// 1018 | /// Panics if the value is either a [`Success`] or [`Mistake`], with a custom 1019 | /// panic message provided by either value. 1020 | /// 1021 | /// # Examples 1022 | /// 1023 | /// ```should_panic 1024 | /// # use outcome::prelude::*; 1025 | /// let x: Outcome = Success(47); 1026 | /// x.unwrap_failure(); // panics with 47 1027 | /// ``` 1028 | /// 1029 | /// ```should_panic 1030 | /// # use outcome::prelude::*; 1031 | /// let x: Outcome = Mistake("try again!"); 1032 | /// x.unwrap_failure(); // panics with 'try again!' 1033 | /// ``` 1034 | /// 1035 | /// ``` 1036 | /// # use outcome::prelude::*; 1037 | /// let x: Outcome = Failure("failure!"); 1038 | /// assert_eq!(x.unwrap_failure(), "failure!"); 1039 | /// ``` 1040 | #[track_caller] 1041 | #[inline] 1042 | pub fn unwrap_failure(self) -> F { 1043 | match self { 1044 | Success(s) => panic("Outcome::unwrap_failure()", "Success", &s), 1045 | Mistake(m) => panic("Outcome::unwrap_failure()", "Mistake", &m), 1046 | Failure(f) => f, 1047 | } 1048 | } 1049 | } 1050 | 1051 | impl Outcome { 1052 | /// Returns the contained [`Mistake`] or [`Failure`] value wrapped in an 1053 | /// [`Aberration`], consuming the `self` value. 1054 | /// 1055 | /// # Panics 1056 | /// 1057 | /// Panics if the value is a [`Success`], with a custom panic message 1058 | /// provided by the contained value. 1059 | /// 1060 | /// # Examples 1061 | /// 1062 | /// ```should_panic 1063 | /// # #![allow(unused_must_use)] 1064 | /// # use outcome::prelude::*; 1065 | /// let x: Outcome = Success(47); 1066 | /// x.unwrap_error(); // panics with '47' 1067 | /// ``` 1068 | /// 1069 | /// ``` 1070 | /// # use outcome::prelude::*; 1071 | /// let x: Outcome = Failure("failure!"); 1072 | /// let ab = x.unwrap_error(); 1073 | /// match ab { 1074 | /// Aberration::Mistake(m) => assert_eq!("mistake!", m), 1075 | /// Aberration::Failure(f) => assert_eq!("failure!", f), 1076 | /// }; 1077 | /// ``` 1078 | #[track_caller] 1079 | #[inline] 1080 | pub fn unwrap_error(self) -> Aberration { 1081 | match self { 1082 | Success(value) => panic("Outcome::unwrap_error()", "Success", &value), 1083 | Mistake(value) => Aberration::Mistake(value), 1084 | Failure(value) => Aberration::Failure(value), 1085 | } 1086 | } 1087 | } 1088 | 1089 | impl Outcome { 1090 | /// Returns the contained [`Success`] value or a default. 1091 | /// 1092 | /// Consumes the `self` argument then, if [`Success`], returns the contained 1093 | /// value, otherwise if the outcome is a [`Mistake`] or [`Failure`], returns 1094 | /// the default value for [`Success`] 1095 | #[track_caller] 1096 | #[inline] 1097 | pub fn unwrap_or_default(self) -> S { 1098 | if let Success(success) = self { 1099 | return success; 1100 | } 1101 | S::default() 1102 | } 1103 | } 1104 | 1105 | impl Outcome, M, F> { 1106 | /// Transposes an `Outcome` of an `Option` into an `Option` of an `Outcome`. 1107 | /// 1108 | /// - `Success(None)` will be mapped to `None`. 1109 | /// - `Success(Some(_))`, `Mistake(_)`, and `Failure(_)` will be mapped to 1110 | /// `Some(Success(_))`, `Some(Mistake(_))`, and `Some(Failure(_))`. 1111 | /// 1112 | /// # Examples 1113 | /// 1114 | /// ``` 1115 | /// # use outcome::prelude::*; 1116 | /// let x: Outcome, &str, &str> = Success(Some(5)); 1117 | /// let y: Option> = Some(Success(5)); 1118 | /// assert_eq!(x.transpose(), y); 1119 | /// ``` 1120 | /// 1121 | /// 1122 | pub fn transpose(self) -> Option> { 1123 | match self { 1124 | Success(Some(s)) => Some(Success(s)), 1125 | Success(None) => None, 1126 | Mistake(m) => Some(Mistake(m)), 1127 | Failure(f) => Some(Failure(f)), 1128 | } 1129 | } 1130 | } 1131 | 1132 | /* Builtin Trait Implementations */ 1133 | impl Clone for Outcome { 1134 | #[inline] 1135 | fn clone(&self) -> Self { 1136 | match self { 1137 | Success(value) => Success(value.clone()), 1138 | Mistake(value) => Mistake(value.clone()), 1139 | Failure(value) => Failure(value.clone()), 1140 | } 1141 | } 1142 | 1143 | #[inline] 1144 | fn clone_from(&mut self, source: &Self) { 1145 | match (self, source) { 1146 | (Success(to), Success(from)) => to.clone_from(from), 1147 | (Mistake(to), Mistake(from)) => to.clone_from(from), 1148 | (Failure(to), Failure(from)) => to.clone_from(from), 1149 | (to, from) => *to = from.clone(), 1150 | } 1151 | } 1152 | } 1153 | 1154 | #[cfg(feature = "std")] 1155 | impl Termination for Outcome<(), M, F> { 1156 | #[inline] 1157 | fn report(self) -> ExitCode { 1158 | #[allow(clippy::print_stderr)] 1159 | match self { 1160 | Success(()) => return ().report(), 1161 | Mistake(m) => eprintln!("Mistake: {m:?}"), 1162 | Failure(f) => eprintln!("Failure: {f:?}"), 1163 | } 1164 | ExitCode::FAILURE 1165 | } 1166 | } 1167 | 1168 | #[cfg(all(test, feature = "std"))] 1169 | mod tests { 1170 | extern crate std; 1171 | use super::*; 1172 | use std::{string::String, vec, vec::Vec}; 1173 | 1174 | #[test] 1175 | fn filter_map_with() { 1176 | let failures: Vec> = vec![ 1177 | Failure("There is an error".into()), 1178 | Mistake(()), 1179 | Failure("There is a second error".into()), 1180 | Success(()), 1181 | Failure("There is a final error".into()), 1182 | Mistake(()), 1183 | Success(()), 1184 | ]; 1185 | 1186 | let filtered: Vec<&str> = failures 1187 | .iter() 1188 | .map(Outcome::as_ref) 1189 | .filter_map(Outcome::failure) 1190 | .map(String::as_str) 1191 | .collect(); 1192 | 1193 | assert_eq!(filtered.len(), 3); 1194 | assert_eq!(failures[0].as_ref().unwrap_failure().as_str(), filtered[0]); 1195 | assert_eq!(failures[2].as_ref().unwrap_failure().as_str(), filtered[1]); 1196 | assert_eq!(failures[4].as_ref().unwrap_failure().as_str(), filtered[2]); 1197 | } 1198 | 1199 | #[cfg(feature = "std")] 1200 | mod termination { 1201 | use super::*; 1202 | 1203 | #[test] 1204 | fn outcome() -> Outcome<(), (), &'static str> { 1205 | Success(()) 1206 | } 1207 | } 1208 | } 1209 | -------------------------------------------------------------------------------- /src/prelude.rs: -------------------------------------------------------------------------------- 1 | //! The Outcome Prelude 2 | //! 3 | //! The `outcome` library comes with several types and traits. However, several 4 | //! of these are the *most important*, while others are optional to be 5 | //! imported. For this reason, the `prelude` module is provided for quick 6 | //! imports. While it can't be automatically imported, it does contain the 7 | //! *stable* interface available for each support Rust edition. 8 | //! 9 | //! When using the [nightly](crate#nightly) feature, [`AttemptFrom`] and 10 | //! [`AttemptInto`] are re-exported from this module. 11 | #[doc(inline)] 12 | pub use Outcome::{Failure, Mistake, Success}; 13 | 14 | // TODO: Change this to be an edition setting? 15 | #[cfg_attr(any(docsrs, nightly), doc(cfg(feature = "nightly")))] 16 | #[cfg(all(nightly, feature = "nightly"))] 17 | #[doc(inline)] 18 | pub use crate::convert::{AttemptFrom, AttemptInto}; 19 | pub use crate::{aberration::Aberration, concern::Concern, outcome::Outcome}; 20 | -------------------------------------------------------------------------------- /src/private.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::Debug; 2 | 3 | /* Much like the internal `unwrap_failed` function found in core::result, this 4 | * function helps reduce method code size. Given that we have several types 5 | * that all have `unwrap(_.+)?` names, this helps immensely for generated code. 6 | */ 7 | #[inline(never)] 8 | #[track_caller] 9 | #[cold] 10 | pub fn panic(method: &str, variant: &str, error: &dyn Debug) -> ! { 11 | panic!("Called `{}` on a `{}` value: {:?}", method, variant, error); 12 | } 13 | 14 | pub trait Sealed {} 15 | 16 | #[cfg(any(feature = "report", feature = "diagnostic"))] 17 | impl Sealed for Result {} 18 | 19 | impl Sealed for crate::outcome::Outcome {} 20 | impl Sealed for crate::aberration::Aberration {} 21 | impl Sealed for crate::concern::Concern {} 22 | -------------------------------------------------------------------------------- /src/report.rs: -------------------------------------------------------------------------------- 1 | //! Support for the [`eyre`] crate. 2 | //! 3 | //! This module re-exports [`Report`] as well as [`Result`], and provides a 4 | //! trait, [`WrapFailure`], as a mirror to the [`WrapErr`] trait. Additionally, 5 | //! [`WrapFailure`] is also implemented for [`Result`]. Lastly, to stay 6 | //! in line with behavior from [`eyre`], the [`WrapFailure`] trait is *also* 7 | //! sealed. 8 | //! 9 | //! [`WrapErr`]: eyre::WrapErr 10 | //! [`Result`]: eyre::Result 11 | //! [`eyre`]: https://crates.io/crates/eyre 12 | extern crate std; 13 | 14 | use crate::prelude::*; 15 | use std::{error::Error, fmt::Display}; 16 | 17 | #[doc(no_inline)] 18 | pub use eyre::{Report, Result}; 19 | 20 | crate::wrap::r#trait!(Error); 21 | crate::wrap::r#impl!(Error); 22 | crate::wrap::result!(eyre); 23 | -------------------------------------------------------------------------------- /src/unstable.rs: -------------------------------------------------------------------------------- 1 | use core::cmp::PartialEq; 2 | 3 | use crate::prelude::*; 4 | 5 | impl Outcome { 6 | /// Returns `true` if the outcome is a [`Success`] value containing the given 7 | /// value. 8 | /// 9 | /// # Examples 10 | /// 11 | /// ``` 12 | /// # use outcome::prelude::*; 13 | /// let x: Outcome = Success(47); 14 | /// assert_eq!(x.contains(&47), true); 15 | /// 16 | /// let x: Outcome = Success(47); 17 | /// assert_eq!(x.contains(&42), false); 18 | /// 19 | /// let x: Outcome = Mistake(0.0f32); 20 | /// assert_eq!(x.contains(&47), false); 21 | /// 22 | /// let x: Outcome = Failure("Some error message"); 23 | /// assert_eq!(x.contains(&47), false); 24 | /// ``` 25 | #[must_use] 26 | #[inline] 27 | pub fn contains(&self, other: &U) -> bool 28 | where 29 | U: PartialEq, 30 | { 31 | if let Success(value) = self { 32 | return other == value; 33 | } 34 | false 35 | } 36 | 37 | /// Returns `true` if the outcome is a [`Mistake`] value containing the given 38 | /// value. 39 | /// 40 | /// # Examples 41 | /// 42 | /// ``` 43 | /// # use outcome::prelude::*; 44 | /// 45 | /// let x: Outcome = Success(47); 46 | /// assert_eq!(x.contains_mistake(&"Some mistake message"), false); 47 | /// 48 | /// let x: Outcome = Mistake("Some mistake message"); 49 | /// assert_eq!(x.contains_mistake(&"Some mistake message"), true); 50 | /// 51 | /// let x: Outcome = Mistake("Some other mistake message"); 52 | /// assert_eq!(x.contains_mistake(&"Some mistake message"), false); 53 | /// 54 | /// let x: Outcome = Failure(47); 55 | /// assert_eq!(x.contains_mistake(&"Some error message"), false); 56 | /// ``` 57 | #[must_use] 58 | #[inline] 59 | pub fn contains_mistake(&self, other: &N) -> bool 60 | where 61 | N: PartialEq, 62 | { 63 | if let Mistake(value) = self { 64 | return other == value; 65 | } 66 | false 67 | } 68 | 69 | /// Returns `true` if the outcome is a [`Failure`] value containing the given 70 | /// value. 71 | /// 72 | /// # Examples 73 | /// ``` 74 | /// # use outcome::prelude::*; 75 | /// 76 | /// let x: Outcome = Success(47); 77 | /// assert_eq!(x.contains_failure(&"Some error message"), false); 78 | /// 79 | /// let x: Outcome = Mistake(47); 80 | /// assert_eq!(x.contains_failure(&"Some error message"), false); 81 | /// 82 | /// let x: Outcome = Failure("Some error message"); 83 | /// assert_eq!(x.contains_failure(&"Some error message"), true); 84 | /// 85 | /// let x: Outcome = Failure("Some other error message"); 86 | /// assert_eq!(x.contains_failure(&"Some error message"), false); 87 | /// ``` 88 | #[must_use] 89 | #[inline] 90 | pub fn contains_failure(&self, other: &G) -> bool 91 | where 92 | G: PartialEq, 93 | { 94 | if let Failure(value) = self { 95 | return other == value; 96 | } 97 | false 98 | } 99 | } 100 | 101 | impl Outcome, M, F> { 102 | /// Converts from `Outcome, M, F>` to `Outcome` 103 | /// 104 | /// # Examples 105 | /// 106 | /// ``` 107 | /// # use outcome::prelude::*; 108 | /// let x: Outcome, u32, u32> = Success(Success("hello")); 109 | /// assert_eq!(Success("hello"), x.flatten()); 110 | /// 111 | /// let x: Outcome, u32, u32> = Success(Mistake(47)); 112 | /// assert_eq!(Mistake(47), x.flatten()); 113 | /// 114 | /// let x: Outcome, u32, u32> = Success(Failure(47)); 115 | /// assert_eq!(Failure(47), x.flatten()); 116 | /// 117 | /// let x: Outcome, u32, u32> = Mistake(47); 118 | /// assert_eq!(Mistake(47), x.flatten()); 119 | /// 120 | /// let x: Outcome, u32, u32> = Failure(47); 121 | /// assert_eq!(Failure(47), x.flatten()); 122 | /// ``` 123 | /// 124 | /// **NOTE**: Flattening only removes *one* level of nesting at a time: 125 | /// 126 | /// ``` 127 | /// # use outcome::prelude::*; 128 | /// type Nested = Outcome, u32, u32>, u32, u32>; 129 | /// let x: Nested<&'static str> = Success(Success(Success("hello"))); 130 | /// assert_eq!(Success(Success("hello")), x.flatten()); 131 | /// assert_eq!(Success("hello"), x.flatten().flatten()); 132 | /// ``` 133 | pub fn flatten(self) -> Outcome { 134 | self.and_then(core::convert::identity) 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/wrap.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::redundant_pub_crate)] 2 | 3 | macro_rules! r#trait { 4 | ($type:ty) => { 5 | /// This trait is meant to be the `outcome` analogue of *both* 6 | /// [`miette::WrapErr`] and [`eyre::WrapErr`]. Therefore, any type that 7 | /// implements `WrapErr` for either of these libraries will automatically work 8 | /// with `WrapFailure`. 9 | /// 10 | /// This trait is sealed and cannot be implemented for types outside of this 11 | /// `outcome`. 12 | /// 13 | /// [`Outcome`]: crate::prelude::Outcome 14 | pub trait WrapFailure: crate::private::Sealed { 15 | /// The expected return type for an `impl`. 16 | /// 17 | /// This will always be the same enumeration type, but with a [`Report`] 18 | /// in the error or failure position. 19 | type Return; 20 | 21 | /// Wrap the failure value with a new adhoc error that is evaluated lazily 22 | /// only once an error does occur. 23 | fn wrap_failure_with(self, message: F) -> Self::Return 24 | where 25 | D: Display + Send + Sync + 'static, 26 | F: FnOnce() -> D; 27 | 28 | /// Wrap the failure value with a new adhoc error. 29 | fn wrap_failure(self, message: D) -> Self::Return 30 | where 31 | D: Display + Send + Sync + 'static; 32 | 33 | /// Compatibility re-export of [`wrap_failure_with`] for interop with 34 | /// [`anyhow`] and [`eyre`]. 35 | /// 36 | /// [`wrap_failure_with`]: WrapFailure::wrap_failure_with 37 | /// [`anyhow`]: https://crates.io/crates/anyhow 38 | /// [`eyre`]: https://crates.io/crates/eyre 39 | fn with_context(self, message: F) -> Self::Return 40 | where 41 | D: Display + Send + Sync + 'static, 42 | F: FnOnce() -> D; 43 | /// Compatibility re-export of [`wrap_failure`] for interop with 44 | /// [`anyhow`] and [`eyre`]. 45 | /// 46 | /// [`wrap_failure`]: WrapFailure::wrap_failure 47 | /// [`anyhow`]: https://crates.io/crates/anyhow 48 | /// [`eyre`]: https://crates.io/crates/eyre 49 | fn context(self, message: D) -> Self::Return 50 | where 51 | D: Display + Send + Sync + 'static; 52 | } 53 | }; 54 | } 55 | 56 | macro_rules! r#impl { 57 | ($type:ident) => { 58 | impl WrapFailure for Outcome 59 | where 60 | E: $type + Send + Sync + 'static, 61 | { 62 | type Return = Outcome; 63 | 64 | #[track_caller] 65 | #[inline] 66 | fn wrap_failure_with(self, message: F) -> Self::Return 67 | where 68 | D: Display + Send + Sync + 'static, 69 | F: FnOnce() -> D, 70 | { 71 | self.map_failure(|f| Report::new(f).wrap_err(message())) 72 | } 73 | 74 | #[track_caller] 75 | #[inline] 76 | fn wrap_failure(self, message: D) -> Self::Return 77 | where 78 | D: Display + Send + Sync + 'static, 79 | { 80 | self.map_failure(|f| Report::new(f).wrap_err(message)) 81 | } 82 | 83 | #[track_caller] 84 | #[inline] 85 | fn with_context(self, message: F) -> Self::Return 86 | where 87 | D: Display + Send + Sync + 'static, 88 | F: FnOnce() -> D, 89 | { 90 | self.wrap_failure_with(message) 91 | } 92 | 93 | #[track_caller] 94 | #[inline] 95 | fn context(self, message: D) -> Self::Return 96 | where 97 | D: Display + Send + Sync + 'static, 98 | { 99 | self.wrap_failure(message) 100 | } 101 | } 102 | 103 | impl WrapFailure for Aberration 104 | where 105 | E: $type + Send + Sync + 'static, 106 | { 107 | type Return = Aberration; 108 | 109 | #[track_caller] 110 | #[inline] 111 | fn wrap_failure_with(self, message: F) -> Self::Return 112 | where 113 | D: Display + Send + Sync + 'static, 114 | F: FnOnce() -> D, 115 | { 116 | self.map_failure(|f| Report::new(f).wrap_err(message())) 117 | } 118 | 119 | #[track_caller] 120 | #[inline] 121 | fn wrap_failure(self, message: D) -> Self::Return 122 | where 123 | D: Display + Send + Sync + 'static, 124 | { 125 | self.map_failure(|f| Report::new(f).wrap_err(message)) 126 | } 127 | 128 | #[track_caller] 129 | #[inline] 130 | fn with_context(self, message: F) -> Self::Return 131 | where 132 | D: Display + Send + Sync + 'static, 133 | F: FnOnce() -> D, 134 | { 135 | self.wrap_failure_with(message) 136 | } 137 | 138 | #[track_caller] 139 | #[inline] 140 | fn context(self, message: D) -> Self::Return 141 | where 142 | D: Display + Send + Sync + 'static, 143 | { 144 | self.wrap_failure(message) 145 | } 146 | } 147 | }; 148 | } 149 | 150 | macro_rules! r#use { 151 | (miette) => { 152 | "use outcome::diagnostic::{WrapFailure, Result, Report};" 153 | }; 154 | (eyre) => { 155 | "use outcome::report::{WrapFailure, Result, Report};" 156 | }; 157 | } 158 | 159 | macro_rules! r#result { 160 | ($module:ident) => { 161 | /// Implementation of [`WrapFailure`] for `Result` for any 162 | /// implementations of [`WrapErr`]. 163 | /// 164 | /// ``` 165 | #[doc = crate::wrap::r#use!($module)] 166 | /// 167 | /// fn execute() -> Result<()> { 168 | /// # Err(Report::msg("error here"))?; 169 | /// # const IGNORE: &str = stringify! { 170 | /// ... 171 | /// # }; 172 | /// # unreachable!() 173 | /// } 174 | /// 175 | /// pub fn invoke() -> Result> { 176 | /// execute().wrap_failure("Failed to execute correctly")?; 177 | /// Ok(vec![]) 178 | /// } 179 | /// ``` 180 | #[doc = concat!("[`WrapErr`]:", stringify!($module), "::WrapErr")] 181 | impl WrapFailure for Result 182 | where 183 | Self: $module::WrapErr, 184 | { 185 | type Return = Result; 186 | 187 | #[track_caller] 188 | #[inline] 189 | fn wrap_failure_with(self, message: F) -> Self::Return 190 | where 191 | D: Display + Send + Sync + 'static, 192 | F: FnOnce() -> D, 193 | { 194 | $module::WrapErr::wrap_err_with(self, message) 195 | } 196 | 197 | #[track_caller] 198 | #[inline] 199 | fn wrap_failure(self, message: D) -> Self::Return 200 | where 201 | D: Display + Send + Sync + 'static, 202 | { 203 | $module::WrapErr::wrap_err(self, message) 204 | } 205 | 206 | #[track_caller] 207 | #[inline] 208 | fn with_context(self, message: F) -> Self::Return 209 | where 210 | D: Display + Send + Sync + 'static, 211 | F: FnOnce() -> D, 212 | { 213 | $module::WrapErr::with_context(self, message) 214 | } 215 | 216 | #[track_caller] 217 | #[inline] 218 | fn context(self, message: D) -> Self::Return 219 | where 220 | D: Display + Send + Sync + 'static, 221 | { 222 | $module::WrapErr::context(self, message) 223 | } 224 | } 225 | }; 226 | } 227 | 228 | pub(crate) use {r#impl, r#result, r#trait, r#use}; 229 | --------------------------------------------------------------------------------