├── .github
└── workflows
│ └── build.yml
├── .gitignore
├── .idea
├── .gitignore
├── duckdb_protobuf.iml
├── modules.xml
├── runConfigurations
│ ├── build_debug.xml
│ └── build_release.xml
└── vcs.xml
├── Cargo.lock
├── Cargo.toml
├── Makefile
├── README.md
├── flake.lock
├── flake.nix
├── packages
├── duckdb_metadata
│ ├── Cargo.toml
│ └── src
│ │ └── lib.rs
├── duckdb_metadata_bin
│ ├── Cargo.toml
│ └── src
│ │ └── main.rs
├── duckdb_protobuf
│ ├── Cargo.toml
│ ├── src
│ │ ├── filtered_dynamic_message.rs
│ │ ├── io.rs
│ │ ├── lib.rs
│ │ ├── read.rs
│ │ ├── types.rs
│ │ └── vtab.rs
│ └── tests
│ │ ├── .gitignore
│ │ ├── it
│ │ └── main.rs
│ │ ├── protos
│ │ └── user.proto
│ │ └── src
│ │ └── user.rs
└── serving
│ ├── .gitignore
│ ├── package.json
│ ├── src
│ └── worker.ts
│ ├── tsconfig.json
│ ├── wrangler.toml
│ └── yarn.lock
└── patches
├── duckdb+1.0.0.patch
├── duckdb-loadable-macros+0.1.2.patch
└── libduckdb-sys+1.0.0.patch
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | on:
2 | workflow_dispatch:
3 | push:
4 | tags:
5 | - '*'
6 | branches:
7 | - main
8 | - master
9 |
10 | jobs:
11 | build:
12 | name: build ${{ matrix.platform }} ${{ matrix.target }}
13 | strategy:
14 | matrix:
15 | include:
16 | - platform: windows-latest
17 | target: x86_64-pc-windows-msvc
18 | duckdb_platform: windows_amd64
19 | library_output: duckdb_protobuf.dll
20 | - platform: macos-latest
21 | target: aarch64-apple-darwin
22 | library_output: libduckdb_protobuf.dylib
23 | duckdb_platform: osx_arm64
24 | - platform: ubuntu-latest
25 | target: x86_64-unknown-linux-gnu
26 | library_output: libduckdb_protobuf.so
27 | duckdb_platform: linux_amd64_gcc4
28 |
29 | runs-on: ${{ matrix.platform }}
30 |
31 | steps:
32 | - uses: actions/checkout@v4
33 |
34 | - run: make vendor
35 |
36 | - run: cargo build --target ${{ matrix.target }} --release
37 |
38 | - name: attach metadata
39 | run: >
40 | cargo run
41 | --package duckdb_metadata_bin
42 | --bin duckdb_metadata
43 | --
44 | --input target/${{ matrix.target }}/release/${{ matrix.library_output }}
45 | --output target/${{ matrix.target }}/release/protobuf.duckdb_extension
46 | --extension-version v0.0.1
47 | --duckdb-api-version v0.0.1
48 | --extension-abi-type C_STRUCT
49 | --platform ${{ matrix.duckdb_platform }}
50 |
51 | - name: save artifacts
52 | if: success()
53 | uses: actions/upload-artifact@v4
54 | with:
55 | name: build-${{ matrix.platform }}-${{ matrix.target }}
56 | path: target/${{ matrix.target }}/release/protobuf.duckdb_extension
57 |
58 | release:
59 | needs: build
60 | runs-on: ubuntu-latest
61 | if: startsWith(github.ref, 'refs/tags/')
62 | steps:
63 | - uses: actions/download-artifact@v4
64 |
65 | - run: |
66 | mkdir release
67 | mv build-macos-latest-aarch64-apple-darwin/protobuf.duckdb_extension ./release/protobuf.osx_arm64.duckdb_extension
68 | mv build-ubuntu-latest-x86_64-unknown-linux-gnu/protobuf.duckdb_extension ./release/protobuf.linux_amd64_gcc4.duckdb_extension
69 | mv build-windows-latest-x86_64-pc-windows-msvc/protobuf.duckdb_extension ./release/protobuf.windows_amd64.duckdb_extension
70 |
71 | - name: upload files to r2
72 | env:
73 | AWS_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
74 | AWS_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
75 | AWS_DEFAULT_REGION: auto
76 | run: |
77 | aws s3 cp \
78 | --endpoint-url https://${{ secrets.R2_ACCOUNT_ID }}.r2.cloudflarestorage.com \
79 | ./release/protobuf.osx_arm64.duckdb_extension \
80 | s3://duckdb-extensions/duckdb-api-version/v0.0.1/osx_arm64/protobuf.duckdb_extension
81 |
82 | aws s3 cp \
83 | --endpoint-url https://${{ secrets.R2_ACCOUNT_ID }}.r2.cloudflarestorage.com \
84 | ./release/protobuf.linux_amd64_gcc4.duckdb_extension \
85 | s3://duckdb-extensions/duckdb-api-version/v0.0.1/linux_amd64_gcc4/protobuf.duckdb_extension
86 |
87 | aws s3 cp \
88 | --endpoint-url https://${{ secrets.R2_ACCOUNT_ID }}.r2.cloudflarestorage.com \
89 | ./release/protobuf.windows_amd64.duckdb_extension \
90 | s3://duckdb-extensions/duckdb-api-version/v0.0.1/windows_amd64/protobuf.duckdb_extension
91 |
92 | - name: release
93 | uses: softprops/action-gh-release@v2
94 | with:
95 | files: release/*
96 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 |
3 | packages/vendor
4 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Editor-based HTTP Client requests
5 | /httpRequests/
6 |
--------------------------------------------------------------------------------
/.idea/duckdb_protobuf.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/build_debug.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/build_release.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [workspace]
2 | resolver = "2"
3 |
4 | members = [
5 | "packages/vendor/duckdb",
6 | "packages/vendor/duckdb-loadable-macros",
7 | "packages/vendor/libduckdb-sys",
8 | "packages/duckdb_metadata",
9 | "packages/duckdb_metadata_bin",
10 | "packages/duckdb_protobuf"
11 | ]
12 |
13 | [patch.crates-io]
14 | duckdb = { path = 'packages/vendor/duckdb' }
15 | duckdb-loadable-macros = { path = 'packages/vendor/duckdb-loadable-macros' }
16 | libduckdb-sys = { path = 'packages/vendor/libduckdb-sys' }
17 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # A Makefile for building stuff during development. This is not used for the CI
2 | # builds. These two are separate concepts with different constraints (which
3 | # target is specified, cross compilation, etc.). Using this as a task runner (a
4 | # la just).
5 |
6 | DUCKDB_PLATFORM := osx_arm64
7 | DUCKDB_EXTENSION_VERSION := v0.0.1
8 | DUCKDB_API_VERSION := v0.0.1
9 |
10 | ifeq ($(DUCKDB_PLATFORM),windows_amd64)
11 | LIBRARY_OUTPUT := duckdb_protobuf.dll
12 | endif
13 | ifeq ($(DUCKDB_PLATFORM),osx_arm64)
14 | LIBRARY_OUTPUT := libduckdb_protobuf.dylib
15 | endif
16 | ifeq ($(DUCKDB_PLATFORM),linux_amd64)
17 | LIBRARY_OUTPUT := libduckdb_protobuf.so
18 | endif
19 |
20 | packages/vendor/duckdb:
21 | mkdir -p packages/vendor/duckdb
22 | curl -L https://crates.io/api/v1/crates/duckdb/1.0.0/download | tar --strip-components=1 -xz -C packages/vendor/duckdb
23 | patch --strip=1 --directory=packages/vendor/duckdb < patches/duckdb+1.0.0.patch
24 |
25 | packages/vendor/duckdb-loadable-macros:
26 | mkdir -p packages/vendor/duckdb-loadable-macros
27 | curl -L https://crates.io/api/v1/crates/duckdb-loadable-macros/0.1.2/download | tar --strip-components=1 -xz -C packages/vendor/duckdb-loadable-macros
28 | patch --strip=1 --directory=packages/vendor/duckdb-loadable-macros < patches/duckdb-loadable-macros+0.1.2.patch
29 |
30 | packages/vendor/libduckdb-sys:
31 | mkdir -p packages/vendor/libduckdb-sys
32 | curl -L https://crates.io/api/v1/crates/libduckdb-sys/1.0.0/download | tar --strip-components=1 -xz -C packages/vendor/libduckdb-sys
33 | patch --strip=1 --directory=packages/vendor/libduckdb-sys < patches/libduckdb-sys+1.0.0.patch
34 |
35 | vendor: packages/vendor/duckdb packages/vendor/duckdb-loadable-macros packages/vendor/libduckdb-sys
36 |
37 | debug: vendor
38 | cargo build --package duckdb_protobuf
39 | cargo run \
40 | --package duckdb_metadata_bin \
41 | --bin duckdb_metadata \
42 | -- \
43 | --input target/debug/$(LIBRARY_OUTPUT) \
44 | --output target/debug/protobuf.duckdb_extension \
45 | --extension-version $(DUCKDB_EXTENSION_VERSION) \
46 | --duckdb-api-version $(DUCKDB_API_VERSION) \
47 | --platform $(DUCKDB_PLATFORM) \
48 | --extension-abi-type C_STRUCT
49 |
50 | release: vendor
51 | cargo build --package duckdb_protobuf --release
52 | cargo run \
53 | --package duckdb_metadata_bin \
54 | --bin duckdb_metadata \
55 | -- \
56 | --input target/release/$(LIBRARY_OUTPUT) \
57 | --output target/release/protobuf.duckdb_extension \
58 | --extension-version $(DUCKDB_EXTENSION_VERSION) \
59 | --duckdb-api-version $(DUCKDB_API_VERSION) \
60 | --platform $(DUCKDB_PLATFORM) \
61 | --extension-abi-type C_STRUCT
62 |
63 | test: release
64 | cargo test --package duckdb_protobuf
65 |
66 | install: release
67 | duckdb \
68 | -unsigned \
69 | -cmd "FORCE INSTALL 'target/release/protobuf.duckdb_extension'" \
70 | -no-stdin
71 |
72 | .PHONY: test release debug vendor
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # duckdb_protobuf
2 |
3 | a duckdb extension for parsing sequences of protobuf messages encoded in either
4 | the standard varint delimited format or a u32 big endian delimited format.
5 |
6 | ## quick start
7 |
8 | ensure you're using duckdb 1.1.0 for support with the latest features. if you
9 | need new features on an old versions, please open an issue.
10 |
11 | ```bash
12 | $ duckdb -version
13 | v1.1.0 fa5c2fe15f
14 | ```
15 |
16 | start duckdb with `-unsigned` flag to allow loading unsigned libraries.
17 |
18 | ```bash
19 | $ duckdb -unsigned
20 | ```
21 |
22 | or if you're using the jdbc connector, you can do this with the
23 | `allow_unsigned_extensions` jdbc connection property.
24 |
25 | now install the extension:
26 |
27 | ```sql
28 | INSTALL protobuf from 'https://duckdb.0xcaff.xyz';
29 | ```
30 |
31 | next load it (you'll need to do this once for every session you want to use
32 | the extension)
33 |
34 | ```sql
35 | LOAD protobuf;
36 | ```
37 |
38 | and start shredding up your protobufs!
39 |
40 | ```sql
41 | SELECT *
42 | FROM protobuf(
43 | descriptors = './descriptor.pb',
44 | files = './scrape/data/SceneVersion/**/*.bin',
45 | message_type = 'test_server.v1.GetUserSceneVersionResponse',
46 | delimiter = 'BigEndianFixed'
47 | )
48 | LIMIT 10;
49 | ```
50 |
51 | if you want builds for a platform or version which currently doesn't have
52 | builds, please open an issue.
53 |
54 |
55 | install from file
56 |
57 | download the latest version from
58 | [releases](https://github.com/0xcaff/duckdb_protobuf/releases). if you're on
59 | macOS, blow away the quarantine params with the following to allow the file
60 | to be loaded
61 |
62 | ```sh
63 | $ xattr -d com.apple.quarantine /Users/martin/Downloads/protobuf.duckdb_extension
64 | ```
65 |
66 | next load the extension
67 |
68 | ```sql
69 | LOAD '/Users/martin/Downloads/protobuf.duckdb_extension';
70 | ```
71 |
72 |
73 | ## why
74 |
75 | sometimes you want to land your row primary data in a format with a well-defined
76 | structure and pretty good decode performance and poke around without a load
77 | step. maybe you're scraping an endpoint which returns protobuf responses, you're
78 | figuring out the schema as you go and iteration speed matters much more than
79 | query performance.
80 |
81 | `duckdb_protobuf` allows for making a new choice along the
82 | flexibility-performance tradeoff continuum for fast exploration of protobuf
83 | streams with little upfront load complexity or time.
84 |
85 | ## configuration
86 |
87 | * `descriptors`: path to the protobuf descriptor file. Generated using something
88 | like `protoc --descriptor_set_out=descriptor.pb ...`
89 | * `files`: glob pattern for the files to read. Uses the [`glob`][glob] crate
90 | for evaluating globs.
91 | * `message_type`: the fully qualified message type to parse.
92 | * `delimiter`: specifies where one message starts and the next one begins
93 | * `BigEndianFixed`: every message is prefixed with a u32 big endian value
94 | specifying its length. files are a sequence of messages
95 | * `Varint`: every message is prefixed with a protobuf Varint value
96 | ([encoding](https://protobuf.dev/programming-guides/encoding/#varints)).
97 | files are a sequence of messages
98 | * `SingleMessagePerFile`: each file contains a single message
99 | * `filename`, `position` and `size`: boolean values enabling columns which add
100 | source information about where the messages originated from
101 |
102 | ## features
103 |
104 | * converts `google.protobuf.Timestamp` messages to duckdb timestamp
105 | * supports nested messages with repeating fields
106 | * scales decoding across as many threads as duckdb allows
107 | * supports projection pushdown (for first level of columns) ensuring only
108 | necessary columns are decoded.
109 |
110 | ## limitations
111 |
112 | * doesn't support a few types (bytes, maps, {s,}fixed{32,64}, sint{32,64}),
113 | contributions and even feedback that these field types are used is welcome!
114 |
115 | i'm releasing this to understand how other folks are using protobuf streams and
116 | duckdb. i'm open to PRs, issues and other feedback.
117 |
118 | [glob]: https://docs.rs/glob/latest/glob/
--------------------------------------------------------------------------------
/flake.lock:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": {
3 | "cachix": {
4 | "inputs": {
5 | "devenv": [
6 | "crate2nix"
7 | ],
8 | "flake-compat": [
9 | "crate2nix"
10 | ],
11 | "nixpkgs": "nixpkgs",
12 | "pre-commit-hooks": [
13 | "crate2nix"
14 | ]
15 | },
16 | "locked": {
17 | "lastModified": 1709700175,
18 | "narHash": "sha256-A0/6ZjLmT9qdYzKHmevnEIC7G+GiZ4UCr8v0poRPzds=",
19 | "owner": "cachix",
20 | "repo": "cachix",
21 | "rev": "be97b37989f11b724197b5f4c7ffd78f12c8c4bf",
22 | "type": "github"
23 | },
24 | "original": {
25 | "owner": "cachix",
26 | "ref": "latest",
27 | "repo": "cachix",
28 | "type": "github"
29 | }
30 | },
31 | "cachix_2": {
32 | "inputs": {
33 | "devenv": [
34 | "crate2nix",
35 | "crate2nix_stable"
36 | ],
37 | "flake-compat": [
38 | "crate2nix",
39 | "crate2nix_stable"
40 | ],
41 | "nixpkgs": "nixpkgs_2",
42 | "pre-commit-hooks": [
43 | "crate2nix",
44 | "crate2nix_stable"
45 | ]
46 | },
47 | "locked": {
48 | "lastModified": 1716549461,
49 | "narHash": "sha256-lHy5kgx6J8uD+16SO47dPrbob98sh+W1tf4ceSqPVK4=",
50 | "owner": "cachix",
51 | "repo": "cachix",
52 | "rev": "e2bb269fb8c0828d5d4d2d7b8d09ea85abcacbd4",
53 | "type": "github"
54 | },
55 | "original": {
56 | "owner": "cachix",
57 | "ref": "latest",
58 | "repo": "cachix",
59 | "type": "github"
60 | }
61 | },
62 | "cachix_3": {
63 | "inputs": {
64 | "devenv": [
65 | "crate2nix",
66 | "crate2nix_stable",
67 | "crate2nix_stable"
68 | ],
69 | "flake-compat": [
70 | "crate2nix",
71 | "crate2nix_stable",
72 | "crate2nix_stable"
73 | ],
74 | "nixpkgs": "nixpkgs_3",
75 | "pre-commit-hooks": [
76 | "crate2nix",
77 | "crate2nix_stable",
78 | "crate2nix_stable"
79 | ]
80 | },
81 | "locked": {
82 | "lastModified": 1716549461,
83 | "narHash": "sha256-lHy5kgx6J8uD+16SO47dPrbob98sh+W1tf4ceSqPVK4=",
84 | "owner": "cachix",
85 | "repo": "cachix",
86 | "rev": "e2bb269fb8c0828d5d4d2d7b8d09ea85abcacbd4",
87 | "type": "github"
88 | },
89 | "original": {
90 | "owner": "cachix",
91 | "ref": "latest",
92 | "repo": "cachix",
93 | "type": "github"
94 | }
95 | },
96 | "crate2nix": {
97 | "inputs": {
98 | "cachix": "cachix",
99 | "crate2nix_stable": "crate2nix_stable",
100 | "devshell": "devshell_3",
101 | "flake-compat": "flake-compat_3",
102 | "flake-parts": "flake-parts_3",
103 | "nix-test-runner": "nix-test-runner_3",
104 | "nixpkgs": "nixpkgs_6",
105 | "pre-commit-hooks": "pre-commit-hooks_3"
106 | },
107 | "locked": {
108 | "lastModified": 1739473963,
109 | "narHash": "sha256-ItAhpjNUzEWd/cgZVyW/jvoGbCec4TK29e1Mnmn1oJE=",
110 | "owner": "nix-community",
111 | "repo": "crate2nix",
112 | "rev": "be31feae9a82c225c0fd1bdf978565dc452a483a",
113 | "type": "github"
114 | },
115 | "original": {
116 | "owner": "nix-community",
117 | "repo": "crate2nix",
118 | "type": "github"
119 | }
120 | },
121 | "crate2nix_stable": {
122 | "inputs": {
123 | "cachix": "cachix_2",
124 | "crate2nix_stable": "crate2nix_stable_2",
125 | "devshell": "devshell_2",
126 | "flake-compat": "flake-compat_2",
127 | "flake-parts": "flake-parts_2",
128 | "nix-test-runner": "nix-test-runner_2",
129 | "nixpkgs": "nixpkgs_5",
130 | "pre-commit-hooks": "pre-commit-hooks_2"
131 | },
132 | "locked": {
133 | "lastModified": 1719760004,
134 | "narHash": "sha256-esWhRnt7FhiYq0CcIxw9pvH+ybOQmWBfHYMtleaMhBE=",
135 | "owner": "nix-community",
136 | "repo": "crate2nix",
137 | "rev": "1dee214bb20855fa3e1e7bb98d28922ddaff8c57",
138 | "type": "github"
139 | },
140 | "original": {
141 | "owner": "nix-community",
142 | "ref": "0.14.1",
143 | "repo": "crate2nix",
144 | "type": "github"
145 | }
146 | },
147 | "crate2nix_stable_2": {
148 | "inputs": {
149 | "cachix": "cachix_3",
150 | "crate2nix_stable": "crate2nix_stable_3",
151 | "devshell": "devshell",
152 | "flake-compat": "flake-compat",
153 | "flake-parts": "flake-parts",
154 | "nix-test-runner": "nix-test-runner",
155 | "nixpkgs": "nixpkgs_4",
156 | "pre-commit-hooks": "pre-commit-hooks"
157 | },
158 | "locked": {
159 | "lastModified": 1712821484,
160 | "narHash": "sha256-rGT3CW64cJS9nlnWPFWSc1iEa3dNZecVVuPVGzcsHe8=",
161 | "owner": "nix-community",
162 | "repo": "crate2nix",
163 | "rev": "42883afcad3823fa5811e967fb7bff54bc3c9d6d",
164 | "type": "github"
165 | },
166 | "original": {
167 | "owner": "nix-community",
168 | "ref": "0.14.0",
169 | "repo": "crate2nix",
170 | "type": "github"
171 | }
172 | },
173 | "crate2nix_stable_3": {
174 | "inputs": {
175 | "flake-utils": "flake-utils"
176 | },
177 | "locked": {
178 | "lastModified": 1702842982,
179 | "narHash": "sha256-A9AowkHIjsy1a4LuiPiVP88FMxyCWK41flZEZOUuwQM=",
180 | "owner": "nix-community",
181 | "repo": "crate2nix",
182 | "rev": "75ac2973affa6b9b4f661a7b592cba6e4f51d426",
183 | "type": "github"
184 | },
185 | "original": {
186 | "owner": "nix-community",
187 | "ref": "0.12.0",
188 | "repo": "crate2nix",
189 | "type": "github"
190 | }
191 | },
192 | "devshell": {
193 | "inputs": {
194 | "flake-utils": "flake-utils_2",
195 | "nixpkgs": [
196 | "crate2nix",
197 | "crate2nix_stable",
198 | "crate2nix_stable",
199 | "nixpkgs"
200 | ]
201 | },
202 | "locked": {
203 | "lastModified": 1717408969,
204 | "narHash": "sha256-Q0OEFqe35fZbbRPPRdrjTUUChKVhhWXz3T9ZSKmaoVY=",
205 | "owner": "numtide",
206 | "repo": "devshell",
207 | "rev": "1ebbe68d57457c8cae98145410b164b5477761f4",
208 | "type": "github"
209 | },
210 | "original": {
211 | "owner": "numtide",
212 | "repo": "devshell",
213 | "type": "github"
214 | }
215 | },
216 | "devshell_2": {
217 | "inputs": {
218 | "flake-utils": "flake-utils_3",
219 | "nixpkgs": [
220 | "crate2nix",
221 | "crate2nix_stable",
222 | "nixpkgs"
223 | ]
224 | },
225 | "locked": {
226 | "lastModified": 1717408969,
227 | "narHash": "sha256-Q0OEFqe35fZbbRPPRdrjTUUChKVhhWXz3T9ZSKmaoVY=",
228 | "owner": "numtide",
229 | "repo": "devshell",
230 | "rev": "1ebbe68d57457c8cae98145410b164b5477761f4",
231 | "type": "github"
232 | },
233 | "original": {
234 | "owner": "numtide",
235 | "repo": "devshell",
236 | "type": "github"
237 | }
238 | },
239 | "devshell_3": {
240 | "inputs": {
241 | "flake-utils": "flake-utils_4",
242 | "nixpkgs": [
243 | "crate2nix",
244 | "nixpkgs"
245 | ]
246 | },
247 | "locked": {
248 | "lastModified": 1711099426,
249 | "narHash": "sha256-HzpgM/wc3aqpnHJJ2oDqPBkNsqWbW0WfWUO8lKu8nGk=",
250 | "owner": "numtide",
251 | "repo": "devshell",
252 | "rev": "2d45b54ca4a183f2fdcf4b19c895b64fbf620ee8",
253 | "type": "github"
254 | },
255 | "original": {
256 | "owner": "numtide",
257 | "repo": "devshell",
258 | "type": "github"
259 | }
260 | },
261 | "flake-compat": {
262 | "locked": {
263 | "lastModified": 1696426674,
264 | "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
265 | "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
266 | "revCount": 57,
267 | "type": "tarball",
268 | "url": "https://api.flakehub.com/f/pinned/edolstra/flake-compat/1.0.1/018afb31-abd1-7bff-a5e4-cff7e18efb7a/source.tar.gz"
269 | },
270 | "original": {
271 | "type": "tarball",
272 | "url": "https://flakehub.com/f/edolstra/flake-compat/1.tar.gz"
273 | }
274 | },
275 | "flake-compat_2": {
276 | "locked": {
277 | "lastModified": 1696426674,
278 | "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
279 | "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
280 | "revCount": 57,
281 | "type": "tarball",
282 | "url": "https://api.flakehub.com/f/pinned/edolstra/flake-compat/1.0.1/018afb31-abd1-7bff-a5e4-cff7e18efb7a/source.tar.gz"
283 | },
284 | "original": {
285 | "type": "tarball",
286 | "url": "https://flakehub.com/f/edolstra/flake-compat/1.tar.gz"
287 | }
288 | },
289 | "flake-compat_3": {
290 | "locked": {
291 | "lastModified": 1696426674,
292 | "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
293 | "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
294 | "revCount": 57,
295 | "type": "tarball",
296 | "url": "https://api.flakehub.com/f/pinned/edolstra/flake-compat/1.0.1/018afb31-abd1-7bff-a5e4-cff7e18efb7a/source.tar.gz"
297 | },
298 | "original": {
299 | "type": "tarball",
300 | "url": "https://flakehub.com/f/edolstra/flake-compat/1.tar.gz"
301 | }
302 | },
303 | "flake-parts": {
304 | "inputs": {
305 | "nixpkgs-lib": [
306 | "crate2nix",
307 | "crate2nix_stable",
308 | "crate2nix_stable",
309 | "nixpkgs"
310 | ]
311 | },
312 | "locked": {
313 | "lastModified": 1719745305,
314 | "narHash": "sha256-xwgjVUpqSviudEkpQnioeez1Uo2wzrsMaJKJClh+Bls=",
315 | "owner": "hercules-ci",
316 | "repo": "flake-parts",
317 | "rev": "c3c5ecc05edc7dafba779c6c1a61cd08ac6583e9",
318 | "type": "github"
319 | },
320 | "original": {
321 | "owner": "hercules-ci",
322 | "repo": "flake-parts",
323 | "type": "github"
324 | }
325 | },
326 | "flake-parts_2": {
327 | "inputs": {
328 | "nixpkgs-lib": [
329 | "crate2nix",
330 | "crate2nix_stable",
331 | "nixpkgs"
332 | ]
333 | },
334 | "locked": {
335 | "lastModified": 1719745305,
336 | "narHash": "sha256-xwgjVUpqSviudEkpQnioeez1Uo2wzrsMaJKJClh+Bls=",
337 | "owner": "hercules-ci",
338 | "repo": "flake-parts",
339 | "rev": "c3c5ecc05edc7dafba779c6c1a61cd08ac6583e9",
340 | "type": "github"
341 | },
342 | "original": {
343 | "owner": "hercules-ci",
344 | "repo": "flake-parts",
345 | "type": "github"
346 | }
347 | },
348 | "flake-parts_3": {
349 | "inputs": {
350 | "nixpkgs-lib": [
351 | "crate2nix",
352 | "nixpkgs"
353 | ]
354 | },
355 | "locked": {
356 | "lastModified": 1712014858,
357 | "narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=",
358 | "owner": "hercules-ci",
359 | "repo": "flake-parts",
360 | "rev": "9126214d0a59633752a136528f5f3b9aa8565b7d",
361 | "type": "github"
362 | },
363 | "original": {
364 | "owner": "hercules-ci",
365 | "repo": "flake-parts",
366 | "type": "github"
367 | }
368 | },
369 | "flake-utils": {
370 | "inputs": {
371 | "systems": "systems"
372 | },
373 | "locked": {
374 | "lastModified": 1694529238,
375 | "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=",
376 | "owner": "numtide",
377 | "repo": "flake-utils",
378 | "rev": "ff7b65b44d01cf9ba6a71320833626af21126384",
379 | "type": "github"
380 | },
381 | "original": {
382 | "owner": "numtide",
383 | "repo": "flake-utils",
384 | "type": "github"
385 | }
386 | },
387 | "flake-utils_2": {
388 | "inputs": {
389 | "systems": "systems_2"
390 | },
391 | "locked": {
392 | "lastModified": 1701680307,
393 | "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
394 | "owner": "numtide",
395 | "repo": "flake-utils",
396 | "rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
397 | "type": "github"
398 | },
399 | "original": {
400 | "owner": "numtide",
401 | "repo": "flake-utils",
402 | "type": "github"
403 | }
404 | },
405 | "flake-utils_3": {
406 | "inputs": {
407 | "systems": "systems_3"
408 | },
409 | "locked": {
410 | "lastModified": 1701680307,
411 | "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
412 | "owner": "numtide",
413 | "repo": "flake-utils",
414 | "rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
415 | "type": "github"
416 | },
417 | "original": {
418 | "owner": "numtide",
419 | "repo": "flake-utils",
420 | "type": "github"
421 | }
422 | },
423 | "flake-utils_4": {
424 | "inputs": {
425 | "systems": "systems_4"
426 | },
427 | "locked": {
428 | "lastModified": 1701680307,
429 | "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
430 | "owner": "numtide",
431 | "repo": "flake-utils",
432 | "rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
433 | "type": "github"
434 | },
435 | "original": {
436 | "owner": "numtide",
437 | "repo": "flake-utils",
438 | "type": "github"
439 | }
440 | },
441 | "flake-utils_5": {
442 | "inputs": {
443 | "systems": "systems_5"
444 | },
445 | "locked": {
446 | "lastModified": 1710146030,
447 | "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
448 | "owner": "numtide",
449 | "repo": "flake-utils",
450 | "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
451 | "type": "github"
452 | },
453 | "original": {
454 | "owner": "numtide",
455 | "repo": "flake-utils",
456 | "type": "github"
457 | }
458 | },
459 | "flake-utils_6": {
460 | "inputs": {
461 | "systems": "systems_6"
462 | },
463 | "locked": {
464 | "lastModified": 1731533236,
465 | "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
466 | "owner": "numtide",
467 | "repo": "flake-utils",
468 | "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
469 | "type": "github"
470 | },
471 | "original": {
472 | "owner": "numtide",
473 | "repo": "flake-utils",
474 | "type": "github"
475 | }
476 | },
477 | "gitignore": {
478 | "inputs": {
479 | "nixpkgs": [
480 | "crate2nix",
481 | "crate2nix_stable",
482 | "crate2nix_stable",
483 | "pre-commit-hooks",
484 | "nixpkgs"
485 | ]
486 | },
487 | "locked": {
488 | "lastModified": 1709087332,
489 | "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
490 | "owner": "hercules-ci",
491 | "repo": "gitignore.nix",
492 | "rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
493 | "type": "github"
494 | },
495 | "original": {
496 | "owner": "hercules-ci",
497 | "repo": "gitignore.nix",
498 | "type": "github"
499 | }
500 | },
501 | "gitignore_2": {
502 | "inputs": {
503 | "nixpkgs": [
504 | "crate2nix",
505 | "crate2nix_stable",
506 | "pre-commit-hooks",
507 | "nixpkgs"
508 | ]
509 | },
510 | "locked": {
511 | "lastModified": 1709087332,
512 | "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
513 | "owner": "hercules-ci",
514 | "repo": "gitignore.nix",
515 | "rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
516 | "type": "github"
517 | },
518 | "original": {
519 | "owner": "hercules-ci",
520 | "repo": "gitignore.nix",
521 | "type": "github"
522 | }
523 | },
524 | "gitignore_3": {
525 | "inputs": {
526 | "nixpkgs": [
527 | "crate2nix",
528 | "pre-commit-hooks",
529 | "nixpkgs"
530 | ]
531 | },
532 | "locked": {
533 | "lastModified": 1709087332,
534 | "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
535 | "owner": "hercules-ci",
536 | "repo": "gitignore.nix",
537 | "rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
538 | "type": "github"
539 | },
540 | "original": {
541 | "owner": "hercules-ci",
542 | "repo": "gitignore.nix",
543 | "type": "github"
544 | }
545 | },
546 | "nix-test-runner": {
547 | "flake": false,
548 | "locked": {
549 | "lastModified": 1588761593,
550 | "narHash": "sha256-FKJykltAN/g3eIceJl4SfDnnyuH2jHImhMrXS2KvGIs=",
551 | "owner": "stoeffel",
552 | "repo": "nix-test-runner",
553 | "rev": "c45d45b11ecef3eb9d834c3b6304c05c49b06ca2",
554 | "type": "github"
555 | },
556 | "original": {
557 | "owner": "stoeffel",
558 | "repo": "nix-test-runner",
559 | "type": "github"
560 | }
561 | },
562 | "nix-test-runner_2": {
563 | "flake": false,
564 | "locked": {
565 | "lastModified": 1588761593,
566 | "narHash": "sha256-FKJykltAN/g3eIceJl4SfDnnyuH2jHImhMrXS2KvGIs=",
567 | "owner": "stoeffel",
568 | "repo": "nix-test-runner",
569 | "rev": "c45d45b11ecef3eb9d834c3b6304c05c49b06ca2",
570 | "type": "github"
571 | },
572 | "original": {
573 | "owner": "stoeffel",
574 | "repo": "nix-test-runner",
575 | "type": "github"
576 | }
577 | },
578 | "nix-test-runner_3": {
579 | "flake": false,
580 | "locked": {
581 | "lastModified": 1588761593,
582 | "narHash": "sha256-FKJykltAN/g3eIceJl4SfDnnyuH2jHImhMrXS2KvGIs=",
583 | "owner": "stoeffel",
584 | "repo": "nix-test-runner",
585 | "rev": "c45d45b11ecef3eb9d834c3b6304c05c49b06ca2",
586 | "type": "github"
587 | },
588 | "original": {
589 | "owner": "stoeffel",
590 | "repo": "nix-test-runner",
591 | "type": "github"
592 | }
593 | },
594 | "nixpkgs": {
595 | "locked": {
596 | "lastModified": 1700612854,
597 | "narHash": "sha256-yrQ8osMD+vDLGFX7pcwsY/Qr5PUd6OmDMYJZzZi0+zc=",
598 | "owner": "NixOS",
599 | "repo": "nixpkgs",
600 | "rev": "19cbff58383a4ae384dea4d1d0c823d72b49d614",
601 | "type": "github"
602 | },
603 | "original": {
604 | "owner": "NixOS",
605 | "ref": "nixos-unstable",
606 | "repo": "nixpkgs",
607 | "type": "github"
608 | }
609 | },
610 | "nixpkgs_2": {
611 | "locked": {
612 | "lastModified": 1715534503,
613 | "narHash": "sha256-5ZSVkFadZbFP1THataCaSf0JH2cAH3S29hU9rrxTEqk=",
614 | "owner": "NixOS",
615 | "repo": "nixpkgs",
616 | "rev": "2057814051972fa1453ddfb0d98badbea9b83c06",
617 | "type": "github"
618 | },
619 | "original": {
620 | "owner": "NixOS",
621 | "ref": "nixos-unstable",
622 | "repo": "nixpkgs",
623 | "type": "github"
624 | }
625 | },
626 | "nixpkgs_3": {
627 | "locked": {
628 | "lastModified": 1715534503,
629 | "narHash": "sha256-5ZSVkFadZbFP1THataCaSf0JH2cAH3S29hU9rrxTEqk=",
630 | "owner": "NixOS",
631 | "repo": "nixpkgs",
632 | "rev": "2057814051972fa1453ddfb0d98badbea9b83c06",
633 | "type": "github"
634 | },
635 | "original": {
636 | "owner": "NixOS",
637 | "ref": "nixos-unstable",
638 | "repo": "nixpkgs",
639 | "type": "github"
640 | }
641 | },
642 | "nixpkgs_4": {
643 | "locked": {
644 | "lastModified": 1719506693,
645 | "narHash": "sha256-C8e9S7RzshSdHB7L+v9I51af1gDM5unhJ2xO1ywxNH8=",
646 | "path": "/nix/store/4p0avw1s3vf27hspgqsrqs37gxk4i83i-source",
647 | "rev": "b2852eb9365c6de48ffb0dc2c9562591f652242a",
648 | "type": "path"
649 | },
650 | "original": {
651 | "id": "nixpkgs",
652 | "type": "indirect"
653 | }
654 | },
655 | "nixpkgs_5": {
656 | "locked": {
657 | "lastModified": 1719506693,
658 | "narHash": "sha256-C8e9S7RzshSdHB7L+v9I51af1gDM5unhJ2xO1ywxNH8=",
659 | "path": "/nix/store/4p0avw1s3vf27hspgqsrqs37gxk4i83i-source",
660 | "rev": "b2852eb9365c6de48ffb0dc2c9562591f652242a",
661 | "type": "path"
662 | },
663 | "original": {
664 | "id": "nixpkgs",
665 | "type": "indirect"
666 | }
667 | },
668 | "nixpkgs_6": {
669 | "locked": {
670 | "lastModified": 1712026416,
671 | "narHash": "sha256-N/3VR/9e1NlN49p7kCiATiEY6Tzdo+CbrAG8kqCQKcI=",
672 | "owner": "NixOS",
673 | "repo": "nixpkgs",
674 | "rev": "080a4a27f206d07724b88da096e27ef63401a504",
675 | "type": "github"
676 | },
677 | "original": {
678 | "id": "nixpkgs",
679 | "type": "indirect"
680 | }
681 | },
682 | "nixpkgs_7": {
683 | "locked": {
684 | "lastModified": 1744440957,
685 | "narHash": "sha256-FHlSkNqFmPxPJvy+6fNLaNeWnF1lZSgqVCl/eWaJRc4=",
686 | "owner": "NixOS",
687 | "repo": "nixpkgs",
688 | "rev": "26d499fc9f1d567283d5d56fcf367edd815dba1d",
689 | "type": "github"
690 | },
691 | "original": {
692 | "owner": "NixOS",
693 | "ref": "nixos-24.11",
694 | "repo": "nixpkgs",
695 | "type": "github"
696 | }
697 | },
698 | "pre-commit-hooks": {
699 | "inputs": {
700 | "flake-compat": [
701 | "crate2nix",
702 | "crate2nix_stable",
703 | "crate2nix_stable",
704 | "flake-compat"
705 | ],
706 | "gitignore": "gitignore",
707 | "nixpkgs": [
708 | "crate2nix",
709 | "crate2nix_stable",
710 | "crate2nix_stable",
711 | "nixpkgs"
712 | ],
713 | "nixpkgs-stable": [
714 | "crate2nix",
715 | "crate2nix_stable",
716 | "crate2nix_stable",
717 | "nixpkgs"
718 | ]
719 | },
720 | "locked": {
721 | "lastModified": 1719259945,
722 | "narHash": "sha256-F1h+XIsGKT9TkGO3omxDLEb/9jOOsI6NnzsXFsZhry4=",
723 | "owner": "cachix",
724 | "repo": "pre-commit-hooks.nix",
725 | "rev": "0ff4381bbb8f7a52ca4a851660fc7a437a4c6e07",
726 | "type": "github"
727 | },
728 | "original": {
729 | "owner": "cachix",
730 | "repo": "pre-commit-hooks.nix",
731 | "type": "github"
732 | }
733 | },
734 | "pre-commit-hooks_2": {
735 | "inputs": {
736 | "flake-compat": [
737 | "crate2nix",
738 | "crate2nix_stable",
739 | "flake-compat"
740 | ],
741 | "gitignore": "gitignore_2",
742 | "nixpkgs": [
743 | "crate2nix",
744 | "crate2nix_stable",
745 | "nixpkgs"
746 | ],
747 | "nixpkgs-stable": [
748 | "crate2nix",
749 | "crate2nix_stable",
750 | "nixpkgs"
751 | ]
752 | },
753 | "locked": {
754 | "lastModified": 1719259945,
755 | "narHash": "sha256-F1h+XIsGKT9TkGO3omxDLEb/9jOOsI6NnzsXFsZhry4=",
756 | "owner": "cachix",
757 | "repo": "pre-commit-hooks.nix",
758 | "rev": "0ff4381bbb8f7a52ca4a851660fc7a437a4c6e07",
759 | "type": "github"
760 | },
761 | "original": {
762 | "owner": "cachix",
763 | "repo": "pre-commit-hooks.nix",
764 | "type": "github"
765 | }
766 | },
767 | "pre-commit-hooks_3": {
768 | "inputs": {
769 | "flake-compat": [
770 | "crate2nix",
771 | "flake-compat"
772 | ],
773 | "flake-utils": "flake-utils_5",
774 | "gitignore": "gitignore_3",
775 | "nixpkgs": [
776 | "crate2nix",
777 | "nixpkgs"
778 | ],
779 | "nixpkgs-stable": [
780 | "crate2nix",
781 | "nixpkgs"
782 | ]
783 | },
784 | "locked": {
785 | "lastModified": 1712055707,
786 | "narHash": "sha256-4XLvuSIDZJGS17xEwSrNuJLL7UjDYKGJSbK1WWX2AK8=",
787 | "owner": "cachix",
788 | "repo": "pre-commit-hooks.nix",
789 | "rev": "e35aed5fda3cc79f88ed7f1795021e559582093a",
790 | "type": "github"
791 | },
792 | "original": {
793 | "owner": "cachix",
794 | "repo": "pre-commit-hooks.nix",
795 | "type": "github"
796 | }
797 | },
798 | "root": {
799 | "inputs": {
800 | "crate2nix": "crate2nix",
801 | "flake-utils": "flake-utils_6",
802 | "nixpkgs": "nixpkgs_7",
803 | "rust-overlay": "rust-overlay"
804 | }
805 | },
806 | "rust-overlay": {
807 | "inputs": {
808 | "nixpkgs": [
809 | "nixpkgs"
810 | ]
811 | },
812 | "locked": {
813 | "lastModified": 1745029910,
814 | "narHash": "sha256-9CtbfTTQWMoOkXejxc5D+K3z/39wkQQt2YfYJW50tnI=",
815 | "owner": "oxalica",
816 | "repo": "rust-overlay",
817 | "rev": "50fefac8cdfd1587ac6d8678f6181e7d348201d2",
818 | "type": "github"
819 | },
820 | "original": {
821 | "owner": "oxalica",
822 | "repo": "rust-overlay",
823 | "type": "github"
824 | }
825 | },
826 | "systems": {
827 | "locked": {
828 | "lastModified": 1681028828,
829 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
830 | "owner": "nix-systems",
831 | "repo": "default",
832 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
833 | "type": "github"
834 | },
835 | "original": {
836 | "owner": "nix-systems",
837 | "repo": "default",
838 | "type": "github"
839 | }
840 | },
841 | "systems_2": {
842 | "locked": {
843 | "lastModified": 1681028828,
844 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
845 | "owner": "nix-systems",
846 | "repo": "default",
847 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
848 | "type": "github"
849 | },
850 | "original": {
851 | "owner": "nix-systems",
852 | "repo": "default",
853 | "type": "github"
854 | }
855 | },
856 | "systems_3": {
857 | "locked": {
858 | "lastModified": 1681028828,
859 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
860 | "owner": "nix-systems",
861 | "repo": "default",
862 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
863 | "type": "github"
864 | },
865 | "original": {
866 | "owner": "nix-systems",
867 | "repo": "default",
868 | "type": "github"
869 | }
870 | },
871 | "systems_4": {
872 | "locked": {
873 | "lastModified": 1681028828,
874 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
875 | "owner": "nix-systems",
876 | "repo": "default",
877 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
878 | "type": "github"
879 | },
880 | "original": {
881 | "owner": "nix-systems",
882 | "repo": "default",
883 | "type": "github"
884 | }
885 | },
886 | "systems_5": {
887 | "locked": {
888 | "lastModified": 1681028828,
889 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
890 | "owner": "nix-systems",
891 | "repo": "default",
892 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
893 | "type": "github"
894 | },
895 | "original": {
896 | "owner": "nix-systems",
897 | "repo": "default",
898 | "type": "github"
899 | }
900 | },
901 | "systems_6": {
902 | "locked": {
903 | "lastModified": 1681028828,
904 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
905 | "owner": "nix-systems",
906 | "repo": "default",
907 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
908 | "type": "github"
909 | },
910 | "original": {
911 | "owner": "nix-systems",
912 | "repo": "default",
913 | "type": "github"
914 | }
915 | }
916 | },
917 | "root": "root",
918 | "version": 7
919 | }
920 |
--------------------------------------------------------------------------------
/flake.nix:
--------------------------------------------------------------------------------
1 | {
2 | inputs = {
3 | nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
4 | flake-utils.url = "github:numtide/flake-utils";
5 | crate2nix.url = "github:nix-community/crate2nix";
6 |
7 | rust-overlay = {
8 | url = "github:oxalica/rust-overlay";
9 | inputs.nixpkgs.follows = "nixpkgs";
10 | };
11 | };
12 |
13 | outputs =
14 | inputs@{
15 | flake-utils,
16 | nixpkgs,
17 | crate2nix,
18 | rust-overlay,
19 | ...
20 | }:
21 | (flake-utils.lib.eachDefaultSystem (
22 | system:
23 | let
24 | pkgs = import nixpkgs {
25 | inherit system;
26 | overlays = [
27 | crate2nix.overlays.default
28 | rust-overlay.overlays.default
29 | ];
30 | };
31 |
32 | applyPatch =
33 | { src, patches }:
34 | pkgs.stdenvNoCC.mkDerivation {
35 | name = "${src.name}-patched";
36 | inherit src patches;
37 | patchFlags = [ "--strip 1" ];
38 |
39 | nativeBuildInputs = [ pkgs.patch ];
40 |
41 | installPhase = ''
42 | mkdir -p $out
43 | cp -r . $out/
44 | '';
45 | };
46 |
47 | duckdbCrate = applyPatch {
48 | src = pkgs.fetchCrate {
49 | pname = "duckdb";
50 | version = "1.0.0";
51 | sha256 = "sha256-XC1+mjocHl0GUSNNNi3pC+BXQEdG6IzeizzAnR5lzMA=";
52 | };
53 | patches = [ patches/duckdb+1.0.0.patch ];
54 | };
55 |
56 | duckdbLoadableMacrosCrate = applyPatch {
57 | src = pkgs.fetchCrate {
58 | pname = "duckdb-loadable-macros";
59 | version = "0.1.2";
60 | sha256 = "sha256-sZzChlJ8O/S/qULlXfeV6UuavbMShIQbiUqPFYb1XKw=";
61 | };
62 | patches = [ patches/duckdb-loadable-macros+0.1.2.patch ];
63 | };
64 |
65 | libduckdbSysCrate = applyPatch {
66 | src = pkgs.fetchCrate {
67 | pname = "libduckdb-sys";
68 | version = "1.0.0";
69 | sha256 = "sha256-k9v0RVHOGZoNzyHGu+IKNAfPr6iTDeNPu7VF8kGMRgw=";
70 | };
71 | patches = [ patches/libduckdb-sys+1.0.0.patch ];
72 | };
73 |
74 | vendoredSrc = pkgs.stdenvNoCC.mkDerivation {
75 | name = "duckdb-protobuf-src-with-vendor";
76 | src = ./.;
77 |
78 | buildPhase = ''
79 | mkdir -p packages/vendor/duckdb
80 | mkdir -p packages/vendor/duckdb-loadable-macros
81 | mkdir -p packages/vendor/libduckdb-sys
82 |
83 | cp -r ${duckdbCrate}/* packages/vendor/duckdb/
84 | cp -r ${duckdbLoadableMacrosCrate}/* packages/vendor/duckdb-loadable-macros/
85 | cp -r ${libduckdbSysCrate}/* packages/vendor/libduckdb-sys/
86 | '';
87 |
88 | installPhase = ''
89 | mkdir -p $out
90 | cp -r . $out/
91 | '';
92 | };
93 |
94 | buildRustCrateForPkgs =
95 | crate:
96 | pkgs.buildRustCrate.override {
97 | rustc = pkgs.rust-bin.stable.latest.default;
98 | cargo = pkgs.rust-bin.stable.latest.default;
99 | };
100 |
101 | generatedCargoNix = inputs.crate2nix.tools.${system}.generatedCargoNix {
102 | name = "duckdb_protobuf";
103 | src = vendoredSrc;
104 | };
105 |
106 | cargoNix = import generatedCargoNix {
107 | inherit pkgs buildRustCrateForPkgs;
108 | };
109 |
110 | duckdb_protobuf = cargoNix.workspaceMembers.duckdb_protobuf.build;
111 | duckdb_metadata_bin = cargoNix.workspaceMembers.duckdb_metadata_bin.build;
112 |
113 | extensionVersion = "v0.0.1";
114 | apiVersion = "v0.0.1";
115 |
116 | platform =
117 | if system == "x86_64-linux" then
118 | "linux_amd64"
119 | else if system == "aarch64-linux" then
120 | "linux_arm64"
121 | else if system == "x86_64-darwin" then
122 | "osx_amd64"
123 | else if system == "aarch64-darwin" then
124 | "osx_arm64"
125 | else
126 | throw "Unsupported platform: ${system}";
127 | in
128 | rec {
129 | devShells.default = pkgs.mkShell {
130 | packages = [
131 | pkgs.crate2nix
132 | ];
133 | };
134 | packages = {
135 | default = pkgs.stdenv.mkDerivation {
136 | name = "duckdb-protobuf-extension";
137 |
138 | phases = [
139 | "buildPhase"
140 | "installPhase"
141 | ];
142 |
143 | buildPhase = ''
144 | LIBRARY_PATH=$(find ${duckdb_protobuf.lib} -type f -name "*.dylib" -o -name "*.so" -o -name "*.dll" | head -n 1)
145 |
146 | ${duckdb_metadata_bin}/bin/duckdb_metadata \
147 | --input "$LIBRARY_PATH" \
148 | --output protobuf.duckdb_extension \
149 | --extension-version ${extensionVersion} \
150 | --duckdb-api-version ${apiVersion} \
151 | --platform ${platform} \
152 | --extension-abi-type C_STRUCT
153 | '';
154 |
155 | installPhase = ''
156 | mkdir -p $out/${platform}
157 | cp protobuf.duckdb_extension $out/${platform}
158 | '';
159 | };
160 | };
161 | }
162 | ));
163 | }
164 |
--------------------------------------------------------------------------------
/packages/duckdb_metadata/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "duckdb_metadata"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | [lib]
7 |
8 | [dependencies]
9 | gimli = "0.31.0"
10 | thiserror = "1.0.63"
11 | bytemuck = { version = "1.16.1", features = ["derive"] }
12 |
--------------------------------------------------------------------------------
/packages/duckdb_metadata/src/lib.rs:
--------------------------------------------------------------------------------
1 | use bytemuck::{Pod, Zeroable};
2 | use gimli::leb128;
3 | use std::io;
4 | use thiserror::Error;
5 |
6 | #[repr(C)]
7 | #[derive(Clone, Copy, Pod, Zeroable)]
8 | pub struct MetadataFields {
9 | pub meta_8: [u8; 32],
10 | pub meta_7: [u8; 32],
11 | pub meta_6: [u8; 32],
12 | pub extension_abi_type: [u8; 32],
13 | pub extension_version: [u8; 32],
14 | pub duckdb_api_version: [u8; 32],
15 | pub platform: [u8; 32],
16 | pub metadata_version: [u8; 32],
17 | pub signature: [u8; 256],
18 | }
19 |
20 | impl MetadataFields {
21 | pub fn write(&self, mut write: impl io::Write) -> io::Result<()> {
22 | let metadata_fields_size = std::mem::size_of::();
23 |
24 | let mut metadata_field_section_bytes = [0u8; 10];
25 | let metadata_fields_section_len = leb128::write::unsigned(
26 | &mut { &mut metadata_field_section_bytes[..] },
27 | metadata_fields_size as u64,
28 | )
29 | .unwrap();
30 | let metadata_fields_section_len_bytes =
31 | &metadata_field_section_bytes[0..metadata_fields_section_len];
32 |
33 | let custom_section_name = b"duckdb_signature";
34 |
35 | let mut custom_section_name_bytes = [0u8; 10];
36 | let custom_section_name_len = leb128::write::unsigned(
37 | &mut { &mut custom_section_name_bytes[..] },
38 | custom_section_name.len() as u64,
39 | )
40 | .unwrap();
41 | let custom_section_name_len_bytes = &custom_section_name_bytes[..custom_section_name_len];
42 |
43 | let metadata_fields_size = custom_section_name_len_bytes.len()
44 | + custom_section_name.len()
45 | + metadata_fields_section_len_bytes.len()
46 | + metadata_fields_size;
47 |
48 | write.write_all(&[0])?;
49 |
50 | {
51 | let mut bytes = [0u8; 10];
52 | let len = leb128::write::unsigned(&mut { &mut bytes[..] }, metadata_fields_size as u64)
53 | .unwrap();
54 | let length_bytes = &bytes[0..len];
55 |
56 | write.write_all(length_bytes)?;
57 | }
58 |
59 | write.write_all(custom_section_name_len_bytes)?;
60 | write.write_all(custom_section_name)?;
61 |
62 | write.write_all(metadata_fields_section_len_bytes)?;
63 |
64 | let metadata_bytes = bytemuck::bytes_of(self);
65 | write.write_all(metadata_bytes)?;
66 |
67 | Ok(())
68 | }
69 | }
70 |
71 | #[derive(Error, Debug)]
72 | #[error("input too long")]
73 | pub struct InputTooLongError;
74 |
75 | pub fn pad_32(bytes: &[u8]) -> Result<[u8; 32], InputTooLongError> {
76 | let mut result = [0u8; 32];
77 | if bytes.len() > result.len() {
78 | return Err(InputTooLongError);
79 | }
80 |
81 | let copy_len = bytes.len();
82 | result[..copy_len].copy_from_slice(&bytes[..copy_len]);
83 | Ok(result)
84 | }
85 |
86 | #[cfg(test)]
87 | mod tests {
88 | use super::*;
89 | use std::io::Cursor;
90 |
91 | #[test]
92 | fn test_create_metadata_section() -> Result<(), InputTooLongError> {
93 | // tail of http://welsch.lu/duckdb/prql/v0.0.19/v1.0.0/osx_arm64/prql.duckdb_extension.gz
94 | let expected: [u8; 0x216] = [
95 | 0x00, 0x93, 0x04, 0x10, 0x64, 0x75, 0x63, 0x6B, 0x64, 0x62, 0x5F, 0x73, 0x69, 0x67,
96 | 0x6E, 0x61, 0x74, 0x75, 0x72, 0x65, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
97 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
98 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
99 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
100 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
101 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
102 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
103 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
104 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
105 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x30, 0x2E, 0x30,
106 | 0x2E, 0x31, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
107 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
108 | 0x76, 0x31, 0x2E, 0x30, 0x2E, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
109 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
110 | 0x00, 0x00, 0x00, 0x00, 0x6F, 0x73, 0x78, 0x5F, 0x61, 0x72, 0x6D, 0x36, 0x34, 0x00,
111 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
112 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00,
113 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
114 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
115 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
116 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
117 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
118 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
119 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
120 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
121 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
122 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
123 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
124 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
125 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
126 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
127 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
128 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
129 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
130 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
131 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
132 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
133 | 0x00, 0x00,
134 | ];
135 |
136 | let args = MetadataFields {
137 | meta_8: [0; 32],
138 | meta_7: [0; 32],
139 | meta_6: [0; 32],
140 | extension_abi_type: [0; 32],
141 | extension_version: pad_32(b"v0.0.19")?,
142 | duckdb_api_version: pad_32(b"v1.0.0")?,
143 | platform: pad_32(b"osx_arm64")?,
144 | metadata_version: pad_32(b"4")?,
145 | signature: [0; 256],
146 | };
147 |
148 | let mut buffer = Cursor::new(Vec::new());
149 | args.write(&mut buffer).unwrap();
150 |
151 | let result = buffer.into_inner();
152 |
153 | assert_eq!(result, &expected);
154 |
155 | Ok(())
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/packages/duckdb_metadata_bin/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "duckdb_metadata_bin"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | [[bin]]
7 | name = "duckdb_metadata"
8 | path = "src/main.rs"
9 |
10 | [dependencies]
11 | clap = { version = "4.5.9", features = ["derive"] }
12 | duckdb_metadata = { path = "../duckdb_metadata" }
13 | anyhow = "1.0.86"
14 |
--------------------------------------------------------------------------------
/packages/duckdb_metadata_bin/src/main.rs:
--------------------------------------------------------------------------------
1 | use anyhow::{Context, Result};
2 | use clap::Parser;
3 | use std::fs::File;
4 | use std::io;
5 | use std::path::PathBuf;
6 |
7 | use duckdb_metadata::{pad_32, MetadataFields};
8 |
9 | #[derive(Parser, Debug)]
10 | #[clap(author, version, about, long_about = None)]
11 | struct Args {
12 | #[clap(long)]
13 | output: PathBuf,
14 |
15 | #[clap(long)]
16 | input: PathBuf,
17 |
18 | #[clap(long)]
19 | extension_version: String,
20 |
21 | #[clap(long)]
22 | duckdb_api_version: String,
23 |
24 | /// Full list on https://duckdb.org/docs/extensions/working_with_extensions.html#platforms
25 | #[clap(long)]
26 | platform: String,
27 |
28 | #[clap(long, default_value = "4")]
29 | metadata_version: String,
30 |
31 | #[clap(long, default_value = "CPP")]
32 | extension_abi_type: String,
33 | }
34 |
35 | fn main() -> Result<()> {
36 | let args = Args::parse();
37 |
38 | let extension_version =
39 | pad_32(args.extension_version.as_bytes()).context("extension_version")?;
40 | let duckdb_api_version =
41 | pad_32(args.duckdb_api_version.as_bytes()).context("duckdb_api_version")?;
42 | let platform = pad_32(args.platform.as_bytes()).context("platform")?;
43 | let metadata_version = pad_32(args.metadata_version.as_bytes()).context("metadata_version")?;
44 | let extension_abi_type =
45 | pad_32(args.extension_abi_type.as_bytes()).context("extension_abi_type")?;
46 |
47 | let metadata_fields = MetadataFields {
48 | meta_8: [0; 32],
49 | meta_7: [0; 32],
50 | meta_6: [0; 32],
51 | extension_abi_type,
52 | extension_version,
53 | duckdb_api_version,
54 | platform,
55 | metadata_version,
56 | signature: [0; 256],
57 | };
58 |
59 | let mut input_file = File::open(&args.input)
60 | .with_context(|| format!("failed to open input file: {:?}", args.input))?;
61 |
62 | let mut output_file = File::create(&args.output)
63 | .with_context(|| format!("failed to create output file: {:?}", args.output))?;
64 |
65 | io::copy(&mut input_file, &mut output_file)?;
66 |
67 | metadata_fields
68 | .write(&mut output_file)
69 | .context("failed to write metadata to output file")?;
70 |
71 | println!("output generated {:?}", args.output);
72 |
73 | Ok(())
74 | }
75 |
--------------------------------------------------------------------------------
/packages/duckdb_protobuf/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "duckdb_protobuf"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | [lib]
7 | crate-type = ["cdylib"]
8 |
9 | [dependencies]
10 | anyhow = "1.0.86"
11 | duckdb = { version = "1.0.0", features = ["vtab-loadable", "loadable_extension"] }
12 | duckdb-loadable-macros = "0.1.2"
13 | prost = "0.13.0"
14 | prost-reflect = "0.14.0"
15 | protobuf = "3.5.0"
16 | glob = "0.3.1"
17 | byteorder = "1.5.0"
18 | log = "0.4.21"
19 | ouroboros = "0.18.4"
20 | strum = { version = "0.26.3", features = ["derive"] }
21 | crossbeam = "0.8.4"
22 |
23 | [dev-dependencies]
24 | anyhow = "1.0"
25 | prost = "0.13.1"
26 | prost-build = "0.13.1"
27 |
28 | [[test]]
29 | name = "it"
30 | path = "tests/it/main.rs"
31 |
32 |
--------------------------------------------------------------------------------
/packages/duckdb_protobuf/src/filtered_dynamic_message.rs:
--------------------------------------------------------------------------------
1 | use prost::bytes::{Buf, BufMut};
2 | use prost::encoding::{DecodeContext, WireType};
3 | use prost::{DecodeError, Message};
4 | use prost_reflect::{DynamicMessage, UnknownField};
5 | use std::collections::HashSet;
6 |
7 | #[derive(Debug, Clone)]
8 | pub struct FilteredDynamicMessage {
9 | message: DynamicMessage,
10 | accepted_fields: HashSet,
11 | }
12 |
13 | impl FilteredDynamicMessage {
14 | pub fn new(message: DynamicMessage, accepted_fields: HashSet) -> FilteredDynamicMessage {
15 | FilteredDynamicMessage {
16 | message,
17 | accepted_fields,
18 | }
19 | }
20 |
21 | pub fn into(self) -> DynamicMessage {
22 | self.message
23 | }
24 | }
25 |
26 | impl Message for FilteredDynamicMessage {
27 | fn encode_raw(&self, buf: &mut impl BufMut)
28 | where
29 | Self: Sized,
30 | {
31 | self.message.encode_raw(buf)
32 | }
33 |
34 | fn merge_field(
35 | &mut self,
36 | number: u32,
37 | wire_type: WireType,
38 | buf: &mut impl Buf,
39 | ctx: DecodeContext,
40 | ) -> Result<(), DecodeError>
41 | where
42 | Self: Sized,
43 | {
44 | if !self.accepted_fields.contains(&number) {
45 | let _field = UnknownField::decode_value(number, wire_type, buf, ctx)?;
46 | return Ok(());
47 | }
48 |
49 | self.message.merge_field(number, wire_type, buf, ctx)
50 | }
51 |
52 | fn encoded_len(&self) -> usize {
53 | self.message.encoded_len()
54 | }
55 |
56 | fn clear(&mut self) {
57 | self.message.clear()
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/packages/duckdb_protobuf/src/io.rs:
--------------------------------------------------------------------------------
1 | use anyhow::format_err;
2 | use byteorder::{BigEndian, ReadBytesExt};
3 | use ouroboros::self_referencing;
4 | use protobuf::CodedInputStream;
5 | use std::error::Error;
6 | use std::fs::File;
7 | use std::io;
8 | use std::path::{Path, PathBuf};
9 | use strum::{AsRefStr, EnumIter, EnumString, IntoEnumIterator};
10 |
11 | #[derive(Copy, Clone, EnumString, EnumIter, AsRefStr)]
12 | pub enum LengthKind {
13 | BigEndianFixed,
14 | Varint,
15 | SingleMessagePerFile,
16 | }
17 |
18 | pub fn parse + IntoEnumIterator + AsRef>(
19 | value: &str,
20 | ) -> Result {
21 | Ok(T::from_str(value).map_err(|err| {
22 | format_err!(
23 | "{}: expected one of: {}, got: {}",
24 | err,
25 | T::iter()
26 | .map(|it| format!("{}", it.as_ref()))
27 | .collect::>()
28 | .join(", "),
29 | value
30 | )
31 | })?)
32 | }
33 |
34 | #[derive(Copy, Clone)]
35 | pub enum DelimitedLengthKind {
36 | BigEndianFixed,
37 | Varint,
38 | }
39 |
40 | #[self_referencing]
41 | pub struct LengthDelimitedRecordsReader {
42 | length_kind: DelimitedLengthKind,
43 | path: PathBuf,
44 | inner: File,
45 |
46 | #[borrows(mut inner)]
47 | #[not_covariant]
48 | reader: CodedInputStream<'this>,
49 | }
50 |
51 | pub struct Record {
52 | pub bytes: Vec,
53 | pub position: u64,
54 | pub size: u32,
55 | }
56 |
57 | impl LengthDelimitedRecordsReader {
58 | pub fn create(inner: File, length_kind: DelimitedLengthKind, path: PathBuf) -> Self {
59 | LengthDelimitedRecordsReaderBuilder {
60 | length_kind,
61 | path,
62 | inner,
63 | reader_builder: |it| CodedInputStream::new(it),
64 | }
65 | .build()
66 | }
67 |
68 | fn get_next(&mut self) -> Result {
69 | let length_kind = *self.borrow_length_kind();
70 | Ok(self.with_reader_mut(move |reader| {
71 | let position = reader.pos();
72 | let len = match length_kind {
73 | DelimitedLengthKind::BigEndianFixed => reader.read_u32::()?,
74 | DelimitedLengthKind::Varint => reader.read_raw_varint32()?,
75 | };
76 |
77 | let mut buf = vec![0; len as usize];
78 | ::read_exact(reader, &mut buf)?;
79 |
80 | Ok::<_, io::Error>(Record {
81 | bytes: buf,
82 | position,
83 | size: len,
84 | })
85 | })?)
86 | }
87 |
88 | pub fn try_get_next(&mut self) -> Result