├── .github
├── dependabot.yml
└── workflows
│ ├── check.yml
│ ├── docs.yml
│ ├── pull-request.yml
│ ├── release.yml
│ └── test.yml
├── .gitignore
├── .gitmodules
├── .prettierignore
├── CHANGELOG.md
├── CONTRIBUTING.md
├── Cargo.lock
├── Cargo.toml
├── LICENSE
├── README.md
├── build.rs
├── commitlint.config.js
├── release-plz.toml
└── src
├── extensions.rs
├── lib.rs
├── parse
├── context.rs
├── mod.rs
├── proto
│ ├── extensions
│ │ ├── mod.rs
│ │ └── simple_extension_uri.rs
│ ├── mod.rs
│ ├── plan_version.rs
│ └── version.rs
├── text
│ ├── mod.rs
│ └── simple_extensions
│ │ ├── argument.rs
│ │ └── mod.rs
└── typed.rs
├── proto.rs
├── text.rs
└── version.rs
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: Apache-2.0
2 |
3 | version: 2
4 | updates:
5 | - package-ecosystem: "cargo"
6 | directory: "/"
7 | schedule:
8 | interval: "daily"
9 | rebase-strategy: "disabled"
10 | commit-message:
11 | prefix: "chore(deps,cargo)"
12 | groups:
13 | proto:
14 | applies-to: version-updates
15 | patterns:
16 | - "prost*"
17 | - "pbjson*"
18 | cargo:
19 | applies-to: version-updates
20 | patterns:
21 | - "*"
22 |
23 | - package-ecosystem: "github-actions"
24 | directory: "/"
25 | schedule:
26 | interval: "daily"
27 | commit-message:
28 | prefix: "chore(deps,github-actions)"
29 | ignore:
30 | - dependency-name: "dtolnay/rust-toolchain"
31 |
32 | - package-ecosystem: "gitsubmodule"
33 | directory: "/"
34 | schedule:
35 | # 4 hours after the weekly Substrait release cron job
36 | # https://github.com/substrait-io/substrait/blob/90fce6a582c1009435d26b63083486ac1eed5906/.github/workflows/release.yml#L1-L6
37 | interval: "weekly"
38 | day: "sunday"
39 | time: "06:00"
40 | commit-message:
41 | prefix: "feat(deps,substrait)!:"
42 |
--------------------------------------------------------------------------------
/.github/workflows/check.yml:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: Apache-2.0
2 |
3 | name: Check
4 |
5 | on: [push, pull_request]
6 |
7 | jobs:
8 | license:
9 | name: SPDX License Header
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v4
13 | - uses: enarx/spdx@master
14 | with:
15 | licenses: Apache-2.0
16 |
17 | formatting:
18 | name: Formatting
19 | runs-on: ubuntu-latest
20 | steps:
21 | - uses: actions/checkout@v4
22 | - uses: actions/setup-node@v4
23 | - run: npm install prettier prettier-plugin-toml
24 | - run: npx prettier --check --no-config .
25 |
--------------------------------------------------------------------------------
/.github/workflows/docs.yml:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: Apache-2.0
2 |
3 | name: Docs
4 |
5 | on: [push, pull_request]
6 |
7 | permissions:
8 | contents: read
9 | pages: write
10 | id-token: write
11 |
12 | concurrency:
13 | group: ${{ github.ref }}
14 |
15 | jobs:
16 | rustdoc:
17 | name: Rustdoc
18 | runs-on: ubuntu-latest
19 | env:
20 | RUSTDOCFLAGS: -Dwarnings
21 | steps:
22 | - uses: actions/checkout@v4
23 | with:
24 | fetch-depth: 0
25 | submodules: recursive
26 | - uses: dtolnay/rust-toolchain@nightly
27 | id: rust-toolchain
28 | - uses: dtolnay/install@cargo-docs-rs
29 | - run: cargo docs-rs
30 | - run: mv "target/$(rustc -vV | awk '/^host/ { print $2 }')/doc" "target/doc"
31 | - run: chmod -c -R +rX "target/doc"
32 | - run: echo "" > target/doc/index.html
33 | - if: github.event_name == 'push' && github.ref == 'refs/heads/main'
34 | uses: actions/upload-pages-artifact@v3
35 | with:
36 | path: target/doc/
37 |
38 | deploy:
39 | name: Deploy
40 | if: github.event_name == 'push' && github.ref == 'refs/heads/main'
41 | runs-on: ubuntu-latest
42 | needs: rustdoc
43 | environment:
44 | name: github-pages
45 | url: ${{ steps.deployment.outputs.page_url }}
46 | steps:
47 | - uses: actions/configure-pages@v5
48 | - uses: actions/deploy-pages@v4
49 | id: deployment
50 |
--------------------------------------------------------------------------------
/.github/workflows/pull-request.yml:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: Apache-2.0
2 |
3 | name: Pull Request
4 |
5 | on:
6 | pull_request_target:
7 | types:
8 | - opened
9 | - reopened
10 | - edited
11 | - synchronize
12 |
13 | jobs:
14 | conventional-commits:
15 | name: Conventional Commits
16 | runs-on: ubuntu-latest
17 | steps:
18 | - uses: actions/checkout@v4
19 | - uses: actions/setup-node@v4
20 | - run: npm install @commitlint/config-conventional
21 | - run: npx commitlint <<< $CONVENTIONAL_COMMIT
22 | env:
23 | CONVENTIONAL_COMMIT: |
24 | ${{ github.event.pull_request.title }}
25 |
26 | ${{ github.event.pull_request.body }}
27 | - if: failure()
28 | run:
29 | echo "Substrait follows the [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/) for release automation.
30 | The PR title and body are used as the merge commit message.
31 |
32 | Please update your PR title to match the specification." >> $GITHUB_STEP_SUMMARY
33 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: Apache-2.0
2 |
3 | name: Release
4 |
5 | permissions:
6 | pull-requests: write
7 | contents: write
8 |
9 | on:
10 | push:
11 | branches:
12 | - main
13 |
14 | jobs:
15 | release:
16 | name: Release
17 | runs-on: ubuntu-latest
18 | environment:
19 | name: crates-io
20 | permissions:
21 | contents: write
22 | steps:
23 | - uses: actions/checkout@v4
24 | with:
25 | fetch-depth: 0
26 | submodules: recursive
27 | - uses: arduino/setup-protoc@v3
28 | with:
29 | repo-token: ${{ secrets.GITHUB_TOKEN }}
30 | - uses: dtolnay/rust-toolchain@stable
31 | # Trigger build script to generate version info
32 | - run: cargo build
33 | - uses: MarcoIeni/release-plz-action@v0.5.107
34 | with:
35 | command: release
36 | env:
37 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
38 | CARGO_REGISTRY_TOKEN: ${{ secrets.CRATES_IO_TOKEN }}
39 |
40 | pr:
41 | name: PR
42 | runs-on: ubuntu-latest
43 | concurrency:
44 | group: release-${{ github.ref }}
45 | cancel-in-progress: false
46 | steps:
47 | - uses: actions/checkout@v4
48 | with:
49 | fetch-depth: 0
50 | submodules: recursive
51 | - uses: dtolnay/rust-toolchain@stable
52 | - uses: arduino/setup-protoc@v3
53 | with:
54 | repo-token: ${{ secrets.GITHUB_TOKEN }}
55 | - run: cargo build
56 | - uses: MarcoIeni/release-plz-action@v0.5.107
57 | with:
58 | command: release-pr
59 | env:
60 | RUST_LOG: debug
61 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
62 | CARGO_REGISTRY_TOKEN: ${{ secrets.CRATES_IO_TOKEN }}
63 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: Apache-2.0
2 |
3 | name: Test
4 |
5 | on: [push, pull_request]
6 |
7 | jobs:
8 | msrv:
9 | name: Minimum Supported Rust Version
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v4
13 | with:
14 | fetch-depth: 0
15 | submodules: recursive
16 | - uses: dtolnay/rust-toolchain@1.80.1
17 | id: rust-toolchain
18 | - uses: actions/cache@v4
19 | with:
20 | path: |
21 | ~/.cargo/bin/
22 | ~/.cargo/registry/index/
23 | ~/.cargo/registry/cache/
24 | ~/.cargo/git/db/
25 | target/
26 | key: ${{ runner.os }}-cargo-${{ steps.rust-toolchain.outputs.cachekey }}-check-${{ hashFiles('**/Cargo.toml') }}
27 | restore-keys: |
28 | ${{ runner.os }}-cargo-${{ steps.rust-toolchain.outputs.cachekey }}-check-
29 | ${{ runner.os }}-cargo-${{ steps.rust-toolchain.outputs.cachekey }}-
30 | ${{ runner.os }}-cargo-
31 | - run: cargo check --all-targets --no-default-features --features protoc
32 | - run: cargo check --all-targets --all-features
33 |
34 | check:
35 | name: Check
36 | runs-on: ubuntu-latest
37 | steps:
38 | - uses: actions/checkout@v4
39 | with:
40 | fetch-depth: 0
41 | submodules: recursive
42 | - uses: dtolnay/rust-toolchain@stable
43 | id: rust-toolchain
44 | - uses: actions/cache@v4
45 | with:
46 | path: |
47 | ~/.cargo/bin/
48 | ~/.cargo/registry/index/
49 | ~/.cargo/registry/cache/
50 | ~/.cargo/git/db/
51 | target/
52 | key: ${{ runner.os }}-cargo-${{ steps.rust-toolchain.outputs.cachekey }}-check-${{ hashFiles('**/Cargo.toml') }}
53 | restore-keys: |
54 | ${{ runner.os }}-cargo-${{ steps.rust-toolchain.outputs.cachekey }}-check-
55 | ${{ runner.os }}-cargo-${{ steps.rust-toolchain.outputs.cachekey }}-
56 | ${{ runner.os }}-cargo-
57 | - run: cargo check --all-targets --no-default-features --features protoc
58 | - run: cargo check --all-targets --all-features
59 |
60 | test:
61 | name: Test
62 | runs-on: ubuntu-latest
63 | steps:
64 | - uses: actions/checkout@v4
65 | with:
66 | fetch-depth: 0
67 | submodules: recursive
68 | - uses: dtolnay/rust-toolchain@stable
69 | id: rust-toolchain
70 | - uses: actions/cache@v4
71 | with:
72 | path: |
73 | ~/.cargo/bin/
74 | ~/.cargo/registry/index/
75 | ~/.cargo/registry/cache/
76 | ~/.cargo/git/db/
77 | target/
78 | key: ${{ runner.os }}-cargo-${{ steps.rust-toolchain.outputs.cachekey }}-test-${{ hashFiles('**/Cargo.toml') }}
79 | restore-keys: |
80 | ${{ runner.os }}-cargo-${{ steps.rust-toolchain.outputs.cachekey }}-test-
81 | ${{ runner.os }}-cargo-${{ steps.rust-toolchain.outputs.cachekey }}-
82 | ${{ runner.os }}-cargo-
83 | - run: cargo test --all-targets --no-default-features --features protoc
84 | - run: cargo test --all-targets --all-features
85 | - run: cargo test --doc --all-features
86 | - run: cargo test --doc --no-default-features --features protoc
87 |
88 | rustfmt:
89 | name: Rustfmt
90 | runs-on: ubuntu-latest
91 | steps:
92 | - uses: actions/checkout@v4
93 | - uses: dtolnay/rust-toolchain@stable
94 | with:
95 | components: rustfmt
96 | - run: cargo fmt -- --check
97 |
98 | clippy:
99 | name: Clippy
100 | runs-on: ubuntu-latest
101 | steps:
102 | - uses: actions/checkout@v4
103 | with:
104 | fetch-depth: 0
105 | submodules: recursive
106 | - uses: dtolnay/rust-toolchain@stable
107 | id: rust-toolchain
108 | with:
109 | components: clippy
110 | - uses: actions/cache@v4
111 | with:
112 | path: |
113 | ~/.cargo/bin/
114 | ~/.cargo/registry/index/
115 | ~/.cargo/registry/cache/
116 | ~/.cargo/git/db/
117 | target/
118 | key: ${{ runner.os }}-cargo-${{ steps.rust-toolchain.outputs.cachekey }}-clippy-${{ hashFiles('**/Cargo.toml') }}
119 | restore-keys: |
120 | ${{ runner.os }}-cargo-${{ steps.rust-toolchain.outputs.cachekey }}-clippy-
121 | ${{ runner.os }}-cargo-${{ steps.rust-toolchain.outputs.cachekey }}-
122 | ${{ runner.os }}-cargo-
123 | - run: cargo clippy --all-targets --no-default-features --features protoc -- -Dwarnings
124 | - run: cargo clippy --all-targets --all-features -- -Dwarnings
125 |
126 | package:
127 | name: Package
128 | runs-on: ubuntu-latest
129 | steps:
130 | - uses: actions/checkout@v4
131 | with:
132 | fetch-depth: 0
133 | submodules: recursive
134 | - uses: dtolnay/rust-toolchain@stable
135 | id: rust-toolchain
136 | - uses: actions/cache@v4
137 | with:
138 | path: |
139 | ~/.cargo/bin/
140 | ~/.cargo/registry/index/
141 | ~/.cargo/registry/cache/
142 | ~/.cargo/git/db/
143 | target/
144 | key: ${{ runner.os }}-cargo-${{ steps.rust-toolchain.outputs.cachekey }}-package-${{ hashFiles('**/Cargo.toml') }}
145 | restore-keys: |
146 | ${{ runner.os }}-cargo-${{ steps.rust-toolchain.outputs.cachekey }}-package-
147 | ${{ runner.os }}-cargo-${{ steps.rust-toolchain.outputs.cachekey }}-
148 | ${{ runner.os }}-cargo-
149 | - run: cargo build --all-features
150 | - run: cargo package --all-features --allow-dirty
151 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: Apache-2.0
2 |
3 | /target
4 |
5 | # Generated by build.rs (substrait submodule version information)
6 | gen/
7 |
8 | .idea
9 |
10 | package.json
11 | package-lock.json
12 | node_modules/
13 | .vscode/
14 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: Apache-2.0
2 |
3 | [submodule "substrait"]
4 | path = substrait
5 | url = https://github.com/substrait-io/substrait
6 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: Apache-2.0
2 |
3 | /target
4 | CHANGELOG.md
5 | LICENSE
6 | substrait/
7 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | All contributors and contributions are welcome! Please open an issue on GitHub if you have issues, questions or ideas.
4 |
5 | ## GitHub
6 |
7 | ### Pull requests
8 |
9 | Substrait follows the [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/) for commit messages. This allows for automation of releases based on commit messages that are merged to the default branch.
10 |
11 | The `Conventional Commits` job of the [Pull Request](.github/workflows/pull-request.yml) workflow check the Pull Request title and body and the resulting merge commit message.
12 |
13 | ### Releases
14 |
15 | Releases are published automatically with the [Release](./github/workflows/release.yml) workflow. The workflow is triggered for every commit to the `main` branch. [`cargo-smart-release`](https://github.com/Byron/gitoxide/tree/main/cargo-smart-release) is used to bump the version, create and publish the new release.
16 |
17 | ### Dependabot
18 |
19 | substrait-rs uses [Depedendabot](https://docs.github.com/en/code-security/dependabot) to update dependencies using the [dependabot.yml](.github/dependabot.yml) configuration file.
20 |
21 | ### Prettier
22 |
23 | substrait-rs uses [Prettier](https://prettier.io/) to format non-Rust source files. The `Formatting` job in the [Check](.github/workflows/check.yml) workflow checks this. To format your files locally (requires [Node.js](https://nodejs.org/en/)):
24 |
25 | ```shell
26 | npm install prettier prettier-plugin-toml --save-dev --save-exact
27 | npx prettier --write --no-config .
28 | ```
29 |
30 | ## Governance
31 |
32 | Please refer to the [Substrait Governance](https://substrait.io/governance/) page.
33 |
34 | ## Community
35 |
36 | Please refer to the [Substrait Community](https://substrait.io/community/) page.
37 |
38 | ## License
39 |
40 | All contributions should be licensed under [Apache License, Version 2.0](LICENSE).
41 | All source files must have a valid SPDX license header. The `SPDX License Header` job in the [Check](.github/workflow/check.yml) workflow checks this.
42 |
43 | Substrait requires all contributors to sign the [Contributor License Agreement (CLA)](https://cla-assistant.io/substrait-io/substrait). There is a GitHub app installed to help new contributors sign it.
44 |
45 | ## Development
46 |
47 | ### Requirements
48 |
49 | - [Rust](https://rustup.rs)
50 | - [protoc (>=3.15)](https://github.com/protocolbuffers/protobuf/releases)
51 |
52 | In environments where no `protoc` is available the `protoc` feature can be enabled to build `protoc` from source:
53 |
54 | ```shell
55 | cargo build --features protoc
56 | ```
57 |
58 | ### Substrait submodule
59 |
60 | There is a git submodule for [Substrait](https://github.com/substrait-io/substrait) that must be cloned when building from source.
61 |
62 | ```shell
63 | git clone --recurse-submodules git@github.com:substrait-io/substrait-rs.git
64 | ```
65 |
66 | When the Substrait version is bumped make sure to update your local submodule.
67 |
68 | ```shell
69 | git submodule update
70 | ```
71 |
72 | Formatting, lints and tests are checked in the [Test](.github/workflows/test.yml) workflow.
73 |
74 | ### Docs
75 |
76 | #### Rustdoc
77 |
78 | The crate documentation is built with [rustdoc](https://doc.rust-lang.org/rustdoc/what-is-rustdoc.html):
79 |
80 | ```shell
81 | cargo doc
82 | ```
83 |
84 | Or to enable automatic feature information (requires a nightly toolchain):
85 |
86 | ```shell
87 | cargo +nightly rustdoc -- --cfg docsrs
88 | ```
89 |
90 | Or use [cargo-doc-rs](https://crates.io/crates/cargo-docs-rs):
91 |
92 | ```
93 | cargo +nightly docs-rs
94 | ```
95 |
96 | ### Formatting
97 |
98 | All Rust code is formatted using [rustfmt](https://github.com/rust-lang/rustfmt):
99 |
100 | ```shell
101 | cargo fmt
102 | ```
103 |
104 | ### Linting
105 |
106 | All Rust code passes [Clippy](https://github.com/rust-lang/rust-clippy) lints without warnings:
107 |
108 | ```shell
109 | cargo clippy -- -Dwarnings
110 | ```
111 |
112 | ### Tests
113 |
114 | To run tests and [documentation tests](https://doc.rust-lang.org/rustdoc/write-documentation/documentation-tests.html):
115 |
116 | ```shell
117 | cargo test
118 | ```
119 |
--------------------------------------------------------------------------------
/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "allocator-api2"
7 | version = "0.2.16"
8 | source = "registry+https://github.com/rust-lang/crates.io-index"
9 | checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
10 |
11 | [[package]]
12 | name = "anyhow"
13 | version = "1.0.71"
14 | source = "registry+https://github.com/rust-lang/crates.io-index"
15 | checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
16 |
17 | [[package]]
18 | name = "autocfg"
19 | version = "1.1.0"
20 | source = "registry+https://github.com/rust-lang/crates.io-index"
21 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
22 |
23 | [[package]]
24 | name = "base64"
25 | version = "0.21.4"
26 | source = "registry+https://github.com/rust-lang/crates.io-index"
27 | checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2"
28 |
29 | [[package]]
30 | name = "bitflags"
31 | version = "1.3.2"
32 | source = "registry+https://github.com/rust-lang/crates.io-index"
33 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
34 |
35 | [[package]]
36 | name = "bytes"
37 | version = "1.4.0"
38 | source = "registry+https://github.com/rust-lang/crates.io-index"
39 | checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
40 |
41 | [[package]]
42 | name = "cc"
43 | version = "1.0.83"
44 | source = "registry+https://github.com/rust-lang/crates.io-index"
45 | checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
46 | dependencies = [
47 | "libc",
48 | ]
49 |
50 | [[package]]
51 | name = "cfg-if"
52 | version = "1.0.0"
53 | source = "registry+https://github.com/rust-lang/crates.io-index"
54 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
55 |
56 | [[package]]
57 | name = "chrono"
58 | version = "0.4.31"
59 | source = "registry+https://github.com/rust-lang/crates.io-index"
60 | checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
61 | dependencies = [
62 | "num-traits",
63 | ]
64 |
65 | [[package]]
66 | name = "cmake"
67 | version = "0.1.53"
68 | source = "registry+https://github.com/rust-lang/crates.io-index"
69 | checksum = "e24a03c8b52922d68a1589ad61032f2c1aa5a8158d2aa0d93c6e9534944bbad6"
70 | dependencies = [
71 | "cc",
72 | ]
73 |
74 | [[package]]
75 | name = "displaydoc"
76 | version = "0.2.5"
77 | source = "registry+https://github.com/rust-lang/crates.io-index"
78 | checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
79 | dependencies = [
80 | "proc-macro2",
81 | "quote",
82 | "syn",
83 | ]
84 |
85 | [[package]]
86 | name = "dyn-clone"
87 | version = "1.0.11"
88 | source = "registry+https://github.com/rust-lang/crates.io-index"
89 | checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30"
90 |
91 | [[package]]
92 | name = "either"
93 | version = "1.8.1"
94 | source = "registry+https://github.com/rust-lang/crates.io-index"
95 | checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
96 |
97 | [[package]]
98 | name = "equivalent"
99 | version = "1.0.0"
100 | source = "registry+https://github.com/rust-lang/crates.io-index"
101 | checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1"
102 |
103 | [[package]]
104 | name = "errno"
105 | version = "0.3.1"
106 | source = "registry+https://github.com/rust-lang/crates.io-index"
107 | checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
108 | dependencies = [
109 | "errno-dragonfly",
110 | "libc",
111 | "windows-sys",
112 | ]
113 |
114 | [[package]]
115 | name = "errno-dragonfly"
116 | version = "0.1.2"
117 | source = "registry+https://github.com/rust-lang/crates.io-index"
118 | checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
119 | dependencies = [
120 | "cc",
121 | "libc",
122 | ]
123 |
124 | [[package]]
125 | name = "fastrand"
126 | version = "1.9.0"
127 | source = "registry+https://github.com/rust-lang/crates.io-index"
128 | checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
129 | dependencies = [
130 | "instant",
131 | ]
132 |
133 | [[package]]
134 | name = "fixedbitset"
135 | version = "0.4.2"
136 | source = "registry+https://github.com/rust-lang/crates.io-index"
137 | checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
138 |
139 | [[package]]
140 | name = "foldhash"
141 | version = "0.1.4"
142 | source = "registry+https://github.com/rust-lang/crates.io-index"
143 | checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f"
144 |
145 | [[package]]
146 | name = "form_urlencoded"
147 | version = "1.2.1"
148 | source = "registry+https://github.com/rust-lang/crates.io-index"
149 | checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
150 | dependencies = [
151 | "percent-encoding",
152 | ]
153 |
154 | [[package]]
155 | name = "hashbrown"
156 | version = "0.12.3"
157 | source = "registry+https://github.com/rust-lang/crates.io-index"
158 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
159 |
160 | [[package]]
161 | name = "hashbrown"
162 | version = "0.14.3"
163 | source = "registry+https://github.com/rust-lang/crates.io-index"
164 | checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
165 |
166 | [[package]]
167 | name = "hashbrown"
168 | version = "0.15.2"
169 | source = "registry+https://github.com/rust-lang/crates.io-index"
170 | checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
171 | dependencies = [
172 | "allocator-api2",
173 | "equivalent",
174 | "foldhash",
175 | ]
176 |
177 | [[package]]
178 | name = "heck"
179 | version = "0.5.0"
180 | source = "registry+https://github.com/rust-lang/crates.io-index"
181 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
182 |
183 | [[package]]
184 | name = "hermit-abi"
185 | version = "0.3.2"
186 | source = "registry+https://github.com/rust-lang/crates.io-index"
187 | checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
188 |
189 | [[package]]
190 | name = "hex"
191 | version = "0.4.3"
192 | source = "registry+https://github.com/rust-lang/crates.io-index"
193 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
194 |
195 | [[package]]
196 | name = "icu_collections"
197 | version = "1.5.0"
198 | source = "registry+https://github.com/rust-lang/crates.io-index"
199 | checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
200 | dependencies = [
201 | "displaydoc",
202 | "yoke",
203 | "zerofrom",
204 | "zerovec",
205 | ]
206 |
207 | [[package]]
208 | name = "icu_locid"
209 | version = "1.5.0"
210 | source = "registry+https://github.com/rust-lang/crates.io-index"
211 | checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637"
212 | dependencies = [
213 | "displaydoc",
214 | "litemap",
215 | "tinystr",
216 | "writeable",
217 | "zerovec",
218 | ]
219 |
220 | [[package]]
221 | name = "icu_locid_transform"
222 | version = "1.5.0"
223 | source = "registry+https://github.com/rust-lang/crates.io-index"
224 | checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e"
225 | dependencies = [
226 | "displaydoc",
227 | "icu_locid",
228 | "icu_locid_transform_data",
229 | "icu_provider",
230 | "tinystr",
231 | "zerovec",
232 | ]
233 |
234 | [[package]]
235 | name = "icu_locid_transform_data"
236 | version = "1.5.0"
237 | source = "registry+https://github.com/rust-lang/crates.io-index"
238 | checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e"
239 |
240 | [[package]]
241 | name = "icu_normalizer"
242 | version = "1.5.0"
243 | source = "registry+https://github.com/rust-lang/crates.io-index"
244 | checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f"
245 | dependencies = [
246 | "displaydoc",
247 | "icu_collections",
248 | "icu_normalizer_data",
249 | "icu_properties",
250 | "icu_provider",
251 | "smallvec",
252 | "utf16_iter",
253 | "utf8_iter",
254 | "write16",
255 | "zerovec",
256 | ]
257 |
258 | [[package]]
259 | name = "icu_normalizer_data"
260 | version = "1.5.0"
261 | source = "registry+https://github.com/rust-lang/crates.io-index"
262 | checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516"
263 |
264 | [[package]]
265 | name = "icu_properties"
266 | version = "1.5.1"
267 | source = "registry+https://github.com/rust-lang/crates.io-index"
268 | checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5"
269 | dependencies = [
270 | "displaydoc",
271 | "icu_collections",
272 | "icu_locid_transform",
273 | "icu_properties_data",
274 | "icu_provider",
275 | "tinystr",
276 | "zerovec",
277 | ]
278 |
279 | [[package]]
280 | name = "icu_properties_data"
281 | version = "1.5.0"
282 | source = "registry+https://github.com/rust-lang/crates.io-index"
283 | checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569"
284 |
285 | [[package]]
286 | name = "icu_provider"
287 | version = "1.5.0"
288 | source = "registry+https://github.com/rust-lang/crates.io-index"
289 | checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9"
290 | dependencies = [
291 | "displaydoc",
292 | "icu_locid",
293 | "icu_provider_macros",
294 | "stable_deref_trait",
295 | "tinystr",
296 | "writeable",
297 | "yoke",
298 | "zerofrom",
299 | "zerovec",
300 | ]
301 |
302 | [[package]]
303 | name = "icu_provider_macros"
304 | version = "1.5.0"
305 | source = "registry+https://github.com/rust-lang/crates.io-index"
306 | checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
307 | dependencies = [
308 | "proc-macro2",
309 | "quote",
310 | "syn",
311 | ]
312 |
313 | [[package]]
314 | name = "idna"
315 | version = "1.0.3"
316 | source = "registry+https://github.com/rust-lang/crates.io-index"
317 | checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
318 | dependencies = [
319 | "idna_adapter",
320 | "smallvec",
321 | "utf8_iter",
322 | ]
323 |
324 | [[package]]
325 | name = "idna_adapter"
326 | version = "1.2.0"
327 | source = "registry+https://github.com/rust-lang/crates.io-index"
328 | checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71"
329 | dependencies = [
330 | "icu_normalizer",
331 | "icu_properties",
332 | ]
333 |
334 | [[package]]
335 | name = "indexmap"
336 | version = "1.9.3"
337 | source = "registry+https://github.com/rust-lang/crates.io-index"
338 | checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
339 | dependencies = [
340 | "autocfg",
341 | "hashbrown 0.12.3",
342 | ]
343 |
344 | [[package]]
345 | name = "indexmap"
346 | version = "2.2.5"
347 | source = "registry+https://github.com/rust-lang/crates.io-index"
348 | checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4"
349 | dependencies = [
350 | "equivalent",
351 | "hashbrown 0.14.3",
352 | ]
353 |
354 | [[package]]
355 | name = "instant"
356 | version = "0.1.12"
357 | source = "registry+https://github.com/rust-lang/crates.io-index"
358 | checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
359 | dependencies = [
360 | "cfg-if",
361 | ]
362 |
363 | [[package]]
364 | name = "io-lifetimes"
365 | version = "1.0.11"
366 | source = "registry+https://github.com/rust-lang/crates.io-index"
367 | checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
368 | dependencies = [
369 | "hermit-abi",
370 | "libc",
371 | "windows-sys",
372 | ]
373 |
374 | [[package]]
375 | name = "itertools"
376 | version = "0.13.0"
377 | source = "registry+https://github.com/rust-lang/crates.io-index"
378 | checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
379 | dependencies = [
380 | "either",
381 | ]
382 |
383 | [[package]]
384 | name = "itoa"
385 | version = "1.0.8"
386 | source = "registry+https://github.com/rust-lang/crates.io-index"
387 | checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a"
388 |
389 | [[package]]
390 | name = "libc"
391 | version = "0.2.153"
392 | source = "registry+https://github.com/rust-lang/crates.io-index"
393 | checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
394 |
395 | [[package]]
396 | name = "linux-raw-sys"
397 | version = "0.3.8"
398 | source = "registry+https://github.com/rust-lang/crates.io-index"
399 | checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
400 |
401 | [[package]]
402 | name = "litemap"
403 | version = "0.7.3"
404 | source = "registry+https://github.com/rust-lang/crates.io-index"
405 | checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704"
406 |
407 | [[package]]
408 | name = "log"
409 | version = "0.4.27"
410 | source = "registry+https://github.com/rust-lang/crates.io-index"
411 | checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
412 |
413 | [[package]]
414 | name = "memchr"
415 | version = "2.5.0"
416 | source = "registry+https://github.com/rust-lang/crates.io-index"
417 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
418 |
419 | [[package]]
420 | name = "multimap"
421 | version = "0.8.3"
422 | source = "registry+https://github.com/rust-lang/crates.io-index"
423 | checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a"
424 |
425 | [[package]]
426 | name = "num-traits"
427 | version = "0.2.15"
428 | source = "registry+https://github.com/rust-lang/crates.io-index"
429 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
430 | dependencies = [
431 | "autocfg",
432 | ]
433 |
434 | [[package]]
435 | name = "once_cell"
436 | version = "1.20.2"
437 | source = "registry+https://github.com/rust-lang/crates.io-index"
438 | checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
439 |
440 | [[package]]
441 | name = "pbjson"
442 | version = "0.7.0"
443 | source = "registry+https://github.com/rust-lang/crates.io-index"
444 | checksum = "c7e6349fa080353f4a597daffd05cb81572a9c031a6d4fff7e504947496fcc68"
445 | dependencies = [
446 | "base64",
447 | "serde",
448 | ]
449 |
450 | [[package]]
451 | name = "pbjson-build"
452 | version = "0.7.0"
453 | source = "registry+https://github.com/rust-lang/crates.io-index"
454 | checksum = "6eea3058763d6e656105d1403cb04e0a41b7bbac6362d413e7c33be0c32279c9"
455 | dependencies = [
456 | "heck",
457 | "itertools",
458 | "prost",
459 | "prost-types",
460 | ]
461 |
462 | [[package]]
463 | name = "pbjson-types"
464 | version = "0.7.0"
465 | source = "registry+https://github.com/rust-lang/crates.io-index"
466 | checksum = "e54e5e7bfb1652f95bc361d76f3c780d8e526b134b85417e774166ee941f0887"
467 | dependencies = [
468 | "bytes",
469 | "chrono",
470 | "pbjson",
471 | "pbjson-build",
472 | "prost",
473 | "prost-build",
474 | "serde",
475 | ]
476 |
477 | [[package]]
478 | name = "percent-encoding"
479 | version = "2.3.1"
480 | source = "registry+https://github.com/rust-lang/crates.io-index"
481 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
482 |
483 | [[package]]
484 | name = "petgraph"
485 | version = "0.6.3"
486 | source = "registry+https://github.com/rust-lang/crates.io-index"
487 | checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4"
488 | dependencies = [
489 | "fixedbitset",
490 | "indexmap 1.9.3",
491 | ]
492 |
493 | [[package]]
494 | name = "prettyplease"
495 | version = "0.2.32"
496 | source = "registry+https://github.com/rust-lang/crates.io-index"
497 | checksum = "664ec5419c51e34154eec046ebcba56312d5a2fc3b09a06da188e1ad21afadf6"
498 | dependencies = [
499 | "proc-macro2",
500 | "syn",
501 | ]
502 |
503 | [[package]]
504 | name = "proc-macro2"
505 | version = "1.0.95"
506 | source = "registry+https://github.com/rust-lang/crates.io-index"
507 | checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
508 | dependencies = [
509 | "unicode-ident",
510 | ]
511 |
512 | [[package]]
513 | name = "prost"
514 | version = "0.13.5"
515 | source = "registry+https://github.com/rust-lang/crates.io-index"
516 | checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5"
517 | dependencies = [
518 | "bytes",
519 | "prost-derive",
520 | ]
521 |
522 | [[package]]
523 | name = "prost-build"
524 | version = "0.13.5"
525 | source = "registry+https://github.com/rust-lang/crates.io-index"
526 | checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf"
527 | dependencies = [
528 | "heck",
529 | "itertools",
530 | "log",
531 | "multimap",
532 | "once_cell",
533 | "petgraph",
534 | "prettyplease",
535 | "prost",
536 | "prost-types",
537 | "regex",
538 | "syn",
539 | "tempfile",
540 | ]
541 |
542 | [[package]]
543 | name = "prost-derive"
544 | version = "0.13.5"
545 | source = "registry+https://github.com/rust-lang/crates.io-index"
546 | checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d"
547 | dependencies = [
548 | "anyhow",
549 | "itertools",
550 | "proc-macro2",
551 | "quote",
552 | "syn",
553 | ]
554 |
555 | [[package]]
556 | name = "prost-types"
557 | version = "0.13.5"
558 | source = "registry+https://github.com/rust-lang/crates.io-index"
559 | checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16"
560 | dependencies = [
561 | "prost",
562 | ]
563 |
564 | [[package]]
565 | name = "protobuf-src"
566 | version = "2.1.1+27.1"
567 | source = "registry+https://github.com/rust-lang/crates.io-index"
568 | checksum = "6217c3504da19b85a3a4b2e9a5183d635822d83507ba0986624b5c05b83bfc40"
569 | dependencies = [
570 | "cmake",
571 | ]
572 |
573 | [[package]]
574 | name = "quote"
575 | version = "1.0.40"
576 | source = "registry+https://github.com/rust-lang/crates.io-index"
577 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
578 | dependencies = [
579 | "proc-macro2",
580 | ]
581 |
582 | [[package]]
583 | name = "redox_syscall"
584 | version = "0.3.5"
585 | source = "registry+https://github.com/rust-lang/crates.io-index"
586 | checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
587 | dependencies = [
588 | "bitflags",
589 | ]
590 |
591 | [[package]]
592 | name = "regex"
593 | version = "1.8.4"
594 | source = "registry+https://github.com/rust-lang/crates.io-index"
595 | checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f"
596 | dependencies = [
597 | "regex-syntax",
598 | ]
599 |
600 | [[package]]
601 | name = "regex-syntax"
602 | version = "0.7.2"
603 | source = "registry+https://github.com/rust-lang/crates.io-index"
604 | checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78"
605 |
606 | [[package]]
607 | name = "regress"
608 | version = "0.10.3"
609 | source = "registry+https://github.com/rust-lang/crates.io-index"
610 | checksum = "78ef7fa9ed0256d64a688a3747d0fef7a88851c18a5e1d57f115f38ec2e09366"
611 | dependencies = [
612 | "hashbrown 0.15.2",
613 | "memchr",
614 | ]
615 |
616 | [[package]]
617 | name = "rustix"
618 | version = "0.37.27"
619 | source = "registry+https://github.com/rust-lang/crates.io-index"
620 | checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2"
621 | dependencies = [
622 | "bitflags",
623 | "errno",
624 | "io-lifetimes",
625 | "libc",
626 | "linux-raw-sys",
627 | "windows-sys",
628 | ]
629 |
630 | [[package]]
631 | name = "ryu"
632 | version = "1.0.14"
633 | source = "registry+https://github.com/rust-lang/crates.io-index"
634 | checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9"
635 |
636 | [[package]]
637 | name = "same-file"
638 | version = "1.0.6"
639 | source = "registry+https://github.com/rust-lang/crates.io-index"
640 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
641 | dependencies = [
642 | "winapi-util",
643 | ]
644 |
645 | [[package]]
646 | name = "schemars"
647 | version = "0.8.22"
648 | source = "registry+https://github.com/rust-lang/crates.io-index"
649 | checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615"
650 | dependencies = [
651 | "dyn-clone",
652 | "schemars_derive",
653 | "serde",
654 | "serde_json",
655 | ]
656 |
657 | [[package]]
658 | name = "schemars_derive"
659 | version = "0.8.22"
660 | source = "registry+https://github.com/rust-lang/crates.io-index"
661 | checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d"
662 | dependencies = [
663 | "proc-macro2",
664 | "quote",
665 | "serde_derive_internals",
666 | "syn",
667 | ]
668 |
669 | [[package]]
670 | name = "semver"
671 | version = "1.0.26"
672 | source = "registry+https://github.com/rust-lang/crates.io-index"
673 | checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
674 | dependencies = [
675 | "serde",
676 | ]
677 |
678 | [[package]]
679 | name = "serde"
680 | version = "1.0.219"
681 | source = "registry+https://github.com/rust-lang/crates.io-index"
682 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
683 | dependencies = [
684 | "serde_derive",
685 | ]
686 |
687 | [[package]]
688 | name = "serde_derive"
689 | version = "1.0.219"
690 | source = "registry+https://github.com/rust-lang/crates.io-index"
691 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
692 | dependencies = [
693 | "proc-macro2",
694 | "quote",
695 | "syn",
696 | ]
697 |
698 | [[package]]
699 | name = "serde_derive_internals"
700 | version = "0.29.1"
701 | source = "registry+https://github.com/rust-lang/crates.io-index"
702 | checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
703 | dependencies = [
704 | "proc-macro2",
705 | "quote",
706 | "syn",
707 | ]
708 |
709 | [[package]]
710 | name = "serde_json"
711 | version = "1.0.140"
712 | source = "registry+https://github.com/rust-lang/crates.io-index"
713 | checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
714 | dependencies = [
715 | "itoa",
716 | "memchr",
717 | "ryu",
718 | "serde",
719 | ]
720 |
721 | [[package]]
722 | name = "serde_tokenstream"
723 | version = "0.2.2"
724 | source = "registry+https://github.com/rust-lang/crates.io-index"
725 | checksum = "64060d864397305347a78851c51588fd283767e7e7589829e8121d65512340f1"
726 | dependencies = [
727 | "proc-macro2",
728 | "quote",
729 | "serde",
730 | "syn",
731 | ]
732 |
733 | [[package]]
734 | name = "serde_yaml"
735 | version = "0.9.34+deprecated"
736 | source = "registry+https://github.com/rust-lang/crates.io-index"
737 | checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
738 | dependencies = [
739 | "indexmap 2.2.5",
740 | "itoa",
741 | "ryu",
742 | "serde",
743 | "unsafe-libyaml",
744 | ]
745 |
746 | [[package]]
747 | name = "smallvec"
748 | version = "1.13.2"
749 | source = "registry+https://github.com/rust-lang/crates.io-index"
750 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
751 |
752 | [[package]]
753 | name = "stable_deref_trait"
754 | version = "1.2.0"
755 | source = "registry+https://github.com/rust-lang/crates.io-index"
756 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
757 |
758 | [[package]]
759 | name = "substrait"
760 | version = "0.57.0"
761 | dependencies = [
762 | "heck",
763 | "hex",
764 | "pbjson",
765 | "pbjson-build",
766 | "pbjson-types",
767 | "prettyplease",
768 | "prost",
769 | "prost-build",
770 | "prost-types",
771 | "protobuf-src",
772 | "regress",
773 | "schemars",
774 | "semver",
775 | "serde",
776 | "serde_json",
777 | "serde_yaml",
778 | "syn",
779 | "thiserror",
780 | "typify",
781 | "url",
782 | "walkdir",
783 | ]
784 |
785 | [[package]]
786 | name = "syn"
787 | version = "2.0.101"
788 | source = "registry+https://github.com/rust-lang/crates.io-index"
789 | checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
790 | dependencies = [
791 | "proc-macro2",
792 | "quote",
793 | "unicode-ident",
794 | ]
795 |
796 | [[package]]
797 | name = "synstructure"
798 | version = "0.13.1"
799 | source = "registry+https://github.com/rust-lang/crates.io-index"
800 | checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
801 | dependencies = [
802 | "proc-macro2",
803 | "quote",
804 | "syn",
805 | ]
806 |
807 | [[package]]
808 | name = "tempfile"
809 | version = "3.6.0"
810 | source = "registry+https://github.com/rust-lang/crates.io-index"
811 | checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6"
812 | dependencies = [
813 | "autocfg",
814 | "cfg-if",
815 | "fastrand",
816 | "redox_syscall",
817 | "rustix",
818 | "windows-sys",
819 | ]
820 |
821 | [[package]]
822 | name = "thiserror"
823 | version = "2.0.12"
824 | source = "registry+https://github.com/rust-lang/crates.io-index"
825 | checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
826 | dependencies = [
827 | "thiserror-impl",
828 | ]
829 |
830 | [[package]]
831 | name = "thiserror-impl"
832 | version = "2.0.12"
833 | source = "registry+https://github.com/rust-lang/crates.io-index"
834 | checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
835 | dependencies = [
836 | "proc-macro2",
837 | "quote",
838 | "syn",
839 | ]
840 |
841 | [[package]]
842 | name = "tinystr"
843 | version = "0.7.6"
844 | source = "registry+https://github.com/rust-lang/crates.io-index"
845 | checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
846 | dependencies = [
847 | "displaydoc",
848 | "zerovec",
849 | ]
850 |
851 | [[package]]
852 | name = "typify"
853 | version = "0.4.1"
854 | source = "registry+https://github.com/rust-lang/crates.io-index"
855 | checksum = "fcc5bec3cdff70fd542e579aa2e52967833e543a25fae0d14579043d2e868a50"
856 | dependencies = [
857 | "typify-impl",
858 | "typify-macro",
859 | ]
860 |
861 | [[package]]
862 | name = "typify-impl"
863 | version = "0.4.1"
864 | source = "registry+https://github.com/rust-lang/crates.io-index"
865 | checksum = "b52a67305054e1da6f3d99ad94875dcd0c7c49adbd17b4b64f0eefb7ae5bf8ab"
866 | dependencies = [
867 | "heck",
868 | "log",
869 | "proc-macro2",
870 | "quote",
871 | "regress",
872 | "schemars",
873 | "semver",
874 | "serde",
875 | "serde_json",
876 | "syn",
877 | "thiserror",
878 | "unicode-ident",
879 | ]
880 |
881 | [[package]]
882 | name = "typify-macro"
883 | version = "0.4.1"
884 | source = "registry+https://github.com/rust-lang/crates.io-index"
885 | checksum = "0ff5799be156e4f635c348c6051d165e1c59997827155133351a8c4d333d9841"
886 | dependencies = [
887 | "proc-macro2",
888 | "quote",
889 | "schemars",
890 | "semver",
891 | "serde",
892 | "serde_json",
893 | "serde_tokenstream",
894 | "syn",
895 | "typify-impl",
896 | ]
897 |
898 | [[package]]
899 | name = "unicode-ident"
900 | version = "1.0.18"
901 | source = "registry+https://github.com/rust-lang/crates.io-index"
902 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
903 |
904 | [[package]]
905 | name = "unsafe-libyaml"
906 | version = "0.2.11"
907 | source = "registry+https://github.com/rust-lang/crates.io-index"
908 | checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
909 |
910 | [[package]]
911 | name = "url"
912 | version = "2.5.4"
913 | source = "registry+https://github.com/rust-lang/crates.io-index"
914 | checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
915 | dependencies = [
916 | "form_urlencoded",
917 | "idna",
918 | "percent-encoding",
919 | ]
920 |
921 | [[package]]
922 | name = "utf16_iter"
923 | version = "1.0.5"
924 | source = "registry+https://github.com/rust-lang/crates.io-index"
925 | checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
926 |
927 | [[package]]
928 | name = "utf8_iter"
929 | version = "1.0.4"
930 | source = "registry+https://github.com/rust-lang/crates.io-index"
931 | checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
932 |
933 | [[package]]
934 | name = "walkdir"
935 | version = "2.5.0"
936 | source = "registry+https://github.com/rust-lang/crates.io-index"
937 | checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
938 | dependencies = [
939 | "same-file",
940 | "winapi-util",
941 | ]
942 |
943 | [[package]]
944 | name = "winapi"
945 | version = "0.3.9"
946 | source = "registry+https://github.com/rust-lang/crates.io-index"
947 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
948 | dependencies = [
949 | "winapi-i686-pc-windows-gnu",
950 | "winapi-x86_64-pc-windows-gnu",
951 | ]
952 |
953 | [[package]]
954 | name = "winapi-i686-pc-windows-gnu"
955 | version = "0.4.0"
956 | source = "registry+https://github.com/rust-lang/crates.io-index"
957 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
958 |
959 | [[package]]
960 | name = "winapi-util"
961 | version = "0.1.5"
962 | source = "registry+https://github.com/rust-lang/crates.io-index"
963 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
964 | dependencies = [
965 | "winapi",
966 | ]
967 |
968 | [[package]]
969 | name = "winapi-x86_64-pc-windows-gnu"
970 | version = "0.4.0"
971 | source = "registry+https://github.com/rust-lang/crates.io-index"
972 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
973 |
974 | [[package]]
975 | name = "windows-sys"
976 | version = "0.48.0"
977 | source = "registry+https://github.com/rust-lang/crates.io-index"
978 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
979 | dependencies = [
980 | "windows-targets",
981 | ]
982 |
983 | [[package]]
984 | name = "windows-targets"
985 | version = "0.48.1"
986 | source = "registry+https://github.com/rust-lang/crates.io-index"
987 | checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f"
988 | dependencies = [
989 | "windows_aarch64_gnullvm",
990 | "windows_aarch64_msvc",
991 | "windows_i686_gnu",
992 | "windows_i686_msvc",
993 | "windows_x86_64_gnu",
994 | "windows_x86_64_gnullvm",
995 | "windows_x86_64_msvc",
996 | ]
997 |
998 | [[package]]
999 | name = "windows_aarch64_gnullvm"
1000 | version = "0.48.0"
1001 | source = "registry+https://github.com/rust-lang/crates.io-index"
1002 | checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
1003 |
1004 | [[package]]
1005 | name = "windows_aarch64_msvc"
1006 | version = "0.48.0"
1007 | source = "registry+https://github.com/rust-lang/crates.io-index"
1008 | checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
1009 |
1010 | [[package]]
1011 | name = "windows_i686_gnu"
1012 | version = "0.48.0"
1013 | source = "registry+https://github.com/rust-lang/crates.io-index"
1014 | checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
1015 |
1016 | [[package]]
1017 | name = "windows_i686_msvc"
1018 | version = "0.48.0"
1019 | source = "registry+https://github.com/rust-lang/crates.io-index"
1020 | checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
1021 |
1022 | [[package]]
1023 | name = "windows_x86_64_gnu"
1024 | version = "0.48.0"
1025 | source = "registry+https://github.com/rust-lang/crates.io-index"
1026 | checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
1027 |
1028 | [[package]]
1029 | name = "windows_x86_64_gnullvm"
1030 | version = "0.48.0"
1031 | source = "registry+https://github.com/rust-lang/crates.io-index"
1032 | checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
1033 |
1034 | [[package]]
1035 | name = "windows_x86_64_msvc"
1036 | version = "0.48.0"
1037 | source = "registry+https://github.com/rust-lang/crates.io-index"
1038 | checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
1039 |
1040 | [[package]]
1041 | name = "write16"
1042 | version = "1.0.0"
1043 | source = "registry+https://github.com/rust-lang/crates.io-index"
1044 | checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
1045 |
1046 | [[package]]
1047 | name = "writeable"
1048 | version = "0.5.5"
1049 | source = "registry+https://github.com/rust-lang/crates.io-index"
1050 | checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
1051 |
1052 | [[package]]
1053 | name = "yoke"
1054 | version = "0.7.4"
1055 | source = "registry+https://github.com/rust-lang/crates.io-index"
1056 | checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5"
1057 | dependencies = [
1058 | "serde",
1059 | "stable_deref_trait",
1060 | "yoke-derive",
1061 | "zerofrom",
1062 | ]
1063 |
1064 | [[package]]
1065 | name = "yoke-derive"
1066 | version = "0.7.4"
1067 | source = "registry+https://github.com/rust-lang/crates.io-index"
1068 | checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95"
1069 | dependencies = [
1070 | "proc-macro2",
1071 | "quote",
1072 | "syn",
1073 | "synstructure",
1074 | ]
1075 |
1076 | [[package]]
1077 | name = "zerofrom"
1078 | version = "0.1.4"
1079 | source = "registry+https://github.com/rust-lang/crates.io-index"
1080 | checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55"
1081 | dependencies = [
1082 | "zerofrom-derive",
1083 | ]
1084 |
1085 | [[package]]
1086 | name = "zerofrom-derive"
1087 | version = "0.1.4"
1088 | source = "registry+https://github.com/rust-lang/crates.io-index"
1089 | checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5"
1090 | dependencies = [
1091 | "proc-macro2",
1092 | "quote",
1093 | "syn",
1094 | "synstructure",
1095 | ]
1096 |
1097 | [[package]]
1098 | name = "zerovec"
1099 | version = "0.10.4"
1100 | source = "registry+https://github.com/rust-lang/crates.io-index"
1101 | checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079"
1102 | dependencies = [
1103 | "yoke",
1104 | "zerofrom",
1105 | "zerovec-derive",
1106 | ]
1107 |
1108 | [[package]]
1109 | name = "zerovec-derive"
1110 | version = "0.10.3"
1111 | source = "registry+https://github.com/rust-lang/crates.io-index"
1112 | checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
1113 | dependencies = [
1114 | "proc-macro2",
1115 | "quote",
1116 | "syn",
1117 | ]
1118 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: Apache-2.0
2 | [package]
3 | name = "substrait"
4 | version = "0.57.0"
5 | edition = "2021"
6 | rust-version = "1.80.1"
7 | description = "Cross-Language Serialization for Relational Algebra"
8 | documentation = "https://docs.rs/substrait"
9 | readme = "README.md"
10 | homepage = "https://substrait.io"
11 | repository = "https://github.com/substrait-io/substrait-rs"
12 | license = "Apache-2.0"
13 | keywords = ["substrait"]
14 | build = "build.rs"
15 | include = [
16 | "LICENSE",
17 | "build.rs",
18 | "gen/",
19 | "src/**/*.rs",
20 | "substrait/LICENSE",
21 | "substrait/README.md",
22 | "substrait/extensions/**/*.yaml",
23 | "substrait/proto/**/*.proto",
24 | "substrait/text/**/*.yaml",
25 | ]
26 |
27 | [features]
28 | default = []
29 | extensions = ["dep:serde_yaml"]
30 | parse = ["dep:hex", "dep:thiserror", "dep:url", "semver"]
31 | protoc = ["dep:protobuf-src"]
32 | semver = ["dep:semver"]
33 | serde = ["dep:pbjson", "dep:pbjson-build", "dep:pbjson-types"]
34 |
35 | [dependencies]
36 | hex = { version = "0.4.3", optional = true }
37 | pbjson = { version = "0.7.0", optional = true }
38 | pbjson-types = { version = "0.7.0", optional = true }
39 | prost = "0.13.5"
40 | prost-types = "0.13.5"
41 | url = { version = "2.5.4", optional = true }
42 | regress = "0.10.3"
43 | semver = { version = "1.0.26", optional = true }
44 | serde = { version = "1.0.219", features = ["derive"] }
45 | serde_json = "1.0.140"
46 | serde_yaml = { version = "0.9.34", optional = true }
47 | thiserror = { version = "2.0.12", optional = true }
48 |
49 | [build-dependencies]
50 | heck = "0.5.0"
51 | pbjson-build = { version = "0.7.0", optional = true }
52 | prettyplease = "0.2.32"
53 | prost-build = { version = "0.13.5", default-features = false }
54 | protobuf-src = { version = "2.1.1", optional = true }
55 | schemars = "0.8.22"
56 | semver = "1.0.26"
57 | serde_yaml = "0.9.34"
58 | syn = "2.0.101"
59 | typify = "0.4.1"
60 | walkdir = "2.5.0"
61 |
62 | [package.metadata.docs.rs]
63 | all-features = true
64 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the reference the license in your work:
179 |
180 | Add the following as a comment header in your files:
181 |
182 | SPDX-License-Identifier: Apache-2.0
183 |
184 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
4 |
5 | # substrait-rs
6 |
7 | [](https://substrait.io)
8 |
9 | [](https://crates.io/crates/substrait)
10 | [](https://docs.rs/substrait)
11 |
12 | Rust crate for [Substrait](https://substrait.io/): Cross-Language Serialization for Relational Algebra.
13 |
14 | ## Documentation
15 |
16 | - [Docs (release)](https://docs.rs/substrait)
17 | - [Docs (main)](https://substrait-io.github.io/substrait-rs/)
18 |
--------------------------------------------------------------------------------
/build.rs:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: Apache-2.0
2 |
3 | use prost_build::Config;
4 | use std::{
5 | env,
6 | error::Error,
7 | fs::{self, File},
8 | io::Write,
9 | path::{Path, PathBuf},
10 | process::Command,
11 | };
12 | use walkdir::{DirEntry, WalkDir};
13 |
14 | const SUBMODULE_ROOT: &str = "substrait";
15 | #[cfg(feature = "extensions")]
16 | const EXTENSIONS_ROOT: &str = "substrait/extensions";
17 | const PROTO_ROOT: &str = "substrait/proto";
18 | const TEXT_ROOT: &str = "substrait/text";
19 | const GEN_ROOT: &str = "gen";
20 |
21 | /// Add Substrait version information to the build
22 | fn substrait_version() -> Result> {
23 | let gen_dir = Path::new(GEN_ROOT);
24 | fs::create_dir_all(gen_dir)?;
25 |
26 | let version_in_file = gen_dir.join("version.in");
27 | let substrait_version_file = gen_dir.join("version");
28 |
29 | // Rerun if the Substrait submodule changed (to allow setting `dirty`)
30 | println!(
31 | "cargo:rerun-if-changed={}",
32 | Path::new("substrait").display()
33 | );
34 |
35 | // Check if there is a submodule. This file is not included in the packaged crate.
36 | if Path::new(SUBMODULE_ROOT).join(".git").exists() {
37 | // Rerun if the Substrait submodule HEAD changed (when there is a submodule)
38 | println!(
39 | "cargo:rerun-if-changed={}",
40 | Path::new(".git/modules/substrait/HEAD").display()
41 | );
42 |
43 | // Get the version of the submodule by directly calling `git describe`.
44 | let git_describe = String::from_utf8(
45 | Command::new("git")
46 | .current_dir(SUBMODULE_ROOT)
47 | .arg("describe")
48 | .arg("--tags")
49 | .arg("--long")
50 | .arg("--dirty=-dirty")
51 | .arg("--abbrev=40")
52 | .output()?
53 | .stdout,
54 | )?;
55 |
56 | // Extract the parts.
57 | let mut split = git_describe.split('-');
58 | let git_version = split.next().unwrap_or_default();
59 | let git_depth = split.next().unwrap_or_default();
60 | let git_hash = split.next().unwrap_or_default().trim_end();
61 | let git_dirty = git_describe.ends_with("dirty");
62 | let version = semver::Version::parse(git_version.trim_start_matches('v'))?;
63 |
64 | let &semver::Version {
65 | major,
66 | minor,
67 | patch,
68 | ..
69 | } = &version;
70 |
71 | fs::write(
72 | version_in_file,
73 | format!(
74 | r#"// SPDX-License-Identifier: Apache-2.0
75 |
76 | // Note that this file is auto-generated and auto-synced using `build.rs`. It is
77 | // included in `version.rs`.
78 |
79 | /// The major version of Substrait used to build this crate
80 | pub const SUBSTRAIT_MAJOR_VERSION: u32 = {major};
81 |
82 | /// The minor version of Substrait used to build this crate
83 | pub const SUBSTRAIT_MINOR_VERSION: u32 = {minor};
84 |
85 | /// The patch version of Substrait used to build this crate
86 | pub const SUBSTRAIT_PATCH_VERSION: u32 = {patch};
87 |
88 | /// The Git SHA (lower hex) of Substrait used to build this crate
89 | pub const SUBSTRAIT_GIT_SHA: &str = "{git_hash}";
90 |
91 | /// The `git describe` output of the Substrait submodule used to build this
92 | /// crate
93 | pub const SUBSTRAIT_GIT_DESCRIBE: &str = "{git_describe}";
94 |
95 | /// The amount of commits between the latest tag and the version of the
96 | /// Substrait submodule used to build this crate
97 | pub const SUBSTRAIT_GIT_DEPTH: u32 = {git_depth};
98 |
99 | /// The dirty state of the Substrait submodule used to build this crate
100 | pub const SUBSTRAIT_GIT_DIRTY: bool = {git_dirty};
101 |
102 | /// A constant with the Substrait version as name, to trigger semver bumps when
103 | /// the Substrait version changes.
104 | #[doc(hidden)]
105 | pub const SUBSTRAIT_{major}_{minor}_{patch}: () = ();
106 | "#
107 | ),
108 | )?;
109 |
110 | // Also write the version to a file
111 | fs::write(substrait_version_file, version.to_string())?;
112 |
113 | Ok(version)
114 | } else {
115 | // If we don't have a version file yet we fail the build.
116 | if !version_in_file.exists() {
117 | panic!("Couldn't find the substrait submodule. Please clone the submodule: `git submodule update --init`.")
118 | }
119 |
120 | // File exists we should get the version and return it.
121 | Ok(semver::Version::parse(&fs::read_to_string(
122 | substrait_version_file,
123 | )?)?)
124 | }
125 | }
126 |
127 | /// `text` type generation
128 | fn text(out_dir: &Path) -> Result<(), Box> {
129 | use heck::ToSnakeCase;
130 | use schemars::schema::{RootSchema, Schema};
131 | use typify::{TypeSpace, TypeSpaceSettings};
132 |
133 | let mut out_file = File::create(out_dir.join("substrait_text").with_extension("rs"))?;
134 |
135 | for schema_path in WalkDir::new(TEXT_ROOT)
136 | .into_iter()
137 | .filter_map(Result::ok)
138 | .filter(|entry| entry.file_type().is_file() || entry.file_type().is_symlink())
139 | .filter(|entry| {
140 | entry
141 | .path()
142 | .extension()
143 | .filter(|&extension| extension == "yaml") // Option::contains
144 | .is_some()
145 | })
146 | .map(DirEntry::into_path)
147 | .inspect(|entry| {
148 | println!("cargo:rerun-if-changed={}", entry.display());
149 | })
150 | {
151 | let schema = serde_yaml::from_reader::<_, RootSchema>(File::open(&schema_path)?)?;
152 | let metadata = schema.schema.metadata.as_ref();
153 | let id = metadata
154 | .and_then(|metadata| metadata.id.as_ref())
155 | .map(ToString::to_string)
156 | .unwrap_or_else(|| {
157 | panic!(
158 | "$id missing in schema metadata (`{}`)",
159 | schema_path.display()
160 | )
161 | });
162 | let title = metadata
163 | .and_then(|metadata| metadata.title.as_ref())
164 | .map(|title| title.to_snake_case())
165 | .unwrap_or_else(|| {
166 | panic!(
167 | "title missing in schema metadata (`{}`)",
168 | schema_path.display()
169 | )
170 | });
171 | let mut type_space = TypeSpace::new(TypeSpaceSettings::default().with_struct_builder(true));
172 | type_space.add_ref_types(schema.definitions)?;
173 | type_space.add_type(&Schema::Object(schema.schema))?;
174 | out_file.write_fmt(format_args!(
175 | r#"
176 | #[doc = "Generated types for `{id}`"]
177 | pub mod {title} {{
178 | {}
179 | }}"#,
180 | prettyplease::unparse(&syn::parse2::(type_space.to_stream())?),
181 | ))?;
182 | }
183 | Ok(())
184 | }
185 |
186 | #[cfg(feature = "extensions")]
187 | /// Add Substrait core extensions
188 | fn extensions(version: semver::Version, out_dir: &Path) -> Result<(), Box> {
189 | use std::collections::HashMap;
190 |
191 | let substrait_extensions_file = out_dir.join("extensions.in");
192 |
193 | let mut output = String::from(
194 | r#"// SPDX-License-Identifier: Apache-2.0
195 | // Note that this file is auto-generated and auto-synced using `build.rs`. It is
196 | // included in `extensions.rs`.
197 | "#,
198 | );
199 | let mut map = HashMap::::default();
200 | for extension in WalkDir::new(EXTENSIONS_ROOT)
201 | .into_iter()
202 | .filter_map(Result::ok)
203 | .filter(|entry| entry.file_type().is_file())
204 | .filter(|entry| {
205 | entry
206 | .path()
207 | .extension()
208 | .filter(|&extension| extension == "yaml")
209 | .is_some()
210 | })
211 | .map(DirEntry::into_path)
212 | .inspect(|entry| {
213 | println!("cargo:rerun-if-changed={}", entry.display());
214 | })
215 | {
216 | let name = extension.file_stem().unwrap_or_default().to_string_lossy();
217 | let url = format!(
218 | "https://github.com/substrait-io/substrait/raw/v{}/extensions/{}",
219 | version,
220 | extension.file_name().unwrap_or_default().to_string_lossy()
221 | );
222 | let var_name = name.to_uppercase();
223 | output.push_str(&format!(
224 | r#"
225 | /// Included source of [`{name}`]({url}).
226 | const {var_name}: &str = include_str!("{}/{}");
227 | "#,
228 | PathBuf::from(dbg!(env::var("CARGO_MANIFEST_DIR").unwrap())).display(),
229 | extension.display()
230 | ));
231 | map.insert(url, var_name);
232 | }
233 | // Add static lookup map.
234 | output.push_str(
235 | r#"
236 | use std::collections::HashMap;
237 | use std::str::FromStr;
238 | use std::sync::LazyLock;
239 | use crate::text::simple_extensions::SimpleExtensions;
240 | use url::Url;
241 |
242 | /// Map with Substrait core extensions. Maps URIs to included extensions.
243 | pub static EXTENSIONS: LazyLock> = LazyLock::new(|| {
244 | let mut map = HashMap::new();"#,
245 | );
246 |
247 | for (url, var_name) in map {
248 | output.push_str(&format!(r#"
249 | map.insert(Url::from_str("{url}").expect("a valid url"), serde_yaml::from_str({var_name}).expect("a valid core extension"));"#));
250 | }
251 |
252 | output.push_str(
253 | r#"
254 | map
255 | });"#,
256 | );
257 |
258 | // Write the file.
259 | fs::write(substrait_extensions_file, output)?;
260 |
261 | Ok(())
262 | }
263 |
264 | #[cfg(feature = "serde")]
265 | /// Serialize and deserialize implementations for proto types using `pbjson`
266 | fn serde(protos: &[impl AsRef], out_dir: PathBuf) -> Result<(), Box> {
267 | use pbjson_build::Builder;
268 |
269 | let descriptor_path = out_dir.join("proto_descriptor.bin");
270 | let mut cfg = Config::new();
271 | cfg.file_descriptor_set_path(&descriptor_path);
272 | cfg.compile_well_known_types()
273 | .extern_path(".google.protobuf", "::pbjson_types")
274 | .compile_protos(protos, &[PROTO_ROOT])?;
275 |
276 | Builder::new()
277 | .register_descriptors(&fs::read(descriptor_path)?)?
278 | .build(&[".substrait"])?;
279 |
280 | Ok(())
281 | }
282 |
283 | fn main() -> Result<(), Box> {
284 | // for use in docker build where file changes can be wonky
285 | println!("cargo:rerun-if-env-changed=FORCE_REBUILD");
286 |
287 | let _version = substrait_version()?;
288 |
289 | #[cfg(feature = "protoc")]
290 | std::env::set_var("PROTOC", protobuf_src::protoc());
291 |
292 | let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
293 |
294 | text(out_dir.as_path())?;
295 |
296 | #[cfg(feature = "extensions")]
297 | extensions(_version, out_dir.as_path())?;
298 |
299 | let protos = WalkDir::new(PROTO_ROOT)
300 | .into_iter()
301 | .filter_map(Result::ok)
302 | .filter(|entry| entry.file_type().is_file() || entry.file_type().is_symlink())
303 | .filter(|entry| {
304 | entry
305 | .path()
306 | .extension()
307 | .filter(|&extension| extension == "proto")
308 | .is_some()
309 | })
310 | .map(DirEntry::into_path)
311 | .inspect(|entry| {
312 | println!("cargo:rerun-if-changed={}", entry.display());
313 | })
314 | .collect::>();
315 |
316 | #[cfg(feature = "serde")]
317 | serde(&protos, out_dir)?;
318 |
319 | #[cfg(not(feature = "serde"))]
320 | Config::new().compile_protos(&protos, &[PROTO_ROOT])?;
321 |
322 | Ok(())
323 | }
324 |
--------------------------------------------------------------------------------
/commitlint.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ["@commitlint/config-conventional"],
3 | rules: {
4 | "body-max-line-length": [0, "always", Infinity],
5 | "footer-max-line-length": [0, "always", Infinity],
6 | "header-max-length": [0, "always", Infinity],
7 | },
8 | };
9 |
--------------------------------------------------------------------------------
/release-plz.toml:
--------------------------------------------------------------------------------
1 | [workspace]
2 | release_always = false
3 |
4 | [[package]]
5 | name = "substrait"
6 | publish_allow_dirty = true
7 |
--------------------------------------------------------------------------------
/src/extensions.rs:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: Apache-2.0
2 |
3 | //! Substrait core extensions
4 | //!
5 | //! The contents of this module are auto-generated using `build.rs`. It is
6 | //! included in the packaged crate, ignored by git, and automatically kept
7 | //! in-sync.
8 |
9 | include!(concat!(env!("OUT_DIR"), "/extensions.in"));
10 |
11 | #[cfg(test)]
12 | mod tests {
13 | use super::*;
14 |
15 | use std::sync::LazyLock;
16 |
17 | #[test]
18 | fn core_extensions() {
19 | // Force evaluation of core extensions.
20 | LazyLock::force(&EXTENSIONS);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/lib.rs:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: Apache-2.0
2 |
3 | //! [Substrait]: Cross-Language Serialization for Relational Algebra
4 | //!
5 | //! # Serialization and deserialization
6 | //!
7 | //! This crate provides generated types to serialize and deserialize Substrait
8 | //! data.
9 | //!
10 | //! ## Protobuf
11 | //!
12 | //! Protobuf serialization and deserialization are provided via [prost] in the
13 | //! [proto] module.
14 | //!
15 | //! ### Example
16 | //!
17 | //! #### Serialize and deserialize a plan
18 | //! ```rust
19 | //! # fn main() -> Result<(), prost::DecodeError> {
20 | //! use prost::Message;
21 | //! use substrait::proto::Plan;
22 | //!
23 | //! let plan = Plan::default();
24 | //!
25 | //! // Serialize the plan
26 | //! let encoded = plan.encode_to_vec();
27 | //!
28 | //! // Deserialize the buffer to a Plan
29 | //! let decoded = Plan::decode(encoded.as_slice())?;
30 | //!
31 | //! assert_eq!(plan, decoded);
32 | //! # Ok(()) }
33 | //! ```
34 | //!
35 | //! ### Serde support
36 | //!
37 | //! The `serde` feature generates serde implementations that match the [Protobuf JSON Mapping]
38 | //! via [pbjson].
39 | //!
40 | //! ##### Example
41 | //! ###### Deserialize a plan version using the `serde` feature
42 | //! ```rust
43 | //! # fn main() -> Result<(), serde_json::Error> {
44 | //! # #[cfg(feature="serde")] {
45 | //! use substrait::proto::Version;
46 | //!
47 | //! let version_json = r#"{
48 | //! "minorNumber": 21
49 | //! }"#;
50 | //!
51 | //! let version = serde_json::from_str::(version_json)?;
52 | //! assert_eq!(
53 | //! version,
54 | //! Version {
55 | //! minor_number: 21,
56 | //! ..Default::default()
57 | //! }
58 | //! );
59 | //! # } Ok(()) }
60 | //! ```
61 | //!
62 | //! ## Text
63 | //!
64 | //! Substrait defines a YAML schema for extensions. Types with serialization and
65 | //! deserialization support for these are provided via [typify] in the [text]
66 | //! module.
67 | //!
68 | //! ### Example
69 | //!
70 | //! #### Read a simple extension
71 | //! ```rust
72 | //! # #[cfg(feature="extensions")]
73 | //! # fn main() -> Result<(), serde_yaml::Error> {
74 | //! use substrait::text::simple_extensions::SimpleExtensions;
75 | //!
76 | //! let simple_extension_yaml = r#"
77 | //! %YAML 1.2
78 | //! ---
79 | //! scalar_functions:
80 | //! -
81 | //! name: "add"
82 | //! description: "Add two values."
83 | //! impls:
84 | //! - args:
85 | //! - name: x
86 | //! value: i8
87 | //! - name: y
88 | //! value: i8
89 | //! options:
90 | //! overflow:
91 | //! values: [ SILENT, SATURATE, ERROR ]
92 | //! return: i8
93 | //! "#;
94 | //!
95 | //! let simple_extension = serde_yaml::from_str::(simple_extension_yaml)?;
96 | //!
97 | //! assert_eq!(simple_extension.scalar_functions.len(), 1);
98 | //! assert_eq!(simple_extension.scalar_functions[0].name, "add");
99 | //! # Ok(()) }
100 | //! # #[cfg(not(feature="extensions"))]
101 | //! # fn main() {}
102 | //! ```
103 | //!
104 | //! [pbjson]: https://docs.rs/pbjson
105 | //! [Protobuf JSON Mapping]:
106 | //! https://developers.google.com/protocol-buffers/docs/proto3#json
107 | //! [Substrait]: https://substrait.io
108 | //! [typify]: https://docs.rs/typify
109 |
110 | #![doc(
111 | html_logo_url = "https://raw.githubusercontent.com/substrait-io/substrait/main/site/docs/img/logo.svg",
112 | html_favicon_url = "https://raw.githubusercontent.com/substrait-io/substrait/main/site/docs/img/logo.svg"
113 | )]
114 | #![cfg_attr(docsrs, feature(doc_auto_cfg))]
115 | #![deny(missing_docs)]
116 |
117 | #[cfg(feature = "extensions")]
118 | pub mod extensions;
119 | #[allow(missing_docs)]
120 | pub mod proto;
121 | #[allow(missing_docs)]
122 | pub mod text;
123 | pub mod version;
124 |
125 | #[cfg(feature = "parse")]
126 | pub mod parse;
127 |
--------------------------------------------------------------------------------
/src/parse/context.rs:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: Apache-2.0
2 |
3 | //! A parse context.
4 |
5 | use thiserror::Error;
6 |
7 | use crate::parse::{
8 | proto::extensions::SimpleExtensionUri, text::simple_extensions::SimpleExtensions, Anchor, Parse,
9 | };
10 |
11 | /// A parse context.
12 | ///
13 | /// Parsing Substrait data is context-sensitive. This trait provides methods
14 | /// that can be used by parser implementations to parse Substrait data.
15 | pub trait Context {
16 | /// Parse an item with this context.
17 | ///
18 | /// See [Parse::parse].
19 | fn parse>(&mut self, item: T) -> Result
20 | where
21 | Self: Sized,
22 | {
23 | item.parse(self)
24 | }
25 |
26 | /// Add a [SimpleExtensionUri] to this context. Must return an error for duplicate
27 | /// anchors or when the URI is not supported.
28 | ///
29 | /// This function must eagerly resolve and parse the simple extension, returning an
30 | /// error if either fails.
31 | fn add_simple_extension_uri(
32 | &mut self,
33 | simple_extension_uri: &SimpleExtensionUri,
34 | ) -> Result<&SimpleExtensions, ContextError>;
35 |
36 | /// Returns the simple extensions for the given simple extension anchor.
37 | fn simple_extensions(
38 | &self,
39 | anchor: &Anchor,
40 | ) -> Result<&SimpleExtensions, ContextError>;
41 | }
42 |
43 | /// Parse context errors.
44 | #[derive(Debug, Error, PartialEq)]
45 | pub enum ContextError {
46 | /// Undefined reference to simple extension.
47 | #[error("undefined reference to simple extension with anchor `{0}`")]
48 | UndefinedSimpleExtension(Anchor),
49 |
50 | /// Duplicate anchor for simple extension.
51 | #[error("duplicate anchor `{0}` for simple extension")]
52 | DuplicateSimpleExtension(Anchor),
53 |
54 | /// Unsupported simple extension URI.
55 | #[error("unsupported simple extension URI: {0}")]
56 | UnsupportedURI(String),
57 | }
58 |
59 | #[cfg(test)]
60 | pub(crate) mod tests {
61 | use std::collections::{hash_map::Entry, HashMap};
62 |
63 | use crate::parse::{
64 | context::ContextError, proto::extensions::SimpleExtensionUri,
65 | text::simple_extensions::SimpleExtensions, Anchor,
66 | };
67 |
68 | /// A test context.
69 | ///
70 | /// This currently mocks support for simple extensions (does not resolve or
71 | /// parse).
72 | pub struct Context {
73 | empty_simple_extensions: SimpleExtensions,
74 | simple_extensions: HashMap, SimpleExtensionUri>,
75 | }
76 |
77 | impl Default for Context {
78 | fn default() -> Self {
79 | Self {
80 | empty_simple_extensions: SimpleExtensions {},
81 | simple_extensions: Default::default(),
82 | }
83 | }
84 | }
85 |
86 | impl super::Context for Context {
87 | fn add_simple_extension_uri(
88 | &mut self,
89 | simple_extension_uri: &crate::parse::proto::extensions::SimpleExtensionUri,
90 | ) -> Result<&SimpleExtensions, ContextError> {
91 | match self.simple_extensions.entry(simple_extension_uri.anchor()) {
92 | Entry::Occupied(_) => Err(ContextError::DuplicateSimpleExtension(
93 | simple_extension_uri.anchor(),
94 | )),
95 | Entry::Vacant(entry) => {
96 | // This is where we would resolve and then parse.
97 | // This check shows the use of the unsupported uri error.
98 | if let "http" | "https" | "file" = simple_extension_uri.uri().scheme() {
99 | entry.insert(simple_extension_uri.clone());
100 | // Here we just return an empty simple extensions.
101 | Ok(&self.empty_simple_extensions)
102 | } else {
103 | Err(ContextError::UnsupportedURI(format!(
104 | "`{}` scheme not supported",
105 | simple_extension_uri.uri().scheme()
106 | )))
107 | }
108 | }
109 | }
110 | }
111 |
112 | fn simple_extensions(
113 | &self,
114 | anchor: &Anchor,
115 | ) -> Result<&SimpleExtensions, ContextError> {
116 | self.simple_extensions
117 | .contains_key(anchor)
118 | .then_some(&self.empty_simple_extensions)
119 | .ok_or(ContextError::UndefinedSimpleExtension(*anchor))
120 | }
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/src/parse/mod.rs:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: Apache-2.0
2 |
3 | //! Parsing of Substrait data.
4 | //!
5 | //! Some requirements of Substrait data can not be expressed via Protobuf
6 | //! definition or schema files. This module provides new types for the generated
7 | //! types, that when constructed are known to be checked. This enables producers
8 | //! and consumers to skip redundant checking of invariants described by the
9 | //! specification.
10 | //!
11 | //! This is based on the idea described in the [Parse don't
12 | //! validate](https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/)
13 | //! blog post.
14 |
15 | use std::{error::Error, fmt::Debug};
16 |
17 | mod context;
18 | pub use context::Context;
19 |
20 | pub mod proto;
21 | pub mod text;
22 |
23 | mod typed;
24 | pub use typed::Anchor;
25 |
26 | /// A parse trait.
27 | pub trait Parse: Debug + Sized {
28 | /// The parsed type.
29 | ///
30 | /// After parsing this type must be able to convert back. Note that it is
31 | /// not required for the conversion to be lossless as long as the semantics
32 | /// don't change.
33 | ///
34 | // This bound also helps with tracking breaking Protobuf definition changes
35 | // via compilation errors.
36 | type Parsed: Into;
37 |
38 | /// The error type for this parser.
39 | type Error: Error;
40 |
41 | /// Parse and return a parsed type or error.
42 | fn parse(self, ctx: &mut C) -> Result;
43 | }
44 |
--------------------------------------------------------------------------------
/src/parse/proto/extensions/mod.rs:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: Apache-2.0
2 |
3 | //! Parsing of [crate::proto::extensions] types.
4 |
5 | mod simple_extension_uri;
6 | pub use simple_extension_uri::SimpleExtensionUri;
7 |
--------------------------------------------------------------------------------
/src/parse/proto/extensions/simple_extension_uri.rs:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: Apache-2.0
2 |
3 | //! Parsing of [proto::extensions::SimpleExtensionUri].
4 |
5 | use thiserror::Error;
6 | use url::Url;
7 |
8 | use crate::{
9 | parse::{context::ContextError, Anchor, Context, Parse},
10 | proto,
11 | };
12 |
13 | /// A parsed [proto::extensions::SimpleExtensionUri].
14 | #[derive(Clone, Debug, PartialEq)]
15 | pub struct SimpleExtensionUri {
16 | /// The URI of this simple extension.
17 | uri: Url,
18 |
19 | /// The anchor value of this simple extension.
20 | anchor: Anchor,
21 | }
22 |
23 | impl SimpleExtensionUri {
24 | /// Returns the uri of this simple extension.
25 | ///
26 | /// See [proto::extensions::SimpleExtensionUri::uri].
27 | pub fn uri(&self) -> &Url {
28 | &self.uri
29 | }
30 |
31 | /// Returns the anchor value of this simple extension.
32 | ///
33 | /// See [proto::extensions::SimpleExtensionUri::extension_uri_anchor].
34 | pub fn anchor(&self) -> Anchor {
35 | self.anchor
36 | }
37 | }
38 |
39 | /// Parse errors for [proto::extensions::SimpleExtensionUri].
40 | #[derive(Debug, Error, PartialEq)]
41 | pub enum SimpleExtensionUriError {
42 | /// Invalid URI
43 | #[error("invalid URI: {0}")]
44 | InvalidURI(#[from] url::ParseError),
45 |
46 | /// Context error
47 | #[error(transparent)]
48 | Context(#[from] ContextError),
49 | }
50 |
51 | impl Parse for proto::extensions::SimpleExtensionUri {
52 | type Parsed = SimpleExtensionUri;
53 | type Error = SimpleExtensionUriError;
54 |
55 | fn parse(self, ctx: &mut C) -> Result {
56 | let proto::extensions::SimpleExtensionUri {
57 | extension_uri_anchor: anchor,
58 | uri,
59 | } = self;
60 |
61 | // The uri is is required and must be valid.
62 | let uri = Url::parse(&uri)?;
63 |
64 | // Construct the parsed simple extension URI.
65 | let simple_extension_uri = SimpleExtensionUri {
66 | uri,
67 | anchor: Anchor::new(anchor),
68 | };
69 |
70 | // Make sure the URI is supported by this parse context, resolves and
71 | // parses, and the anchor is unique.
72 | ctx.add_simple_extension_uri(&simple_extension_uri)?;
73 |
74 | Ok(simple_extension_uri)
75 | }
76 | }
77 |
78 | impl From for proto::extensions::SimpleExtensionUri {
79 | fn from(simple_extension_uri: SimpleExtensionUri) -> Self {
80 | let SimpleExtensionUri { uri, anchor } = simple_extension_uri;
81 | proto::extensions::SimpleExtensionUri {
82 | uri: uri.to_string(),
83 | extension_uri_anchor: anchor.into_inner(),
84 | }
85 | }
86 | }
87 |
88 | #[cfg(test)]
89 | mod tests {
90 | use super::*;
91 | use crate::parse::{context::tests::Context, Context as _};
92 |
93 | #[test]
94 | fn parse() -> Result<(), SimpleExtensionUriError> {
95 | let simple_extension_uri = proto::extensions::SimpleExtensionUri {
96 | extension_uri_anchor: 1,
97 | uri: "https://substrait.io".to_string(),
98 | };
99 | let simple_extension_uri = simple_extension_uri.parse(&mut Context::default())?;
100 | assert_eq!(simple_extension_uri.anchor(), Anchor::new(1));
101 | assert_eq!(simple_extension_uri.uri().as_str(), "https://substrait.io/");
102 | Ok(())
103 | }
104 |
105 | #[test]
106 | fn invalid_uri() {
107 | let simple_extension_uri = proto::extensions::SimpleExtensionUri::default();
108 | assert_eq!(
109 | simple_extension_uri.parse(&mut Context::default()),
110 | Err(SimpleExtensionUriError::InvalidURI(
111 | url::ParseError::RelativeUrlWithoutBase
112 | ))
113 | );
114 | let simple_extension_uri = proto::extensions::SimpleExtensionUri {
115 | extension_uri_anchor: 1,
116 | uri: "http://".to_string(),
117 | };
118 | assert_eq!(
119 | simple_extension_uri.parse(&mut Context::default()),
120 | Err(SimpleExtensionUriError::InvalidURI(
121 | url::ParseError::EmptyHost
122 | ))
123 | );
124 | }
125 |
126 | #[test]
127 | fn duplicate_simple_extension() {
128 | let mut ctx = Context::default();
129 | let simple_extension_uri = proto::extensions::SimpleExtensionUri {
130 | extension_uri_anchor: 1,
131 | uri: "https://substrait.io".to_string(),
132 | };
133 | assert!(ctx.parse(simple_extension_uri.clone()).is_ok());
134 | assert_eq!(
135 | ctx.parse(simple_extension_uri),
136 | Err(SimpleExtensionUriError::Context(
137 | ContextError::DuplicateSimpleExtension(Anchor::new(1))
138 | ))
139 | );
140 | }
141 |
142 | #[test]
143 | fn unsupported_uri() {
144 | let simple_extension_uri = proto::extensions::SimpleExtensionUri {
145 | extension_uri_anchor: 1,
146 | uri: "ftp://substrait.io".to_string(),
147 | };
148 | assert_eq!(
149 | simple_extension_uri.parse(&mut Context::default()),
150 | Err(SimpleExtensionUriError::Context(
151 | ContextError::UnsupportedURI("`ftp` scheme not supported".to_string())
152 | ))
153 | );
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/src/parse/proto/mod.rs:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: Apache-2.0
2 |
3 | //! Parsing of [proto](crate::proto) types.
4 |
5 | mod version;
6 | pub use version::{Version, VersionError};
7 |
8 | mod plan_version;
9 | pub use plan_version::{PlanVersion, PlanVersionError};
10 |
11 | pub mod extensions;
12 |
--------------------------------------------------------------------------------
/src/parse/proto/plan_version.rs:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: Apache-2.0
2 |
3 | //! Parsing of [proto::PlanVersion].
4 |
5 | use crate::{
6 | parse::{context::Context, proto::Version, Parse},
7 | proto,
8 | };
9 | use thiserror::Error;
10 |
11 | use super::VersionError;
12 |
13 | /// A parsed [proto::PlanVersion].
14 | #[derive(Clone, Debug, PartialEq)]
15 | pub struct PlanVersion {
16 | /// The version of the plan.
17 | version: Version,
18 | }
19 |
20 | impl PlanVersion {
21 | /// Returns the version of this plan version.
22 | ///
23 | /// See [proto::PlanVersion::version].
24 | pub fn version(&self) -> &Version {
25 | &self.version
26 | }
27 | }
28 |
29 | /// Parse errors for [proto::PlanVersion].
30 | #[derive(Debug, Error, PartialEq)]
31 | pub enum PlanVersionError {
32 | /// Version is missing.
33 | #[error("version must be specified")]
34 | Missing,
35 |
36 | /// Version error.
37 | #[error("version must be valid")]
38 | Version(#[from] VersionError),
39 | }
40 |
41 | impl Parse for proto::PlanVersion {
42 | type Parsed = PlanVersion;
43 | type Error = PlanVersionError;
44 |
45 | fn parse(self, ctx: &mut C) -> Result {
46 | let proto::PlanVersion { version } = self;
47 |
48 | // The version is required, and must be valid.
49 | let version = version
50 | .map(|version| ctx.parse(version))
51 | .transpose()?
52 | .ok_or(PlanVersionError::Missing)?;
53 |
54 | let plan_version = PlanVersion { version };
55 |
56 | Ok(plan_version)
57 | }
58 | }
59 |
60 | impl From for proto::PlanVersion {
61 | fn from(plan_version: PlanVersion) -> Self {
62 | let PlanVersion { version } = plan_version;
63 |
64 | proto::PlanVersion {
65 | version: Some(version.into()),
66 | }
67 | }
68 | }
69 |
70 | #[cfg(test)]
71 | mod tests {
72 | use super::*;
73 | use crate::{
74 | parse::{context::tests::Context, proto::VersionError},
75 | version,
76 | };
77 |
78 | #[test]
79 | fn parse() -> Result<(), PlanVersionError> {
80 | let plan_version = proto::PlanVersion {
81 | version: Some(version::version()),
82 | };
83 | plan_version.parse(&mut Context::default())?;
84 | Ok(())
85 | }
86 |
87 | #[test]
88 | fn missing() {
89 | let plan_version = proto::PlanVersion::default();
90 | assert_eq!(
91 | plan_version.parse(&mut Context::default()),
92 | Err(PlanVersionError::Missing)
93 | );
94 | }
95 |
96 | #[test]
97 | fn version_error() {
98 | let plan_version = proto::PlanVersion {
99 | version: Some(proto::Version::default()),
100 | };
101 | assert_eq!(
102 | plan_version.parse(&mut Context::default()),
103 | Err(PlanVersionError::Version(VersionError::Missing))
104 | );
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/parse/proto/version.rs:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: Apache-2.0
2 |
3 | //! Parsing of [proto::Version].
4 |
5 | use crate::{
6 | parse::{context::Context, Parse},
7 | proto, version,
8 | };
9 | use hex::FromHex;
10 | use thiserror::Error;
11 |
12 | /// A parsed [proto::Version].
13 | ///
14 | /// This parses only for compatible versions. See [`version::semver_req`].
15 | #[derive(Clone, Debug, PartialEq)]
16 | pub struct Version {
17 | /// The semantic version.
18 | version: semver::Version,
19 | /// The git hash if set as bytes.
20 | git_hash: Option<[u8; 20]>,
21 | /// The producer string if set.
22 | producer: Option,
23 | }
24 |
25 | impl Version {
26 | /// Returns the semantic version of this version.
27 | ///
28 | /// See [proto::Version::major_number], [proto::Version::minor_number] and
29 | /// [proto::Version::patch_number].
30 | pub fn version(&self) -> &semver::Version {
31 | &self.version
32 | }
33 |
34 | /// Returns the git hash of this version.
35 | ///
36 | /// See [proto::Version::git_hash].
37 | pub fn git_hash(&self) -> Option<&[u8; 20]> {
38 | self.git_hash.as_ref()
39 | }
40 |
41 | /// Returns the producer of this version.
42 | ///
43 | /// See [proto::Version::producer].
44 | pub fn producer(&self) -> Option<&str> {
45 | self.producer.as_deref()
46 | }
47 |
48 | /// Returns [VersionError::Substrait] if this version is incompatible with
49 | /// the Substrait [version::version] of this crate.
50 | pub(crate) fn compatible(&self) -> Result<(), VersionError> {
51 | let version = self.version();
52 | let version_req = version::semver_req();
53 | version_req
54 | .matches(version)
55 | .then_some(())
56 | .ok_or_else(|| VersionError::Substrait(version.clone(), version_req))
57 | }
58 | }
59 |
60 | /// Parse errors for [proto::Version].
61 | #[derive(Debug, Error, PartialEq)]
62 | pub enum VersionError {
63 | /// Git hash is incorrect.
64 | #[error(
65 | "git hash must be a lowercase hex ASCII string, 40 characters in length: (git hash: {0})"
66 | )]
67 | GitHash(String),
68 |
69 | /// Version is missing.
70 | #[error("version must be specified")]
71 | Missing,
72 |
73 | /// Version is incompatible.
74 | #[error("substrait version incompatible (version: `{0}`, supported: `{1}`)")]
75 | Substrait(semver::Version, semver::VersionReq),
76 | }
77 |
78 | impl Parse for proto::Version {
79 | type Parsed = Version;
80 | type Error = VersionError;
81 |
82 | fn parse(self, _ctx: &mut C) -> Result {
83 | let proto::Version {
84 | major_number,
85 | minor_number,
86 | patch_number,
87 | git_hash,
88 | producer,
89 | } = self;
90 |
91 | // All version numbers unset (u32::default()) is an error, because
92 | // version is required.
93 | if major_number == u32::default()
94 | && minor_number == u32::default()
95 | && patch_number == u32::default()
96 | {
97 | return Err(VersionError::Missing);
98 | }
99 |
100 | // The git hash, when set, must be a lowercase hex ASCII string, 40
101 | // characters in length.
102 | if !git_hash.is_empty()
103 | && (git_hash.len() != 40
104 | || !git_hash.chars().all(|x| matches!(x, '0'..='9' | 'a'..='f')))
105 | {
106 | return Err(VersionError::GitHash(git_hash));
107 | }
108 |
109 | let version = Version {
110 | version: semver::Version::new(major_number as _, minor_number as _, patch_number as _),
111 | git_hash: (!git_hash.is_empty()).then(|| <[u8; 20]>::from_hex(git_hash).unwrap()),
112 | producer: (!producer.is_empty()).then_some(producer),
113 | };
114 |
115 | // The version must be compatible with the substrait version of this crate.
116 | version.compatible()?;
117 |
118 | Ok(version)
119 | }
120 | }
121 |
122 | impl From for proto::Version {
123 | fn from(version: Version) -> Self {
124 | let Version {
125 | version,
126 | git_hash,
127 | producer,
128 | } = version;
129 |
130 | proto::Version {
131 | // Note: we can use `as _` here because this Version is always
132 | // constructed from `u32` values.
133 | major_number: version.major as _,
134 | minor_number: version.minor as _,
135 | patch_number: version.patch as _,
136 | git_hash: git_hash.map(hex::encode).unwrap_or_default(),
137 | producer: producer.unwrap_or_default(),
138 | }
139 | }
140 | }
141 |
142 | #[cfg(test)]
143 | mod tests {
144 | use super::*;
145 | use crate::parse::context::tests::Context;
146 |
147 | #[test]
148 | fn version() -> Result<(), VersionError> {
149 | let version = proto::Version::default();
150 | assert_eq!(
151 | version.parse(&mut Context::default()),
152 | Err(VersionError::Missing)
153 | );
154 |
155 | let version = version::version();
156 | version.parse(&mut Context::default())?;
157 | Ok(())
158 | }
159 |
160 | #[test]
161 | fn git_hash() {
162 | let base = version::version();
163 |
164 | // Bad length.
165 | let git_hash = String::from("short");
166 | let version = proto::Version {
167 | git_hash: git_hash.clone(),
168 | ..base.clone()
169 | };
170 | assert_eq!(
171 | version.parse(&mut Context::default()),
172 | Err(VersionError::GitHash(git_hash))
173 | );
174 |
175 | // Not lowercase.
176 | let git_hash = String::from("2FD4E1C67A2D28FCED849EE1BB76E7391B93EB12");
177 | let version = proto::Version {
178 | git_hash: git_hash.clone(),
179 | ..base.clone()
180 | };
181 | assert_eq!(
182 | version.parse(&mut Context::default()),
183 | Err(VersionError::GitHash(git_hash))
184 | );
185 |
186 | // Not all hex digits.
187 | let git_hash = String::from("2fd4e1c67a2d28fced849ee1bb76e7391b93eb1g");
188 | let version = proto::Version {
189 | git_hash: git_hash.clone(),
190 | ..base.clone()
191 | };
192 | assert_eq!(
193 | version.parse(&mut Context::default()),
194 | Err(VersionError::GitHash(git_hash))
195 | );
196 |
197 | // Not all ascii.
198 | let git_hash = String::from("2fd4e1c67a2d28fced849ee1bb76e7391b93eb1å");
199 | let version = proto::Version {
200 | git_hash: git_hash.clone(),
201 | ..base.clone()
202 | };
203 | assert_eq!(
204 | version.parse(&mut Context::default()),
205 | Err(VersionError::GitHash(git_hash))
206 | );
207 |
208 | // Valid.
209 | let git_hash = String::from("2fd4e1c67a2d28fced849ee1bb76e7391b93eb12");
210 | let version = proto::Version { git_hash, ..base };
211 | assert!(version.parse(&mut Context::default()).is_ok());
212 | }
213 |
214 | #[test]
215 | fn producer() -> Result<(), VersionError> {
216 | // Empty producer maps to `None`
217 | let version = proto::Version {
218 | producer: String::from(""),
219 | ..version::version()
220 | };
221 | let version = version.parse(&mut Context::default())?;
222 | assert!(version.producer.is_none());
223 | Ok(())
224 | }
225 |
226 | #[test]
227 | fn convert() -> Result<(), VersionError> {
228 | let version = version::version();
229 | assert_eq!(
230 | proto::Version::from(version.clone().parse(&mut Context::default())?),
231 | version
232 | );
233 | Ok(())
234 | }
235 |
236 | #[test]
237 | fn compatible() -> Result<(), VersionError> {
238 | let _version = version::version().parse(&mut Context::default())?;
239 |
240 | let mut version = version::version();
241 | version.major_number += 1;
242 | let version = version.parse(&mut Context::default());
243 | matches!(version, Err(VersionError::Substrait(_, _)));
244 |
245 | let mut version = version::version();
246 | version.minor_number += 1;
247 | let version = version.parse(&mut Context::default());
248 | matches!(version, Err(VersionError::Substrait(_, _)));
249 |
250 | let mut version = version::version();
251 | version.patch_number += 1;
252 | let _version = version.parse(&mut Context::default())?;
253 |
254 | Ok(())
255 | }
256 | }
257 |
--------------------------------------------------------------------------------
/src/parse/text/mod.rs:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: Apache-2.0
2 |
3 | //! Parsing of [text](crate::text) types.
4 |
5 | pub mod simple_extensions;
6 |
--------------------------------------------------------------------------------
/src/parse/text/simple_extensions/argument.rs:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: Apache-2.0
2 |
3 | //! Parsing of [simple_extensions::ArgumentsItem].
4 |
5 | use std::{collections::HashSet, ops::Deref};
6 |
7 | use thiserror::Error;
8 |
9 | use crate::{
10 | parse::{Context, Parse},
11 | text::simple_extensions,
12 | };
13 |
14 | /// A parsed [simple_extensions::ArgumentsItem].
15 | #[derive(Clone, Debug)]
16 | pub enum ArgumentsItem {
17 | /// Arguments that support a fixed set of declared values as constant arguments.
18 | EnumArgument(EnumerationArg),
19 |
20 | /// Arguments that refer to a data value.
21 | ValueArgument(ValueArg),
22 |
23 | /// Arguments that are used only to inform the evaluation and/or type derivation of the function.
24 | TypeArgument(TypeArg),
25 | }
26 |
27 | impl ArgumentsItem {
28 | /// Parses an `Option` field, rejecting it if an empty string is provided.
29 | #[inline]
30 | fn parse_optional_string(
31 | name: &str,
32 | value: Option,
33 | ) -> Result