From b6157906f99a99b6bab3bda470dbfe78a1c5b5bb Mon Sep 17 00:00:00 2001 From: bradschwartz Date: Tue, 30 Apr 2024 16:16:56 -0700 Subject: [PATCH 01/19] Initial commit with ex code, rust code, CI actions, unit tests, license and all --- .formatter.exs | 4 + .github/CODEOWNERS | 1 + .github/ISSUE_TEMPLATE/bug_report.md | 23 + .github/dependabot.yml | 12 + .github/workflows/ci.yml | 59 + .github/workflows/release.yml | 89 ++ .gitignore | 31 + .iex.exs | 39 + .tool-versions | 2 + CHANGELOG.md | 16 + CONTRIBUTING.md | 36 + LICENSE.md | 8 + README.md | 32 + lib/json_schema_nif.ex | 46 + mix.exs | 58 + mix.lock | 17 + native/json_schema_nif/.cargo/config.toml | 15 + native/json_schema_nif/.gitignore | 7 + native/json_schema_nif/Cargo.lock | 1415 +++++++++++++++++++++ native/json_schema_nif/Cargo.toml | 16 + native/json_schema_nif/src/lib.rs | 61 + renovate.json | 4 + test/json_schema_podium_test.exs | 32 + test/test_helper.exs | 1 + 24 files changed, 2024 insertions(+) create mode 100644 .formatter.exs create mode 100644 .github/CODEOWNERS create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/release.yml create mode 100644 .gitignore create mode 100644 .iex.exs create mode 100644 .tool-versions create mode 100644 CHANGELOG.md create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE.md create mode 100644 lib/json_schema_nif.ex create mode 100644 mix.exs create mode 100644 mix.lock create mode 100644 native/json_schema_nif/.cargo/config.toml create mode 100644 native/json_schema_nif/.gitignore create mode 100644 native/json_schema_nif/Cargo.lock create mode 100644 native/json_schema_nif/Cargo.toml create mode 100644 native/json_schema_nif/src/lib.rs create mode 100644 renovate.json create mode 100644 test/json_schema_podium_test.exs create mode 100644 test/test_helper.exs diff --git a/.formatter.exs b/.formatter.exs new file mode 100644 index 0000000..d2cda26 --- /dev/null +++ b/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..6164b34 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @podium/oss-engineers diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..5cd7c03 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,23 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug +assignees: epinault + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +** Provide the following details + +- Elixir version (elixir -v): +- Erlang version (erl -v): +- Operating system: + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Actual behavior** +A clear and concise description of what actually happens. diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..a2407b8 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +version: 2 +updates: + # Maintain dependencies for GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + + - package-ecosystem: "mix" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..de2e667 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,59 @@ +name: CI + +on: + pull_request: + branches: + - master + push: + branches: + - master + +jobs: + setup: + runs-on: ${{ matrix.os }} + env: + MIX_ENV: test + + strategy: + fail-fast: false + matrix: + os: [ubuntu-22.04, ubuntu-20.04] + elixir_version: [1.13, 1.14, 1.15] + otp_version: [24, 25, 26] + exclude: + - otp_version: 26 + elixir_version: 1.14 + - otp_version: 26 + elixir_version: 1.13 + - otp_version: 25 + elixir_version: 1.13 + steps: + - uses: actions/checkout@v4 + + - uses: erlef/setup-beam@v1 + with: + otp-version: ${{matrix.otp_version}} + elixir-version: ${{matrix.elixir_version}} + + - uses: actions/cache@v4 + with: + path: | + deps + _build + key: deps-${{ runner.os }}-${{ matrix.otp_version }}-${{ matrix.elixir_version }}-${{ hashFiles('**/mix.lock') }} + restore-keys: | + deps-${{ runner.os }}-${{ matrix.otp_version }}-${{ matrix.elixir_version }} + + - run: mix deps.get + + - run: mix format --check-formatted + + - run: mix deps.unlock --check-unused + + - run: mix deps.compile + + - run: mix compile --warnings-as-errors + + - run: mix credo --strict --format=oneline + + - run: mix test --warnings-as-errors --cover diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..dedcc51 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,89 @@ +name: Build precompiled NIFs +# lifted from: https://github.com/philss/rustler_precompilation_example/blob/main/.github/workflows/release.yml + +on: + push: + branches: + - main + tags: + - "*" + +jobs: + build_release: + name: NIF ${{ matrix.nif }} - ${{ matrix.job.target }} (${{ matrix.job.os }}) + runs-on: ${{ matrix.job.os }} + strategy: + fail-fast: false + matrix: + nif: ["2.16", "2.15"] + job: + - { + target: arm-unknown-linux-gnueabihf, + os: ubuntu-20.04, + use-cross: true, + } + - { + target: aarch64-unknown-linux-gnu, + os: ubuntu-20.04, + use-cross: true, + } + - { + target: aarch64-unknown-linux-musl, + os: ubuntu-20.04, + use-cross: true, + } + - { target: aarch64-apple-darwin, os: macos-11 } + - { + target: riscv64gc-unknown-linux-gnu, + os: ubuntu-20.04, + use-cross: true, + } + - { target: x86_64-apple-darwin, os: macos-11 } + - { target: x86_64-unknown-linux-gnu, os: ubuntu-20.04 } + - { + target: x86_64-unknown-linux-musl, + os: ubuntu-20.04, + use-cross: true, + } + - { target: x86_64-pc-windows-gnu, os: windows-2019 } + - { target: x86_64-pc-windows-msvc, os: windows-2019 } + + steps: + - name: Checkout source code + uses: actions/checkout@v3 + + - name: Extract project version + shell: bash + run: | + # Get the project version from mix.exs + echo "PROJECT_VERSION=$(sed -n 's/^ @version "\(.*\)"/\1/p' mix.exs | head -n1)" >> $GITHUB_ENV + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + toolchain: stable + target: ${{ matrix.job.target }} + + - name: Build the project + id: build-crate + uses: philss/rustler-precompiled-action@v1.0.1 + with: + project-name: example + project-version: ${{ env.PROJECT_VERSION }} + target: ${{ matrix.job.target }} + nif-version: ${{ matrix.nif }} + use-cross: ${{ matrix.job.use-cross }} + project-dir: "native/example" + + - name: Artifact upload + uses: actions/upload-artifact@v3 + with: + name: ${{ steps.build-crate.outputs.file-name }} + path: ${{ steps.build-crate.outputs.file-path }} + + - name: Publish archives and packages + uses: softprops/action-gh-release@v1 + with: + files: | + ${{ steps.build-crate.outputs.file-path }} + if: startsWith(github.ref, 'refs/tags/') diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..60e02dd --- /dev/null +++ b/.gitignore @@ -0,0 +1,31 @@ +# The directory Mix will write compiled artifacts to. +/_build/ + +# If you run "mix test --cover", coverage assets end up here. +/cover/ + +# The directory Mix downloads your dependencies sources to. +/deps/ + +# Where third-party dependencies like ExDoc output generated docs. +/doc/ + +# Ignore .fetch files in case you like to edit your project deps locally. +/.fetch + +# If the VM crashes, it generates a dump, let's ignore it too. +erl_crash.dump + +# Also ignore archive artifacts (built via "mix archive.build"). +*.ez + +# Ignore package tarball (built via "mix hex.build"). +json_schema_nif-*.tar + +# Temporary files, for example, from tests. +/tmp/ + +# --- + +# Rustler +*.so diff --git a/.iex.exs b/.iex.exs new file mode 100644 index 0000000..89048c3 --- /dev/null +++ b/.iex.exs @@ -0,0 +1,39 @@ +IO.puts("Using .iex.exs file loaded from #{__DIR__}/.iex.exs") + +defmodule :_util do + defdelegate exit(), to: System, as: :halt + defdelegate q(), to: System, as: :halt + defdelegate restart(), to: System, as: :restart + + def atom_status do + limit = :erlang.system_info(:atom_limit) + count = :erlang.system_info(:atom_count) + + IO.puts("Currently using #{count} / #{limit} atoms") + end + + def cls, do: IO.puts("\ec") + + def raw(any, label \\ "iex") do + IO.inspect(any, + label: label, + pretty: true, + limit: :infinity, + structs: false, + syntax_colors: [ + number: :yellow, + atom: :cyan, + string: :green, + nil: :magenta, + boolean: :magenta + ], + width: 0 + ) + end +end + +import :_util + +# --- + +alias JsonSchemaNif diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 0000000..d77e0b2 --- /dev/null +++ b/.tool-versions @@ -0,0 +1,2 @@ +elixir 1.15.7-otp-26 +erlang 26.1.1 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..918a606 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,16 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/), and this project adheres to [Semantic +Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [0.1.0] - 2023-11-17 + +Initial release. + +### Added + +- `JsonSchemaNif.validate_json/2` to validate a JSON instance against a provided JSON schema. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..dc8e21f --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,36 @@ +# Contributing + +This library is kept intentionally simple. + +That said, contributions (especially bug fixes!) are welcome. To contribute: + +- Propose the changes as a GitHub Issue +- If feedback is supportive, create a new branch for your feature or bug fix +- Implement your changes +- Write tests for your changes +- Document your changes +- Ensure all tests pass +- Submit a merge request + +# Building the Project + +Build dependencies: + +1. Rust toolchain + +```bash +cd native/json_schema_nif/ +# If you need extra toolchains +rustup target add x86_64-apple-darwin +rustup target add aarch64-apple-darwin +rustup target add aarch64-unknown-linux-gnu + +cargo build --target aarch64-unknown-linux-gnu --release +cargo build --target aarch64-apple-darwin --release +cargo build --target x86_64-apple-darwin --release +``` + +To generate the checksum file **after** the NIF files have been uploaded to GitLab, +run: `mix rustler_precompiled.download JsonSchemaNif --only-local`. + +These files are required as part of the Hex package, but don't need to be tracked in VCS. diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..6a2750e --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,8 @@ +The MIT License (MIT) +Copyright (c) 2024 Podium + +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. \ No newline at end of file diff --git a/README.md b/README.md index d2f45ac..02fdf71 100644 --- a/README.md +++ b/README.md @@ -4,3 +4,35 @@ Library for validating JSON instances against JSON schemas. Provides a straightforward way to ensure that JSON data adheres to a predefined schema, enhancing the reliability and consistency of data. Especially useful in contexts like Kafka. + +## Installation + +The package can be installed by adding `json_schema_nif` to your list of dependencies in `mix.exs`: + +```elixir +defp deps do + [ + {:json_schema_nif, "~> 0.1.0"} + ] +end +``` + +## Contributing + +See [CONTRIBUTING.md](./CONTRIBUTING.md). + +## Documentation + +Documentation is available in [HexDocs](https://hexdocs.pm/json_schema_nif/readme.html). + +## Releasing + +Since we rely on RustlerPrecompiled actually building our Rust bindings in advance, +we need to follow their [recommended flow](https://hexdocs.pm/rustler_precompiled/precompilation_guide.html#recommended-flow): + +1. Release a new tag +1. Push the code to your repository with the new tag: git push origin main --tags +1. Wait for all NIFs to be built +1. Run the `mix rustler_precompiled.download JsonSchemaNif --all`task (with the flag --all) + release the package to Hex.pm (make sure your release includes the correct files). + - Run `mix hex.build --unpack` to ensure checksum files exist diff --git a/lib/json_schema_nif.ex b/lib/json_schema_nif.ex new file mode 100644 index 0000000..3eac22d --- /dev/null +++ b/lib/json_schema_nif.ex @@ -0,0 +1,46 @@ +defmodule JsonSchemaNif do + @moduledoc """ + Provides functionality for validating JSON instances against JSON schemas. + + This Elixir module interfaces with Rust-implemented functions via Rustler, + leveraging Rust's performance for efficient JSON validation. + """ + + # version = Mix.Project.config()[:version] + + use Rustler, otp_app: :json_schema_nif, crate: "json_schema_nif" + + @doc """ + Validates a JSON instance against a JSON schema. + + This function is a wrapper for the Rust-implemented NIF (Native Implemented Function). + + ## Parameters + + - `instance`: The JSON instance to be validated. + - `schema`: The JSON schema against which the instance is validated. + + ## Returns + + Returns `{:ok, :matches_schema}` if the JSON instance matches the schema, `{:error, :violates_schema}` if it violates + the schema, or an appropriate error tuple for other failure modes. + + ## Examples + + iex> JsonSchemaNif.validate_json("{\\"name\\":\\"John\\"}", "{\\"type\\":\\"object\\"}") + {:ok, :matches_schema} + + iex> JsonSchemaNif.validate_json("{\\"age\\":30}", "{\\"type\\":\\"string\\"}") + {:error, :violates_schema} + + ## Note + + This function will raise an error if the NIF is not loaded, which typically indicates + a compilation or deployment issue with the Rust code. + """ + @spec validate_json(binary, binary) :: {:ok, :matches_schema} | {:error, atom()} + def validate_json(instance, schema) when is_binary(instance) and is_binary(schema), + do: nif_not_loaded() + + defp nif_not_loaded, do: :erlang.nif_error(:nif_not_loaded) +end diff --git a/mix.exs b/mix.exs new file mode 100644 index 0000000..719d604 --- /dev/null +++ b/mix.exs @@ -0,0 +1,58 @@ +defmodule JsonSchemaNif.MixProject do + use Mix.Project + + @project_url "https://github.com/podium/json_schema_nif" + + def project do + [ + app: :json_schema_nif, + name: "JSON Schema NIF", + description: "A JSON Schema Validator via NIF bindings to Rust", + version: "0.1.0", + elixir: "~> 1.15", + start_permanent: Mix.env() == :prod, + deps: deps(), + aliases: aliases(), + package: package() + ] + end + + defp package do + [ + maintainers: ["Podium"], + licenses: ["MIT"], + links: %{ + "GitHub" => @project_url, + "Changelog" => "#{@project_url}/blob/master/CHANGELOG.md" + }, + files: [ + "lib", + "native/json_schema_nif/.cargo", + "native/json_schema_nif/src", + "native/json_schema_nif/Cargo*", + "checksum-*.exs", + "mix.exs", + "README.md", + "CHANGELOG.md" + ] + ] + end + + defp deps do + [ + {:rustler, "~> 0.30.0", optional: true}, + {:rustler_precompiled, "~> 0.7"}, + + # test + {:mix_test_watch, "~> 1.1.1", only: [:dev, :test], runtime: false}, + {:ex_doc, "~> 0.30", only: :dev, runtime: false}, + {:credo, "~> 1.7", only: [:dev, :test], runtime: false} + ] + end + + defp aliases do + [ + "deps.get": ["deps.get", "deps.unlock --unused"] + ] + end +end diff --git a/mix.lock b/mix.lock new file mode 100644 index 0000000..1ca4e8b --- /dev/null +++ b/mix.lock @@ -0,0 +1,17 @@ +%{ + "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, + "castore": {:hex, :castore, "1.0.5", "9eeebb394cc9a0f3ae56b813459f990abb0a3dedee1be6b27fdb50301930502f", [:mix], [], "hexpm", "8d7c597c3e4a64c395980882d4bca3cebb8d74197c590dc272cfd3b6a6310578"}, + "credo": {:hex, :credo, "1.7.5", "643213503b1c766ec0496d828c90c424471ea54da77c8a168c725686377b9545", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "f799e9b5cd1891577d8c773d245668aa74a2fcd15eb277f51a0131690ebfb3fd"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"}, + "ex_doc": {:hex, :ex_doc, "0.32.1", "21e40f939515373bcdc9cffe65f3b3543f05015ac6c3d01d991874129d173420", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.1", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "5142c9db521f106d61ff33250f779807ed2a88620e472ac95dc7d59c380113da"}, + "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, + "jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"}, + "makeup": {:hex, :makeup, "1.1.1", "fa0bc768698053b2b3869fa8a62616501ff9d11a562f3ce39580d60860c3a55e", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "5dc62fbdd0de44de194898b6710692490be74baa02d9d108bc29f007783b0b48"}, + "makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"}, + "makeup_erlang": {:hex, :makeup_erlang, "0.1.5", "e0ff5a7c708dda34311f7522a8758e23bfcd7d8d8068dc312b5eb41c6fd76eba", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "94d2e986428585a21516d7d7149781480013c56e30c6a233534bedf38867a59a"}, + "mix_test_watch": {:hex, :mix_test_watch, "1.1.1", "eee6fc570d77ad6851c7bc08de420a47fd1e449ef5ccfa6a77ef68b72e7e51ad", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "f82262b54dee533467021723892e15c3267349849f1f737526523ecba4e6baae"}, + "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, + "rustler": {:hex, :rustler, "0.30.0", "cefc49922132b072853fa9b0ca4dc2ffcb452f68fb73b779042b02d545e097fb", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:toml, "~> 0.6", [hex: :toml, repo: "hexpm", optional: false]}], "hexpm", "9ef1abb6a7dda35c47cfc649e6a5a61663af6cf842a55814a554a84607dee389"}, + "rustler_precompiled": {:hex, :rustler_precompiled, "0.7.1", "ecadf02cc59a0eccbaed6c1937303a5827fbcf60010c541595e6d3747d3d0f9f", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:rustler, "~> 0.23", [hex: :rustler, repo: "hexpm", optional: true]}], "hexpm", "b9e4657b99a1483ea31502e1d58c464bedebe9028808eda45c3a429af4550c66"}, + "toml": {:hex, :toml, "0.7.0", "fbcd773caa937d0c7a02c301a1feea25612720ac3fa1ccb8bfd9d30d822911de", [:mix], [], "hexpm", "0690246a2478c1defd100b0c9b89b4ea280a22be9a7b313a8a058a2408a2fa70"}, +} diff --git a/native/json_schema_nif/.cargo/config.toml b/native/json_schema_nif/.cargo/config.toml new file mode 100644 index 0000000..f9d879d --- /dev/null +++ b/native/json_schema_nif/.cargo/config.toml @@ -0,0 +1,15 @@ +[target.'cfg(target_os = "macos")'] +rustflags = [ + "-C", "link-arg=-undefined", + "-C", "link-arg=dynamic_lookup", +] + +# See https://github.com/rust-lang/rust/issues/59302 +[target.x86_64-unknown-linux-musl] +rustflags = [ + "-C", "target-feature=-crt-static" +] + +# Provides a small build size, but takes more time to build. +[profile.release] +lto = true \ No newline at end of file diff --git a/native/json_schema_nif/.gitignore b/native/json_schema_nif/.gitignore new file mode 100644 index 0000000..27c97c9 --- /dev/null +++ b/native/json_schema_nif/.gitignore @@ -0,0 +1,7 @@ +# Generated by Cargo +# will have compiled files and executables +/debug/ +/target/ + +# These are backup files generated by rustfmt +**/*.rs.bk diff --git a/native/json_schema_nif/Cargo.lock b/native/json_schema_nif/Cargo.lock new file mode 100644 index 0000000..3cbc518 --- /dev/null +++ b/native/json_schema_nif/Cargo.lock @@ -0,0 +1,1415 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "serde", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" + +[[package]] +name = "anstyle-parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "bytecount" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[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.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2275f18819641850fa26c89acc84d465c1bf91ce57bc2748b28c420473352f64" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07cdf1b148b25c1e1f7a42225e30a0d99a615cd4637eae7365548dd4529b95bc" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "deranged" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "fancy-regex" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b95f7c0680e4142284cf8b22c14a476e87d61b004a3a0861872b32ef7ead40a2" +dependencies = [ + "bit-set", + "regex", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fraction" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3027ae1df8d41b4bed2241c8fdad4acc1e7af60c8e17743534b545e77182d678" +dependencies = [ + "lazy_static", + "num", +] + +[[package]] +name = "futures-channel" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" + +[[package]] +name = "futures-io" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" + +[[package]] +name = "futures-sink" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" + +[[package]] +name = "futures-task" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" + +[[package]] +name = "futures-util" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +dependencies = [ + "futures-core", + "futures-io", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "gimli" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" + +[[package]] +name = "h2" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "http" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.4.10", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] +name = "iso8601" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924e5d73ea28f59011fec52a0d12185d496a9b075d360657aed2a5707f701153" +dependencies = [ + "nom", +] + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "js-sys" +version = "0.3.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "json_schema_nif" +version = "0.1.0" +dependencies = [ + "jsonschema", + "log", + "rustler", + "serde_json", +] + +[[package]] +name = "jsonschema" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a071f4f7efc9a9118dfb627a0a94ef247986e1ab8606a4c806ae2b3aa3b6978" +dependencies = [ + "ahash", + "anyhow", + "base64", + "bytecount", + "clap", + "fancy-regex", + "fraction", + "getrandom", + "iso8601", + "itoa", + "memchr", + "num-cmp", + "once_cell", + "parking_lot", + "percent-encoding", + "regex", + "reqwest", + "serde", + "serde_json", + "time", + "url", + "uuid", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +dependencies = [ + "libc", + "wasi", + "windows-sys", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-cmp" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63335b2e2c34fae2fb0aa2cecfd9f0832a1e24b3b32ecec612c3426d46dc8aaa" + +[[package]] +name = "num-complex" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[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 = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "reqwest" +version = "0.11.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "system-configuration", + "tokio", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustler" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4b4fea69e23de68c42c06769d6624d2d018da550c17244dd4b691f90ced4a7e" +dependencies = [ + "lazy_static", + "rustler_codegen", + "rustler_sys", +] + +[[package]] +name = "rustler_codegen" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "406061bd07aaf052c344257afed4988c5ec8efe4d2352b4c2cf27ea7c8575b12" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "rustler_sys" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a7c0740e5322b64e2b952d8f0edce5f90fcf6f6fe74cca3f6e78eb3de5ea858" +dependencies = [ + "regex", + "unreachable", +] + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.192" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.192" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" + +[[package]] +name = "socket2" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "2.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "time" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" +dependencies = [ + "deranged", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" +dependencies = [ + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "pin-project-lite", + "socket2 0.5.5", + "windows-sys", +] + +[[package]] +name = "tokio-util" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unreachable" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" +dependencies = [ + "void", +] + +[[package]] +name = "url" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "uuid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afec9963e3d0994cac82455b2b3502b81a7f40f9a0d32181f7528d9f4b43e02" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" + +[[package]] +name = "web-sys" +version = "0.3.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5db499c5f66323272151db0e666cd34f78617522fb0c1604d31a27c50c206a85" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[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.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +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.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys", +] + +[[package]] +name = "zerocopy" +version = "0.7.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e97e415490559a91254a2979b4829267a57d2fcd741a98eee8b722fb57289aa0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd7e48ccf166952882ca8bd778a43502c64f33bf94c12ebe2a7f08e5a0f6689f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/native/json_schema_nif/Cargo.toml b/native/json_schema_nif/Cargo.toml new file mode 100644 index 0000000..5cbde8b --- /dev/null +++ b/native/json_schema_nif/Cargo.toml @@ -0,0 +1,16 @@ +[package] + authors = [] + edition = "2021" + name = "json_schema_nif" + version = "0.1.0" + +[lib] + crate-type = ["cdylib"] + name = "json_schema_nif" + path = "src/lib.rs" + +[dependencies] + jsonschema = "0.17.1" + log = "0.4.20" + rustler = "0.30.0" + serde_json = "1.0.108" diff --git a/native/json_schema_nif/src/lib.rs b/native/json_schema_nif/src/lib.rs new file mode 100644 index 0000000..4e25a1f --- /dev/null +++ b/native/json_schema_nif/src/lib.rs @@ -0,0 +1,61 @@ +//! This module provides functionality for validating JSON instances against JSON schemas. +//! It is designed to be integrated into an Elixir project through Rustler, allowing +//! Elixir code to leverage Rust's performance for JSON validation tasks. + +#[macro_use] +extern crate log; + +use jsonschema::is_valid; +use rustler::Atom; +use serde_json::Value; + +mod atoms { + rustler::atoms! { + bad_instance, + bad_schema, + matches_schema, + violates_schema + } +} + +/// Validates a JSON instance against a JSON schema. +/// +/// # Arguments +/// * `instance` - A string representation of the JSON instance to be validated. +/// * `schema` - A string representation of the JSON schema against which the instance is validated. +/// +/// # Returns +/// * `Ok(atoms::matches_schema())` if the instance matches the schema. +/// * `Err(atoms::violates_schema())` if the instance does not match the schema. +/// * `Err(atoms::bad_instance())` if the instance string cannot be parsed as JSON. +/// * `Err(atoms::bad_schema())` if the schema string cannot be parsed as JSON. +#[rustler::nif] +fn validate_json(instance: String, schema: String) -> Result { + let instance_value = match serde_json::from_str::(&instance) { + Ok(val) => val, + Err(err) => { + error!("Failed to parse JSON: {:?}", err); + return Err(atoms::bad_instance()); + } + }; + + let schema_value = match serde_json::from_str::(&schema) { + Ok(val) => val, + Err(err) => { + error!("Failed to parse schema: {:?}", err); + return Err(atoms::bad_schema()); + } + }; + + if is_valid(&schema_value, &instance_value) { + return Ok(atoms::matches_schema()); + } else { + debug!( + "JSON instance does not match schema:\nInstance: {:?}\nSchema: {:?}", + &instance_value, &schema_value + ); + return Err(atoms::violates_schema()); + } +} + +rustler::init!("Elixir.JsonSchemaNif", [validate_json]); diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..d45f535 --- /dev/null +++ b/renovate.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": ["local>engineering/ops/renovate-config"] +} diff --git a/test/json_schema_podium_test.exs b/test/json_schema_podium_test.exs new file mode 100644 index 0000000..5bccfcf --- /dev/null +++ b/test/json_schema_podium_test.exs @@ -0,0 +1,32 @@ +defmodule JsonSchemaNifTest do + use ExUnit.Case, async: true + + doctest JsonSchemaNif + + describe "validate_json/2" do + test "validates a matching JSON instance and schema" do + instance = "{\"name\":\"John\"}" + schema = "{\"type\":\"object\"}" + assert {:ok, :matches_schema} = JsonSchemaNif.validate_json(instance, schema) + end + + test "returns error for a non-matching JSON instance and schema" do + instance = "{\"age\":30}" + schema = "{\"type\":\"string\"}" + + assert {:error, :violates_schema} = JsonSchemaNif.validate_json(instance, schema) + end + + test "returns error for invalid JSON instance" do + instance = "not a json instance" + schema = "{\"type\":\"object\"}" + assert {:error, :bad_instance} = JsonSchemaNif.validate_json(instance, schema) + end + + test "returns error for invalid JSON schema" do + instance = "{\"name\":\"John\"}" + schema = "not a json schema" + assert {:error, :bad_schema} = JsonSchemaNif.validate_json(instance, schema) + end + end +end diff --git a/test/test_helper.exs b/test/test_helper.exs new file mode 100644 index 0000000..869559e --- /dev/null +++ b/test/test_helper.exs @@ -0,0 +1 @@ +ExUnit.start() From ede125f534fa3141ce378def317490c7855a697e Mon Sep 17 00:00:00 2001 From: bradschwartz Date: Tue, 30 Apr 2024 16:42:02 -0700 Subject: [PATCH 02/19] Remove < 1.15 elixir --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index de2e667..8820372 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-22.04, ubuntu-20.04] - elixir_version: [1.13, 1.14, 1.15] + elixir_version: [1.15] otp_version: [24, 25, 26] exclude: - otp_version: 26 From e19a034958c081b290feb9cfb9b079838158dfa7 Mon Sep 17 00:00:00 2001 From: bradschwartz Date: Tue, 30 Apr 2024 16:42:13 -0700 Subject: [PATCH 03/19] rename to match project name --- test/{json_schema_podium_test.exs => json_schema_nif_test.exs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/{json_schema_podium_test.exs => json_schema_nif_test.exs} (100%) diff --git a/test/json_schema_podium_test.exs b/test/json_schema_nif_test.exs similarity index 100% rename from test/json_schema_podium_test.exs rename to test/json_schema_nif_test.exs From 194bc9baee74d49e8fa8d17e5d9a3e85f2d3a3e2 Mon Sep 17 00:00:00 2001 From: bradschwartz Date: Tue, 30 Apr 2024 16:52:05 -0700 Subject: [PATCH 04/19] Actually switch to using the precompiled targets when not building --- .github/workflows/ci.yml | 1 + lib/json_schema_nif.ex | 12 +++++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8820372..8040f21 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,6 +13,7 @@ jobs: runs-on: ${{ matrix.os }} env: MIX_ENV: test + RUSTLER_PRECOMPILATION_FORCE_BUILD: true strategy: fail-fast: false diff --git a/lib/json_schema_nif.ex b/lib/json_schema_nif.ex index 3eac22d..cfff647 100644 --- a/lib/json_schema_nif.ex +++ b/lib/json_schema_nif.ex @@ -6,9 +6,15 @@ defmodule JsonSchemaNif do leveraging Rust's performance for efficient JSON validation. """ - # version = Mix.Project.config()[:version] - - use Rustler, otp_app: :json_schema_nif, crate: "json_schema_nif" + version = Mix.Project.config()[:version] + + use RustlerPrecompiled, + otp_app: :json_schema_nif, + crate: "json_schema_nif", + version: version, + base_url: "https://github.com/podium/json_schema_nif/releases/download/v#{version}", + force_build: System.get_env("RUSTLER_PRECOMPILATION_FORCE_BUILD") in ["1", "true"], + targets: RustlerPrecompiled.Config.default_targets() @doc """ Validates a JSON instance against a JSON schema. From 0b8640d22d3939b91661931ef1e7a226835e5b04 Mon Sep 17 00:00:00 2001 From: bradschwartz Date: Tue, 30 Apr 2024 16:57:28 -0700 Subject: [PATCH 05/19] allow 33% coverage to pass --- mix.exs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index 719d604..a4f81db 100644 --- a/mix.exs +++ b/mix.exs @@ -13,7 +13,8 @@ defmodule JsonSchemaNif.MixProject do start_permanent: Mix.env() == :prod, deps: deps(), aliases: aliases(), - package: package() + package: package(), + test_coverage: [summary: [threshold: 33.33]] ] end From f1de473f580fe05159f0b588cff6c2e878f79cac Mon Sep 17 00:00:00 2001 From: bradschwartz Date: Tue, 30 Apr 2024 17:15:53 -0700 Subject: [PATCH 06/19] fix name of rust project, try building on PRs --- .github/workflows/release.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index dedcc51..6c13d3d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,6 +2,9 @@ name: Build precompiled NIFs # lifted from: https://github.com/philss/rustler_precompilation_example/blob/main/.github/workflows/release.yml on: + pull_request: + branches: + - master push: branches: - main @@ -68,12 +71,12 @@ jobs: id: build-crate uses: philss/rustler-precompiled-action@v1.0.1 with: - project-name: example + project-name: json_schema_nif project-version: ${{ env.PROJECT_VERSION }} target: ${{ matrix.job.target }} nif-version: ${{ matrix.nif }} use-cross: ${{ matrix.job.use-cross }} - project-dir: "native/example" + project-dir: "native/json_schema_nif" - name: Artifact upload uses: actions/upload-artifact@v3 From 91766bf1736f26ad549020c1dacd88bedf78c2ea Mon Sep 17 00:00:00 2001 From: bradschwartz Date: Tue, 30 Apr 2024 17:19:15 -0700 Subject: [PATCH 07/19] Add "Cross.toml" in order to fix cross compilation --- native/json_schema_nif/Cross.toml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 native/json_schema_nif/Cross.toml diff --git a/native/json_schema_nif/Cross.toml b/native/json_schema_nif/Cross.toml new file mode 100644 index 0000000..ea92ea7 --- /dev/null +++ b/native/json_schema_nif/Cross.toml @@ -0,0 +1,4 @@ +[build.env] +passthrough = [ + "RUSTLER_NIF_VERSION" +] \ No newline at end of file From 0e93a3acef5c9fed56e83c8be5878235676baf7e Mon Sep 17 00:00:00 2001 From: bradschwartz Date: Wed, 1 May 2024 10:25:34 -0700 Subject: [PATCH 08/19] static compilation for arm musl --- native/json_schema_nif/.cargo/config.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/native/json_schema_nif/.cargo/config.toml b/native/json_schema_nif/.cargo/config.toml index f9d879d..54bc703 100644 --- a/native/json_schema_nif/.cargo/config.toml +++ b/native/json_schema_nif/.cargo/config.toml @@ -10,6 +10,11 @@ rustflags = [ "-C", "target-feature=-crt-static" ] +[target.aarch64-unknown-linux-musl] +rustflags = [ + "-C", "target-feature=-crt-static" +] + # Provides a small build size, but takes more time to build. [profile.release] lto = true \ No newline at end of file From 6d4ce47cca9001a42de7dc8e186f18cd3cf3f601 Mon Sep 17 00:00:00 2001 From: bradschwartz Date: Wed, 1 May 2024 10:43:57 -0700 Subject: [PATCH 09/19] explicitly install rust toolchain for elixir CI runners, add some docs on how to force compilation --- .github/workflows/ci.yml | 5 ++++- .github/workflows/release.yml | 2 +- CONTRIBUTING.md | 4 ++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8040f21..4991c69 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: CI +name: Elixir CI on: pull_request: @@ -36,6 +36,9 @@ jobs: otp-version: ${{matrix.otp_version}} elixir-version: ${{matrix.elixir_version}} + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + - uses: actions/cache@v4 with: path: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6c13d3d..b4a70f2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,4 +1,4 @@ -name: Build precompiled NIFs +name: Build Precompiled NIFs # lifted from: https://github.com/philss/rustler_precompilation_example/blob/main/.github/workflows/release.yml on: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dc8e21f..8d0ea1e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -16,8 +16,12 @@ That said, contributions (especially bug fixes!) are welcome. To contribute: Build dependencies: +1. Elixir toolchain (default to asdf installation) 1. Rust toolchain +To force a compilation of the Rust portions locally, instead of relying +on pre-built artifacts, set `RUSTLER_PRECOMPILATION_FORCE_BUILD=true`. + ```bash cd native/json_schema_nif/ # If you need extra toolchains From 6e77f6228898dd1ab3b0a33221a4866534ae7c31 Mon Sep 17 00:00:00 2001 From: bradschwartz Date: Wed, 1 May 2024 11:02:54 -0700 Subject: [PATCH 10/19] Try bundling as lib as well as cdylib per https://stackoverflow.com/a/49762980/11081822 --- native/json_schema_nif/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native/json_schema_nif/Cargo.toml b/native/json_schema_nif/Cargo.toml index 5cbde8b..d99e97d 100644 --- a/native/json_schema_nif/Cargo.toml +++ b/native/json_schema_nif/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" [lib] - crate-type = ["cdylib"] + crate-type = ["cdylib", "lib"] name = "json_schema_nif" path = "src/lib.rs" From 8dd353f6fe0becebbf6c6ff60221020944bdf07a Mon Sep 17 00:00:00 2001 From: bradschwartz Date: Wed, 1 May 2024 11:27:28 -0700 Subject: [PATCH 11/19] Add vscode workspace settings to for rust-analyzer --- .vscode/settings.json | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..2af9458 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "rust-analyzer.linkedProjects": [ + "./native/json_schema_nif/Cargo.toml" + ] +} \ No newline at end of file From ed4922873f00e25bbf756cf398f780eae6c101ee Mon Sep 17 00:00:00 2001 From: bradschwartz Date: Wed, 1 May 2024 11:28:50 -0700 Subject: [PATCH 12/19] Only run on ubuntu-latest --- .github/workflows/ci.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4991c69..e55d740 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,7 +10,7 @@ on: jobs: setup: - runs-on: ${{ matrix.os }} + runs-on: ubuntu-latest env: MIX_ENV: test RUSTLER_PRECOMPILATION_FORCE_BUILD: true @@ -18,7 +18,6 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-22.04, ubuntu-20.04] elixir_version: [1.15] otp_version: [24, 25, 26] exclude: From 6b8245c3ee3dc25119617e80d12822c09206c178 Mon Sep 17 00:00:00 2001 From: bradschwartz Date: Wed, 1 May 2024 14:02:42 -0700 Subject: [PATCH 13/19] Fix push branch for release flow --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b4a70f2..1e5bb30 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,7 +7,7 @@ on: - master push: branches: - - main + - master tags: - "*" From 96052f30e064ff723d3d9ef368b053c569742ecd Mon Sep 17 00:00:00 2001 From: bradschwartz Date: Tue, 14 May 2024 17:19:50 -0400 Subject: [PATCH 14/19] remove renovate.json --- renovate.json | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 renovate.json diff --git a/renovate.json b/renovate.json deleted file mode 100644 index d45f535..0000000 --- a/renovate.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": ["local>engineering/ops/renovate-config"] -} From 48eda379ff488772e8a03d4165379c73ad61ed43 Mon Sep 17 00:00:00 2001 From: bradschwartz Date: Tue, 14 May 2024 18:07:27 -0400 Subject: [PATCH 15/19] fix rustler code to only return :ok naked atom on success. Fix some clippy violations --- lib/json_schema_nif.ex | 6 ++-- native/json_schema_nif/src/lib.rs | 56 +++++++++++++++++++++++++------ test/json_schema_nif_test.exs | 2 +- 3 files changed, 49 insertions(+), 15 deletions(-) diff --git a/lib/json_schema_nif.ex b/lib/json_schema_nif.ex index cfff647..5d06b11 100644 --- a/lib/json_schema_nif.ex +++ b/lib/json_schema_nif.ex @@ -28,13 +28,13 @@ defmodule JsonSchemaNif do ## Returns - Returns `{:ok, :matches_schema}` if the JSON instance matches the schema, `{:error, :violates_schema}` if it violates + Returns `:ok` if the JSON instance matches the schema, `{:error, :violates_schema}` if it violates the schema, or an appropriate error tuple for other failure modes. ## Examples iex> JsonSchemaNif.validate_json("{\\"name\\":\\"John\\"}", "{\\"type\\":\\"object\\"}") - {:ok, :matches_schema} + :ok iex> JsonSchemaNif.validate_json("{\\"age\\":30}", "{\\"type\\":\\"string\\"}") {:error, :violates_schema} @@ -44,7 +44,7 @@ defmodule JsonSchemaNif do This function will raise an error if the NIF is not loaded, which typically indicates a compilation or deployment issue with the Rust code. """ - @spec validate_json(binary, binary) :: {:ok, :matches_schema} | {:error, atom()} + @spec validate_json(binary, binary) :: :ok | {:error, atom()} def validate_json(instance, schema) when is_binary(instance) and is_binary(schema), do: nif_not_loaded() diff --git a/native/json_schema_nif/src/lib.rs b/native/json_schema_nif/src/lib.rs index 4e25a1f..589b031 100644 --- a/native/json_schema_nif/src/lib.rs +++ b/native/json_schema_nif/src/lib.rs @@ -6,18 +6,52 @@ extern crate log; use jsonschema::is_valid; -use rustler::Atom; use serde_json::Value; mod atoms { rustler::atoms! { bad_instance, bad_schema, - matches_schema, violates_schema } } +enum ValidationResult { + Ok, + BadInstance, + BadSchema, + ViolatesSchema, +} + +impl rustler::Encoder for ValidationResult { + fn encode<'a>(&self, env: rustler::Env<'a>) -> rustler::Term<'a> { + match self { + ValidationResult::Ok => rustler::types::atom::ok().encode(env), + ValidationResult::BadInstance => rustler::types::tuple::make_tuple( + env, + &[ + rustler::types::atom::error().encode(env), + atoms::bad_instance().encode(env), + ], + ), + ValidationResult::BadSchema => rustler::types::tuple::make_tuple( + env, + &[ + rustler::types::atom::error().encode(env), + atoms::bad_schema().encode(env), + ], + ), + ValidationResult::ViolatesSchema => rustler::types::tuple::make_tuple( + env, + &[ + rustler::types::atom::error().encode(env), + atoms::violates_schema().encode(env), + ], + ), + } + } +} + /// Validates a JSON instance against a JSON schema. /// /// # Arguments @@ -25,17 +59,17 @@ mod atoms { /// * `schema` - A string representation of the JSON schema against which the instance is validated. /// /// # Returns -/// * `Ok(atoms::matches_schema())` if the instance matches the schema. -/// * `Err(atoms::violates_schema())` if the instance does not match the schema. -/// * `Err(atoms::bad_instance())` if the instance string cannot be parsed as JSON. -/// * `Err(atoms::bad_schema())` if the schema string cannot be parsed as JSON. +/// * `ValidationResult::Ok` if the instance matches the schema. +/// * `ValidationResult::ViolatesSchema` if the instance does not match the schema. +/// * `ValidationResult::BadInstance` if the instance string cannot be parsed as JSON. +/// * `ValidationResult::BadSchema` if the schema string cannot be parsed as JSON. #[rustler::nif] -fn validate_json(instance: String, schema: String) -> Result { +fn validate_json(instance: String, schema: String) -> ValidationResult { let instance_value = match serde_json::from_str::(&instance) { Ok(val) => val, Err(err) => { error!("Failed to parse JSON: {:?}", err); - return Err(atoms::bad_instance()); + return ValidationResult::BadInstance; } }; @@ -43,18 +77,18 @@ fn validate_json(instance: String, schema: String) -> Result { Ok(val) => val, Err(err) => { error!("Failed to parse schema: {:?}", err); - return Err(atoms::bad_schema()); + return ValidationResult::BadSchema; } }; if is_valid(&schema_value, &instance_value) { - return Ok(atoms::matches_schema()); + ValidationResult::Ok } else { debug!( "JSON instance does not match schema:\nInstance: {:?}\nSchema: {:?}", &instance_value, &schema_value ); - return Err(atoms::violates_schema()); + ValidationResult::ViolatesSchema } } diff --git a/test/json_schema_nif_test.exs b/test/json_schema_nif_test.exs index 5bccfcf..a5d9c75 100644 --- a/test/json_schema_nif_test.exs +++ b/test/json_schema_nif_test.exs @@ -7,7 +7,7 @@ defmodule JsonSchemaNifTest do test "validates a matching JSON instance and schema" do instance = "{\"name\":\"John\"}" schema = "{\"type\":\"object\"}" - assert {:ok, :matches_schema} = JsonSchemaNif.validate_json(instance, schema) + assert :ok = JsonSchemaNif.validate_json(instance, schema) end test "returns error for a non-matching JSON instance and schema" do From ec55c885f5084935a7b5d1090391e2a05faac107 Mon Sep 17 00:00:00 2001 From: bradschwartz Date: Tue, 14 May 2024 18:10:07 -0400 Subject: [PATCH 16/19] Update changelog date --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 918a606..7a9dd9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] -## [0.1.0] - 2023-11-17 +## [0.1.0] - 2024-05-15 Initial release. From c3867a8322010d4137e9a106889462d057faf2b1 Mon Sep 17 00:00:00 2001 From: bradschwartz Date: Tue, 14 May 2024 18:23:42 -0400 Subject: [PATCH 17/19] Elixir 1.15/1.16, otp 25/26 only for CI --- .github/workflows/ci.yml | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e55d740..e4e97c6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,15 +18,8 @@ jobs: strategy: fail-fast: false matrix: - elixir_version: [1.15] - otp_version: [24, 25, 26] - exclude: - - otp_version: 26 - elixir_version: 1.14 - - otp_version: 26 - elixir_version: 1.13 - - otp_version: 25 - elixir_version: 1.13 + elixir_version: [1.15, 1.16] + otp_version: [25, 26] steps: - uses: actions/checkout@v4 From cfbba31635c9c22205cc54af6467adc92109a768 Mon Sep 17 00:00:00 2001 From: bradschwartz Date: Wed, 15 May 2024 09:19:18 -0400 Subject: [PATCH 18/19] Fix github name --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8d0ea1e..f38d5b6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -34,7 +34,7 @@ cargo build --target aarch64-apple-darwin --release cargo build --target x86_64-apple-darwin --release ``` -To generate the checksum file **after** the NIF files have been uploaded to GitLab, +To generate the checksum file **after** the NIF files have been uploaded to GitHub, run: `mix rustler_precompiled.download JsonSchemaNif --only-local`. These files are required as part of the Hex package, but don't need to be tracked in VCS. From 1b82edc26d72fe9fe00d7a3c4e9b70bd0a7e5d5e Mon Sep 17 00:00:00 2001 From: bradschwartz Date: Thu, 16 May 2024 15:11:25 -0400 Subject: [PATCH 19/19] Remove unneeded alias --- .iex.exs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.iex.exs b/.iex.exs index 89048c3..d02d29c 100644 --- a/.iex.exs +++ b/.iex.exs @@ -33,7 +33,3 @@ defmodule :_util do end import :_util - -# --- - -alias JsonSchemaNif