z-base-32-0.1.4/.cargo_vcs_info.json0000644000000001360000000000100124410ustar { "git": { "sha1": "08ca2b48392ebd4503cab389907223617921b472" }, "path_in_vcs": "" }z-base-32-0.1.4/.github/renovate.json000064400000000000000000000002001046102023000152770ustar 00000000000000{ "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ "config:base" ], "automerge": true } z-base-32-0.1.4/.github/workflows/build.yml000064400000000000000000000061301046102023000164500ustar 00000000000000# This file is autogenerated by maturin v1.4.0 # To update, run # # maturin generate-ci github # name: CI on: push: branches: - main - master tags: - "*" pull_request: workflow_dispatch: permissions: contents: read jobs: linux: runs-on: ubuntu-latest strategy: matrix: target: [x86_64, x86, aarch64, armv7, s390x, ppc64le] steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: python-version: "3.10" - name: Build wheels uses: PyO3/maturin-action@v1 with: target: ${{ matrix.target }} args: --release --out dist --find-interpreter sccache: "true" manylinux: auto - name: Upload wheels uses: actions/upload-artifact@v3 with: name: wheels path: dist windows: runs-on: windows-latest strategy: matrix: target: [x64, x86] steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: python-version: "3.10" architecture: ${{ matrix.target }} - name: Build wheels uses: PyO3/maturin-action@v1 with: target: ${{ matrix.target }} args: --release --out dist --find-interpreter sccache: "true" - name: Upload wheels uses: actions/upload-artifact@v3 with: name: wheels path: dist macos: runs-on: macos-latest strategy: matrix: target: [x86_64, aarch64] steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: python-version: "3.10" - name: Build wheels uses: PyO3/maturin-action@v1 with: target: ${{ matrix.target }} args: --release --out dist --find-interpreter sccache: "true" - name: Upload wheels uses: actions/upload-artifact@v3 with: name: wheels path: dist sdist: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Build sdist uses: PyO3/maturin-action@v1 with: command: sdist args: --out dist - name: Upload sdist uses: actions/upload-artifact@v3 with: name: wheels path: dist release: name: Release runs-on: ubuntu-latest environment: release permissions: # IMPORTANT: this permission is mandatory for trusted publishing id-token: write if: "startsWith(github.ref, 'refs/tags/')" needs: [linux, windows, macos, sdist] steps: - uses: actions/download-artifact@v3 with: name: wheels - name: Publish to PyPI uses: PyO3/maturin-action@v1 with: command: upload args: --non-interactive --skip-existing * - uses: actions/checkout@v4 - name: Install Rust uses: dtolnay/rust-toolchain@master with: toolchain: stable - name: Publish on crates.io run: | cargo login ${{ secrets.CRATES_TOKEN }} cargo publish z-base-32-0.1.4/.github/workflows/ci.yml000064400000000000000000000020461046102023000157460ustar 00000000000000on: [push, pull_request, workflow_dispatch] jobs: test-rust: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Install Rust uses: dtolnay/rust-toolchain@master with: toolchain: stable components: clippy, rustfmt - name: Lint Rust implementation run: cargo clippy - name: Check Rust format run: cargo fmt --check - name: Test Rust implementation run: cargo test --all-features test-python: runs-on: ubuntu-latest strategy: matrix: python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"] steps: - uses: actions/checkout@v4 - name: Install Rust uses: dtolnay/rust-toolchain@master with: toolchain: stable - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - run: pip install maturin - run: pip install . - name: Test Python implementation run: python -m unittest test.py z-base-32-0.1.4/.github/workflows/release.yml000064400000000000000000000254301046102023000167750ustar 00000000000000# Copyright 2022-2023, axodotdev # SPDX-License-Identifier: MIT or Apache-2.0 # # CI that: # # * checks for a Git Tag that looks like a release # * builds artifacts with cargo-dist (archives, installers, hashes) # * uploads those artifacts to temporary workflow zip # * on success, uploads the artifacts to a Github Release # # Note that the Github Release will be created with a generated # title/body based on your changelogs. name: Release permissions: contents: write # This task will run whenever you push a git tag that looks like a version # like "1.0.0", "v0.1.0-prerelease.1", "my-app/0.1.0", "releases/v1.0.0", etc. # Various formats will be parsed into a VERSION and an optional PACKAGE_NAME, where # PACKAGE_NAME must be the name of a Cargo package in your workspace, and VERSION # must be a Cargo-style SemVer Version (must have at least major.minor.patch). # # If PACKAGE_NAME is specified, then the announcement will be for that # package (erroring out if it doesn't have the given version or isn't cargo-dist-able). # # If PACKAGE_NAME isn't specified, then the announcement will be for all # (cargo-dist-able) packages in the workspace with that version (this mode is # intended for workspaces with only one dist-able package, or with all dist-able # packages versioned/released in lockstep). # # If you push multiple tags at once, separate instances of this workflow will # spin up, creating an independent announcement for each one. However Github # will hard limit this to 3 tags per commit, as it will assume more tags is a # mistake. # # If there's a prerelease-style suffix to the version, then the release(s) # will be marked as a prerelease. on: push: tags: - '**[0-9]+.[0-9]+.[0-9]+*' pull_request: jobs: # Run 'cargo dist plan' (or host) to determine what tasks we need to do plan: runs-on: ubuntu-latest outputs: val: ${{ steps.plan.outputs.manifest }} tag: ${{ !github.event.pull_request && github.ref_name || '' }} tag-flag: ${{ !github.event.pull_request && format('--tag={0}', github.ref_name) || '' }} publishing: ${{ !github.event.pull_request }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v4 with: submodules: recursive - name: Install cargo-dist # we specify bash to get pipefail; it guards against the `curl` command # failing. otherwise `sh` won't catch that `curl` returned non-0 shell: bash run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.8.2/cargo-dist-installer.sh | sh" # sure would be cool if github gave us proper conditionals... # so here's a doubly-nested ternary-via-truthiness to try to provide the best possible # functionality based on whether this is a pull_request, and whether it's from a fork. # (PRs run on the *source* but secrets are usually on the *target* -- that's *good* # but also really annoying to build CI around when it needs secrets to work right.) - id: plan run: | cargo dist ${{ (!github.event.pull_request && format('host --steps=create --tag={0}', github.ref_name)) || 'plan' }} --output-format=json > dist-manifest.json echo "cargo dist ran successfully" cat dist-manifest.json echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT" - name: "Upload dist-manifest.json" uses: actions/upload-artifact@v3 with: name: artifacts path: dist-manifest.json # Build and packages all the platform-specific things build-local-artifacts: name: build-local-artifacts (${{ join(matrix.targets, ', ') }}) # Let the initial task tell us to not run (currently very blunt) needs: - plan if: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix.include != null && (needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload') }} strategy: fail-fast: false # Target platforms/runners are computed by cargo-dist in create-release. # Each member of the matrix has the following arguments: # # - runner: the github runner # - dist-args: cli flags to pass to cargo dist # - install-dist: expression to run to install cargo-dist on the runner # # Typically there will be: # - 1 "global" task that builds universal installers # - N "local" tasks that build each platform's binaries and platform-specific installers matrix: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix }} runs-on: ${{ matrix.runner }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} BUILD_MANIFEST_NAME: target/distrib/${{ join(matrix.targets, '-') }}-dist-manifest.json steps: - uses: actions/checkout@v4 with: submodules: recursive - uses: swatinem/rust-cache@v2 - name: Install cargo-dist run: ${{ matrix.install_dist }} # Get the dist-manifest - name: Fetch local artifacts uses: actions/download-artifact@v3 with: name: artifacts path: target/distrib/ - name: Install dependencies run: | ${{ matrix.packages_install }} - name: Build artifacts run: | # Actually do builds and make zips and whatnot cargo dist build ${{ needs.plan.outputs.tag-flag }} --print=linkage --output-format=json ${{ matrix.dist_args }} > dist-manifest.json echo "cargo dist ran successfully" - id: cargo-dist name: Post-build # We force bash here just because github makes it really hard to get values up # to "real" actions without writing to env-vars, and writing to env-vars has # inconsistent syntax between shell and powershell. shell: bash run: | # Parse out what we just built and upload it to scratch storage echo "paths<> "$GITHUB_OUTPUT" jq --raw-output ".artifacts[]?.path | select( . != null )" dist-manifest.json >> "$GITHUB_OUTPUT" echo "EOF" >> "$GITHUB_OUTPUT" cp dist-manifest.json "$BUILD_MANIFEST_NAME" - name: "Upload artifacts" uses: actions/upload-artifact@v3 with: name: artifacts path: | ${{ steps.cargo-dist.outputs.paths }} ${{ env.BUILD_MANIFEST_NAME }} # Build and package all the platform-agnostic(ish) things build-global-artifacts: needs: - plan - build-local-artifacts runs-on: "ubuntu-20.04" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json steps: - uses: actions/checkout@v4 with: submodules: recursive - name: Install cargo-dist run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.8.2/cargo-dist-installer.sh | sh" # Get all the local artifacts for the global tasks to use (for e.g. checksums) - name: Fetch local artifacts uses: actions/download-artifact@v3 with: name: artifacts path: target/distrib/ - id: cargo-dist shell: bash run: | cargo dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json "--artifacts=global" > dist-manifest.json echo "cargo dist ran successfully" # Parse out what we just built and upload it to scratch storage echo "paths<> "$GITHUB_OUTPUT" jq --raw-output ".artifacts[]?.path | select( . != null )" dist-manifest.json >> "$GITHUB_OUTPUT" echo "EOF" >> "$GITHUB_OUTPUT" cp dist-manifest.json "$BUILD_MANIFEST_NAME" - name: "Upload artifacts" uses: actions/upload-artifact@v3 with: name: artifacts path: | ${{ steps.cargo-dist.outputs.paths }} ${{ env.BUILD_MANIFEST_NAME }} # Determines if we should publish/announce host: needs: - plan - build-local-artifacts - build-global-artifacts # Only run if we're "publishing", and only if local and global didn't fail (skipped is fine) if: ${{ always() && needs.plan.outputs.publishing == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.build-local-artifacts.result == 'skipped' || needs.build-local-artifacts.result == 'success') }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} runs-on: "ubuntu-20.04" outputs: val: ${{ steps.host.outputs.manifest }} steps: - uses: actions/checkout@v4 with: submodules: recursive - name: Install cargo-dist run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.8.2/cargo-dist-installer.sh | sh" # Fetch artifacts from scratch-storage - name: Fetch artifacts uses: actions/download-artifact@v3 with: name: artifacts path: target/distrib/ # This is a harmless no-op for Github Releases, hosting for that happens in "announce" - id: host shell: bash run: | cargo dist host ${{ needs.plan.outputs.tag-flag }} --steps=upload --steps=release --output-format=json > dist-manifest.json echo "artifacts uploaded and released successfully" cat dist-manifest.json echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT" - name: "Upload dist-manifest.json" uses: actions/upload-artifact@v3 with: name: artifacts path: dist-manifest.json # Create a Github Release while uploading all files to it announce: needs: - plan - host # use "always() && ..." to allow us to wait for all publish jobs while # still allowing individual publish jobs to skip themselves (for prereleases). # "host" however must run to completion, no skipping allowed! if: ${{ always() && needs.host.result == 'success' }} runs-on: "ubuntu-20.04" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v4 with: submodules: recursive - name: "Download Github Artifacts" uses: actions/download-artifact@v3 with: name: artifacts path: artifacts - name: Cleanup run: | # Remove the granular manifests rm -f artifacts/*-dist-manifest.json - name: Create Github Release uses: ncipollo/release-action@v1 with: tag: ${{ needs.plan.outputs.tag }} name: ${{ fromJson(needs.host.outputs.val).announcement_title }} body: ${{ fromJson(needs.host.outputs.val).announcement_github_body }} prerelease: ${{ fromJson(needs.host.outputs.val).announcement_is_prerelease }} artifacts: "artifacts/*" z-base-32-0.1.4/.gitignore000064400000000000000000000000101046102023000132100ustar 00000000000000/target z-base-32-0.1.4/CHANGELOG.md000064400000000000000000000014041046102023000130410ustar 00000000000000# Changelog ## Unreleased ## [0.1.4] - 2024-02-27 ### Added - Enable trusted publishing for PyPI - Add CLI utillity - Implement Error trait for `DecodeError` ### Changed - bump pyo3 and maturin versions ## [0.1.3] - 2023-11-21 ### Changed - bump pyo3 and maturin versions ## [0.1.2] - 2021-08-25 ### Changed - fix maturin version in `pyproject.toml` ## [0.1.1] - 2021-08-25 ### Added - `pyproject.toml` for building from source ## 0.1.0 - 2021-08-19 ### Added - initial release [0.1.4]: [0.1.3]: [0.1.2]: [0.1.1]: z-base-32-0.1.4/Cargo.lock0000644000000336020000000000100104200ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "aho-corasick" version = "0.7.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" dependencies = [ "memchr", ] [[package]] name = "anstream" version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96b09b5178381e0874812a9b157f7fe84982617e48f71f4e3235482775e5b540" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", "utf8parse", ] [[package]] name = "anstyle" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" [[package]] name = "anstyle-parse" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" dependencies = [ "windows-sys", ] [[package]] name = "anstyle-wincon" version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" dependencies = [ "anstyle", "windows-sys", ] [[package]] name = "anyhow" version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da" dependencies = [ "clap_builder", "clap_derive", ] [[package]] name = "clap_builder" version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb" dependencies = [ "anstream", "anstyle", "clap_lex", "strsim", ] [[package]] name = "clap_derive" version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" dependencies = [ "heck", "proc-macro2", "quote", "syn", ] [[package]] name = "clap_lex" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "colorchoice" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "env_logger" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" dependencies = [ "log", "regex", ] [[package]] name = "getrandom" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "heck" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "indoc" version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8" [[package]] name = "instant" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if", ] [[package]] name = "libc" version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "lock_api" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" dependencies = [ "autocfg", "scopeguard", ] [[package]] name = "log" version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if", ] [[package]] name = "memchr" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memoffset" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" dependencies = [ "autocfg", ] [[package]] name = "once_cell" version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "parking_lot" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" dependencies = [ "instant", "lock_api", "parking_lot_core", ] [[package]] name = "parking_lot_core" version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" dependencies = [ "cfg-if", "instant", "libc", "redox_syscall", "smallvec", "winapi", ] [[package]] name = "proc-macro2" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] [[package]] name = "pyo3" version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a89dc7a5850d0e983be1ec2a463a171d20990487c3cfcd68b5363f1ee3d6fe0" dependencies = [ "cfg-if", "indoc", "libc", "memoffset", "parking_lot", "pyo3-build-config", "pyo3-ffi", "pyo3-macros", "unindent", ] [[package]] name = "pyo3-build-config" version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07426f0d8fe5a601f26293f300afd1a7b1ed5e78b2a705870c5f30893c5163be" dependencies = [ "once_cell", "target-lexicon", ] [[package]] name = "pyo3-ffi" version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbb7dec17e17766b46bca4f1a4215a85006b4c2ecde122076c562dd058da6cf1" dependencies = [ "libc", "pyo3-build-config", ] [[package]] name = "pyo3-macros" version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f738b4e40d50b5711957f142878cfa0f28e054aa0ebdfc3fd137a843f74ed3" dependencies = [ "proc-macro2", "pyo3-macros-backend", "quote", "syn", ] [[package]] name = "pyo3-macros-backend" version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fc910d4851847827daf9d6cdd4a823fbdaab5b8818325c5e97a86da79e8881f" dependencies = [ "heck", "proc-macro2", "quote", "syn", ] [[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 = "quote" version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] [[package]] name = "rand" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "rand_core", ] [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] [[package]] name = "redox_syscall" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ "bitflags", ] [[package]] name = "regex" version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" [[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "smallvec" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "strsim" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" [[package]] name = "syn" version = "2.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "target-lexicon" version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ae9980cab1db3fceee2f6c6f643d5d8de2997c58ee8d25fb0cc8a9e9e7348e5" [[package]] name = "unicode-ident" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" [[package]] name = "unindent" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce" [[package]] name = "utf8parse" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d380ba1dc7187569a8a9e91ed34b8ccfc33123bbacb8c0aed2d1ad7f3ef2dc5f" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68e5dcfb9413f53afd9c8f86e56a7b4d86d9a2fa26090ea2dc9e40fba56c6ec6" [[package]] name = "windows_aarch64_msvc" version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8dab469ebbc45798319e69eebf92308e541ce46760b49b18c6b3fe5e8965b30f" [[package]] name = "windows_i686_gnu" version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a4e9b6a7cac734a8b4138a4e1044eac3404d8326b6c0f939276560687a033fb" [[package]] name = "windows_i686_msvc" version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28b0ec9c422ca95ff34a78755cfa6ad4a51371da2a5ace67500cf7ca5f232c58" [[package]] name = "windows_x86_64_gnu" version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "704131571ba93e89d7cd43482277d6632589b18ecf4468f591fbae0a8b101614" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42079295511643151e98d61c38c0acc444e52dd42ab456f7ccfd5152e8ecf21c" [[package]] name = "windows_x86_64_msvc" version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6" [[package]] name = "z-base-32" version = "0.1.4" dependencies = [ "anyhow", "clap", "pyo3", "quickcheck", ] z-base-32-0.1.4/Cargo.toml0000644000000025120000000000100104370ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "z-base-32" version = "0.1.4" authors = ["Matus Ferech "] description = "z-base-32: human-oriented base-32 encoding" readme = "README.md" keywords = [ "zbase32", "base32", "encode", "decode", "python", ] categories = ["encoding"] license = "MIT" repository = "https://github.com/matusf/z-base-32" [profile.dist] lto = "thin" inherits = "release" [lib] name = "zbase32" crate-type = [ "cdylib", "rlib", ] [[bin]] name = "zbase32" required-features = ["cli"] [dependencies.anyhow] version = "1" optional = true [dependencies.clap] version = "4.5.1" features = ["derive"] optional = true [dependencies.pyo3] version = "0.20.2" features = ["extension-module"] optional = true [dev-dependencies.quickcheck] version = "1" [features] cli = [ "dep:clap", "dep:anyhow", ] python = ["dep:pyo3"] z-base-32-0.1.4/Cargo.toml.orig000064400000000000000000000027301046102023000141220ustar 00000000000000[package] name = "z-base-32" version = "0.1.4" edition = "2018" authors = ["Matus Ferech "] license = "MIT" repository = "https://github.com/matusf/z-base-32" readme = "README.md" description = "z-base-32: human-oriented base-32 encoding" keywords = ["zbase32", "base32", "encode", "decode", "python"] categories = ["encoding"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] name = "zbase32" crate-type = ["cdylib", "rlib"] [[bin]] name = "zbase32" required-features = ["cli"] [features] python = ["dep:pyo3"] cli = ["dep:clap", "dep:anyhow"] [dependencies] pyo3 = {version = "0.20.2", features = ["extension-module"], optional = true} clap = {version = "4.5.1", features = ["derive"], optional = true} anyhow = {version = "1", optional = true} [dev-dependencies] quickcheck = "1" # The profile that 'cargo dist' will build with [profile.dist] inherits = "release" lto = "thin" # Config for 'cargo dist' [workspace.metadata.dist] # The preferred cargo-dist version to use in CI (Cargo.toml SemVer syntax) cargo-dist-version = "0.8.2" # CI backends to support ci = ["github"] # The installers to generate for each app installers = [] # Target platforms to build apps for (Rust target-triple syntax) targets = ["aarch64-apple-darwin", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl", "x86_64-pc-windows-msvc"] # Publish jobs to run in CI pr-run-mode = "plan" features = ["cli"] z-base-32-0.1.4/LICENSE000064400000000000000000000020571046102023000122420ustar 00000000000000MIT License Copyright (c) 2021 Matúš Ferech Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. z-base-32-0.1.4/README.md000064400000000000000000000042351046102023000125140ustar 00000000000000# z-base-32 ![ci](https://github.com/matusf/z-base-32/actions/workflows/ci.yml/badge.svg) The `z-base-32` is a human-oriented base-32 encoding. ## Rust ### Crate #### Installation ```sh cargo add z-base-32 ``` #### API The library exposes two functions with the following signatures and an error type: ```rs pub fn encode(input: &[u8]) -> String; pub fn decode(input: &str) -> Result, DecodeError>; pub struct DecodeError; ``` #### Example ```rs use zbase32::{encode, decode}; fn main() { assert_eq!(encode(b"foo"), "c3zs6".to_string()); assert_eq!(Ok(b"foo"), decode("c3zs6".to_string())); assert_eq!(decode(&encode(b"foo")).unwrap(), b"foo") } ``` ### CLI This project also provides a CLI utility with a similar interface to the well-known `base64` command. #### Installation To install `z-base-32` CLI you can build it from source or download prebuild binary from [releases](https://github.com/matusf/z-base-32/releases/latest). ```console cargo install --features cli z-base-32 ``` #### Example ```console $ zbase32 -h z-base-32: human-oriented base-32 encoding Usage: zbase32 [OPTIONS] [FILE] Arguments: [FILE] File to encode or decode Options: -d, --decode Decode data -w, --wrap Wrap encoded lines after COLS character [default: 76] -h, --help Print help -V, --version Print version ``` ## Python ### Installation The `z-base-32` package is published at [PyPI](https://pypi.org/project/z-base-32/). Install it using the following command: ```console pip install z-base-32 ``` ### Building This crate can be compiled with the feature flag `python` in which case it produces Python bindings. To build Python wheels use [`maturin`](https://github.com/PyO3/maturin): ```console maturin build ``` ### API ```py def encode(input: bytes) -> str: def decode(input: str) -> bytes: class DecodeError(Exception): ``` ### Example ```py import zbase32 assert zbase32.encode(b'foo') == 'c3zs6' assert zbase32.decode('c3zs6') == b'foo' try: zbase32.decode('invalid@char') except zbase32.DecodeError as e: print(e) ``` ## References - z-base-32-0.1.4/pyproject.toml000064400000000000000000000005721046102023000141510ustar 00000000000000[build-system] requires = ["maturin>=1.2,<2.0"] build-backend = "maturin" [tool.maturin] features = ["python", "pyo3/extension-module"] [project] name = "z-base-32" requires-python = ">=3.7" classifiers = [ "Programming Language :: Rust", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", ] z-base-32-0.1.4/src/bin/zbase32.rs000064400000000000000000000045471046102023000144210ustar 00000000000000use std::{ fs::File, io::{self, BufRead, Read, Write}, path::PathBuf, }; use anyhow::Result; use clap::Parser; #[derive(Parser, Debug)] #[command(version, about, long_about = None)] struct Args { /// Decode data #[arg(short, long)] decode: bool, /// File to encode or decode file: Option, /// Wrap encoded lines after WRAP character. Use 0 to disable line wrapping #[arg(short, long, default_value_t = 76)] wrap: usize, } fn decode(reader: impl Read, writer: &mut impl Write) -> Result<()> { let buf: Result, std::io::Error> = io::BufReader::new(reader).lines().collect(); let buf: String = buf? .into_iter() .map(|mut line| { line.truncate(line.trim_end().len()); line }) .collect(); writer.write_all(&zbase32::decode(&buf)?)?; Ok(()) } fn encode(reader: &mut impl Read, mut writer: &mut impl Write, wrap: usize) -> Result<()> { let mut buf = Vec::new(); reader.read_to_end(&mut buf)?; let buf = zbase32::encode(&buf); if wrap > 0 { buf.as_bytes() .chunks(wrap) .map(|chunk| { let chars = unsafe { std::str::from_utf8_unchecked(chunk) }; write!(&mut writer, "{}\n", chars) }) .collect::, _>>()?; } else { writer.write_all(buf.as_bytes())?; writer.write(b"\n")?; } return Ok(()); } fn main() -> Result<()> { let args = Args::parse(); if args.decode { if let Some(file) = args.file { decode(File::open(&file)?, &mut io::stdout())?; } else { decode(io::stdin().lock(), &mut io::stdout())?; }; } else { if let Some(file) = args.file { encode(&mut File::open(&file)?, &mut io::stdout(), args.wrap) } else { encode(&mut io::stdin().lock(), &mut io::stdout(), args.wrap) }?; }; Ok(()) } #[cfg(test)] mod tests { use super::*; use quickcheck::quickcheck; quickcheck! { fn prop(input: Vec, wrap: usize) -> bool { let mut encoded = Vec::new(); let mut decoded = Vec::new(); encode(&mut input.as_slice(), &mut encoded, wrap).unwrap(); decode(encoded.as_slice(), &mut decoded).unwrap(); decoded == input } } } z-base-32-0.1.4/src/lib.rs000064400000000000000000000073021046102023000131360ustar 00000000000000#[cfg(feature = "python")] mod python; #[cfg(test)] #[macro_use] extern crate quickcheck; use std::{error::Error, fmt}; const ALPHABET: &[u8] = b"ybndrfg8ejkmcpqxot1uwisza345h769"; const INVERSE_ALPHABET: [i8; 123] = [ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 18, -1, 25, 26, 27, 30, 29, 7, 31, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 24, 1, 12, 3, 8, 5, 6, 28, 21, 9, 10, -1, 11, 2, 16, 13, 14, 4, 22, 17, 19, -1, 20, 15, 0, 23, ]; #[derive(Debug, PartialEq)] pub struct DecodeError; impl Error for DecodeError {} impl fmt::Display for DecodeError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "DecodeError: Non-zbase32 digit found") } } pub fn encode(input: &[u8]) -> String { let mut result = Vec::new(); let chunks = input.chunks(5); for chunk in chunks { let buf = { let mut buf = [0u8; 5]; for (i, &b) in chunk.iter().enumerate() { buf[i] = b; } buf }; result.push(ALPHABET[((buf[0] & 0xF8) >> 3) as usize]); result.push(ALPHABET[((buf[0] & 0x07) << 2 | (buf[1] & 0xC0) >> 6) as usize]); result.push(ALPHABET[((buf[1] & 0x3E) >> 1) as usize]); result.push(ALPHABET[((buf[1] & 0x01) << 4 | (buf[2] & 0xF0) >> 4) as usize]); result.push(ALPHABET[((buf[2] & 0x0F) << 1 | (buf[3] & 0x80) >> 7) as usize]); result.push(ALPHABET[((buf[3] & 0x7C) >> 2) as usize]); result.push(ALPHABET[((buf[3] & 0x03) << 3 | (buf[4] & 0xE0) >> 5) as usize]); result.push(ALPHABET[(buf[4] & 0x1F) as usize]); } let expected_len = (input.len() as f32 * 8.0 / 5.0).ceil() as usize; for _ in 0..(result.len() - expected_len) { result.pop(); } unsafe { String::from_utf8_unchecked(result) } } pub fn decode(input: &str) -> Result, DecodeError> { let mut result = Vec::new(); for chunk in input.as_bytes().chunks(8) { let buf = { let mut buf = [0u8; 8]; for (i, &ch) in chunk.iter().enumerate() { match INVERSE_ALPHABET.get(ch as usize) { Some(-1) => return Err(DecodeError), Some(x) => buf[i] = *x as u8, None => return Err(DecodeError), }; } buf }; result.push((buf[0] << 3) | (buf[1] >> 2)); result.push((buf[1] << 6) | (buf[2] << 1) | (buf[3] >> 4)); result.push((buf[3] << 4) | (buf[4] >> 1)); result.push((buf[4] << 7) | (buf[5] << 2) | (buf[6] >> 3)); result.push((buf[6] << 5) | buf[7]); } for _ in 0..(result.len() - input.len() * 5 / 8) { result.pop(); } Ok(result) } #[cfg(test)] mod tests { use super::*; #[test] fn simple_encode() { assert_eq!(encode(b"asdasd"), "cf3seamuco".to_string()); } #[test] fn simple_decode() { assert_eq!(decode("cf3seamu"), Ok(b"asdas".to_vec())) } #[test] fn encode_decode() { assert_eq!(decode(&encode(b"foo")).unwrap(), b"foo") } #[test] fn invalid_decode() { assert_eq!(decode("bar#"), Err(DecodeError)) } quickcheck! { fn prop(input: Vec) -> bool { decode(&encode(&input)).unwrap() == input } } quickcheck! { #[allow(unused_must_use)] fn not_panic(input: String) -> bool { decode(&input); true } } } z-base-32-0.1.4/src/python.rs000064400000000000000000000016331046102023000137120ustar 00000000000000use pyo3::{create_exception, exceptions::PyException, prelude::*, types::PyBytes}; create_exception!(zbase32, DecodeError, PyException); #[inline] #[pyfunction] #[pyo3(text_signature = "(input: str) -> bytes")] /// Decode zbase32 encoded string to bytes fn decode<'a>(py: Python<'a>, input: &'a str) -> PyResult<&'a PyBytes> { match crate::decode(input) { Ok(b) => Ok(PyBytes::new(py, &b)), Err(_) => Err(DecodeError::new_err("Non-zbase32 digit found")), } } #[inline] #[pyfunction] #[pyo3(text_signature = "(input: bytes) -> str")] /// Encode bytes using a zbase32 and return encoded string fn encode(input: &[u8]) -> String { crate::encode(input) } #[pymodule] fn zbase32(py: Python, m: &PyModule) -> PyResult<()> { m.add("DecodeError", py.get_type::())?; m.add_function(wrap_pyfunction!(decode, m)?)?; m.add_function(wrap_pyfunction!(encode, m)?)?; Ok(()) } z-base-32-0.1.4/test.py000064400000000000000000000007241046102023000125650ustar 00000000000000from unittest import main, TestCase import zbase32 class Test(TestCase): def test_encode_decode(self): self.assertEqual(b'foo', zbase32.decode((zbase32.encode(b'foo')))) self.assertEqual('c3zs6', zbase32.encode(b'foo')) self.assertEqual(b'foo', zbase32.decode('c3zs6')) def test_exception(self): with self.assertRaises(zbase32.DecodeError): zbase32.decode('invalid@char') if __name__ == '__main__': main()