diff --git a/.github/DOCS.md b/.github/DOCS.md
new file mode 100644
index 0000000..e932784
--- /dev/null
+++ b/.github/DOCS.md
@@ -0,0 +1,23 @@
+# Github config and workflows
+
+In this folder there is configuration for codecoverage, dependabot, and ci
+workflows that check the library more deeply than the default configurations.
+
+This folder can be or was merged using a --allow-unrelated-histories merge
+strategy from which provides a
+reasonably sensible base for writing your own ci on. By using this strategy
+the history of the CI repo is included in your repo, and future updates to
+the CI can be merged later.
+
+To perform this merge run:
+
+```shell
+git remote add ci https://github.com/jonhoo/rust-ci-conf.git
+git fetch ci
+git merge --allow-unrelated-histories ci/main
+```
+
+An overview of the files in this project is available at:
+, which contains some
+rationale for decisions and runs through an example of solving minimal version
+and OpenSSL issues.
diff --git a/.github/codecov.yml b/.github/codecov.yml
new file mode 100644
index 0000000..cd5ce8f
--- /dev/null
+++ b/.github/codecov.yml
@@ -0,0 +1,21 @@
+# ref: https://docs.codecov.com/docs/codecovyml-reference
+coverage:
+ # Hold ourselves to a high bar
+ range: 85..100
+ round: down
+ precision: 1
+ status:
+ # ref: https://docs.codecov.com/docs/commit-status
+ project:
+ default:
+ # Avoid false negatives
+ threshold: 1%
+
+# Test files aren't important for coverage
+ignore:
+ - "tests"
+
+# Make comments less noisy
+comment:
+ layout: "files"
+ require_changes: true
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..d0f091e
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,19 @@
+version: 2
+updates:
+ - package-ecosystem: github-actions
+ directory: /
+ schedule:
+ interval: daily
+ - package-ecosystem: cargo
+ directory: /
+ schedule:
+ interval: daily
+ ignore:
+ - dependency-name: "*"
+ # patch and minor updates don't matter for libraries as consumers of this library build
+ # with their own lockfile, rather than the version specified in this library's lockfile
+ # remove this ignore rule if your package has binaries to ensure that the binaries are
+ # built with the exact set of dependencies and those are up to date.
+ update-types:
+ - "version-update:semver-patch"
+ - "version-update:semver-minor"
diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml
new file mode 100644
index 0000000..2fc8abd
--- /dev/null
+++ b/.github/workflows/check.yml
@@ -0,0 +1,126 @@
+# This workflow runs whenever a PR is opened or updated, or a commit is pushed to main. It runs
+# several checks:
+# - fmt: checks that the code is formatted according to rustfmt
+# - clippy: checks that the code does not contain any clippy warnings
+# - doc: checks that the code can be documented without errors
+# - hack: check combinations of feature flags
+# - msrv: check that the msrv specified in the crate is correct
+permissions:
+ contents: read
+# This configuration allows maintainers of this repo to create a branch and pull request based on
+# the new branch. Restricting the push trigger to the main branch ensures that the PR only gets
+# built once.
+on:
+ push:
+ branches: [main]
+ pull_request:
+# If new code is pushed to a PR branch, then cancel in progress workflows for that PR. Ensures that
+# we don't waste CI time, and returns results quicker https://github.com/jonhoo/rust-ci-conf/pull/5
+concurrency:
+ group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
+ cancel-in-progress: true
+name: check
+jobs:
+ fmt:
+ runs-on: ubuntu-latest
+ name: stable / fmt
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+ - name: Install stable
+ uses: dtolnay/rust-toolchain@stable
+ with:
+ components: rustfmt
+ - name: cargo fmt --check
+ run: cargo fmt --check
+ clippy:
+ runs-on: ubuntu-latest
+ name: ${{ matrix.toolchain }} / clippy
+ permissions:
+ contents: read
+ checks: write
+ strategy:
+ fail-fast: false
+ matrix:
+ # Get early warning of new lints which are regularly introduced in beta channels.
+ toolchain: [stable, beta]
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+ - name: Install ${{ matrix.toolchain }}
+ uses: dtolnay/rust-toolchain@master
+ with:
+ toolchain: ${{ matrix.toolchain }}
+ components: clippy
+ - name: cargo clippy
+ uses: giraffate/clippy-action@v1
+ with:
+ reporter: "github-pr-check"
+ github_token: ${{ secrets.GITHUB_TOKEN }}
+ semver:
+ runs-on: ubuntu-latest
+ name: semver
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+ - name: Install stable
+ uses: dtolnay/rust-toolchain@stable
+ with:
+ components: rustfmt
+ - name: cargo-semver-checks
+ uses: obi1kenobi/cargo-semver-checks-action@v2
+ doc:
+ # run docs generation on nightly rather than stable. This enables features like
+ # https://doc.rust-lang.org/beta/unstable-book/language-features/doc-cfg.html which allows an
+ # API be documented as only available in some specific platforms.
+ runs-on: ubuntu-latest
+ name: nightly / doc
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+ - name: Install nightly
+ uses: dtolnay/rust-toolchain@nightly
+ - name: cargo doc
+ run: cargo doc --no-deps --all-features
+ env:
+ RUSTDOCFLAGS: --cfg docsrs
+ hack:
+ # cargo-hack checks combinations of feature flags to ensure that features are all additive
+ # which is required for feature unification
+ runs-on: ubuntu-latest
+ name: ubuntu / stable / features
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+ - name: Install stable
+ uses: dtolnay/rust-toolchain@stable
+ - name: cargo install cargo-hack
+ uses: taiki-e/install-action@cargo-hack
+ # intentionally no target specifier; see https://github.com/jonhoo/rust-ci-conf/pull/4
+ # --feature-powerset runs for every combination of features
+ - name: cargo hack
+ run: cargo hack --feature-powerset check
+ msrv:
+ # check that we can build using the minimal rust version that is specified by this crate
+ runs-on: ubuntu-latest
+ # we use a matrix here just because env can't be used in job names
+ # https://docs.github.com/en/actions/learn-github-actions/contexts#context-availability
+ strategy:
+ matrix:
+ msrv: ["1.76"]
+ name: ubuntu / ${{ matrix.msrv }}
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+ - name: Install ${{ matrix.msrv }}
+ uses: dtolnay/rust-toolchain@master
+ with:
+ toolchain: ${{ matrix.msrv }}
+ - name: cargo +${{ matrix.msrv }} check
+ run: cargo check
diff --git a/.github/workflows/nostd.yml b/.github/workflows/nostd.yml
new file mode 100644
index 0000000..c12227a
--- /dev/null
+++ b/.github/workflows/nostd.yml
@@ -0,0 +1,30 @@
+# This workflow checks whether the library is able to run without the std library (e.g., embedded).
+# This entire file should be removed if this crate does not support no-std. See check.yml for
+# information about how the concurrency cancellation and workflow triggering works
+permissions:
+ contents: read
+on:
+ push:
+ branches: [main]
+ pull_request:
+concurrency:
+ group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
+ cancel-in-progress: true
+name: no-std
+jobs:
+ nostd:
+ runs-on: ubuntu-latest
+ name: ${{ matrix.target }}
+ strategy:
+ matrix:
+ target: [thumbv7m-none-eabi, aarch64-unknown-none]
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+ - name: Install stable
+ uses: dtolnay/rust-toolchain@stable
+ - name: rustup target add ${{ matrix.target }}
+ run: rustup target add ${{ matrix.target }}
+ - name: cargo check
+ run: cargo check --target ${{ matrix.target }} --no-default-features
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
deleted file mode 100644
index 31000a2..0000000
--- a/.github/workflows/rust.yml
+++ /dev/null
@@ -1,22 +0,0 @@
-name: Rust
-
-on:
- push:
- branches: [ "main" ]
- pull_request:
- branches: [ "main" ]
-
-env:
- CARGO_TERM_COLOR: always
-
-jobs:
- build:
-
- runs-on: ubuntu-latest
-
- steps:
- - uses: actions/checkout@v3
- - name: Build
- run: cargo build --verbose
- - name: Run tests
- run: cargo test --verbose
diff --git a/.github/workflows/safety.yml b/.github/workflows/safety.yml
new file mode 100644
index 0000000..9481dea
--- /dev/null
+++ b/.github/workflows/safety.yml
@@ -0,0 +1,70 @@
+# This workflow runs checks for unsafe code. In crates that don't have any unsafe code, this can be
+# removed. Runs:
+# - miri - detects undefined behavior and memory leaks
+# - address sanitizer - detects memory errors
+# - leak sanitizer - detects memory leaks
+# See check.yml for information about how the concurrency cancellation and workflow triggering works
+permissions:
+ contents: read
+on:
+ push:
+ branches: [main]
+ pull_request:
+concurrency:
+ group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
+ cancel-in-progress: true
+name: safety
+jobs:
+ sanitizers:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+ - name: Install nightly
+ uses: dtolnay/rust-toolchain@nightly
+ - run: |
+ # to get the symbolizer for debug symbol resolution
+ sudo apt install llvm
+ # to fix buggy leak analyzer:
+ # https://github.com/japaric/rust-san#unrealiable-leaksanitizer
+ # ensure there's a profile.dev section
+ if ! grep -qE '^[ \t]*[profile.dev]' Cargo.toml; then
+ echo >> Cargo.toml
+ echo '[profile.dev]' >> Cargo.toml
+ fi
+ # remove pre-existing opt-levels in profile.dev
+ sed -i '/^\s*\[profile.dev\]/,/^\s*\[/ {/^\s*opt-level/d}' Cargo.toml
+ # now set opt-level to 1
+ sed -i '/^\s*\[profile.dev\]/a opt-level = 1' Cargo.toml
+ cat Cargo.toml
+ name: Enable debug symbols
+ - name: cargo test -Zsanitizer=address
+ # only --lib --tests b/c of https://github.com/rust-lang/rust/issues/53945
+ run: cargo test --lib --tests --all-features --target x86_64-unknown-linux-gnu
+ env:
+ ASAN_OPTIONS: "detect_odr_violation=0:detect_leaks=0"
+ RUSTFLAGS: "-Z sanitizer=address"
+ - name: cargo test -Zsanitizer=leak
+ if: always()
+ run: cargo test --all-features --target x86_64-unknown-linux-gnu
+ env:
+ LSAN_OPTIONS: "suppressions=lsan-suppressions.txt"
+ RUSTFLAGS: "-Z sanitizer=leak"
+ miri:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+ - run: |
+ echo "NIGHTLY=nightly-$(curl -s https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/miri)" >> $GITHUB_ENV
+ - name: Install ${{ env.NIGHTLY }}
+ uses: dtolnay/rust-toolchain@master
+ with:
+ toolchain: ${{ env.NIGHTLY }}
+ components: miri
+ - name: cargo miri test
+ run: cargo miri test -- --skip pointer::tests::qc
+ env:
+ MIRIFLAGS: ""
diff --git a/.github/workflows/scheduled.yml b/.github/workflows/scheduled.yml
new file mode 100644
index 0000000..02aa275
--- /dev/null
+++ b/.github/workflows/scheduled.yml
@@ -0,0 +1,58 @@
+# Run scheduled (rolling) jobs on a nightly basis, as your crate may break independently of any
+# given PR. E.g., updates to rust nightly and updates to this crates dependencies. See check.yml for
+# information about how the concurrency cancellation and workflow triggering works
+permissions:
+ contents: read
+on:
+ push:
+ branches: [main]
+ pull_request:
+ schedule:
+ - cron: '7 7 * * *'
+concurrency:
+ group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
+ cancel-in-progress: true
+name: rolling
+jobs:
+ # https://twitter.com/mycoliza/status/1571295690063753218
+ nightly:
+ runs-on: ubuntu-latest
+ name: ubuntu / nightly
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+ - name: Install nightly
+ uses: dtolnay/rust-toolchain@nightly
+ - name: cargo generate-lockfile
+ if: hashFiles('Cargo.lock') == ''
+ run: cargo generate-lockfile
+ - name: cargo test --locked
+ run: cargo test --locked --all-features --all-targets
+ # https://twitter.com/alcuadrado/status/1571291687837732873
+ update:
+ # This action checks that updating the dependencies of this crate to the latest available that
+ # satisfy the versions in Cargo.toml does not break this crate. This is important as consumers
+ # of this crate will generally use the latest available crates. This is subject to the standard
+ # Cargo semver rules (i.e cargo does not update to a new major version unless explicitly told
+ # to).
+ runs-on: ubuntu-latest
+ name: ubuntu / beta / updated
+ # There's no point running this if no Cargo.lock was checked in in the first place, since we'd
+ # just redo what happened in the regular test job. Unfortunately, hashFiles only works in if on
+ # steps, so we repeat it.
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+ - name: Install beta
+ if: hashFiles('Cargo.lock') != ''
+ uses: dtolnay/rust-toolchain@beta
+ - name: cargo update
+ if: hashFiles('Cargo.lock') != ''
+ run: cargo update
+ - name: cargo test
+ if: hashFiles('Cargo.lock') != ''
+ run: cargo test --locked --all-features --all-targets
+ env:
+ RUSTFLAGS: -D deprecated
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000..6826f77
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,156 @@
+# This is the main CI workflow that runs the test suite on all pushes to main and all pull requests.
+# It runs the following jobs:
+# - required: runs the test suite on ubuntu with stable and beta rust toolchains
+# - minimal: runs the test suite with the minimal versions of the dependencies that satisfy the
+# requirements of this crate, and its dependencies
+# - os-check: runs the test suite on mac and windows
+# - coverage: runs the test suite and collects coverage information
+# See check.yml for information about how the concurrency cancellation and workflow triggering works
+permissions:
+ contents: read
+on:
+ push:
+ branches: [main]
+ pull_request:
+concurrency:
+ group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
+ cancel-in-progress: true
+name: test
+jobs:
+ required:
+ runs-on: ubuntu-latest
+ name: ubuntu / ${{ matrix.toolchain }}
+ strategy:
+ matrix:
+ # run on stable and beta to ensure that tests won't break on the next version of the rust
+ # toolchain
+ toolchain: [stable, beta]
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+ - name: Install ${{ matrix.toolchain }}
+ uses: dtolnay/rust-toolchain@master
+ with:
+ toolchain: ${{ matrix.toolchain }}
+ - name: cargo generate-lockfile
+ # enable this ci template to run regardless of whether the lockfile is checked in or not
+ if: hashFiles('Cargo.lock') == ''
+ run: cargo generate-lockfile
+ # https://twitter.com/jonhoo/status/1571290371124260865
+ - name: cargo test --locked
+ run: cargo test --locked --all-features --all-targets
+ # https://github.com/rust-lang/cargo/issues/6669
+ - name: cargo test --doc
+ run: cargo test --locked --all-features --doc
+ minimal:
+ # This action chooses the oldest version of the dependencies permitted by Cargo.toml to ensure
+ # that this crate is compatible with the minimal version that this crate and its dependencies
+ # require. This will pickup issues where this create relies on functionality that was introduced
+ # later than the actual version specified (e.g., when we choose just a major version, but a
+ # method was added after this version).
+ #
+ # This particular check can be difficult to get to succeed as often transitive dependencies may
+ # be incorrectly specified (e.g., a dependency specifies 1.0 but really requires 1.1.5). There
+ # is an alternative flag available -Zdirect-minimal-versions that uses the minimal versions for
+ # direct dependencies of this crate, while selecting the maximal versions for the transitive
+ # dependencies. Alternatively, you can add a line in your Cargo.toml to artificially increase
+ # the minimal dependency, which you do with e.g.:
+ # ```toml
+ # # for minimal-versions
+ # [target.'cfg(any())'.dependencies]
+ # openssl = { version = "0.10.55", optional = true } # needed to allow foo to build with -Zminimal-versions
+ # ```
+ # The optional = true is necessary in case that dependency isn't otherwise transitively required
+ # by your library, and the target bit is so that this dependency edge never actually affects
+ # Cargo build order. See also
+ # https://github.com/jonhoo/fantoccini/blob/fde336472b712bc7ebf5b4e772023a7ba71b2262/Cargo.toml#L47-L49.
+ # This action is run on ubuntu with the stable toolchain, as it is not expected to fail
+ runs-on: ubuntu-latest
+ name: ubuntu / stable / minimal-versions
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+ - name: Install stable
+ uses: dtolnay/rust-toolchain@stable
+ - name: Install nightly for -Zminimal-versions
+ uses: dtolnay/rust-toolchain@nightly
+ - name: rustup default stable
+ run: rustup default stable
+ - name: cargo update -Zminimal-versions
+ run: cargo +nightly update -Zminimal-versions
+ - name: cargo test
+ run: cargo test --locked --all-features --all-targets
+ os-check:
+ # run cargo test on mac and windows
+ runs-on: ${{ matrix.os }}
+ name: ${{ matrix.os }} / stable
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [macos-latest, windows-latest]
+ steps:
+ # if your project needs OpenSSL, uncomment this to fix Windows builds.
+ # it's commented out by default as the install command takes 5-10m.
+ # - run: echo "VCPKG_ROOT=$env:VCPKG_INSTALLATION_ROOT" | Out-File -FilePath $env:GITHUB_ENV -Append
+ # if: runner.os == 'Windows'
+ # - run: vcpkg install openssl:x64-windows-static-md
+ # if: runner.os == 'Windows'
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+ - name: Install stable
+ uses: dtolnay/rust-toolchain@stable
+ - name: cargo generate-lockfile
+ if: hashFiles('Cargo.lock') == ''
+ run: cargo generate-lockfile
+ - name: cargo test
+ run: cargo test --locked --all-features --all-targets
+ coverage:
+ # use llvm-cov to build and collect coverage and outputs in a format that
+ # is compatible with codecov.io
+ #
+ # note that codecov as of v4 requires that CODECOV_TOKEN from
+ #
+ # https://app.codecov.io/gh///settings
+ #
+ # is set in two places on your repo:
+ #
+ # - https://github.com/jonhoo/guardian/settings/secrets/actions
+ # - https://github.com/jonhoo/guardian/settings/secrets/dependabot
+ #
+ # (the former is needed for codecov uploads to work with Dependabot PRs)
+ #
+ # PRs coming from forks of your repo will not have access to the token, but
+ # for those, codecov allows uploading coverage reports without a token.
+ # it's all a little weird and inconvenient. see
+ #
+ # https://github.com/codecov/feedback/issues/112
+ #
+ # for lots of more discussion
+ runs-on: ubuntu-latest
+ name: ubuntu / stable / coverage
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: true
+ - name: Install stable
+ uses: dtolnay/rust-toolchain@stable
+ with:
+ components: llvm-tools-preview
+ - name: cargo install cargo-llvm-cov
+ uses: taiki-e/install-action@cargo-llvm-cov
+ - name: cargo generate-lockfile
+ if: hashFiles('Cargo.lock') == ''
+ run: cargo generate-lockfile
+ - name: cargo llvm-cov
+ run: cargo llvm-cov --locked --all-features --lcov --output-path lcov.info
+ - name: Record Rust version
+ run: echo "RUST=$(rustc --version)" >> "$GITHUB_ENV"
+ - name: Upload to codecov.io
+ uses: codecov/codecov-action@v4
+ with:
+ fail_ci_if_error: true
+ token: ${{ secrets.CODECOV_TOKEN }}
+ env_vars: OS,RUST
diff --git a/.gitignore b/.gitignore
index 96ef6c0..a3d6b48 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,2 @@
/target
-Cargo.lock
+# Cargo.lock
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..156b406
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,314 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "aho-corasick"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "cfg-if"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de"
+
+[[package]]
+name = "env_logger"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26ecb66b4bdca6c1409b40fb255eefc2bd4f6d135dab3c3124f80ffa2a9661e"
+dependencies = [
+ "log",
+ "regex",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1"
+
+[[package]]
+name = "getrandom"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee8025cf36f917e6a52cce185b7c7177689b838b7ec138364e50cc2277a56cf4"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
+
+[[package]]
+name = "indexmap"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2f3e61cf687687b30c9e6ddf0fc36cf15f035e66d491e6da968fa49ffa9a378"
+
+[[package]]
+name = "jsonptr"
+version = "0.5.0"
+dependencies = [
+ "quickcheck",
+ "quickcheck_macros",
+ "serde",
+ "serde_json",
+ "syn 1.0.109",
+ "toml",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d"
+
+[[package]]
+name = "libc"
+version = "0.2.64"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "74dfca3d9957906e8d1e6a0b641dc9a59848e793f1da2165889fd4f62d10d79c"
+
+[[package]]
+name = "log"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.74"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2de98502f212cfcea8d0bb305bd0f49d7ebdd75b64ba0a68f937d888f4e0d6db"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quickcheck"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6"
+dependencies = [
+ "env_logger",
+ "log",
+ "rand",
+]
+
+[[package]]
+name = "quickcheck_macros"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b22a693222d716a9587786f37ac3f6b4faedb5b80c23914e7303ff5a1d8016e9"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a76330fb486679b4ace3670f117bbc9e16204005c4bde9c4bd372f45bed34f12"
+dependencies = [
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "regex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc98360d9e6ad383647702acc90f80b0582eac3ea577ab47d96325d3575de908"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+ "thread_local",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716"
+
+[[package]]
+name = "ryu"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997"
+
+[[package]]
+name = "serde"
+version = "1.0.203"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.203"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.46",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.119"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8eddb61f0697cc3989c5d64b452f5488e2b8a60fd7d5076a3045076ffef8cb0"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "serde_spanned"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.46"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89456b690ff72fddcecf231caedbe615c59480c93358a93dfae7fc29e3ebbf0e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "thread_local"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
+dependencies = [
+ "lazy_static",
+]
+
+[[package]]
+name = "toml"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c226a7bba6d859b63c92c4b4fe69c5b6b72d0cb897dbc8e6012298e6154cb56e"
+dependencies = [
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_edit",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ff63e60a958cefbb518ae1fd6566af80d9d4be430a33f3723dfc47d1d411d95"
+dependencies = [
+ "indexmap",
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "winnow",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
+
+[[package]]
+name = "wasi"
+version = "0.9.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
+
+[[package]]
+name = "winnow"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81fac9742fd1ad1bd9643b991319f72dd031016d44b77039a26977eb667141e7"
+dependencies = [
+ "memchr",
+]
diff --git a/Cargo.toml b/Cargo.toml
index 954d978..5efddef 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -8,24 +8,29 @@ keywords = ["json-pointer", "rfc-6901", "6901"]
license = "MIT OR Apache-2.0"
name = "jsonptr"
repository = "https://github.com/chanced/jsonptr"
-version = "0.4.7"
+rust-version = "1.76.0"
+version = "0.5.0"
[dependencies]
-serde = { version = "1.0", optional = true, features = ["alloc"] }
-serde_json = { version = "1.0", optional = true, features = ["alloc"] }
+serde = { version = "1.0.203", optional = true, features = ["alloc"] }
+serde_json = { version = "1.0.119", optional = true, features = ["alloc"] }
toml = { version = "0.8", optional = true }
[dev-dependencies]
quickcheck = "1.0.3"
quickcheck_macros = "1.0.0"
+# quickcheck-macros requires a later version than it declares
+# so we use this hack to make `-Zminimal-versions` work
+[target.'cfg(any())'.dependencies]
+syn = { version = "1.0.109", optional = true }
+
[features]
assign = []
-default = ["std", "serde", "json", "toml", "resolve", "assign", "delete"]
-delete = []
+default = ["std", "serde", "json", "resolve", "assign", "delete"]
+delete = ["resolve"]
impl = []
json = ["dep:serde_json", "serde"]
resolve = []
-serde = []
-std = ["serde/std", "serde_json/std"]
+std = ["serde/std", "serde_json?/std"]
toml = ["dep:toml", "serde", "std"]
diff --git a/src/assign.rs b/src/assign.rs
index b30f521..7890b16 100644
--- a/src/assign.rs
+++ b/src/assign.rs
@@ -202,9 +202,15 @@ enum Assigned<'v, V> {
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
*/
+#[cfg(feature = "json")]
mod json {
use super::{Assign, AssignError, Assigned};
use crate::{Pointer, Token};
+ use alloc::{
+ string::{String, ToString},
+ vec::Vec,
+ };
+
use core::mem;
use serde_json::{map::Entry, Map, Value};
@@ -374,6 +380,7 @@ mod json {
mod toml {
use super::{Assign, AssignError, Assigned};
use crate::{Pointer, Token};
+ use alloc::{string::String, vec, vec::Vec};
use core::mem;
use toml::{map::Entry, map::Map, Value};
@@ -542,8 +549,8 @@ mod toml {
mod tests {
use super::{Assign, AssignError};
use crate::{OutOfBoundsError, ParseIndexError, Pointer};
+ use alloc::str::FromStr;
use core::fmt::{Debug, Display};
- use std::str::FromStr;
#[derive(Debug)]
struct Test {
@@ -592,6 +599,7 @@ mod tests {
#[test]
#[cfg(feature = "json")]
fn test_assign_json() {
+ use alloc::vec;
use serde_json::json;
Test::all([
Test {
@@ -750,8 +758,8 @@ mod tests {
#[test]
#[cfg(feature = "toml")]
fn test_assign_toml() {
+ use alloc::vec;
use toml::{toml, Table, Value};
-
Test::all([
Test {
data: Value::Table(toml::Table::new()),
diff --git a/src/delete.rs b/src/delete.rs
index dfe49a2..8db5850 100644
--- a/src/delete.rs
+++ b/src/delete.rs
@@ -194,7 +194,7 @@ mod tests {
let ptr = Pointer::from_static(ptr);
let deleted = ptr.delete(&mut data);
assert_eq!(
- expected_data,
+ expected_data,
data,
"\ntest delete #{i} failed:\ndata not as expected\n\nptr: \"{ptr}\"\n\nexpected data:\n{expected_data:#?}\n\nactual data:\n{data:#?}\n\n"
);
@@ -215,20 +215,20 @@ mod tests {
fn test_delete_json() {
Test::all([
// 0
- Test {
+ Test {
ptr: "/foo",
data: json!({"foo": "bar"}),
expected_data: json!({}),
expected_deleted: Some(json!("bar")),
},
// 1
- Test {
+ Test {
ptr: "/foo/bar",
data: json!({"foo": {"bar": "baz"}}),
expected_data: json!({"foo": {}}),
expected_deleted: Some(json!("baz")),
},
- // 2
+ // 2
Test {
ptr: "/foo/bar",
data: json!({"foo": "bar"}),
@@ -236,13 +236,13 @@ mod tests {
expected_deleted: None,
},
// 3
- Test {
+ Test {
ptr: "/foo/bar",
data: json!({"foo": {"bar": "baz"}}),
expected_data: json!({"foo": {}}),
expected_deleted: Some(json!("baz")),
},
- // 4
+ // 4
Test {
ptr: "/foo/bar/0",
data: json!({"foo": {"bar": ["baz", "qux"]}}),
@@ -250,27 +250,27 @@ mod tests {
expected_deleted: Some(json!("baz")),
},
// 5
- Test {
+ Test {
ptr: "/foo/0",
data: json!({"foo": "bar"}),
expected_data: json!({"foo": "bar"}),
expected_deleted: None,
},
- // 6
+ // 6
Test {
ptr: "/foo/bar/0/baz",
data: json!({"foo": { "bar": [{"baz": "qux", "remaining": "field"}]}}),
expected_data: json!({"foo": { "bar": [{"remaining": "field"}]} }),
expected_deleted: Some(json!("qux")),
},
- // 7
- // issue #18 - unable to delete root token https://github.com/chanced/jsonptr/issues/18
- Test {
+ // 7
+ // issue #18 - unable to delete root token https://github.com/chanced/jsonptr/issues/18
+ Test {
ptr: "/Example",
data: json!({"Example": 21, "test": "test"}),
expected_data: json!({"test": "test"}),
expected_deleted: Some(json!(21)),
- }
+ },
]);
}
/*
@@ -281,66 +281,66 @@ mod tests {
#[test]
#[cfg(feature = "toml")]
fn test_delete_toml() {
- use toml::{Value,Table,toml};
+ use toml::{toml, Table, Value};
Test::all([
// 0
- Test {
- data: toml!{"foo" = "bar"}.into(),
+ Test {
+ data: toml! {"foo" = "bar"}.into(),
ptr: "/foo",
expected_data: Value::Table(Table::new()),
expected_deleted: Some("bar".into()),
},
// 1
- Test {
- data: toml!{"foo" = {"bar" = "baz"}}.into(),
+ Test {
+ data: toml! {"foo" = {"bar" = "baz"}}.into(),
ptr: "/foo/bar",
- expected_data: toml!{"foo" = {}}.into(),
+ expected_data: toml! {"foo" = {}}.into(),
expected_deleted: Some("baz".into()),
},
// 2
- Test {
- data: toml!{"foo" = "bar"}.into(),
+ Test {
+ data: toml! {"foo" = "bar"}.into(),
ptr: "/foo/bar",
- expected_data: toml!{"foo" = "bar"}.into(),
+ expected_data: toml! {"foo" = "bar"}.into(),
expected_deleted: None,
},
// 3
- Test {
- data: toml!{"foo" = {"bar" = "baz"}}.into(),
+ Test {
+ data: toml! {"foo" = {"bar" = "baz"}}.into(),
ptr: "/foo/bar",
- expected_data: toml!{"foo" = {}}.into(),
+ expected_data: toml! {"foo" = {}}.into(),
expected_deleted: Some("baz".into()),
},
- // 4
+ // 4
Test {
- data: toml!{"foo" = {"bar" = ["baz", "qux"]}}.into(),
+ data: toml! {"foo" = {"bar" = ["baz", "qux"]}}.into(),
ptr: "/foo/bar/0",
- expected_data: toml!{"foo" = {"bar" = ["qux"]}}.into(),
+ expected_data: toml! {"foo" = {"bar" = ["qux"]}}.into(),
expected_deleted: Some("baz".into()),
},
// 5
- Test {
- data: toml!{"foo" = "bar"}.into(),
+ Test {
+ data: toml! {"foo" = "bar"}.into(),
ptr: "/foo/0",
- expected_data: toml!{"foo" = "bar"}.into(),
+ expected_data: toml! {"foo" = "bar"}.into(),
expected_deleted: None,
},
- // 6
+ // 6
Test {
- data: toml!{"foo" = { "bar" = [{"baz" = "qux", "remaining" = "field"}]}}.into(),
+ data: toml! {"foo" = { "bar" = [{"baz" = "qux", "remaining" = "field"}]}}.into(),
ptr: "/foo/bar/0/baz",
- expected_data: toml!{"foo" = { "bar" = [{"remaining" = "field"}]} }.into(),
+ expected_data: toml! {"foo" = { "bar" = [{"remaining" = "field"}]} }.into(),
expected_deleted: Some("qux".into()),
},
- // 7
- // issue #18 - unable to delete root token https://github.com/chanced/jsonptr/issues/18
+ // 7
+ // issue #18 - unable to delete root token https://github.com/chanced/jsonptr/issues/18
Test {
- data: toml!{"Example" = 21 "test" = "test"}.into(),
+ data: toml! {"Example" = 21 "test" = "test"}.into(),
ptr: "/Example",
- expected_data: toml!{"test" = "test"}.into(),
+ expected_data: toml! {"test" = "test"}.into(),
expected_deleted: Some(21.into()),
- }
+ },
]);
}
}
diff --git a/src/index.rs b/src/index.rs
index 1531ece..2d770f2 100644
--- a/src/index.rs
+++ b/src/index.rs
@@ -34,6 +34,7 @@
//! ````
use crate::{OutOfBoundsError, ParseIndexError, Token};
+use alloc::string::String;
use core::fmt::Display;
/// Represents an abstract index into an array.
@@ -127,7 +128,7 @@ impl Index {
/// assert_eq!(Index::Num(42).for_len_unchecked(30), 42);
/// assert_eq!(Index::Next.for_len_unchecked(30), 30);
/// ````
- #[must_use]
+
pub fn for_len_unchecked(&self, length: usize) -> usize {
match *self {
Self::Num(idx) => idx,
diff --git a/src/lib.rs b/src/lib.rs
index 2865c3d..76b55fe 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -6,12 +6,14 @@
clippy::module_name_repetitions,
clippy::into_iter_without_iter,
clippy::needless_pass_by_value,
- clippy::expect_fun_call
+ clippy::expect_fun_call,
+ clippy::must_use_candidate
)]
+
+#[cfg_attr(not(feature = "std"), macro_use)]
extern crate alloc;
use core::{fmt, num::ParseIntError};
-
pub mod prelude;
#[cfg(feature = "assign")]
@@ -88,14 +90,12 @@ impl fmt::Display for ParseError {
impl ParseError {
/// Returns `true` if this error is `NoLeadingBackslash`; otherwise returns
/// `false`.
- #[must_use]
pub fn is_no_leading_backslash(&self) -> bool {
matches!(self, Self::NoLeadingBackslash { .. })
}
/// Returns `true` if this error is `InvalidEncoding`; otherwise returns
/// `false`.
- #[must_use]
pub fn is_invalid_encoding(&self) -> bool {
matches!(self, Self::InvalidEncoding { .. })
}
@@ -112,7 +112,6 @@ impl ParseError {
/// let err = PointerBuf::parse("/foo/invalid~tilde/invalid").unwrap_err();
/// assert_eq!(err.pointer_offset(), 4)
/// ```
- #[must_use]
pub fn pointer_offset(&self) -> usize {
match *self {
Self::NoLeadingBackslash { .. } => 0,
@@ -132,7 +131,6 @@ impl ParseError {
/// let err = PointerBuf::parse("/foo/invalid~tilde/invalid").unwrap_err();
/// assert_eq!(err.source_offset(), 8)
/// ```
- #[must_use]
pub fn source_offset(&self) -> usize {
match self {
Self::NoLeadingBackslash { .. } => 0,
@@ -151,7 +149,6 @@ impl ParseError {
/// let err = PointerBuf::parse("/foo/invalid~tilde/invalid").unwrap_err();
/// assert_eq!(err.pointer_offset(), 4)
/// ```
- #[must_use]
pub fn complete_offset(&self) -> usize {
self.source_offset() + self.pointer_offset()
}
@@ -224,7 +221,6 @@ pub struct InvalidEncodingError {
impl InvalidEncodingError {
/// The byte offset of the first invalid `~`.
- #[must_use]
pub fn offset(&self) -> usize {
self.offset
}
@@ -242,25 +238,6 @@ impl fmt::Display for InvalidEncodingError {
#[cfg(feature = "std")]
impl std::error::Error for InvalidEncodingError {}
-/// Indicates that the `Token` could not be parsed as valid RFC 6901 index.
-#[derive(Debug, PartialEq, Eq)]
-pub struct IndexError {
- source: ParseIntError,
-}
-
-impl core::fmt::Display for IndexError {
- fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
- write!(f, "failed to parse token as an integer")
- }
-}
-
-#[cfg(feature = "std")]
-impl std::error::Error for IndexError {
- fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
- Some(&self.source)
- }
-}
-
/*
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
╔══════════════════════════════════════════════════════════════════════════════╗
diff --git a/src/pointer.rs b/src/pointer.rs
index a2191f0..b8501e2 100644
--- a/src/pointer.rs
+++ b/src/pointer.rs
@@ -4,7 +4,7 @@ use alloc::{
string::{String, ToString},
vec::Vec,
};
-use core::{borrow::Borrow, cmp::Ordering, fmt, ops::Deref, slice, str::FromStr};
+use core::{borrow::Borrow, cmp::Ordering, ops::Deref, str::FromStr};
/*
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
@@ -56,9 +56,12 @@ impl Pointer {
}
/// Constant reference to a root pointer
- #[must_use]
pub const fn root() -> &'static Self {
- unsafe { &*(core::ptr::from_ref::("") as *const Self) }
+ // unsafe { &*(core::ptr::from_ref::("") as *const Self) }
+ #[allow(clippy::ref_as_ptr)]
+ unsafe {
+ &*("" as *const str as *const Self)
+ }
}
/// Attempts to parse a string into a `Pointer`.
@@ -88,26 +91,22 @@ impl Pointer {
/// let bar = data.resolve(POINTER).unwrap();
/// assert_eq!(bar, "baz");
/// ````
- #[must_use]
pub const fn from_static(s: &'static str) -> &'static Self {
assert!(validate(s).is_ok(), "invalid json pointer");
- unsafe { &*(std::ptr::from_ref::(s) as *const Self) }
+ unsafe { &*(core::ptr::from_ref::(s) as *const Self) }
}
/// The encoded string representation of this `Pointer`
- #[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
/// Converts into an owned [`PointerBuf`]
- #[must_use]
pub fn to_buf(&self) -> PointerBuf {
PointerBuf(self.0.to_string())
}
/// Returns an iterator of `Token`s in the `Pointer`.
- #[must_use]
pub fn tokens(&self) -> Tokens {
let mut s = self.0.split('/');
// skipping the first '/'
@@ -116,26 +115,22 @@ impl Pointer {
}
/// Returns the number of tokens in the `Pointer`.
- #[must_use]
pub fn count(&self) -> usize {
self.tokens().count()
}
/// Returns `true` if the JSON Pointer equals `""`.
- #[must_use]
pub fn is_root(&self) -> bool {
self.0.is_empty()
}
/// Returns a `serde_json::Value` representation of this `Pointer`
- #[must_use]
#[cfg(feature = "json")]
pub fn to_json_value(&self) -> serde_json::Value {
serde_json::Value::String(self.0.to_string())
}
/// Returns the last `Token` in the `Pointer`.
- #[must_use]
pub fn back(&self) -> Option {
self.0
.rsplit_once('/')
@@ -145,13 +140,11 @@ impl Pointer {
/// Returns the last token in the `Pointer`.
///
/// alias for `back`
- #[must_use]
pub fn last(&self) -> Option {
self.back()
}
/// Returns the first `Token` in the `Pointer`.
- #[must_use]
pub fn front(&self) -> Option {
if self.is_root() {
return None;
@@ -168,26 +161,21 @@ impl Pointer {
/// Returns the first `Token` in the `Pointer`.
///
/// alias for `front`
- #[must_use]
pub fn first(&self) -> Option {
self.front()
}
/// Splits the `Pointer` into the first `Token` and a remainder `Pointer`.
- #[must_use]
pub fn split_front(&self) -> Option<(Token, &Self)> {
if self.is_root() {
return None;
}
self.0[1..]
- .split_once('/')
+ .find('/')
.map_or_else(
|| (Token::from_encoded_unchecked(&self.0[1..]), Self::root()),
- |(front, back)| {
- // We want to include the delimiter character too!
- // SAFETY: if split was successful, then the delimiter
- // character exists before the start of the second `str`.
- let back = unsafe { extend_one_before(back) };
+ |idx| {
+ let (front, back) = self.0[1..].split_at(idx);
(Token::from_encoded_unchecked(front), Self::new(back))
},
)
@@ -215,7 +203,6 @@ impl Pointer {
/// assert_eq!(tail, Pointer::from_static("/bar/baz"));
/// assert_eq!(ptr.split_at(3), None);
/// ```
- #[must_use]
pub fn split_at(&self, idx: usize) -> Option<(&Self, &Self)> {
if self.0.as_bytes().get(idx).copied() != Some(b'/') {
return None;
@@ -225,7 +212,6 @@ impl Pointer {
}
/// Splits the `Pointer` into the parent path and the last `Token`.
- #[must_use]
pub fn split_back(&self) -> Option<(&Self, Token)> {
self.0
.rsplit_once('/')
@@ -233,7 +219,6 @@ impl Pointer {
}
/// A pointer to the parent of the current path.
- #[must_use]
pub fn parent(&self) -> Option<&Self> {
self.0.rsplit_once('/').map(|(front, _)| Self::new(front))
}
@@ -258,7 +243,6 @@ impl Pointer {
/// let ptr = Pointer::root();
/// assert_eq!(ptr.get(0), None);
/// ```
- #[must_use]
pub fn get(&self, index: usize) -> Option {
self.tokens().nth(index).clone()
}
@@ -309,7 +293,6 @@ impl Pointer {
}
/// Finds the commonality between this and another `Pointer`.
- #[must_use]
pub fn intersection<'a>(&'a self, other: &Self) -> &'a Self {
if self.is_root() || other.is_root() {
return Self::root();
@@ -427,7 +410,7 @@ impl<'de: 'p, 'p> serde::Deserialize<'de> for &'p Pointer {
impl<'a> Visitor<'a> for PointerVisitor {
type Value = &'a Pointer;
- fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
formatter.write_str("a borrowed Pointer")
}
@@ -587,7 +570,6 @@ pub struct PointerBuf(String);
impl PointerBuf {
/// Creates a new `PointerBuf` pointing to a document root.
- #[must_use]
pub fn new() -> Self {
Self(String::new())
}
@@ -614,7 +596,6 @@ impl PointerBuf {
}
/// Coerces to a Pointer slice.
- #[must_use]
pub fn as_ptr(&self) -> &Pointer {
self
}
@@ -864,13 +845,6 @@ const fn validate(value: &str) -> Result<&str, ParseError> {
Ok(value)
}
-unsafe fn extend_one_before(s: &str) -> &str {
- let ptr = s.as_ptr().offset(-1);
- let len = s.len() + 1;
- let slice = slice::from_raw_parts(ptr, len);
- core::str::from_utf8_unchecked(slice)
-}
-
/*
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
╔══════════════════════════════════════════════════════════════════════════════╗
diff --git a/src/resolve.rs b/src/resolve.rs
index e6c402a..f11bbd9 100644
--- a/src/resolve.rs
+++ b/src/resolve.rs
@@ -156,7 +156,6 @@ pub enum ResolveError {
impl ResolveError {
/// Offset of the partial pointer starting with the token which caused the
/// error.
- #[must_use]
pub fn offset(&self) -> usize {
match self {
Self::FailedToParseIndex { offset, .. }
@@ -168,28 +167,24 @@ impl ResolveError {
/// Returns `true` if this error is `FailedToParseIndex`; otherwise returns
/// `false`.
- #[must_use]
pub fn is_unreachable(&self) -> bool {
matches!(self, Self::Unreachable { .. })
}
/// Returns `true` if this error is `FailedToParseIndex`; otherwise returns
/// `false`.
- #[must_use]
pub fn is_not_found(&self) -> bool {
matches!(self, Self::NotFound { .. })
}
/// Returns `true` if this error is `FailedToParseIndex`; otherwise returns
/// `false`.
- #[must_use]
pub fn is_out_of_bounds(&self) -> bool {
matches!(self, Self::OutOfBounds { .. })
}
/// Returns `true` if this error is `FailedToParseIndex`; otherwise returns
/// `false`.
- #[must_use]
pub fn is_failed_to_parse_index(&self) -> bool {
matches!(self, Self::FailedToParseIndex { .. })
}
diff --git a/src/token.rs b/src/token.rs
index 4b20862..bafa5b1 100644
--- a/src/token.rs
+++ b/src/token.rs
@@ -2,8 +2,8 @@ use crate::{index::Index, InvalidEncodingError, ParseIndexError};
use alloc::{
borrow::Cow,
string::{String, ToString},
+ vec::Vec,
};
-use serde::{Deserialize, Serialize};
const ENCODED_TILDE: &[u8] = b"~0";
const ENCODED_SLASH: &[u8] = b"~1";
@@ -143,7 +143,6 @@ impl<'a> Token<'a> {
///
/// If the token is not already owned, this will clone the referenced string
/// slice.
- #[must_use]
pub fn into_owned(self) -> Token<'static> {
Token {
inner: Cow::Owned(self.inner.into_owned()),
@@ -157,7 +156,6 @@ impl<'a> Token<'a> {
///
/// This method is like [`Self::into_owned`], except it doesn't take
/// ownership of the original `Token`.
- #[must_use]
pub fn to_owned(&self) -> Token<'static> {
Token {
inner: Cow::Owned(self.inner.clone().into_owned()),
@@ -172,7 +170,6 @@ impl<'a> Token<'a> {
/// # use jsonptr::Token;
/// assert_eq!(Token::new("~bar").encoded(), "~0bar");
/// ```
- #[must_use]
pub fn encoded(&self) -> &str {
&self.inner
}
@@ -185,7 +182,6 @@ impl<'a> Token<'a> {
/// # use jsonptr::Token;
/// assert_eq!(Token::new("~bar").decoded(), "~bar");
/// ```
- #[must_use]
pub fn decoded(&self) -> Cow<'_, str> {
if let Some(i) = self.inner.bytes().position(|b| b == ENC_PREFIX) {
let input = self.inner.as_bytes();
@@ -250,7 +246,8 @@ impl<'a> Token<'a> {
}
}
-impl Serialize for Token<'_> {
+#[cfg(feature = "serde")]
+impl serde::Serialize for Token<'_> {
fn serialize(&self, serializer: S) -> Result
where
S: serde::Serializer,
@@ -259,7 +256,8 @@ impl Serialize for Token<'_> {
}
}
-impl<'de> Deserialize<'de> for Token<'de> {
+#[cfg(feature = "serde")]
+impl<'de> serde::Deserialize<'de> for Token<'de> {
fn deserialize(deserializer: D) -> Result
where
D: serde::Deserializer<'de>,
@@ -311,8 +309,8 @@ impl<'a> From<&Token<'a>> for Token<'a> {
}
}
-impl core::fmt::Display for Token<'_> {
- fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+impl alloc::fmt::Display for Token<'_> {
+ fn fmt(&self, f: &mut alloc::fmt::Formatter<'_>) -> alloc::fmt::Result {
write!(f, "{}", self.decoded())
}
}