├── .cargo └── config.toml ├── .github ├── DOCS.md ├── codecov.yml ├── dependabot.yml └── workflows │ ├── check.yml │ ├── nostd.yml │ ├── safety.yml │ ├── scheduled.yml │ └── test.yml ├── .gitignore ├── .helix └── languages.toml ├── .taplo.toml ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md └── src ├── arbitrary.rs ├── assign.rs ├── component.rs ├── delete.rs ├── diagnostic.rs ├── index.rs ├── lib.rs ├── pointer.rs ├── pointer └── slice.rs ├── resolve.rs └── token.rs /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [alias] 2 | cov = "llvm-cov --lcov --output-path lcov.info" 3 | 4 | [build] 5 | rustdocflags = ["--cfg", "docsrs"] 6 | -------------------------------------------------------------------------------- /.github/DOCS.md: -------------------------------------------------------------------------------- 1 | # Github config and workflows 2 | 3 | In this folder there is configuration for codecoverage, dependabot, and ci 4 | workflows that check the library more deeply than the default configurations. 5 | 6 | This folder can be or was merged using a --allow-unrelated-histories merge 7 | strategy from which provides a 8 | reasonably sensible base for writing your own ci on. By using this strategy 9 | the history of the CI repo is included in your repo, and future updates to 10 | the CI can be merged later. 11 | 12 | To perform this merge run: 13 | 14 | ```shell 15 | git remote add ci https://github.com/jonhoo/rust-ci-conf.git 16 | git fetch ci 17 | git merge --allow-unrelated-histories ci/main 18 | ``` 19 | 20 | An overview of the files in this project is available at: 21 | , which contains some 22 | rationale for decisions and runs through an example of solving minimal version 23 | and OpenSSL issues. 24 | -------------------------------------------------------------------------------- /.github/codecov.yml: -------------------------------------------------------------------------------- 1 | # ref: https://docs.codecov.com/docs/codecovyml-reference 2 | coverage: 3 | # Hold ourselves to a high bar 4 | range: 85..100 5 | round: down 6 | precision: 1 7 | status: 8 | # ref: https://docs.codecov.com/docs/commit-status 9 | project: 10 | default: 11 | # Avoid false negatives 12 | threshold: 1% 13 | 14 | # Test files aren't important for coverage 15 | ignore: 16 | - "tests" 17 | - "arbitrary.rs" 18 | - "src/arbitrary.rs" 19 | 20 | # Make comments less noisy 21 | comment: 22 | layout: "files" 23 | require_changes: true 24 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: / 5 | schedule: 6 | interval: daily 7 | - package-ecosystem: cargo 8 | directory: / 9 | schedule: 10 | interval: daily 11 | ignore: 12 | - dependency-name: "*" 13 | # patch and minor updates don't matter for libraries as consumers of this library build 14 | # with their own lockfile, rather than the version specified in this library's lockfile 15 | # remove this ignore rule if your package has binaries to ensure that the binaries are 16 | # built with the exact set of dependencies and those are up to date. 17 | update-types: 18 | - "version-update:semver-patch" 19 | - "version-update:semver-minor" 20 | -------------------------------------------------------------------------------- /.github/workflows/check.yml: -------------------------------------------------------------------------------- 1 | # This workflow runs whenever a PR is opened or updated, or a commit is pushed to main. It runs 2 | # several checks: 3 | # - fmt: checks that the code is formatted according to rustfmt 4 | # - clippy: checks that the code does not contain any clippy warnings 5 | # - doc: checks that the code can be documented without errors 6 | # - hack: check combinations of feature flags 7 | # - msrv: check that the msrv specified in the crate is correct 8 | permissions: 9 | contents: read 10 | # This configuration allows maintainers of this repo to create a branch and pull request based on 11 | # the new branch. Restricting the push trigger to the main branch ensures that the PR only gets 12 | # built once. 13 | on: 14 | push: 15 | branches: [main] 16 | pull_request: 17 | # If new code is pushed to a PR branch, then cancel in progress workflows for that PR. Ensures that 18 | # we don't waste CI time, and returns results quicker https://github.com/jonhoo/rust-ci-conf/pull/5 19 | concurrency: 20 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 21 | cancel-in-progress: true 22 | name: check 23 | jobs: 24 | fmt: 25 | runs-on: ubuntu-latest 26 | name: stable / fmt 27 | steps: 28 | - uses: actions/checkout@v4 29 | with: 30 | submodules: true 31 | - name: Install stable 32 | uses: dtolnay/rust-toolchain@stable 33 | with: 34 | components: rustfmt 35 | - name: cargo fmt --check 36 | run: cargo fmt --check 37 | clippy: 38 | runs-on: ubuntu-latest 39 | name: ${{ matrix.toolchain }} / clippy 40 | permissions: 41 | contents: read 42 | checks: write 43 | strategy: 44 | fail-fast: false 45 | matrix: 46 | # Get early warning of new lints which are regularly introduced in beta channels. 47 | toolchain: [stable, beta] 48 | steps: 49 | - uses: actions/checkout@v4 50 | with: 51 | submodules: true 52 | - name: Install ${{ matrix.toolchain }} 53 | uses: dtolnay/rust-toolchain@master 54 | with: 55 | toolchain: ${{ matrix.toolchain }} 56 | components: clippy 57 | - name: cargo clippy 58 | uses: giraffate/clippy-action@v1 59 | with: 60 | reporter: "github-pr-check" 61 | github_token: ${{ secrets.GITHUB_TOKEN }} 62 | semver: 63 | runs-on: ubuntu-latest 64 | name: semver 65 | steps: 66 | - uses: actions/checkout@v4 67 | with: 68 | submodules: true 69 | - name: Install stable 70 | uses: dtolnay/rust-toolchain@stable 71 | with: 72 | components: rustfmt 73 | - name: cargo-semver-checks 74 | uses: obi1kenobi/cargo-semver-checks-action@v2 75 | doc: 76 | # run docs generation on nightly rather than stable. This enables features like 77 | # https://doc.rust-lang.org/beta/unstable-book/language-features/doc-cfg.html which allows an 78 | # API be documented as only available in some specific platforms. 79 | runs-on: ubuntu-latest 80 | name: nightly / doc 81 | steps: 82 | - uses: actions/checkout@v4 83 | with: 84 | submodules: true 85 | - name: Install nightly 86 | uses: dtolnay/rust-toolchain@nightly 87 | - name: cargo doc 88 | run: cargo doc --no-deps --all-features 89 | env: 90 | RUSTDOCFLAGS: --cfg docsrs 91 | hack: 92 | # cargo-hack checks combinations of feature flags to ensure that features are all additive 93 | # which is required for feature unification 94 | runs-on: ubuntu-latest 95 | name: ubuntu / stable / features 96 | steps: 97 | - uses: actions/checkout@v4 98 | with: 99 | submodules: true 100 | - name: Install stable 101 | uses: dtolnay/rust-toolchain@stable 102 | - name: cargo install cargo-hack 103 | uses: taiki-e/install-action@cargo-hack 104 | # intentionally no target specifier; see https://github.com/jonhoo/rust-ci-conf/pull/4 105 | # --feature-powerset runs for every combination of features 106 | - name: cargo hack 107 | run: cargo hack --feature-powerset check 108 | msrv: 109 | # check that we can build using the minimal rust version that is specified by this crate 110 | runs-on: ubuntu-latest 111 | # we use a matrix here just because env can't be used in job names 112 | # https://docs.github.com/en/actions/learn-github-actions/contexts#context-availability 113 | # TODO: would be nice to just parse this from the manifest 114 | strategy: 115 | matrix: 116 | msrv: ["1.79"] 117 | name: ubuntu / ${{ matrix.msrv }} 118 | steps: 119 | - uses: actions/checkout@v4 120 | with: 121 | submodules: true 122 | - name: Install ${{ matrix.msrv }} 123 | uses: dtolnay/rust-toolchain@master 124 | with: 125 | toolchain: ${{ matrix.msrv }} 126 | - name: cargo +${{ matrix.msrv }} check 127 | run: cargo check 128 | toml-fmt: 129 | runs-on: ubuntu-latest 130 | name: toml / fmt 131 | steps: 132 | - uses: actions/checkout@v4 133 | with: 134 | submodules: true 135 | - name: Install taplo 136 | uses: docker://docker.io/tamasfe/taplo:latest 137 | with: 138 | args: fmt --check --diff 139 | -------------------------------------------------------------------------------- /.github/workflows/nostd.yml: -------------------------------------------------------------------------------- 1 | # This workflow checks whether the library is able to run without the std library (e.g., embedded). 2 | # This entire file should be removed if this crate does not support no-std. See check.yml for 3 | # information about how the concurrency cancellation and workflow triggering works 4 | permissions: 5 | contents: read 6 | on: 7 | push: 8 | branches: [main] 9 | pull_request: 10 | concurrency: 11 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 12 | cancel-in-progress: true 13 | name: no-std 14 | jobs: 15 | nostd: 16 | runs-on: ubuntu-latest 17 | name: ${{ matrix.target }} 18 | strategy: 19 | matrix: 20 | target: [thumbv7m-none-eabi, aarch64-unknown-none] 21 | steps: 22 | - uses: actions/checkout@v4 23 | with: 24 | submodules: true 25 | - name: Install stable 26 | uses: dtolnay/rust-toolchain@stable 27 | - name: rustup target add ${{ matrix.target }} 28 | run: rustup target add ${{ matrix.target }} 29 | - name: cargo check 30 | run: cargo check --target ${{ matrix.target }} --no-default-features 31 | -------------------------------------------------------------------------------- /.github/workflows/safety.yml: -------------------------------------------------------------------------------- 1 | # This workflow runs checks for unsafe code. In crates that don't have any unsafe code, this can be 2 | # removed. Runs: 3 | # - miri - detects undefined behavior and memory leaks 4 | # - address sanitizer - detects memory errors 5 | # - leak sanitizer - detects memory leaks 6 | # See check.yml for information about how the concurrency cancellation and workflow triggering works 7 | permissions: 8 | contents: read 9 | on: 10 | push: 11 | branches: [main] 12 | pull_request: 13 | concurrency: 14 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 15 | cancel-in-progress: true 16 | name: safety 17 | jobs: 18 | sanitizers: 19 | runs-on: ubuntu-latest 20 | steps: 21 | - uses: actions/checkout@v4 22 | with: 23 | submodules: true 24 | - name: Install nightly 25 | uses: dtolnay/rust-toolchain@nightly 26 | - run: | 27 | # to get the symbolizer for debug symbol resolution 28 | sudo apt install llvm 29 | # to fix buggy leak analyzer: 30 | # https://github.com/japaric/rust-san#unrealiable-leaksanitizer 31 | # ensure there's a profile.dev section 32 | if ! grep -qE '^[ \t]*[profile.dev]' Cargo.toml; then 33 | echo >> Cargo.toml 34 | echo '[profile.dev]' >> Cargo.toml 35 | fi 36 | # remove pre-existing opt-levels in profile.dev 37 | sed -i '/^\s*\[profile.dev\]/,/^\s*\[/ {/^\s*opt-level/d}' Cargo.toml 38 | # now set opt-level to 1 39 | sed -i '/^\s*\[profile.dev\]/a opt-level = 1' Cargo.toml 40 | cat Cargo.toml 41 | name: Enable debug symbols 42 | - name: cargo test -Zsanitizer=address 43 | # only --lib --tests b/c of https://github.com/rust-lang/rust/issues/53945 44 | run: cargo test --lib --tests --all-features --target x86_64-unknown-linux-gnu 45 | env: 46 | ASAN_OPTIONS: "detect_odr_violation=0:detect_leaks=0" 47 | RUSTFLAGS: "-Z sanitizer=address" 48 | - name: cargo test -Zsanitizer=leak 49 | if: always() 50 | run: cargo test --all-features --target x86_64-unknown-linux-gnu 51 | env: 52 | LSAN_OPTIONS: "suppressions=lsan-suppressions.txt" 53 | RUSTFLAGS: "-Z sanitizer=leak" 54 | miri: 55 | runs-on: ubuntu-latest 56 | steps: 57 | - uses: actions/checkout@v4 58 | with: 59 | submodules: true 60 | - run: | 61 | echo "NIGHTLY=nightly-$(curl -s https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/miri)" >> $GITHUB_ENV 62 | - name: Install ${{ env.NIGHTLY }} 63 | uses: dtolnay/rust-toolchain@master 64 | with: 65 | toolchain: ${{ env.NIGHTLY }} 66 | components: miri 67 | - name: cargo miri test 68 | run: cargo miri test -- --skip pointer::tests::qc 69 | env: 70 | MIRIFLAGS: "" 71 | -------------------------------------------------------------------------------- /.github/workflows/scheduled.yml: -------------------------------------------------------------------------------- 1 | # Run scheduled (rolling) jobs on a nightly basis, as your crate may break independently of any 2 | # given PR. E.g., updates to rust nightly and updates to this crates dependencies. See check.yml for 3 | # information about how the concurrency cancellation and workflow triggering works 4 | permissions: 5 | contents: read 6 | on: 7 | push: 8 | branches: [main] 9 | pull_request: 10 | schedule: 11 | - cron: '7 7 * * *' 12 | concurrency: 13 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 14 | cancel-in-progress: true 15 | name: rolling 16 | jobs: 17 | # https://twitter.com/mycoliza/status/1571295690063753218 18 | nightly: 19 | runs-on: ubuntu-latest 20 | name: ubuntu / nightly 21 | steps: 22 | - uses: actions/checkout@v4 23 | with: 24 | submodules: true 25 | - name: Install nightly 26 | uses: dtolnay/rust-toolchain@nightly 27 | - name: cargo generate-lockfile 28 | if: hashFiles('Cargo.lock') == '' 29 | run: cargo generate-lockfile 30 | - name: cargo test --locked 31 | run: cargo test --locked --all-features --all-targets 32 | # https://twitter.com/alcuadrado/status/1571291687837732873 33 | update: 34 | # This action checks that updating the dependencies of this crate to the latest available that 35 | # satisfy the versions in Cargo.toml does not break this crate. This is important as consumers 36 | # of this crate will generally use the latest available crates. This is subject to the standard 37 | # Cargo semver rules (i.e cargo does not update to a new major version unless explicitly told 38 | # to). 39 | runs-on: ubuntu-latest 40 | name: ubuntu / beta / updated 41 | # There's no point running this if no Cargo.lock was checked in in the first place, since we'd 42 | # just redo what happened in the regular test job. Unfortunately, hashFiles only works in if on 43 | # steps, so we repeat it. 44 | steps: 45 | - uses: actions/checkout@v4 46 | with: 47 | submodules: true 48 | - name: Install beta 49 | if: hashFiles('Cargo.lock') != '' 50 | uses: dtolnay/rust-toolchain@beta 51 | - name: cargo update 52 | if: hashFiles('Cargo.lock') != '' 53 | run: cargo update 54 | - name: cargo test 55 | if: hashFiles('Cargo.lock') != '' 56 | run: cargo test --locked --all-features --all-targets 57 | env: 58 | RUSTFLAGS: -D deprecated 59 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | # This is the main CI workflow that runs the test suite on all pushes to main and all pull requests. 2 | # It runs the following jobs: 3 | # - required: runs the test suite on ubuntu with stable and beta rust toolchains 4 | # - minimal: runs the test suite with the minimal versions of the dependencies that satisfy the 5 | # requirements of this crate, and its dependencies 6 | # - os-check: runs the test suite on mac and windows 7 | # - coverage: runs the test suite and collects coverage information 8 | # See check.yml for information about how the concurrency cancellation and workflow triggering works 9 | permissions: 10 | contents: read 11 | on: 12 | push: 13 | branches: [main] 14 | pull_request: 15 | concurrency: 16 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 17 | cancel-in-progress: true 18 | name: test 19 | jobs: 20 | required: 21 | runs-on: ubuntu-latest 22 | name: ubuntu / ${{ matrix.toolchain }} 23 | strategy: 24 | matrix: 25 | # run on stable and beta to ensure that tests won't break on the next version of the rust 26 | # toolchain 27 | toolchain: [stable, beta] 28 | steps: 29 | - uses: actions/checkout@v4 30 | with: 31 | submodules: true 32 | - name: Install ${{ matrix.toolchain }} 33 | uses: dtolnay/rust-toolchain@master 34 | with: 35 | toolchain: ${{ matrix.toolchain }} 36 | - name: cargo generate-lockfile 37 | # enable this ci template to run regardless of whether the lockfile is checked in or not 38 | if: hashFiles('Cargo.lock') == '' 39 | run: cargo generate-lockfile 40 | # https://twitter.com/jonhoo/status/1571290371124260865 41 | - name: cargo test --locked 42 | run: cargo test --locked --all-features --all-targets 43 | # https://github.com/rust-lang/cargo/issues/6669 44 | - name: cargo test --doc 45 | run: cargo test --locked --all-features --doc 46 | minimal: 47 | # This action chooses the oldest version of the dependencies permitted by Cargo.toml to ensure 48 | # that this crate is compatible with the minimal version that this crate and its dependencies 49 | # require. This will pickup issues where this create relies on functionality that was introduced 50 | # later than the actual version specified (e.g., when we choose just a major version, but a 51 | # method was added after this version). 52 | # 53 | # This particular check can be difficult to get to succeed as often transitive dependencies may 54 | # be incorrectly specified (e.g., a dependency specifies 1.0 but really requires 1.1.5). There 55 | # is an alternative flag available -Zdirect-minimal-versions that uses the minimal versions for 56 | # direct dependencies of this crate, while selecting the maximal versions for the transitive 57 | # dependencies. Alternatively, you can add a line in your Cargo.toml to artificially increase 58 | # the minimal dependency, which you do with e.g.: 59 | # ```toml 60 | # # for minimal-versions 61 | # [target.'cfg(any())'.dependencies] 62 | # openssl = { version = "0.10.55", optional = true } # needed to allow foo to build with -Zminimal-versions 63 | # ``` 64 | # The optional = true is necessary in case that dependency isn't otherwise transitively required 65 | # by your library, and the target bit is so that this dependency edge never actually affects 66 | # Cargo build order. See also 67 | # https://github.com/jonhoo/fantoccini/blob/fde336472b712bc7ebf5b4e772023a7ba71b2262/Cargo.toml#L47-L49. 68 | # This action is run on ubuntu with the stable toolchain, as it is not expected to fail 69 | runs-on: ubuntu-latest 70 | name: ubuntu / stable / minimal-versions 71 | steps: 72 | - uses: actions/checkout@v4 73 | with: 74 | submodules: true 75 | - name: Install stable 76 | uses: dtolnay/rust-toolchain@stable 77 | - name: Install nightly for -Zminimal-versions 78 | uses: dtolnay/rust-toolchain@nightly 79 | - name: rustup default stable 80 | run: rustup default stable 81 | - name: cargo update -Zminimal-versions 82 | run: cargo +nightly update -Zminimal-versions 83 | - name: cargo test 84 | run: cargo test --locked --all-features --all-targets 85 | os-check: 86 | # run cargo test on mac and windows 87 | runs-on: ${{ matrix.os }} 88 | name: ${{ matrix.os }} / stable 89 | strategy: 90 | fail-fast: false 91 | matrix: 92 | os: [macos-latest, windows-latest] 93 | steps: 94 | # if your project needs OpenSSL, uncomment this to fix Windows builds. 95 | # it's commented out by default as the install command takes 5-10m. 96 | # - run: echo "VCPKG_ROOT=$env:VCPKG_INSTALLATION_ROOT" | Out-File -FilePath $env:GITHUB_ENV -Append 97 | # if: runner.os == 'Windows' 98 | # - run: vcpkg install openssl:x64-windows-static-md 99 | # if: runner.os == 'Windows' 100 | - uses: actions/checkout@v4 101 | with: 102 | submodules: true 103 | - name: Install stable 104 | uses: dtolnay/rust-toolchain@stable 105 | - name: cargo generate-lockfile 106 | if: hashFiles('Cargo.lock') == '' 107 | run: cargo generate-lockfile 108 | - name: cargo test 109 | run: cargo test --locked --all-features --all-targets 110 | coverage: 111 | # use llvm-cov to build and collect coverage and outputs in a format that 112 | # is compatible with codecov.io 113 | # 114 | # note that codecov as of v4 requires that CODECOV_TOKEN from 115 | # 116 | # https://app.codecov.io/gh///settings 117 | # 118 | # is set in two places on your repo: 119 | # 120 | # - https://github.com/jonhoo/guardian/settings/secrets/actions 121 | # - https://github.com/jonhoo/guardian/settings/secrets/dependabot 122 | # 123 | # (the former is needed for codecov uploads to work with Dependabot PRs) 124 | # 125 | # PRs coming from forks of your repo will not have access to the token, but 126 | # for those, codecov allows uploading coverage reports without a token. 127 | # it's all a little weird and inconvenient. see 128 | # 129 | # https://github.com/codecov/feedback/issues/112 130 | # 131 | # for lots of more discussion 132 | runs-on: ubuntu-latest 133 | name: ubuntu / stable / coverage 134 | steps: 135 | - uses: actions/checkout@v4 136 | with: 137 | submodules: true 138 | - name: Install stable 139 | uses: dtolnay/rust-toolchain@stable 140 | with: 141 | components: llvm-tools-preview 142 | - name: cargo install cargo-llvm-cov 143 | uses: taiki-e/install-action@cargo-llvm-cov 144 | - name: cargo generate-lockfile 145 | if: hashFiles('Cargo.lock') == '' 146 | run: cargo generate-lockfile 147 | - name: cargo llvm-cov 148 | run: cargo llvm-cov --locked --all-features --lcov --output-path lcov.info 149 | - name: Record Rust version 150 | run: echo "RUST=$(rustc --version)" >> "$GITHUB_ENV" 151 | - name: Upload to codecov.io 152 | uses: codecov/codecov-action@v4 153 | with: 154 | fail_ci_if_error: true 155 | token: ${{ secrets.CODECOV_TOKEN }} 156 | env_vars: OS,RUST 157 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | # Cargo.lock 3 | lcov.info 4 | -------------------------------------------------------------------------------- /.helix/languages.toml: -------------------------------------------------------------------------------- 1 | [language-server.rust-analyzer] 2 | config.cargo.features = "all" 3 | -------------------------------------------------------------------------------- /.taplo.toml: -------------------------------------------------------------------------------- 1 | include = ["**/*.toml", "**/Cargo.toml", "Cargo.toml"] 2 | [formatting] 3 | align_comments = true 4 | align_entries = true 5 | allowed_blank_lines = 1 6 | indent_entries = false 7 | reorder_arrays = false 8 | reorder_keys = true 9 | trailing_newline = true 10 | 11 | [[rule]] 12 | formatting.align_entries = true 13 | formatting.array_auto_expand = false 14 | formatting.reorder_arrays = true 15 | formatting.reorder_keys = true 16 | include = ["Cargo.toml", "**/Cargo.toml"] 17 | keys = [ 18 | "dependencies", 19 | "dev-dependencies", 20 | "build-dependencies", 21 | "workspace.dependencies", 22 | "workspace.dev-dependencies", 23 | "workspace.build-dependencies", 24 | ] 25 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## Unreleased 9 | 10 | ### Deprecated 11 | 12 | - Deprecated `ResolveError` name. 13 | 14 | ### Fixed 15 | 16 | - `EncodingError` and `ParseIndexError` now implement `Diagnostic`, which 17 | unifies the API for errors originating from parse-like operations. 18 | - Fixes returning an incorrect error type when parsing a `Token` that 19 | terminates with `~`. This now correctly classifies the error as a `Tilde` 20 | error. 21 | 22 | ### Removed 23 | 24 | - Some methods were removed from `InvalidCharacterError`, as that type no 25 | longer holds a copy of the input string internally. This is a breaking 26 | change. To access equivalent functionality, use the `Diagnostic` API 27 | integration. 28 | 29 | ### Changed 30 | - `Token::from_encoded` now accepts owned or borrowed strings (any type that 31 | implements `Into>`). 32 | - Sealed the `Diagnose` trait. 33 | - Implementation of the `Default` trait for `Pointer` now doesn't constrain the lifetime. 34 | 35 | ## [0.7.1] 2025-02-16 36 | 37 | ### Changed 38 | 39 | - Removes accidentally enabled default features `"miette"` and `"toml"` 40 | 41 | ## [0.7.0] 2025-02-13 42 | 43 | ### Added 44 | 45 | - Adds method `into_buf` for `Box` and `impl From for Box`. 46 | - Adds unsafe associated methods `Pointer::new_unchecked` and `PointerBuf::new_unchecked` for 47 | external zero-cost construction. 48 | - Adds `Pointer::starts_with` and `Pointer::ends_with` for prefix and suffix matching. 49 | - Adds new `ParseIndexError` variant to express the presence non-digit characters in the token. 50 | - Adds `Token::is_next` for checking if a token represents the `-` character. 51 | - Adds `InvalidEncoding` to represent the two possible encoding errors when decoding a token. 52 | - Adds `diagnotic::Diagnostic` trait to facilitate error reporting and 53 | `miette` integration. All errors intended for usage with `assign::Assign` or 54 | `resolve::Resolve` must implement this trait. 55 | - Adds `diagnostic::Report` to capture the input for `PointerBuf::parse` 56 | and to facilitate `miette` integration for all errors. 57 | - Adds `"miette"` feature flag to enable `miette` integration for error reporting. 58 | 59 | ### Changed 60 | 61 | - `Pointer::get` now accepts ranges and can produce `Pointer` segments as output (similar to 62 | `slice::get`). 63 | - Bumps minimum Rust version to 1.79. 64 | - `PointerBuf::parse` now returns `RichParseError`, an alias to 65 | `Report` which contains the allocated string as well as the 66 | error. Use `Report::original` for matches or `Report:: 67 | - Renames `ParseError::NoLeadingBackslash` to `ParseError::NoLeadingSlash` 68 | (sorry for the churn, I spaced hard - @chanced). 69 | - Adds field `position` to variants of `resolve::Error` and `assign::Error` to indicate the 70 | token index of where the error occurred. 71 | - Renames `ParseError::is_no_leading_backslash` to `ParseError::is_no_leading_slash`. 72 | - Renames `assign::AssignError` to `assign::Error` 73 | - Renames `resolve::ResolveError` to `resolve::Error` 74 | - Renames `InvalidEncodingError` to `EncodingError` 75 | 76 | ### Fixed 77 | 78 | - Make validation of array indices conform to RFC 6901 in the presence of non-digit characters. 79 | 80 | ### Deprecated 81 | 82 | - `ParseError::is_no_leading_backslash` renamed to `ParseError::is_no_leading_slash`. 83 | - `assign::AssignError` renamed to `assign::Error` 84 | - `resolve::ResolveError` renamed to `resolve::Error` 85 | - `InvalidEncodingError` renamed to `EncodingError` 86 | 87 | ## [0.6.2] 2024-09-30 88 | 89 | ### Added 90 | 91 | - Adds methods `len` and `is_empty` to `Pointer` 92 | 93 | ## [0.6.1] 2024-09-26 94 | 95 | ## Added 96 | 97 | - Adds fluid methods `with_trailing_token`, `with_leading_token`, `concat` to `Pointer`. 98 | 99 | ## [0.6.0] - 2024-08-06 100 | 101 | ### Fixed 102 | 103 | - `Token::to_index` now fails if the token contains leading zeros, as mandated by the RFC. 104 | 105 | ### Changed 106 | 107 | - `ParseIndexError` is now an enum to reflect the new failure mode when parsing indices. 108 | 109 | ## [0.5.1] 110 | 111 | ### Changed 112 | 113 | - README tweak. 114 | 115 | ## [0.5.0] 116 | 117 | This is a breaking release including: 118 | 119 | - [#30](https://github.com/chanced/jsonptr/pull/30) and [#37](https://github.com/chanced/jsonptr/pull/37) by [@asmello](https://github.com/asmello) 120 | - [#41](https://github.com/chanced/jsonptr/pull/41) by [@chanced](https://github.com/chanced) & [@asmello](https://github.com/asmello) 121 | 122 | ### Added 123 | 124 | - New slice type `Pointer` that enables zero-copy usage patterns 125 | - New const constructor `const fn Pointer::from_static` for compile-time allocated `Pointer`s 126 | - Zero-allocation `Pointer::root` singleton pointer 127 | - [Quickcheck](https://docs.rs/quickcheck/latest/quickcheck/index.html)-based testing 128 | - New methods: `Pointer::split_front`, `Pointer::split_back`, `Pointer::parent`, `Pointer::strip_suffix` 129 | - Implemented `Display` and `Debug` for `ParseError` 130 | - Adds `Pointer::split_at` which utilizes character offsets to split a pointer at a separator 131 | - Adds specific error types `ParseError`, `ResolveError`, `AssignError` 132 | 133 | ### Changed 134 | 135 | - JSON Pointers with leading `"#"` are no longer accepted. Previously, the erroneous leading hashtag was allowed during parsing but discarded. 136 | - `Assign`, `Resolve`, `ResolveMut`, `Delete` all now use associated types `Value` and `Error`, allowing for more impls other than JSON 137 | - Debug implementation now preserves type information (e.g. prints `PathBuf("/foo/bar")` instead of `"/foo/bar"`) - `Display` remains the same 138 | - Original `Pointer` type renamed to `PointerBuf` 139 | - Error types now use character `offset` indexing instead of owned copies of `Pointer` and `Token`. 140 | - `Pointer::root` is now `PointerBuf::new` 141 | - `Pointer::new` is now `PointerBuf::from_tokens` (and takes an `IntoIterator` argument - arrays still work) 142 | - `Pointer::union` is now `PointerBuf::intersection` 143 | - `Token` type has been simplified and is now by default a borrowed type (use `Token::to_owned` or `Token::into_owned` to make it owned) 144 | - `Assign::assign` now returns `Result, AssignError>`, where `Option` is the replaced value 145 | 146 | ### Fixed 147 | 148 | - Fixes [#28](https://github.com/chanced/jsonptr/pull/28): `Pointer::union` is misleadingly named 149 | 150 | ### Removed 151 | 152 | - Removes `Assignment` 153 | - Removes `MaybePointer` 154 | - Removes `Error` 155 | - Removes `impl Deref` from `Pointer` 156 | - Removes optional dependencies of `url`, `fluent-uri` and `uniresid` as well as the `TryFrom` implementations for their respective types 157 | - Removed `Token::as_key` and `Token::as_str` - use `Token::decoded().as_ref()` to achieve the same effect 158 | - Several redundant or error-prone trait implementations were removed from `Token` 159 | 160 | ## [0.4.7] 2024-03-18 161 | 162 | - Fixes issue with `pop_front` on a token with an empty string leaving the pointer in an invalid state. #25 by [@wngr](https://github.com/wngr) 163 | - Fixes issue with `pop_back` on a token with an empty string. #26 by [@asmello](https://github.com/asmello) 164 | 165 | ## [0.4.6] 2024-03-24 166 | 167 | - Fixes `Pointer::last` panicking for empty/root pointers #23 by [@wngr](https://github.com/wngr) 168 | 169 | ## [0.4.5] 2024-02-23 170 | 171 | ### Fixed 172 | 173 | - Fixes issue with `Pointer::push_back` that does not allow for empty strings 174 | to be appended as tokens. #21 fixed by [@wngr](https://github.com/wngr) 175 | 176 | ## [0.4.3] 2023-08-20 177 | 178 | ### Added 179 | 180 | - Adds `parse` method to `Pointer` which calls the currently existing `FromStr` 181 | impl 182 | 183 | ## [0.4.2] 2023-06-23 184 | 185 | ### Added 186 | 187 | - implements `IntoIterator` for `&Pointer` 188 | 189 | ## [0.4.1] 2023-06-21 190 | 191 | ### Added 192 | 193 | - implements `Borrow<[u8]>` and `AsRef<[u8]>` for `Pointer` 194 | 195 | ## [0.4.0] 2023-05-31 196 | 197 | ### Added 198 | 199 | - Adds `CHANGELOG.md` which will be better upkept moving forward. 200 | - Adds `MaybePointer` to assist with deserialization which should not fail fast. 201 | 202 | ### Changed 203 | 204 | - `Pointer::new` now accepts a generic list, so `&["example"]` can be replaced by `["example"]`. For untyped, empty slices (i.e. `Pointer::new(&[])`), use `Pointer::default()`. 205 | - `std` is now enabled by default. 206 | 207 | ### Removed 208 | 209 | - Removes optional `MalformedPointerError` from `Pointer`. 210 | 211 | ## [0.3.6] 2023-05-23 212 | 213 | ### Changed 214 | 215 | - Adds quotes around `Pointer` debug output (#11) 216 | 217 | ### Fixed 218 | 219 | - Adds missing `impl std::error::Error` for `Error`, `NotFoundError`, `MalformedError` 220 | - Fixes build for `std` feature flag 221 | 222 | ## [0.3.4] 2023-05-11 223 | 224 | ### Added 225 | 226 | - Adds feature flag `fluent-uri` for `From` impl (#3) 227 | 228 | ## [0.2.0] 2023-02-24 229 | 230 | ### Changed 231 | 232 | - `std` is now optional 233 | - Adds feature flags `"uniresid"`, `"url"` to enable implementing `From`, `From` (respectively). 234 | 235 | ### Removed 236 | 237 | - Removes `Cargo.lock` 238 | - Makes `uniresid` and `uri` optional 239 | 240 | ## [0.1.0] - 2022-06-12 241 | 242 | ### Fixed 243 | 244 | - Fixes root pointer representation `""` rather than the erroneous `"/"` 245 | - Fixes an issue where encoded tokens were not being resolved properly 246 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.24.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler2" 16 | version = "2.0.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" 19 | 20 | [[package]] 21 | name = "aho-corasick" 22 | version = "1.1.3" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 25 | dependencies = [ 26 | "memchr", 27 | ] 28 | 29 | [[package]] 30 | name = "backtrace" 31 | version = "0.3.74" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" 34 | dependencies = [ 35 | "addr2line", 36 | "cfg-if", 37 | "libc", 38 | "miniz_oxide", 39 | "object", 40 | "rustc-demangle", 41 | "windows-targets", 42 | ] 43 | 44 | [[package]] 45 | name = "backtrace-ext" 46 | version = "0.2.1" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "537beee3be4a18fb023b570f80e3ae28003db9167a751266b259926e25539d50" 49 | dependencies = [ 50 | "backtrace", 51 | ] 52 | 53 | [[package]] 54 | name = "bitflags" 55 | version = "2.6.0" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" 58 | 59 | [[package]] 60 | name = "cfg-if" 61 | version = "1.0.0" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 64 | 65 | [[package]] 66 | name = "env_logger" 67 | version = "0.8.4" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" 70 | dependencies = [ 71 | "log", 72 | "regex", 73 | ] 74 | 75 | [[package]] 76 | name = "equivalent" 77 | version = "1.0.1" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 80 | 81 | [[package]] 82 | name = "errno" 83 | version = "0.3.9" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" 86 | dependencies = [ 87 | "libc", 88 | "windows-sys 0.52.0", 89 | ] 90 | 91 | [[package]] 92 | name = "getrandom" 93 | version = "0.2.15" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" 96 | dependencies = [ 97 | "cfg-if", 98 | "libc", 99 | "wasi", 100 | ] 101 | 102 | [[package]] 103 | name = "gimli" 104 | version = "0.31.1" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" 107 | 108 | [[package]] 109 | name = "hashbrown" 110 | version = "0.15.1" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" 113 | 114 | [[package]] 115 | name = "indexmap" 116 | version = "2.6.0" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" 119 | dependencies = [ 120 | "equivalent", 121 | "hashbrown", 122 | ] 123 | 124 | [[package]] 125 | name = "is_ci" 126 | version = "1.2.0" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45" 129 | 130 | [[package]] 131 | name = "itoa" 132 | version = "1.0.11" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" 135 | 136 | [[package]] 137 | name = "jsonptr" 138 | version = "0.7.1" 139 | dependencies = [ 140 | "miette", 141 | "quickcheck", 142 | "quickcheck_macros", 143 | "serde", 144 | "serde_json", 145 | "syn 1.0.109", 146 | "toml", 147 | ] 148 | 149 | [[package]] 150 | name = "libc" 151 | version = "0.2.162" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" 154 | 155 | [[package]] 156 | name = "linux-raw-sys" 157 | version = "0.4.14" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" 160 | 161 | [[package]] 162 | name = "log" 163 | version = "0.4.22" 164 | source = "registry+https://github.com/rust-lang/crates.io-index" 165 | checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" 166 | 167 | [[package]] 168 | name = "memchr" 169 | version = "2.7.4" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 172 | 173 | [[package]] 174 | name = "miette" 175 | version = "7.4.0" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | checksum = "317f146e2eb7021892722af37cf1b971f0a70c8406f487e24952667616192c64" 178 | dependencies = [ 179 | "backtrace", 180 | "backtrace-ext", 181 | "cfg-if", 182 | "miette-derive", 183 | "owo-colors", 184 | "supports-color", 185 | "supports-hyperlinks", 186 | "supports-unicode", 187 | "terminal_size", 188 | "textwrap", 189 | "thiserror", 190 | "unicode-width", 191 | ] 192 | 193 | [[package]] 194 | name = "miette-derive" 195 | version = "7.4.0" 196 | source = "registry+https://github.com/rust-lang/crates.io-index" 197 | checksum = "23c9b935fbe1d6cbd1dac857b54a688145e2d93f48db36010514d0f612d0ad67" 198 | dependencies = [ 199 | "proc-macro2", 200 | "quote", 201 | "syn 2.0.87", 202 | ] 203 | 204 | [[package]] 205 | name = "miniz_oxide" 206 | version = "0.8.0" 207 | source = "registry+https://github.com/rust-lang/crates.io-index" 208 | checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" 209 | dependencies = [ 210 | "adler2", 211 | ] 212 | 213 | [[package]] 214 | name = "object" 215 | version = "0.36.5" 216 | source = "registry+https://github.com/rust-lang/crates.io-index" 217 | checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" 218 | dependencies = [ 219 | "memchr", 220 | ] 221 | 222 | [[package]] 223 | name = "owo-colors" 224 | version = "4.1.0" 225 | source = "registry+https://github.com/rust-lang/crates.io-index" 226 | checksum = "fb37767f6569cd834a413442455e0f066d0d522de8630436e2a1761d9726ba56" 227 | 228 | [[package]] 229 | name = "proc-macro2" 230 | version = "1.0.89" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" 233 | dependencies = [ 234 | "unicode-ident", 235 | ] 236 | 237 | [[package]] 238 | name = "quickcheck" 239 | version = "1.0.3" 240 | source = "registry+https://github.com/rust-lang/crates.io-index" 241 | checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6" 242 | dependencies = [ 243 | "env_logger", 244 | "log", 245 | "rand", 246 | ] 247 | 248 | [[package]] 249 | name = "quickcheck_macros" 250 | version = "1.0.0" 251 | source = "registry+https://github.com/rust-lang/crates.io-index" 252 | checksum = "b22a693222d716a9587786f37ac3f6b4faedb5b80c23914e7303ff5a1d8016e9" 253 | dependencies = [ 254 | "proc-macro2", 255 | "quote", 256 | "syn 1.0.109", 257 | ] 258 | 259 | [[package]] 260 | name = "quote" 261 | version = "1.0.37" 262 | source = "registry+https://github.com/rust-lang/crates.io-index" 263 | checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" 264 | dependencies = [ 265 | "proc-macro2", 266 | ] 267 | 268 | [[package]] 269 | name = "rand" 270 | version = "0.8.5" 271 | source = "registry+https://github.com/rust-lang/crates.io-index" 272 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 273 | dependencies = [ 274 | "rand_core", 275 | ] 276 | 277 | [[package]] 278 | name = "rand_core" 279 | version = "0.6.4" 280 | source = "registry+https://github.com/rust-lang/crates.io-index" 281 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 282 | dependencies = [ 283 | "getrandom", 284 | ] 285 | 286 | [[package]] 287 | name = "regex" 288 | version = "1.11.1" 289 | source = "registry+https://github.com/rust-lang/crates.io-index" 290 | checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 291 | dependencies = [ 292 | "aho-corasick", 293 | "memchr", 294 | "regex-automata", 295 | "regex-syntax", 296 | ] 297 | 298 | [[package]] 299 | name = "regex-automata" 300 | version = "0.4.9" 301 | source = "registry+https://github.com/rust-lang/crates.io-index" 302 | checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 303 | dependencies = [ 304 | "aho-corasick", 305 | "memchr", 306 | "regex-syntax", 307 | ] 308 | 309 | [[package]] 310 | name = "regex-syntax" 311 | version = "0.8.5" 312 | source = "registry+https://github.com/rust-lang/crates.io-index" 313 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 314 | 315 | [[package]] 316 | name = "rustc-demangle" 317 | version = "0.1.24" 318 | source = "registry+https://github.com/rust-lang/crates.io-index" 319 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 320 | 321 | [[package]] 322 | name = "rustix" 323 | version = "0.38.40" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0" 326 | dependencies = [ 327 | "bitflags", 328 | "errno", 329 | "libc", 330 | "linux-raw-sys", 331 | "windows-sys 0.52.0", 332 | ] 333 | 334 | [[package]] 335 | name = "ryu" 336 | version = "1.0.18" 337 | source = "registry+https://github.com/rust-lang/crates.io-index" 338 | checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" 339 | 340 | [[package]] 341 | name = "serde" 342 | version = "1.0.215" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" 345 | dependencies = [ 346 | "serde_derive", 347 | ] 348 | 349 | [[package]] 350 | name = "serde_derive" 351 | version = "1.0.215" 352 | source = "registry+https://github.com/rust-lang/crates.io-index" 353 | checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" 354 | dependencies = [ 355 | "proc-macro2", 356 | "quote", 357 | "syn 2.0.87", 358 | ] 359 | 360 | [[package]] 361 | name = "serde_json" 362 | version = "1.0.132" 363 | source = "registry+https://github.com/rust-lang/crates.io-index" 364 | checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" 365 | dependencies = [ 366 | "itoa", 367 | "memchr", 368 | "ryu", 369 | "serde", 370 | ] 371 | 372 | [[package]] 373 | name = "serde_spanned" 374 | version = "0.6.8" 375 | source = "registry+https://github.com/rust-lang/crates.io-index" 376 | checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" 377 | dependencies = [ 378 | "serde", 379 | ] 380 | 381 | [[package]] 382 | name = "supports-color" 383 | version = "3.0.1" 384 | source = "registry+https://github.com/rust-lang/crates.io-index" 385 | checksum = "8775305acf21c96926c900ad056abeef436701108518cf890020387236ac5a77" 386 | dependencies = [ 387 | "is_ci", 388 | ] 389 | 390 | [[package]] 391 | name = "supports-hyperlinks" 392 | version = "3.0.0" 393 | source = "registry+https://github.com/rust-lang/crates.io-index" 394 | checksum = "2c0a1e5168041f5f3ff68ff7d95dcb9c8749df29f6e7e89ada40dd4c9de404ee" 395 | 396 | [[package]] 397 | name = "supports-unicode" 398 | version = "3.0.0" 399 | source = "registry+https://github.com/rust-lang/crates.io-index" 400 | checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2" 401 | 402 | [[package]] 403 | name = "syn" 404 | version = "1.0.109" 405 | source = "registry+https://github.com/rust-lang/crates.io-index" 406 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 407 | dependencies = [ 408 | "proc-macro2", 409 | "quote", 410 | "unicode-ident", 411 | ] 412 | 413 | [[package]] 414 | name = "syn" 415 | version = "2.0.87" 416 | source = "registry+https://github.com/rust-lang/crates.io-index" 417 | checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" 418 | dependencies = [ 419 | "proc-macro2", 420 | "quote", 421 | "unicode-ident", 422 | ] 423 | 424 | [[package]] 425 | name = "terminal_size" 426 | version = "0.4.1" 427 | source = "registry+https://github.com/rust-lang/crates.io-index" 428 | checksum = "5352447f921fda68cf61b4101566c0bdb5104eff6804d0678e5227580ab6a4e9" 429 | dependencies = [ 430 | "rustix", 431 | "windows-sys 0.59.0", 432 | ] 433 | 434 | [[package]] 435 | name = "textwrap" 436 | version = "0.16.1" 437 | source = "registry+https://github.com/rust-lang/crates.io-index" 438 | checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" 439 | dependencies = [ 440 | "unicode-linebreak", 441 | "unicode-width", 442 | ] 443 | 444 | [[package]] 445 | name = "thiserror" 446 | version = "1.0.69" 447 | source = "registry+https://github.com/rust-lang/crates.io-index" 448 | checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" 449 | dependencies = [ 450 | "thiserror-impl", 451 | ] 452 | 453 | [[package]] 454 | name = "thiserror-impl" 455 | version = "1.0.69" 456 | source = "registry+https://github.com/rust-lang/crates.io-index" 457 | checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" 458 | dependencies = [ 459 | "proc-macro2", 460 | "quote", 461 | "syn 2.0.87", 462 | ] 463 | 464 | [[package]] 465 | name = "toml" 466 | version = "0.8.20" 467 | source = "registry+https://github.com/rust-lang/crates.io-index" 468 | checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" 469 | dependencies = [ 470 | "serde", 471 | "serde_spanned", 472 | "toml_datetime", 473 | "toml_edit", 474 | ] 475 | 476 | [[package]] 477 | name = "toml_datetime" 478 | version = "0.6.8" 479 | source = "registry+https://github.com/rust-lang/crates.io-index" 480 | checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" 481 | dependencies = [ 482 | "serde", 483 | ] 484 | 485 | [[package]] 486 | name = "toml_edit" 487 | version = "0.22.24" 488 | source = "registry+https://github.com/rust-lang/crates.io-index" 489 | checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" 490 | dependencies = [ 491 | "indexmap", 492 | "serde", 493 | "serde_spanned", 494 | "toml_datetime", 495 | "winnow", 496 | ] 497 | 498 | [[package]] 499 | name = "unicode-ident" 500 | version = "1.0.13" 501 | source = "registry+https://github.com/rust-lang/crates.io-index" 502 | checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" 503 | 504 | [[package]] 505 | name = "unicode-linebreak" 506 | version = "0.1.5" 507 | source = "registry+https://github.com/rust-lang/crates.io-index" 508 | checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" 509 | 510 | [[package]] 511 | name = "unicode-width" 512 | version = "0.1.14" 513 | source = "registry+https://github.com/rust-lang/crates.io-index" 514 | checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" 515 | 516 | [[package]] 517 | name = "wasi" 518 | version = "0.11.0+wasi-snapshot-preview1" 519 | source = "registry+https://github.com/rust-lang/crates.io-index" 520 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 521 | 522 | [[package]] 523 | name = "windows-sys" 524 | version = "0.52.0" 525 | source = "registry+https://github.com/rust-lang/crates.io-index" 526 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 527 | dependencies = [ 528 | "windows-targets", 529 | ] 530 | 531 | [[package]] 532 | name = "windows-sys" 533 | version = "0.59.0" 534 | source = "registry+https://github.com/rust-lang/crates.io-index" 535 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 536 | dependencies = [ 537 | "windows-targets", 538 | ] 539 | 540 | [[package]] 541 | name = "windows-targets" 542 | version = "0.52.6" 543 | source = "registry+https://github.com/rust-lang/crates.io-index" 544 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 545 | dependencies = [ 546 | "windows_aarch64_gnullvm", 547 | "windows_aarch64_msvc", 548 | "windows_i686_gnu", 549 | "windows_i686_gnullvm", 550 | "windows_i686_msvc", 551 | "windows_x86_64_gnu", 552 | "windows_x86_64_gnullvm", 553 | "windows_x86_64_msvc", 554 | ] 555 | 556 | [[package]] 557 | name = "windows_aarch64_gnullvm" 558 | version = "0.52.6" 559 | source = "registry+https://github.com/rust-lang/crates.io-index" 560 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 561 | 562 | [[package]] 563 | name = "windows_aarch64_msvc" 564 | version = "0.52.6" 565 | source = "registry+https://github.com/rust-lang/crates.io-index" 566 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 567 | 568 | [[package]] 569 | name = "windows_i686_gnu" 570 | version = "0.52.6" 571 | source = "registry+https://github.com/rust-lang/crates.io-index" 572 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 573 | 574 | [[package]] 575 | name = "windows_i686_gnullvm" 576 | version = "0.52.6" 577 | source = "registry+https://github.com/rust-lang/crates.io-index" 578 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 579 | 580 | [[package]] 581 | name = "windows_i686_msvc" 582 | version = "0.52.6" 583 | source = "registry+https://github.com/rust-lang/crates.io-index" 584 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 585 | 586 | [[package]] 587 | name = "windows_x86_64_gnu" 588 | version = "0.52.6" 589 | source = "registry+https://github.com/rust-lang/crates.io-index" 590 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 591 | 592 | [[package]] 593 | name = "windows_x86_64_gnullvm" 594 | version = "0.52.6" 595 | source = "registry+https://github.com/rust-lang/crates.io-index" 596 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 597 | 598 | [[package]] 599 | name = "windows_x86_64_msvc" 600 | version = "0.52.6" 601 | source = "registry+https://github.com/rust-lang/crates.io-index" 602 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 603 | 604 | [[package]] 605 | name = "winnow" 606 | version = "0.7.2" 607 | source = "registry+https://github.com/rust-lang/crates.io-index" 608 | checksum = "59690dea168f2198d1a3b0cac23b8063efcd11012f10ae4698f284808c8ef603" 609 | dependencies = [ 610 | "memchr", 611 | ] 612 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = [ 3 | "chance dinkins", 4 | "André Sá de Mello ", 5 | "Oliver Wangler ", 6 | ] 7 | description = "Data structures and logic for resolving, assigning, and deleting by JSON Pointers (RFC 6901)" 8 | documentation = "https://docs.rs/jsonptr" 9 | edition = "2021" 10 | homepage = "https://github.com/chanced/jsonptr" 11 | keywords = ["json-pointer", "rfc-6901", "6901"] 12 | license = "MIT OR Apache-2.0" 13 | name = "jsonptr" 14 | repository = "https://github.com/chanced/jsonptr" 15 | rust-version = "1.79.0" 16 | version = "0.7.1" 17 | 18 | [dependencies] 19 | miette = { version = "7.4.0", optional = true, features = ["fancy"] } 20 | serde = { version = "1.0.203", optional = true, features = ["alloc"] } 21 | serde_json = { version = "1.0.119", optional = true, features = ["alloc"] } 22 | toml = { version = "0.8", optional = true } 23 | 24 | [dev-dependencies] 25 | quickcheck = "1.0.3" 26 | quickcheck_macros = "1.0.0" 27 | 28 | # quickcheck-macros requires a later version than it declares 29 | # so we use this hack to make `-Zminimal-versions` work 30 | [target.'cfg(any())'.dependencies] 31 | syn = { version = "1.0.109", optional = true } 32 | 33 | [features] 34 | assign = [] 35 | default = ["std", "serde", "json", "resolve", "assign", "delete"] 36 | delete = ["resolve"] 37 | json = ["dep:serde_json", "serde"] 38 | miette = ["dep:miette", "std"] 39 | resolve = [] 40 | std = ["serde/std", "serde_json?/std"] 41 | toml = ["dep:toml", "serde", "std"] 42 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2024 Chance Dinkins 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Chance Dinkins 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 |
2 | 3 | # jsonptr - JSON Pointers (RFC 6901) for Rust 4 | 5 |
6 | 7 | [github](https://github.com/chanced/jsonptr) 8 | [crates.io](https://crates.io/crates/jsonptr) 9 | [docs.rs](https://docs.rs/jsonptr) 10 | [build status](https://github.com/chanced/jsonptr/actions?query=branch%3Amain) 11 | [code coverage](https://codecov.io/gh/chanced/jsonptr) 12 | 13 | JSON Pointers ([RFC 6901](https://datatracker.ietf.org/doc/html/rfc6901)) 14 | defines a string syntax for identifying a specific location within a JSON, or 15 | similar, document. This crate provides two types, [`Pointer`] and [`PointerBuf`] 16 | (akin to [`Path`] and [`PathBuf`]), for working with them abstractly. 17 | 18 | A pointer is composed of zero or more [`Token`]s, single segments which 19 | represent a field of an object or an [`index`] of an array, and are bounded by 20 | either `'/'` or the end of the string. Tokens are lightly encoded, where `'~'` 21 | is escaped as `"~0"` due to it signaling encoding and `'/'` is escaped as `"~1"` 22 | because `'/'` separates tokens and would split the token into two otherwise. 23 | 24 | [`Token`]s can be iterated over using either [`Tokens`], returned from the 25 | [`tokens`] method of a pointer or [`Components`], returned from the 26 | [`components`] method. The difference being that `Tokens` iterates over each 27 | token in the pointer, while `Components` iterates over [`Component`]s, which can 28 | represent the root of the document or a single token of the pointer. 29 | 30 | Operations [`resolve`], [`assign`] and [`delete`] are provided as traits with 31 | corresponding methods on pointer types. Implementations of each trait are 32 | provided for value types of the crates [`serde_json`] and [`toml`]. All 33 | operations are enabled by default but are gated by [feature 34 | flags](#feature-flags). 35 | 36 | ## Usage 37 | 38 | ### Parsing and General Usage 39 | 40 | To parse a [`Pointer`] from a string, use either [`Pointer::parse`], for 41 | potentially fallible parsing, or the `const fn` `from_static` to produce a 42 | `&'static Pointer` from a string that is known to be valid. 43 | 44 | ```rust 45 | use jsonptr::Pointer; 46 | 47 | let ptr = Pointer::parse("/examples/0/name").unwrap(); 48 | let static_ptr = Pointer::from_static("/examples/0/name"); 49 | assert_eq!(ptr, static_ptr); 50 | 51 | assert_eq!(ptr.get(1..).unwrap(), Pointer::parse("/0/name").unwrap()); 52 | 53 | let parent = ptr.parent().unwrap(); 54 | assert_eq!(parent, Pointer::parse("/examples/0").unwrap()); 55 | 56 | let (token, remaining) = ptr.split_front().unwrap(); 57 | assert_eq!(token.decoded(), "examples"); 58 | assert_eq!(remaining, Pointer::parse("/0/name").unwrap()); 59 | ``` 60 | 61 | [`PointerBuf`]s can be parsed using [`PointerBuf::parse`] or constructed from an 62 | iterator of [`Token`]s with the [`from_tokens`] method: 63 | 64 | ```rust 65 | use jsonptr::PointerBuf; 66 | let mut buf = PointerBuf::parse("/examples/0/name").unwrap(); 67 | 68 | let from_tokens = PointerBuf::from_tokens(["examples", "0", "name"]); 69 | assert_eq!(&buf, &from_tokens); 70 | 71 | buf.push_front("pointer"); 72 | buf.push_front("~"); 73 | buf.push_back("/"); 74 | assert_eq!(buf.as_str(), "/~0/pointer/examples/0/name/~1"); 75 | ``` 76 | 77 | ### Token Iteration 78 | 79 | Iterating over the tokens or components of a pointer: 80 | 81 | ```rust 82 | use jsonptr::{Pointer, Component, Token}; 83 | let ptr = Pointer::from_static("/path/to/value"); 84 | 85 | // Using the `tokens` method: 86 | let tokens: Vec<_> = ptr.tokens().collect(); 87 | assert_eq!(tokens, vec![Token::new("path"), Token::new("to"), Token::new("value")]); 88 | 89 | // Using the `components` method: 90 | let mut components = ptr.components(); 91 | assert_eq!(components.next(), Some(Component::Root)); 92 | assert_eq!(components.next(), Some(Component::Token(Token::new("path")))); 93 | assert_eq!(components.next(), Some(Component::Token(Token::new("to")))); 94 | assert_eq!(components.next(), Some(Component::Token(Token::new("value")))); 95 | ``` 96 | 97 | ### Resolving Values 98 | 99 | To get a value at the location of a pointer, use either the [`Resolve`] and 100 | [`ResolveMut`] traits or [`Pointer::resolve`] and [`Pointer::resolve_mut`] 101 | methods. See the [`resolve`] mod for more information. 102 | 103 | ```rust 104 | use jsonptr::Pointer; 105 | use serde_json::json; 106 | 107 | let ptr = Pointer::parse("/foo/bar").unwrap(); 108 | let data = json!({"foo": { "bar": 34 }}); 109 | let bar = ptr.resolve(&data).unwrap(); 110 | assert_eq!(bar, &json!(34)); 111 | ``` 112 | 113 | ### Assigning Values 114 | 115 | Values can be set, with path expansion, using the either the [`Assign`] trait or 116 | [`Pointer::assign`]. See [`assign`] for more information. 117 | 118 | ```rust 119 | use jsonptr::Pointer; 120 | use serde_json::json; 121 | 122 | let ptr = Pointer::parse("/secret/universe").unwrap(); 123 | let mut data = json!({"secret": { "universe": 42 }}); 124 | let replaced = ptr.assign(&mut data, json!(34)).unwrap(); 125 | assert_eq!(replaced, Some(json!(42))); 126 | assert_eq!(data, json!({"secret": { "universe": 34 }})); 127 | ``` 128 | 129 | ### Deleting Values 130 | 131 | Values can be removed with the either the [`Delete`] trait or 132 | [`Pointer::delete`]. See [`delete`] for more information. 133 | 134 | ```rust 135 | use jsonptr::Pointer; 136 | use serde_json::json; 137 | 138 | let ptr = Pointer::parse("/secret/universe").unwrap(); 139 | let mut data = json!({"secret": { "life": 42, "universe": 42, "everything": 42 }}); 140 | let deleted = ptr.delete(&mut data); 141 | assert_eq!(deleted, Some(json!(42))); 142 | assert_eq!(data, json!({"secret": { "life": 42, "everything": 42 }})); 143 | ``` 144 | 145 | ### Error Reporting 146 | 147 | Any error produced by function calls into methods of traits or types of this 148 | crate can be converted into a [`Report`] which contains the original error 149 | and the [`String`] which failed to parse or the [`PointerBuf`] which failed to 150 | resolve or assign. 151 | 152 | ```rust 153 | use jsonptr::{Pointer, Diagnose}; 154 | let ptr_str = "foo/bar"; 155 | let err /* Result<&Pointer, Report> */ = Pointer::parse(ptr_str).diagnose(ptr_str).unwrap_err(); 156 | assert!(err.original().is_no_leading_slash()); 157 | ``` 158 | 159 | In the case of [`PointerBuf::parse`], the [`ParseError`] is always wrapped in a 160 | [`Report`] so that the input `String` is not dropped. 161 | 162 | ```rust 163 | use jsonptr::{PointerBuf}; 164 | let ptr_str = "foo/bar"; 165 | let err /* Result<&PointerBuf, Report> */ = PointerBuf::parse(ptr_str).unwrap_err(); 166 | assert!(err.original().is_no_leading_slash()); 167 | ``` 168 | 169 | ## Feature Flags 170 | 171 | | Flag | Description | Enables | Default | 172 | | :---------: | ----------------------------------------------------------------------------------------------------------------------------------------- | --------------- | :-----: | 173 | | `"std"` | Implements `std::error::Error` for error types | | ✓ | 174 | | `"serde"` | Enables [`serde`] support for types | | ✓ | 175 | | `"json"` | Implements ops for [`serde_json::Value`] | `"serde"` | ✓ | 176 | | `"toml"` | Implements ops for [`toml::Value`] | `"std"`, `toml` | | 177 | | `"assign"` | Enables the [`assign`] module and related pointer methods, providing a means to assign a value to a specific location within a document | | ✓ | 178 | | `"resolve"` | Enables the [`resolve`] module and related pointer methods, providing a means to resolve a value at a specific location within a document | | ✓ | 179 | | `"delete"` | Enables the [`delete`] module and related pointer methods, providing a means to delete a value at a specific location within a document | `"resolve"` | ✓ | 180 | | `"miette"` | Enables integration with [`miette`](https://docs.rs/miette) for error reporting | `"std"` | | 181 | 182 |
183 | 184 | ## License 185 | 186 | Licensed under either of 187 | 188 | - Apache License, Version 2.0 189 | ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 190 | - MIT license 191 | ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 192 | 193 | at your convenience. 194 | 195 | ## Contribution 196 | 197 | Contributions and feedback are always welcome and appreciated. If you find an 198 | issue, please open a ticket or a pull request. 199 | 200 | Unless you explicitly state otherwise, any contribution intentionally submitted 201 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 202 | dual licensed as above, without any additional terms or conditions. 203 | 204 | [LICENSE-APACHE]: LICENSE-APACHE 205 | [LICENSE-MIT]: LICENSE-MIT 206 | 207 |
208 | 209 | [`Pointer::components`]: https://docs.rs/jsonptr/latest/jsonptrstruct.Pointer.html#method.components 210 | [`Pointer::tokens`]: https://docs.rs/jsonptr/latest/jsonptrstruct.Pointer.html#method.tokens 211 | [`Pointer`]: https://docs.rs/jsonptr/latest/jsonptr/struct.Pointer.html 212 | [`Pointer::parse`]: https://docs.rs/jsonptr/latest/jsonptr/struct.Pointer.html#method.parse 213 | [`Pointer::resolve`]: https://docs.rs/jsonptr/latest/jsonptr/struct.Pointer.html#method.resolve 214 | [`Pointer::resolve_mut`]: https://docs.rs/jsonptr/latest/jsonptr/struct.Pointer.html#method.resolve_mut 215 | [`Pointer::assign`]: https://docs.rs/jsonptr/latest/jsonptr/struct.Pointer.html#method.assign 216 | [`Pointer::delete`]: https://docs.rs/jsonptr/latest/jsonptr/struct.Pointer.html#method.delete 217 | [`PointerBuf::parse`]: https://docs.rs/jsonptr/latest/jsonptr/struct.PointerBuf.html#method.parse 218 | [`from_tokens`]: https://docs.rs/jsonptr/latest/jsonptr/struct.PointerBuf.html#method.from_tokens 219 | [`PointerBuf`]: https://docs.rs/jsonptr/latest/jsonptr/struct.PointerBuf.html 220 | [`Token`]: https://docs.rs/jsonptr/latest/jsonptr/struct.Token.html 221 | [`Tokens`]: https://docs.rs/jsonptr/latest/jsonptr/struct.Tokens.html 222 | [`Components`]: https://docs.rs/jsonptr/latest/jsonptr/struct.Components.html 223 | [`Component`]: https://docs.rs/jsonptr/latest/jsonptr/enum.Component.html 224 | [`index`]: https://docs.rs/jsonptr/latest/jsonptr/index/index.html 225 | [`tokens`]: https://docs.rs/jsonptr/latest/jsonptr/struct.Pointer.html#method.tokens 226 | [`components`]: https://docs.rs/jsonptr/latest/jsonptr/struct.Pointer.html#method.components 227 | [`resolve`]: https://docs.rs/jsonptr/latest/jsonptr/resolve/index.html 228 | [`assign`]: https://docs.rs/jsonptr/latest/jsonptr/assign/index.html 229 | [`delete`]: https://docs.rs/jsonptr/latest/jsonptr/delete/index.html 230 | [`Resolve`]: https://docs.rs/jsonptr/latest/jsonptr/resolve/trait.Resolve.html 231 | [`ResolveMut`]: https://docs.rs/jsonptr/latest/jsonptr/resolve/trait.ResolveMut.html 232 | [`Assign`]: https://docs.rs/jsonptr/latest/jsonptr/assign/trait.Assign.html 233 | [`Delete`]: https://docs.rs/jsonptr/latest/jsonptr/delete/trait.Delete.html 234 | [`serde`]: https://docs.rs/serde/1.0/serde/index 235 | [`serde_json`]: https://docs.rs/serde_json/1.0/serde_json/enum.Value.html 236 | [`serde_json::Value`]: https://docs.rs/serde_json/1.0/serde_json/enum.Value.html 237 | [`toml`]: https://docs.rs/toml/0.8/toml/enum.Value.html 238 | [`toml::Value`]: https://docs.rs/toml/0.8/toml/enum.Value.html 239 | [`Path`]: https://doc.rust-lang.org/std/path/struct.Path.html 240 | [`PathBuf`]: https://doc.rust-lang.org/std/path/struct.PathBuf.html 241 | -------------------------------------------------------------------------------- /src/arbitrary.rs: -------------------------------------------------------------------------------- 1 | use crate::{PointerBuf, Token}; 2 | use alloc::{boxed::Box, string::String, vec::Vec}; 3 | use quickcheck::Arbitrary; 4 | 5 | impl Arbitrary for Token<'static> { 6 | fn arbitrary(g: &mut quickcheck::Gen) -> Self { 7 | Self::new(String::arbitrary(g)) 8 | } 9 | 10 | fn shrink(&self) -> Box> { 11 | Box::new(self.decoded().into_owned().shrink().map(Self::new)) 12 | } 13 | } 14 | 15 | impl Arbitrary for PointerBuf { 16 | fn arbitrary(g: &mut quickcheck::Gen) -> Self { 17 | let size = usize::arbitrary(g) % g.size(); 18 | Self::from_tokens((0..size).map(|_| Token::arbitrary(g)).collect::>()) 19 | } 20 | 21 | fn shrink(&self) -> Box> { 22 | let tokens: Vec<_> = self.tokens().map(Token::into_owned).collect(); 23 | Box::new((0..self.count()).map(move |i| { 24 | let subset: Vec<_> = tokens 25 | .iter() 26 | .enumerate() 27 | .filter_map(|(j, t)| (i != j).then_some(t.clone())) 28 | .collect(); 29 | Self::from_tokens(subset) 30 | })) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/component.rs: -------------------------------------------------------------------------------- 1 | use crate::{Pointer, Token, Tokens}; 2 | 3 | /// A single [`Token`] or the root of a JSON Pointer 4 | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] 5 | pub enum Component<'t> { 6 | /// The document root 7 | Root, 8 | /// A segment of a JSON Pointer 9 | Token(Token<'t>), 10 | } 11 | impl<'t> From> for Component<'t> { 12 | fn from(token: Token<'t>) -> Self { 13 | Self::Token(token) 14 | } 15 | } 16 | 17 | /// An iterator over the [`Component`]s of a JSON Pointer 18 | #[derive(Debug)] 19 | pub struct Components<'t> { 20 | tokens: Tokens<'t>, 21 | sent_root: bool, 22 | } 23 | 24 | impl<'t> Iterator for Components<'t> { 25 | type Item = Component<'t>; 26 | fn next(&mut self) -> Option { 27 | if !self.sent_root { 28 | self.sent_root = true; 29 | return Some(Component::Root); 30 | } 31 | self.tokens.next().map(Component::Token) 32 | } 33 | } 34 | 35 | impl<'t> From<&'t Pointer> for Components<'t> { 36 | fn from(pointer: &'t Pointer) -> Self { 37 | Self { 38 | sent_root: false, 39 | tokens: pointer.tokens(), 40 | } 41 | } 42 | } 43 | 44 | #[cfg(test)] 45 | mod tests { 46 | use super::*; 47 | 48 | #[test] 49 | fn components() { 50 | let ptr = Pointer::from_static(""); 51 | let components: Vec<_> = Components::from(ptr).collect(); 52 | assert_eq!(components, vec![Component::Root]); 53 | 54 | let ptr = Pointer::from_static("/foo"); 55 | let components = ptr.components().collect::>(); 56 | assert_eq!( 57 | components, 58 | vec![Component::Root, Component::Token("foo".into())] 59 | ); 60 | 61 | let ptr = Pointer::from_static("/foo/bar/-/0/baz"); 62 | let components = ptr.components().collect::>(); 63 | assert_eq!( 64 | components, 65 | vec![ 66 | Component::Root, 67 | Component::from(Token::from("foo")), 68 | Component::Token("bar".into()), 69 | Component::Token("-".into()), 70 | Component::Token("0".into()), 71 | Component::Token("baz".into()) 72 | ] 73 | ); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/delete.rs: -------------------------------------------------------------------------------- 1 | //! # Delete values based on JSON Pointers 2 | //! 3 | //! This module provides the [`Delete`] trait which is implemented by types that 4 | //! can internally remove a value based on a JSON Pointer. 5 | //! 6 | //! The rules of deletion are determined by the implementation, with the 7 | //! provided implementations (`"json"` & `"toml"`) operating as follows: 8 | //! - If the [`Pointer`] can be resolved, then the [`Value`](`Delete::Value`) is 9 | //! deleted and returned as `Some(value)`. 10 | //! - If the [`Pointer`] fails to resolve for any reason, `None` is 11 | //! returned. 12 | //! - If the [`Pointer`] is root, `value` is replaced: 13 | //! - `"json"` - `serde_json::Value::Null` 14 | //! - `"toml"` - `toml::Value::Table::Default` 15 | //! 16 | //! This module is enabled by default with the `"delete"` feature flag. 17 | //! 18 | //! ## Usage 19 | //! Deleting a resolved pointer: 20 | //! ```rust 21 | //! use jsonptr::{Pointer, delete::Delete}; 22 | //! use serde_json::json; 23 | //! 24 | //! let mut data = json!({ "foo": { "bar": { "baz": "qux" } } }); 25 | //! let ptr = Pointer::from_static("/foo/bar/baz"); 26 | //! assert_eq!(data.delete(&ptr), Some("qux".into())); 27 | //! assert_eq!(data, json!({ "foo": { "bar": {} } })); 28 | //! ``` 29 | //! Deleting a non-existent Pointer returns `None`: 30 | //! ```rust 31 | //! use jsonptr::{ Pointer, delete::Delete }; 32 | //! use serde_json::json; 33 | //! 34 | //! let mut data = json!({}); 35 | //! let ptr = Pointer::from_static("/foo/bar/baz"); 36 | //! assert_eq!(ptr.delete(&mut data), None); 37 | //! assert_eq!(data, json!({})); 38 | //! ``` 39 | //! Deleting a root pointer replaces the value with `Value::Null`: 40 | //! ```rust 41 | //! use jsonptr::{Pointer, delete::Delete}; 42 | //! use serde_json::json; 43 | //! 44 | //! let mut data = json!({ "foo": { "bar": "baz" } }); 45 | //! let ptr = Pointer::root(); 46 | //! assert_eq!(data.delete(&ptr), Some(json!({ "foo": { "bar": "baz" } }))); 47 | //! assert!(data.is_null()); 48 | //! ``` 49 | //! 50 | //! ## Provided implementations 51 | //! 52 | //! | Lang | value type | feature flag | Default | 53 | //! | ----- |: ----------------- :|: ---------- :| ------- | 54 | //! | JSON | `serde_json::Value` | `"json"` | ✓ | 55 | //! | TOML | `toml::Value` | `"toml"` | | 56 | 57 | use crate::Pointer; 58 | 59 | /* 60 | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 61 | ╔══════════════════════════════════════════════════════════════════════════════╗ 62 | ║ ║ 63 | ║ Delete ║ 64 | ║ ¯¯¯¯¯¯¯¯ ║ 65 | ╚══════════════════════════════════════════════════════════════════════════════╝ 66 | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 67 | */ 68 | 69 | /// Delete is implemented by types which can internally remove a value based on 70 | /// a JSON Pointer 71 | pub trait Delete { 72 | /// The type of value that this implementation can operate on. 73 | type Value; 74 | 75 | /// Attempts to internally delete a value based upon a [Pointer]. 76 | fn delete(&mut self, ptr: &Pointer) -> Option; 77 | } 78 | 79 | /* 80 | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 81 | ╔══════════════════════════════════════════════════════════════════════════════╗ 82 | ║ ║ 83 | ║ json impl ║ 84 | ║ ¯¯¯¯¯¯¯¯¯¯¯ ║ 85 | ╚══════════════════════════════════════════════════════════════════════════════╝ 86 | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 87 | */ 88 | 89 | #[cfg(feature = "json")] 90 | mod json { 91 | use super::Delete; 92 | use crate::Pointer; 93 | use core::mem; 94 | use serde_json::Value; 95 | 96 | impl Delete for Value { 97 | type Value = Value; 98 | fn delete(&mut self, ptr: &Pointer) -> Option { 99 | let Some((parent_ptr, last)) = ptr.split_back() else { 100 | // deleting at root 101 | return Some(mem::replace(self, Value::Null)); 102 | }; 103 | parent_ptr 104 | .resolve_mut(self) 105 | .ok() 106 | .and_then(|parent| match parent { 107 | Value::Array(children) => { 108 | let idx = last.to_index().ok()?.for_len_incl(children.len()).ok()?; 109 | children.remove(idx).into() 110 | } 111 | Value::Object(children) => children.remove(last.decoded().as_ref()), 112 | _ => None, 113 | }) 114 | } 115 | } 116 | } 117 | 118 | /* 119 | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 120 | ╔══════════════════════════════════════════════════════════════════════════════╗ 121 | ║ ║ 122 | ║ toml impl ║ 123 | ║ ¯¯¯¯¯¯¯¯¯¯¯ ║ 124 | ╚══════════════════════════════════════════════════════════════════════════════╝ 125 | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 126 | */ 127 | #[cfg(feature = "toml")] 128 | mod toml { 129 | use super::Delete; 130 | use crate::Pointer; 131 | use core::mem; 132 | use toml::{Table, Value}; 133 | 134 | impl Delete for Value { 135 | type Value = Value; 136 | fn delete(&mut self, ptr: &Pointer) -> Option { 137 | let Some((parent_ptr, last)) = ptr.split_back() else { 138 | // deleting at root 139 | return Some(mem::replace(self, Table::default().into())); 140 | }; 141 | parent_ptr 142 | .resolve_mut(self) 143 | .ok() 144 | .and_then(|parent| match parent { 145 | Value::Array(children) => { 146 | let idx = last.to_index().ok()?.for_len_incl(children.len()).ok()?; 147 | children.remove(idx).into() 148 | } 149 | Value::Table(children) => children.remove(last.decoded().as_ref()), 150 | _ => None, 151 | }) 152 | } 153 | } 154 | } 155 | 156 | /* 157 | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 158 | ╔══════════════════════════════════════════════════════════════════════════════╗ 159 | ║ ║ 160 | ║ Tests ║ 161 | ║ ¯¯¯¯¯¯¯ ║ 162 | ╚══════════════════════════════════════════════════════════════════════════════╝ 163 | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 164 | */ 165 | 166 | #[cfg(test)] 167 | mod tests { 168 | use super::Delete; 169 | use crate::Pointer; 170 | use core::fmt; 171 | 172 | use serde_json::json; 173 | struct Test { 174 | data: V, 175 | ptr: &'static str, 176 | expected_data: V, 177 | expected_deleted: Option, 178 | } 179 | impl Test 180 | where 181 | V: Delete + Clone + PartialEq + fmt::Display + fmt::Debug, 182 | { 183 | fn all(tests: impl IntoIterator>) { 184 | tests.into_iter().enumerate().for_each(|(i, t)| t.run(i)); 185 | } 186 | fn run(self, _i: usize) { 187 | let Test { 188 | mut data, 189 | ptr, 190 | expected_data, 191 | expected_deleted, 192 | } = self; 193 | 194 | let ptr = Pointer::from_static(ptr); 195 | let deleted = ptr.delete(&mut data); 196 | assert_eq!(expected_data, data); 197 | assert_eq!(expected_deleted, deleted); 198 | } 199 | } 200 | /* 201 | ╔═══════════════════════════════════════════════════╗ 202 | ║ json ║ 203 | ╚═══════════════════════════════════════════════════╝ 204 | */ 205 | #[test] 206 | #[cfg(feature = "json")] 207 | fn delete_json() { 208 | Test::all([ 209 | // 0 210 | Test { 211 | ptr: "/foo", 212 | data: json!({"foo": "bar"}), 213 | expected_data: json!({}), 214 | expected_deleted: Some(json!("bar")), 215 | }, 216 | // 1 217 | Test { 218 | ptr: "/foo/bar", 219 | data: json!({"foo": {"bar": "baz"}}), 220 | expected_data: json!({"foo": {}}), 221 | expected_deleted: Some(json!("baz")), 222 | }, 223 | // 2 224 | Test { 225 | ptr: "/foo/bar", 226 | data: json!({"foo": "bar"}), 227 | expected_data: json!({"foo": "bar"}), 228 | expected_deleted: None, 229 | }, 230 | // 3 231 | Test { 232 | ptr: "/foo/bar", 233 | data: json!({"foo": {"bar": "baz"}}), 234 | expected_data: json!({"foo": {}}), 235 | expected_deleted: Some(json!("baz")), 236 | }, 237 | // 4 238 | Test { 239 | ptr: "/foo/bar/0", 240 | data: json!({"foo": {"bar": ["baz", "qux"]}}), 241 | expected_data: json!({"foo": {"bar": ["qux"]}}), 242 | expected_deleted: Some(json!("baz")), 243 | }, 244 | // 5 245 | Test { 246 | ptr: "/foo/0", 247 | data: json!({"foo": "bar"}), 248 | expected_data: json!({"foo": "bar"}), 249 | expected_deleted: None, 250 | }, 251 | // 6 252 | Test { 253 | ptr: "/foo/bar/0/baz", 254 | data: json!({"foo": { "bar": [{"baz": "qux", "remaining": "field"}]}}), 255 | expected_data: json!({"foo": { "bar": [{"remaining": "field"}]} }), 256 | expected_deleted: Some(json!("qux")), 257 | }, 258 | // 7 259 | // issue #18 - unable to delete root token https://github.com/chanced/jsonptr/issues/18 260 | Test { 261 | ptr: "/Example", 262 | data: json!({"Example": 21, "test": "test"}), 263 | expected_data: json!({"test": "test"}), 264 | expected_deleted: Some(json!(21)), 265 | }, 266 | Test { 267 | ptr: "", 268 | data: json!({"Example": 21, "test": "test"}), 269 | expected_data: json!(null), 270 | expected_deleted: Some(json!({"Example": 21, "test": "test"})), 271 | }, 272 | ]); 273 | } 274 | /* 275 | ╔═══════════════════════════════════════════════════╗ 276 | ║ toml ║ 277 | ╚═══════════════════════════════════════════════════╝ 278 | */ 279 | #[test] 280 | #[cfg(feature = "toml")] 281 | fn delete_toml() { 282 | use toml::{toml, Table, Value}; 283 | 284 | Test::all([ 285 | // 0 286 | Test { 287 | data: toml! {"foo" = "bar"}.into(), 288 | ptr: "/foo", 289 | expected_data: Value::Table(Table::new()), 290 | expected_deleted: Some("bar".into()), 291 | }, 292 | // 1 293 | Test { 294 | data: toml! {"foo" = {"bar" = "baz"}}.into(), 295 | ptr: "/foo/bar", 296 | expected_data: toml! {"foo" = {}}.into(), 297 | expected_deleted: Some("baz".into()), 298 | }, 299 | // 2 300 | Test { 301 | data: toml! {"foo" = "bar"}.into(), 302 | ptr: "/foo/bar", 303 | expected_data: toml! {"foo" = "bar"}.into(), 304 | expected_deleted: None, 305 | }, 306 | // 3 307 | Test { 308 | data: toml! {"foo" = {"bar" = "baz"}}.into(), 309 | ptr: "/foo/bar", 310 | expected_data: toml! {"foo" = {}}.into(), 311 | expected_deleted: Some("baz".into()), 312 | }, 313 | // 4 314 | Test { 315 | data: toml! {"foo" = {"bar" = ["baz", "qux"]}}.into(), 316 | ptr: "/foo/bar/0", 317 | expected_data: toml! {"foo" = {"bar" = ["qux"]}}.into(), 318 | expected_deleted: Some("baz".into()), 319 | }, 320 | // 5 321 | Test { 322 | data: toml! {"foo" = "bar"}.into(), 323 | ptr: "/foo/0", 324 | expected_data: toml! {"foo" = "bar"}.into(), 325 | expected_deleted: None, 326 | }, 327 | // 6 328 | Test { 329 | data: toml! {"foo" = { "bar" = [{"baz" = "qux", "remaining" = "field"}]}}.into(), 330 | ptr: "/foo/bar/0/baz", 331 | expected_data: toml! {"foo" = { "bar" = [{"remaining" = "field"}]} }.into(), 332 | expected_deleted: Some("qux".into()), 333 | }, 334 | // 7 335 | // issue #18 - unable to delete root token https://github.com/chanced/jsonptr/issues/18 336 | Test { 337 | data: toml! {"Example" = 21 "test" = "test"}.into(), 338 | ptr: "/Example", 339 | expected_data: toml! {"test" = "test"}.into(), 340 | expected_deleted: Some(21.into()), 341 | }, 342 | ]); 343 | } 344 | } 345 | -------------------------------------------------------------------------------- /src/diagnostic.rs: -------------------------------------------------------------------------------- 1 | //! Error reporting data structures and miette integration. 2 | //! 3 | 4 | use alloc::{boxed::Box, string::String}; 5 | use core::{fmt, ops::Deref}; 6 | 7 | /// Implemented by errors which can be converted into a [`Report`]. 8 | pub trait Diagnostic: Sized { 9 | /// The value which caused the error. 10 | type Subject: Deref; 11 | 12 | /// Combine the error with its subject to generate a [`Report`]. 13 | fn into_report(self, subject: impl Into) -> Report { 14 | Report::new(self, subject.into()) 15 | } 16 | 17 | /// The docs.rs URL for this error 18 | fn url() -> &'static str; 19 | 20 | /// Returns the label for the given [`Subject`] if applicable. 21 | fn labels(&self, subject: &Self::Subject) -> Option>>; 22 | } 23 | 24 | /// A label for a span within a JSON Pointer or malformed string. 25 | #[derive(Debug, PartialEq, Eq, Clone)] 26 | pub struct Label { 27 | text: String, 28 | offset: usize, 29 | len: usize, 30 | } 31 | 32 | impl Label { 33 | /// Creates a new instance of a [`Label`] from its parts 34 | // NOTE: this is deliberately public, so that users can use 35 | // the `Assign` and `Resolve` traits with custom types and errors, 36 | // and then implement `Diagnostic` for those errors. 37 | pub fn new(text: String, offset: usize, len: usize) -> Self { 38 | Self { text, offset, len } 39 | } 40 | } 41 | 42 | #[cfg(feature = "miette")] 43 | impl From