diff options
-rw-r--r-- | cmd/git-lfs-authenticate/main.c | 13 | ||||
-rw-r--r-- | flake.lock | 20 | ||||
-rw-r--r-- | flake.nix | 9 | ||||
-rw-r--r-- | rs/.gitignore | 1 | ||||
-rw-r--r-- | rs/Cargo.lock | 2022 | ||||
-rw-r--r-- | rs/Cargo.toml | 7 | ||||
-rw-r--r-- | rs/common/Cargo.toml | 10 | ||||
-rw-r--r-- | rs/common/src/lib.rs | 337 | ||||
-rw-r--r-- | rs/git-lfs-authenticate/Cargo.toml | 8 | ||||
-rw-r--r-- | rs/git-lfs-authenticate/src/main.rs | 239 | ||||
-rw-r--r-- | rs/server/Cargo.toml | 15 | ||||
-rw-r--r-- | rs/server/src/main.rs | 347 |
12 files changed, 3020 insertions, 8 deletions
diff --git a/cmd/git-lfs-authenticate/main.c b/cmd/git-lfs-authenticate/main.c index 71481e9..a7ec031 100644 --- a/cmd/git-lfs-authenticate/main.c +++ b/cmd/git-lfs-authenticate/main.c | |||
@@ -65,8 +65,6 @@ void checkrepopath(const char *path) { | |||
65 | die("Bad repository name: Is unresolved path"); | 65 | die("Bad repository name: Is unresolved path"); |
66 | if (strlen(path) > 100) | 66 | if (strlen(path) > 100) |
67 | die("Bad repository name: Longer than 100 characters"); | 67 | die("Bad repository name: Longer than 100 characters"); |
68 | if (hassuffix(path, "/")) | ||
69 | die("Bad repositry name: Unexpected trailing slash"); | ||
70 | if (hasprefix(path, "/")) | 68 | if (hasprefix(path, "/")) |
71 | die("Bad repository name: Unexpected absolute path"); | 69 | die("Bad repository name: Unexpected absolute path"); |
72 | if (!hassuffix(path, ".git")) | 70 | if (!hassuffix(path, ".git")) |
@@ -152,6 +150,10 @@ void u64tobe(uint64_t x, uint8_t b[8]) { | |||
152 | b[7] = (uint8_t)(x >> 0); | 150 | b[7] = (uint8_t)(x >> 0); |
153 | } | 151 | } |
154 | 152 | ||
153 | void *memcat(void *dest, const void *src, size_t n) { | ||
154 | return memcpy(dest, src, n) + n; | ||
155 | } | ||
156 | |||
155 | #define MAX_TAG_SIZE EVP_MAX_MD_SIZE | 157 | #define MAX_TAG_SIZE EVP_MAX_MD_SIZE |
156 | 158 | ||
157 | typedef struct taginfo { | 159 | typedef struct taginfo { |
@@ -161,10 +163,6 @@ typedef struct taginfo { | |||
161 | const int64_t expiresat_s; | 163 | const int64_t expiresat_s; |
162 | } taginfo_t; | 164 | } taginfo_t; |
163 | 165 | ||
164 | void *memcat(void *dest, const void *src, size_t n) { | ||
165 | return memcpy(dest, src, n) + n; | ||
166 | } | ||
167 | |||
168 | void maketag(const taginfo_t info, uint8_t key[KEYSIZE], uint8_t dest[MAX_TAG_SIZE], uint32_t *len) { | 166 | void maketag(const taginfo_t info, uint8_t key[KEYSIZE], uint8_t dest[MAX_TAG_SIZE], uint32_t *len) { |
169 | uint8_t expiresat_b[8]; | 167 | uint8_t expiresat_b[8]; |
170 | u64tobe(info.expiresat_s, expiresat_b); | 168 | u64tobe(info.expiresat_s, expiresat_b); |
@@ -247,7 +245,8 @@ int main(int argc, char *argv[]) { | |||
247 | char hextag[MAX_HEXTAG_STRLEN + 1]; | 245 | char hextag[MAX_HEXTAG_STRLEN + 1]; |
248 | makehextag(taginfo, key, hextag); | 246 | makehextag(taginfo, key, hextag); |
249 | 247 | ||
250 | printf("{\"header\":{\"Authorization\":\"Gitolfs3-Hmac-Sha256 %s\"},\"expires_in\":%ld,\"href\":\"", hextag, expiresin_s); | 248 | printf("{\"header\":{\"Authorization\":\"Gitolfs3-Hmac-Sha256 %s\"}," |
249 | "\"expires_in\":%ld,\"href\":\"", hextag, expiresin_s); | ||
251 | printescjson(hrefbase); | 250 | printescjson(hrefbase); |
252 | printescjson(repopath); | 251 | printescjson(repopath); |
253 | printf("/info/lfs?p=1&te=%ld\"}\n", expiresat_s); | 252 | printf("/info/lfs?p=1&te=%ld\"}\n", expiresat_s); |
@@ -1,5 +1,24 @@ | |||
1 | { | 1 | { |
2 | "nodes": { | 2 | "nodes": { |
3 | "crane": { | ||
4 | "inputs": { | ||
5 | "nixpkgs": [ | ||
6 | "nixpkgs" | ||
7 | ] | ||
8 | }, | ||
9 | "locked": { | ||
10 | "lastModified": 1701384536, | ||
11 | "narHash": "sha256-PdYjpXmkn4FYJU7uvmCa54b0PPXBgDZHJaJpSEWR1Ek=", | ||
12 | "rev": "07c531adf572dafd494db0672cdac00e48216171", | ||
13 | "revCount": 469, | ||
14 | "type": "tarball", | ||
15 | "url": "https://api.flakehub.com/f/pinned/ipetkov/crane/0.15.1/018c226e-9985-758b-9809-17419c4ebd2d/source.tar.gz" | ||
16 | }, | ||
17 | "original": { | ||
18 | "type": "tarball", | ||
19 | "url": "https://flakehub.com/f/ipetkov/crane/0.15.1.tar.gz" | ||
20 | } | ||
21 | }, | ||
3 | "flake-utils": { | 22 | "flake-utils": { |
4 | "inputs": { | 23 | "inputs": { |
5 | "systems": "systems" | 24 | "systems": "systems" |
@@ -33,6 +52,7 @@ | |||
33 | }, | 52 | }, |
34 | "root": { | 53 | "root": { |
35 | "inputs": { | 54 | "inputs": { |
55 | "crane": "crane", | ||
36 | "flake-utils": "flake-utils", | 56 | "flake-utils": "flake-utils", |
37 | "nixpkgs": "nixpkgs" | 57 | "nixpkgs": "nixpkgs" |
38 | } | 58 | } |
@@ -2,9 +2,11 @@ | |||
2 | inputs = { | 2 | inputs = { |
3 | nixpkgs.url = "https://flakehub.com/f/NixOS/nixpkgs/0.2311.*.tar.gz"; | 3 | nixpkgs.url = "https://flakehub.com/f/NixOS/nixpkgs/0.2311.*.tar.gz"; |
4 | flake-utils.url = "https://flakehub.com/f/numtide/flake-utils/0.1.88.tar.gz"; | 4 | flake-utils.url = "https://flakehub.com/f/numtide/flake-utils/0.1.88.tar.gz"; |
5 | crane.url = "https://flakehub.com/f/ipetkov/crane/0.15.1.tar.gz"; | ||
6 | crane.inputs.nixpkgs.follows = "nixpkgs"; | ||
5 | }; | 7 | }; |
6 | 8 | ||
7 | outputs = { self, nixpkgs, flake-utils, ... }@inputs: | 9 | outputs = { self, crane, nixpkgs, flake-utils, ... }@inputs: |
8 | flake-utils.lib.eachDefaultSystem | 10 | flake-utils.lib.eachDefaultSystem |
9 | (system: | 11 | (system: |
10 | let | 12 | let |
@@ -13,6 +15,8 @@ | |||
13 | overlays = [ ]; | 15 | overlays = [ ]; |
14 | }; | 16 | }; |
15 | 17 | ||
18 | craneLib = crane.lib.${system}; | ||
19 | |||
16 | gitolfs3 = pkgs.buildGoModule { | 20 | gitolfs3 = pkgs.buildGoModule { |
17 | name = "gitolfs3"; | 21 | name = "gitolfs3"; |
18 | src = ./.; | 22 | src = ./.; |
@@ -21,6 +25,9 @@ | |||
21 | in | 25 | in |
22 | { | 26 | { |
23 | packages.gitolfs3 = gitolfs3; | 27 | packages.gitolfs3 = gitolfs3; |
28 | packages.gitolfs3-rs = craneLib.buildPackage { | ||
29 | src = craneLib.cleanCargoSource (craneLib.path ./rs); | ||
30 | }; | ||
24 | packages.default = self.packages.${system}.gitolfs3; | 31 | packages.default = self.packages.${system}.gitolfs3; |
25 | 32 | ||
26 | devShells.default = pkgs.mkShell { | 33 | devShells.default = pkgs.mkShell { |
diff --git a/rs/.gitignore b/rs/.gitignore new file mode 100644 index 0000000..b83d222 --- /dev/null +++ b/rs/.gitignore | |||
@@ -0,0 +1 @@ | |||
/target/ | |||
diff --git a/rs/Cargo.lock b/rs/Cargo.lock new file mode 100644 index 0000000..6069bcb --- /dev/null +++ b/rs/Cargo.lock | |||
@@ -0,0 +1,2022 @@ | |||
1 | # This file is automatically @generated by Cargo. | ||
2 | # It is not intended for manual editing. | ||
3 | version = 3 | ||
4 | |||
5 | [[package]] | ||
6 | name = "addr2line" | ||
7 | version = "0.21.0" | ||
8 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
9 | checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" | ||
10 | dependencies = [ | ||
11 | "gimli", | ||
12 | ] | ||
13 | |||
14 | [[package]] | ||
15 | name = "adler" | ||
16 | version = "1.0.2" | ||
17 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" | ||
19 | |||
20 | [[package]] | ||
21 | name = "ahash" | ||
22 | version = "0.7.7" | ||
23 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
24 | checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" | ||
25 | dependencies = [ | ||
26 | "getrandom", | ||
27 | "once_cell", | ||
28 | "version_check", | ||
29 | ] | ||
30 | |||
31 | [[package]] | ||
32 | name = "android-tzdata" | ||
33 | version = "0.1.1" | ||
34 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
35 | checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" | ||
36 | |||
37 | [[package]] | ||
38 | name = "android_system_properties" | ||
39 | version = "0.1.5" | ||
40 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
41 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" | ||
42 | dependencies = [ | ||
43 | "libc", | ||
44 | ] | ||
45 | |||
46 | [[package]] | ||
47 | name = "async-trait" | ||
48 | version = "0.1.77" | ||
49 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
50 | checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" | ||
51 | dependencies = [ | ||
52 | "proc-macro2", | ||
53 | "quote", | ||
54 | "syn 2.0.48", | ||
55 | ] | ||
56 | |||
57 | [[package]] | ||
58 | name = "attohttpc" | ||
59 | version = "0.22.0" | ||
60 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
61 | checksum = "1fcf00bc6d5abb29b5f97e3c61a90b6d3caa12f3faf897d4a3e3607c050a35a7" | ||
62 | dependencies = [ | ||
63 | "http 0.2.11", | ||
64 | "log", | ||
65 | "native-tls", | ||
66 | "serde", | ||
67 | "serde_json", | ||
68 | "url", | ||
69 | ] | ||
70 | |||
71 | [[package]] | ||
72 | name = "autocfg" | ||
73 | version = "1.1.0" | ||
74 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
75 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" | ||
76 | |||
77 | [[package]] | ||
78 | name = "aws-creds" | ||
79 | version = "0.34.1" | ||
80 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
81 | checksum = "3776743bb68d4ad02ba30ba8f64373f1be4e082fe47651767171ce75bb2f6cf5" | ||
82 | dependencies = [ | ||
83 | "attohttpc", | ||
84 | "dirs", | ||
85 | "log", | ||
86 | "quick-xml", | ||
87 | "rust-ini", | ||
88 | "serde", | ||
89 | "thiserror", | ||
90 | "time", | ||
91 | "url", | ||
92 | ] | ||
93 | |||
94 | [[package]] | ||
95 | name = "aws-region" | ||
96 | version = "0.25.4" | ||
97 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
98 | checksum = "42fed2b9fca70f2908268d057a607f2a906f47edbf856ea8587de9038d264e22" | ||
99 | dependencies = [ | ||
100 | "thiserror", | ||
101 | ] | ||
102 | |||
103 | [[package]] | ||
104 | name = "axum" | ||
105 | version = "0.7.4" | ||
106 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
107 | checksum = "1236b4b292f6c4d6dc34604bb5120d85c3fe1d1aa596bd5cc52ca054d13e7b9e" | ||
108 | dependencies = [ | ||
109 | "async-trait", | ||
110 | "axum-core", | ||
111 | "bytes", | ||
112 | "futures-util", | ||
113 | "http 1.0.0", | ||
114 | "http-body 1.0.0", | ||
115 | "http-body-util", | ||
116 | "hyper 1.1.0", | ||
117 | "hyper-util", | ||
118 | "itoa", | ||
119 | "matchit", | ||
120 | "memchr", | ||
121 | "mime", | ||
122 | "percent-encoding", | ||
123 | "pin-project-lite", | ||
124 | "rustversion", | ||
125 | "serde", | ||
126 | "serde_json", | ||
127 | "serde_path_to_error", | ||
128 | "serde_urlencoded", | ||
129 | "sync_wrapper", | ||
130 | "tokio", | ||
131 | "tower", | ||
132 | "tower-layer", | ||
133 | "tower-service", | ||
134 | "tracing", | ||
135 | ] | ||
136 | |||
137 | [[package]] | ||
138 | name = "axum-core" | ||
139 | version = "0.4.3" | ||
140 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
141 | checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" | ||
142 | dependencies = [ | ||
143 | "async-trait", | ||
144 | "bytes", | ||
145 | "futures-util", | ||
146 | "http 1.0.0", | ||
147 | "http-body 1.0.0", | ||
148 | "http-body-util", | ||
149 | "mime", | ||
150 | "pin-project-lite", | ||
151 | "rustversion", | ||
152 | "sync_wrapper", | ||
153 | "tower-layer", | ||
154 | "tower-service", | ||
155 | "tracing", | ||
156 | ] | ||
157 | |||
158 | [[package]] | ||
159 | name = "backtrace" | ||
160 | version = "0.3.69" | ||
161 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
162 | checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" | ||
163 | dependencies = [ | ||
164 | "addr2line", | ||
165 | "cc", | ||
166 | "cfg-if", | ||
167 | "libc", | ||
168 | "miniz_oxide", | ||
169 | "object", | ||
170 | "rustc-demangle", | ||
171 | ] | ||
172 | |||
173 | [[package]] | ||
174 | name = "base64" | ||
175 | version = "0.13.1" | ||
176 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
177 | checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" | ||
178 | |||
179 | [[package]] | ||
180 | name = "base64" | ||
181 | version = "0.21.7" | ||
182 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
183 | checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" | ||
184 | |||
185 | [[package]] | ||
186 | name = "bitflags" | ||
187 | version = "1.3.2" | ||
188 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
189 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" | ||
190 | |||
191 | [[package]] | ||
192 | name = "bitflags" | ||
193 | version = "2.4.2" | ||
194 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
195 | checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" | ||
196 | |||
197 | [[package]] | ||
198 | name = "block-buffer" | ||
199 | version = "0.10.4" | ||
200 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
201 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" | ||
202 | dependencies = [ | ||
203 | "generic-array", | ||
204 | ] | ||
205 | |||
206 | [[package]] | ||
207 | name = "bumpalo" | ||
208 | version = "3.14.0" | ||
209 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
210 | checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" | ||
211 | |||
212 | [[package]] | ||
213 | name = "bytes" | ||
214 | version = "1.5.0" | ||
215 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
216 | checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" | ||
217 | |||
218 | [[package]] | ||
219 | name = "cc" | ||
220 | version = "1.0.83" | ||
221 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
222 | checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" | ||
223 | dependencies = [ | ||
224 | "libc", | ||
225 | ] | ||
226 | |||
227 | [[package]] | ||
228 | name = "cfg-if" | ||
229 | version = "1.0.0" | ||
230 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
231 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" | ||
232 | |||
233 | [[package]] | ||
234 | name = "chrono" | ||
235 | version = "0.4.31" | ||
236 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
237 | checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" | ||
238 | dependencies = [ | ||
239 | "android-tzdata", | ||
240 | "iana-time-zone", | ||
241 | "js-sys", | ||
242 | "num-traits", | ||
243 | "serde", | ||
244 | "wasm-bindgen", | ||
245 | "windows-targets 0.48.5", | ||
246 | ] | ||
247 | |||
248 | [[package]] | ||
249 | name = "common" | ||
250 | version = "0.1.0" | ||
251 | dependencies = [ | ||
252 | "chrono", | ||
253 | "hmac-sha256", | ||
254 | "serde", | ||
255 | "subtle", | ||
256 | ] | ||
257 | |||
258 | [[package]] | ||
259 | name = "core-foundation" | ||
260 | version = "0.9.4" | ||
261 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
262 | checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" | ||
263 | dependencies = [ | ||
264 | "core-foundation-sys", | ||
265 | "libc", | ||
266 | ] | ||
267 | |||
268 | [[package]] | ||
269 | name = "core-foundation-sys" | ||
270 | version = "0.8.6" | ||
271 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
272 | checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" | ||
273 | |||
274 | [[package]] | ||
275 | name = "cpufeatures" | ||
276 | version = "0.2.12" | ||
277 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
278 | checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" | ||
279 | dependencies = [ | ||
280 | "libc", | ||
281 | ] | ||
282 | |||
283 | [[package]] | ||
284 | name = "crypto-common" | ||
285 | version = "0.1.6" | ||
286 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
287 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" | ||
288 | dependencies = [ | ||
289 | "generic-array", | ||
290 | "typenum", | ||
291 | ] | ||
292 | |||
293 | [[package]] | ||
294 | name = "deranged" | ||
295 | version = "0.3.11" | ||
296 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
297 | checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" | ||
298 | dependencies = [ | ||
299 | "powerfmt", | ||
300 | "serde", | ||
301 | ] | ||
302 | |||
303 | [[package]] | ||
304 | name = "digest" | ||
305 | version = "0.10.7" | ||
306 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
307 | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" | ||
308 | dependencies = [ | ||
309 | "block-buffer", | ||
310 | "crypto-common", | ||
311 | "subtle", | ||
312 | ] | ||
313 | |||
314 | [[package]] | ||
315 | name = "dirs" | ||
316 | version = "4.0.0" | ||
317 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
318 | checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" | ||
319 | dependencies = [ | ||
320 | "dirs-sys", | ||
321 | ] | ||
322 | |||
323 | [[package]] | ||
324 | name = "dirs-sys" | ||
325 | version = "0.3.7" | ||
326 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
327 | checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" | ||
328 | dependencies = [ | ||
329 | "libc", | ||
330 | "redox_users", | ||
331 | "winapi", | ||
332 | ] | ||
333 | |||
334 | [[package]] | ||
335 | name = "dlv-list" | ||
336 | version = "0.3.0" | ||
337 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
338 | checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257" | ||
339 | |||
340 | [[package]] | ||
341 | name = "encoding_rs" | ||
342 | version = "0.8.33" | ||
343 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
344 | checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" | ||
345 | dependencies = [ | ||
346 | "cfg-if", | ||
347 | ] | ||
348 | |||
349 | [[package]] | ||
350 | name = "equivalent" | ||
351 | version = "1.0.1" | ||
352 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
353 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" | ||
354 | |||
355 | [[package]] | ||
356 | name = "errno" | ||
357 | version = "0.3.8" | ||
358 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
359 | checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" | ||
360 | dependencies = [ | ||
361 | "libc", | ||
362 | "windows-sys 0.52.0", | ||
363 | ] | ||
364 | |||
365 | [[package]] | ||
366 | name = "fastrand" | ||
367 | version = "2.0.1" | ||
368 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
369 | checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" | ||
370 | |||
371 | [[package]] | ||
372 | name = "fnv" | ||
373 | version = "1.0.7" | ||
374 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
375 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" | ||
376 | |||
377 | [[package]] | ||
378 | name = "foreign-types" | ||
379 | version = "0.3.2" | ||
380 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
381 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" | ||
382 | dependencies = [ | ||
383 | "foreign-types-shared", | ||
384 | ] | ||
385 | |||
386 | [[package]] | ||
387 | name = "foreign-types-shared" | ||
388 | version = "0.1.1" | ||
389 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
390 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" | ||
391 | |||
392 | [[package]] | ||
393 | name = "form_urlencoded" | ||
394 | version = "1.2.1" | ||
395 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
396 | checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" | ||
397 | dependencies = [ | ||
398 | "percent-encoding", | ||
399 | ] | ||
400 | |||
401 | [[package]] | ||
402 | name = "futures" | ||
403 | version = "0.3.30" | ||
404 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
405 | checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" | ||
406 | dependencies = [ | ||
407 | "futures-channel", | ||
408 | "futures-core", | ||
409 | "futures-executor", | ||
410 | "futures-io", | ||
411 | "futures-sink", | ||
412 | "futures-task", | ||
413 | "futures-util", | ||
414 | ] | ||
415 | |||
416 | [[package]] | ||
417 | name = "futures-channel" | ||
418 | version = "0.3.30" | ||
419 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
420 | checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" | ||
421 | dependencies = [ | ||
422 | "futures-core", | ||
423 | "futures-sink", | ||
424 | ] | ||
425 | |||
426 | [[package]] | ||
427 | name = "futures-core" | ||
428 | version = "0.3.30" | ||
429 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
430 | checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" | ||
431 | |||
432 | [[package]] | ||
433 | name = "futures-executor" | ||
434 | version = "0.3.30" | ||
435 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
436 | checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" | ||
437 | dependencies = [ | ||
438 | "futures-core", | ||
439 | "futures-task", | ||
440 | "futures-util", | ||
441 | ] | ||
442 | |||
443 | [[package]] | ||
444 | name = "futures-io" | ||
445 | version = "0.3.30" | ||
446 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
447 | checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" | ||
448 | |||
449 | [[package]] | ||
450 | name = "futures-macro" | ||
451 | version = "0.3.30" | ||
452 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
453 | checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" | ||
454 | dependencies = [ | ||
455 | "proc-macro2", | ||
456 | "quote", | ||
457 | "syn 2.0.48", | ||
458 | ] | ||
459 | |||
460 | [[package]] | ||
461 | name = "futures-sink" | ||
462 | version = "0.3.30" | ||
463 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
464 | checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" | ||
465 | |||
466 | [[package]] | ||
467 | name = "futures-task" | ||
468 | version = "0.3.30" | ||
469 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
470 | checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" | ||
471 | |||
472 | [[package]] | ||
473 | name = "futures-util" | ||
474 | version = "0.3.30" | ||
475 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
476 | checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" | ||
477 | dependencies = [ | ||
478 | "futures-channel", | ||
479 | "futures-core", | ||
480 | "futures-io", | ||
481 | "futures-macro", | ||
482 | "futures-sink", | ||
483 | "futures-task", | ||
484 | "memchr", | ||
485 | "pin-project-lite", | ||
486 | "pin-utils", | ||
487 | "slab", | ||
488 | ] | ||
489 | |||
490 | [[package]] | ||
491 | name = "generic-array" | ||
492 | version = "0.14.7" | ||
493 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
494 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" | ||
495 | dependencies = [ | ||
496 | "typenum", | ||
497 | "version_check", | ||
498 | ] | ||
499 | |||
500 | [[package]] | ||
501 | name = "getrandom" | ||
502 | version = "0.2.12" | ||
503 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
504 | checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" | ||
505 | dependencies = [ | ||
506 | "cfg-if", | ||
507 | "libc", | ||
508 | "wasi", | ||
509 | ] | ||
510 | |||
511 | [[package]] | ||
512 | name = "gimli" | ||
513 | version = "0.28.1" | ||
514 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
515 | checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" | ||
516 | |||
517 | [[package]] | ||
518 | name = "git-lfs-authenticate" | ||
519 | version = "0.1.0" | ||
520 | dependencies = [ | ||
521 | "chrono", | ||
522 | "common", | ||
523 | ] | ||
524 | |||
525 | [[package]] | ||
526 | name = "h2" | ||
527 | version = "0.3.24" | ||
528 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
529 | checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" | ||
530 | dependencies = [ | ||
531 | "bytes", | ||
532 | "fnv", | ||
533 | "futures-core", | ||
534 | "futures-sink", | ||
535 | "futures-util", | ||
536 | "http 0.2.11", | ||
537 | "indexmap", | ||
538 | "slab", | ||
539 | "tokio", | ||
540 | "tokio-util", | ||
541 | "tracing", | ||
542 | ] | ||
543 | |||
544 | [[package]] | ||
545 | name = "h2" | ||
546 | version = "0.4.2" | ||
547 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
548 | checksum = "31d030e59af851932b72ceebadf4a2b5986dba4c3b99dd2493f8273a0f151943" | ||
549 | dependencies = [ | ||
550 | "bytes", | ||
551 | "fnv", | ||
552 | "futures-core", | ||
553 | "futures-sink", | ||
554 | "futures-util", | ||
555 | "http 1.0.0", | ||
556 | "indexmap", | ||
557 | "slab", | ||
558 | "tokio", | ||
559 | "tokio-util", | ||
560 | "tracing", | ||
561 | ] | ||
562 | |||
563 | [[package]] | ||
564 | name = "hashbrown" | ||
565 | version = "0.12.3" | ||
566 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
567 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" | ||
568 | dependencies = [ | ||
569 | "ahash", | ||
570 | ] | ||
571 | |||
572 | [[package]] | ||
573 | name = "hashbrown" | ||
574 | version = "0.14.3" | ||
575 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
576 | checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" | ||
577 | |||
578 | [[package]] | ||
579 | name = "hermit-abi" | ||
580 | version = "0.3.3" | ||
581 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
582 | checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" | ||
583 | |||
584 | [[package]] | ||
585 | name = "hex" | ||
586 | version = "0.4.3" | ||
587 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
588 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" | ||
589 | |||
590 | [[package]] | ||
591 | name = "hmac" | ||
592 | version = "0.12.1" | ||
593 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
594 | checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" | ||
595 | dependencies = [ | ||
596 | "digest", | ||
597 | ] | ||
598 | |||
599 | [[package]] | ||
600 | name = "hmac-sha256" | ||
601 | version = "1.1.7" | ||
602 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
603 | checksum = "3688e69b38018fec1557254f64c8dc2cc8ec502890182f395dbb0aa997aa5735" | ||
604 | |||
605 | [[package]] | ||
606 | name = "http" | ||
607 | version = "0.2.11" | ||
608 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
609 | checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" | ||
610 | dependencies = [ | ||
611 | "bytes", | ||
612 | "fnv", | ||
613 | "itoa", | ||
614 | ] | ||
615 | |||
616 | [[package]] | ||
617 | name = "http" | ||
618 | version = "1.0.0" | ||
619 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
620 | checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea" | ||
621 | dependencies = [ | ||
622 | "bytes", | ||
623 | "fnv", | ||
624 | "itoa", | ||
625 | ] | ||
626 | |||
627 | [[package]] | ||
628 | name = "http-body" | ||
629 | version = "0.4.6" | ||
630 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
631 | checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" | ||
632 | dependencies = [ | ||
633 | "bytes", | ||
634 | "http 0.2.11", | ||
635 | "pin-project-lite", | ||
636 | ] | ||
637 | |||
638 | [[package]] | ||
639 | name = "http-body" | ||
640 | version = "1.0.0" | ||
641 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
642 | checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" | ||
643 | dependencies = [ | ||
644 | "bytes", | ||
645 | "http 1.0.0", | ||
646 | ] | ||
647 | |||
648 | [[package]] | ||
649 | name = "http-body-util" | ||
650 | version = "0.1.0" | ||
651 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
652 | checksum = "41cb79eb393015dadd30fc252023adb0b2400a0caee0fa2a077e6e21a551e840" | ||
653 | dependencies = [ | ||
654 | "bytes", | ||
655 | "futures-util", | ||
656 | "http 1.0.0", | ||
657 | "http-body 1.0.0", | ||
658 | "pin-project-lite", | ||
659 | ] | ||
660 | |||
661 | [[package]] | ||
662 | name = "httparse" | ||
663 | version = "1.8.0" | ||
664 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
665 | checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" | ||
666 | |||
667 | [[package]] | ||
668 | name = "httpdate" | ||
669 | version = "1.0.3" | ||
670 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
671 | checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" | ||
672 | |||
673 | [[package]] | ||
674 | name = "hyper" | ||
675 | version = "0.14.28" | ||
676 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
677 | checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" | ||
678 | dependencies = [ | ||
679 | "bytes", | ||
680 | "futures-channel", | ||
681 | "futures-core", | ||
682 | "futures-util", | ||
683 | "h2 0.3.24", | ||
684 | "http 0.2.11", | ||
685 | "http-body 0.4.6", | ||
686 | "httparse", | ||
687 | "httpdate", | ||
688 | "itoa", | ||
689 | "pin-project-lite", | ||
690 | "socket2", | ||
691 | "tokio", | ||
692 | "tower-service", | ||
693 | "tracing", | ||
694 | "want", | ||
695 | ] | ||
696 | |||
697 | [[package]] | ||
698 | name = "hyper" | ||
699 | version = "1.1.0" | ||
700 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
701 | checksum = "fb5aa53871fc917b1a9ed87b683a5d86db645e23acb32c2e0785a353e522fb75" | ||
702 | dependencies = [ | ||
703 | "bytes", | ||
704 | "futures-channel", | ||
705 | "futures-util", | ||
706 | "h2 0.4.2", | ||
707 | "http 1.0.0", | ||
708 | "http-body 1.0.0", | ||
709 | "httparse", | ||
710 | "httpdate", | ||
711 | "itoa", | ||
712 | "pin-project-lite", | ||
713 | "tokio", | ||
714 | ] | ||
715 | |||
716 | [[package]] | ||
717 | name = "hyper-tls" | ||
718 | version = "0.5.0" | ||
719 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
720 | checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" | ||
721 | dependencies = [ | ||
722 | "bytes", | ||
723 | "hyper 0.14.28", | ||
724 | "native-tls", | ||
725 | "tokio", | ||
726 | "tokio-native-tls", | ||
727 | ] | ||
728 | |||
729 | [[package]] | ||
730 | name = "hyper-util" | ||
731 | version = "0.1.2" | ||
732 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
733 | checksum = "bdea9aac0dbe5a9240d68cfd9501e2db94222c6dc06843e06640b9e07f0fdc67" | ||
734 | dependencies = [ | ||
735 | "bytes", | ||
736 | "futures-channel", | ||
737 | "futures-util", | ||
738 | "http 1.0.0", | ||
739 | "http-body 1.0.0", | ||
740 | "hyper 1.1.0", | ||
741 | "pin-project-lite", | ||
742 | "socket2", | ||
743 | "tokio", | ||
744 | "tracing", | ||
745 | ] | ||
746 | |||
747 | [[package]] | ||
748 | name = "iana-time-zone" | ||
749 | version = "0.1.59" | ||
750 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
751 | checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" | ||
752 | dependencies = [ | ||
753 | "android_system_properties", | ||
754 | "core-foundation-sys", | ||
755 | "iana-time-zone-haiku", | ||
756 | "js-sys", | ||
757 | "wasm-bindgen", | ||
758 | "windows-core", | ||
759 | ] | ||
760 | |||
761 | [[package]] | ||
762 | name = "iana-time-zone-haiku" | ||
763 | version = "0.1.2" | ||
764 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
765 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" | ||
766 | dependencies = [ | ||
767 | "cc", | ||
768 | ] | ||
769 | |||
770 | [[package]] | ||
771 | name = "idna" | ||
772 | version = "0.5.0" | ||
773 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
774 | checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" | ||
775 | dependencies = [ | ||
776 | "unicode-bidi", | ||
777 | "unicode-normalization", | ||
778 | ] | ||
779 | |||
780 | [[package]] | ||
781 | name = "indexmap" | ||
782 | version = "2.1.0" | ||
783 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
784 | checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" | ||
785 | dependencies = [ | ||
786 | "equivalent", | ||
787 | "hashbrown 0.14.3", | ||
788 | ] | ||
789 | |||
790 | [[package]] | ||
791 | name = "ipnet" | ||
792 | version = "2.9.0" | ||
793 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
794 | checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" | ||
795 | |||
796 | [[package]] | ||
797 | name = "itoa" | ||
798 | version = "1.0.10" | ||
799 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
800 | checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" | ||
801 | |||
802 | [[package]] | ||
803 | name = "js-sys" | ||
804 | version = "0.3.67" | ||
805 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
806 | checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" | ||
807 | dependencies = [ | ||
808 | "wasm-bindgen", | ||
809 | ] | ||
810 | |||
811 | [[package]] | ||
812 | name = "lazy_static" | ||
813 | version = "1.4.0" | ||
814 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
815 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" | ||
816 | |||
817 | [[package]] | ||
818 | name = "libc" | ||
819 | version = "0.2.152" | ||
820 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
821 | checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" | ||
822 | |||
823 | [[package]] | ||
824 | name = "libredox" | ||
825 | version = "0.0.1" | ||
826 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
827 | checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" | ||
828 | dependencies = [ | ||
829 | "bitflags 2.4.2", | ||
830 | "libc", | ||
831 | "redox_syscall", | ||
832 | ] | ||
833 | |||
834 | [[package]] | ||
835 | name = "linux-raw-sys" | ||
836 | version = "0.4.13" | ||
837 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
838 | checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" | ||
839 | |||
840 | [[package]] | ||
841 | name = "lock_api" | ||
842 | version = "0.4.11" | ||
843 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
844 | checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" | ||
845 | dependencies = [ | ||
846 | "autocfg", | ||
847 | "scopeguard", | ||
848 | ] | ||
849 | |||
850 | [[package]] | ||
851 | name = "log" | ||
852 | version = "0.4.20" | ||
853 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
854 | checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" | ||
855 | |||
856 | [[package]] | ||
857 | name = "matchit" | ||
858 | version = "0.7.3" | ||
859 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
860 | checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" | ||
861 | |||
862 | [[package]] | ||
863 | name = "maybe-async" | ||
864 | version = "0.2.7" | ||
865 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
866 | checksum = "0f1b8c13cb1f814b634a96b2c725449fe7ed464a7b8781de8688be5ffbd3f305" | ||
867 | dependencies = [ | ||
868 | "proc-macro2", | ||
869 | "quote", | ||
870 | "syn 1.0.109", | ||
871 | ] | ||
872 | |||
873 | [[package]] | ||
874 | name = "md5" | ||
875 | version = "0.7.0" | ||
876 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
877 | checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" | ||
878 | |||
879 | [[package]] | ||
880 | name = "memchr" | ||
881 | version = "2.7.1" | ||
882 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
883 | checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" | ||
884 | |||
885 | [[package]] | ||
886 | name = "mime" | ||
887 | version = "0.3.17" | ||
888 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
889 | checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" | ||
890 | |||
891 | [[package]] | ||
892 | name = "minidom" | ||
893 | version = "0.15.2" | ||
894 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
895 | checksum = "f45614075738ce1b77a1768912a60c0227525971b03e09122a05b8a34a2a6278" | ||
896 | dependencies = [ | ||
897 | "rxml", | ||
898 | ] | ||
899 | |||
900 | [[package]] | ||
901 | name = "miniz_oxide" | ||
902 | version = "0.7.1" | ||
903 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
904 | checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" | ||
905 | dependencies = [ | ||
906 | "adler", | ||
907 | ] | ||
908 | |||
909 | [[package]] | ||
910 | name = "mio" | ||
911 | version = "0.8.10" | ||
912 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
913 | checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" | ||
914 | dependencies = [ | ||
915 | "libc", | ||
916 | "wasi", | ||
917 | "windows-sys 0.48.0", | ||
918 | ] | ||
919 | |||
920 | [[package]] | ||
921 | name = "native-tls" | ||
922 | version = "0.2.11" | ||
923 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
924 | checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" | ||
925 | dependencies = [ | ||
926 | "lazy_static", | ||
927 | "libc", | ||
928 | "log", | ||
929 | "openssl", | ||
930 | "openssl-probe", | ||
931 | "openssl-sys", | ||
932 | "schannel", | ||
933 | "security-framework", | ||
934 | "security-framework-sys", | ||
935 | "tempfile", | ||
936 | ] | ||
937 | |||
938 | [[package]] | ||
939 | name = "num-traits" | ||
940 | version = "0.2.17" | ||
941 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
942 | checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" | ||
943 | dependencies = [ | ||
944 | "autocfg", | ||
945 | ] | ||
946 | |||
947 | [[package]] | ||
948 | name = "num_cpus" | ||
949 | version = "1.16.0" | ||
950 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
951 | checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" | ||
952 | dependencies = [ | ||
953 | "hermit-abi", | ||
954 | "libc", | ||
955 | ] | ||
956 | |||
957 | [[package]] | ||
958 | name = "object" | ||
959 | version = "0.32.2" | ||
960 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
961 | checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" | ||
962 | dependencies = [ | ||
963 | "memchr", | ||
964 | ] | ||
965 | |||
966 | [[package]] | ||
967 | name = "once_cell" | ||
968 | version = "1.19.0" | ||
969 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
970 | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" | ||
971 | |||
972 | [[package]] | ||
973 | name = "openssl" | ||
974 | version = "0.10.62" | ||
975 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
976 | checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671" | ||
977 | dependencies = [ | ||
978 | "bitflags 2.4.2", | ||
979 | "cfg-if", | ||
980 | "foreign-types", | ||
981 | "libc", | ||
982 | "once_cell", | ||
983 | "openssl-macros", | ||
984 | "openssl-sys", | ||
985 | ] | ||
986 | |||
987 | [[package]] | ||
988 | name = "openssl-macros" | ||
989 | version = "0.1.1" | ||
990 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
991 | checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" | ||
992 | dependencies = [ | ||
993 | "proc-macro2", | ||
994 | "quote", | ||
995 | "syn 2.0.48", | ||
996 | ] | ||
997 | |||
998 | [[package]] | ||
999 | name = "openssl-probe" | ||
1000 | version = "0.1.5" | ||
1001 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1002 | checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" | ||
1003 | |||
1004 | [[package]] | ||
1005 | name = "openssl-sys" | ||
1006 | version = "0.9.98" | ||
1007 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1008 | checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7" | ||
1009 | dependencies = [ | ||
1010 | "cc", | ||
1011 | "libc", | ||
1012 | "pkg-config", | ||
1013 | "vcpkg", | ||
1014 | ] | ||
1015 | |||
1016 | [[package]] | ||
1017 | name = "ordered-multimap" | ||
1018 | version = "0.4.3" | ||
1019 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1020 | checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a" | ||
1021 | dependencies = [ | ||
1022 | "dlv-list", | ||
1023 | "hashbrown 0.12.3", | ||
1024 | ] | ||
1025 | |||
1026 | [[package]] | ||
1027 | name = "parking_lot" | ||
1028 | version = "0.12.1" | ||
1029 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1030 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" | ||
1031 | dependencies = [ | ||
1032 | "lock_api", | ||
1033 | "parking_lot_core", | ||
1034 | ] | ||
1035 | |||
1036 | [[package]] | ||
1037 | name = "parking_lot_core" | ||
1038 | version = "0.9.9" | ||
1039 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1040 | checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" | ||
1041 | dependencies = [ | ||
1042 | "cfg-if", | ||
1043 | "libc", | ||
1044 | "redox_syscall", | ||
1045 | "smallvec", | ||
1046 | "windows-targets 0.48.5", | ||
1047 | ] | ||
1048 | |||
1049 | [[package]] | ||
1050 | name = "percent-encoding" | ||
1051 | version = "2.3.1" | ||
1052 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1053 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" | ||
1054 | |||
1055 | [[package]] | ||
1056 | name = "pin-project" | ||
1057 | version = "1.1.3" | ||
1058 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1059 | checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" | ||
1060 | dependencies = [ | ||
1061 | "pin-project-internal", | ||
1062 | ] | ||
1063 | |||
1064 | [[package]] | ||
1065 | name = "pin-project-internal" | ||
1066 | version = "1.1.3" | ||
1067 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1068 | checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" | ||
1069 | dependencies = [ | ||
1070 | "proc-macro2", | ||
1071 | "quote", | ||
1072 | "syn 2.0.48", | ||
1073 | ] | ||
1074 | |||
1075 | [[package]] | ||
1076 | name = "pin-project-lite" | ||
1077 | version = "0.2.13" | ||
1078 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1079 | checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" | ||
1080 | |||
1081 | [[package]] | ||
1082 | name = "pin-utils" | ||
1083 | version = "0.1.0" | ||
1084 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1085 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" | ||
1086 | |||
1087 | [[package]] | ||
1088 | name = "pkg-config" | ||
1089 | version = "0.3.29" | ||
1090 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1091 | checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb" | ||
1092 | |||
1093 | [[package]] | ||
1094 | name = "powerfmt" | ||
1095 | version = "0.2.0" | ||
1096 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1097 | checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" | ||
1098 | |||
1099 | [[package]] | ||
1100 | name = "proc-macro2" | ||
1101 | version = "1.0.76" | ||
1102 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1103 | checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" | ||
1104 | dependencies = [ | ||
1105 | "unicode-ident", | ||
1106 | ] | ||
1107 | |||
1108 | [[package]] | ||
1109 | name = "quick-xml" | ||
1110 | version = "0.26.0" | ||
1111 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1112 | checksum = "7f50b1c63b38611e7d4d7f68b82d3ad0cc71a2ad2e7f61fc10f1328d917c93cd" | ||
1113 | dependencies = [ | ||
1114 | "memchr", | ||
1115 | "serde", | ||
1116 | ] | ||
1117 | |||
1118 | [[package]] | ||
1119 | name = "quote" | ||
1120 | version = "1.0.35" | ||
1121 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1122 | checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" | ||
1123 | dependencies = [ | ||
1124 | "proc-macro2", | ||
1125 | ] | ||
1126 | |||
1127 | [[package]] | ||
1128 | name = "redox_syscall" | ||
1129 | version = "0.4.1" | ||
1130 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1131 | checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" | ||
1132 | dependencies = [ | ||
1133 | "bitflags 1.3.2", | ||
1134 | ] | ||
1135 | |||
1136 | [[package]] | ||
1137 | name = "redox_users" | ||
1138 | version = "0.4.4" | ||
1139 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1140 | checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" | ||
1141 | dependencies = [ | ||
1142 | "getrandom", | ||
1143 | "libredox", | ||
1144 | "thiserror", | ||
1145 | ] | ||
1146 | |||
1147 | [[package]] | ||
1148 | name = "reqwest" | ||
1149 | version = "0.11.23" | ||
1150 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1151 | checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" | ||
1152 | dependencies = [ | ||
1153 | "base64 0.21.7", | ||
1154 | "bytes", | ||
1155 | "encoding_rs", | ||
1156 | "futures-core", | ||
1157 | "futures-util", | ||
1158 | "h2 0.3.24", | ||
1159 | "http 0.2.11", | ||
1160 | "http-body 0.4.6", | ||
1161 | "hyper 0.14.28", | ||
1162 | "hyper-tls", | ||
1163 | "ipnet", | ||
1164 | "js-sys", | ||
1165 | "log", | ||
1166 | "mime", | ||
1167 | "native-tls", | ||
1168 | "once_cell", | ||
1169 | "percent-encoding", | ||
1170 | "pin-project-lite", | ||
1171 | "serde", | ||
1172 | "serde_json", | ||
1173 | "serde_urlencoded", | ||
1174 | "system-configuration", | ||
1175 | "tokio", | ||
1176 | "tokio-native-tls", | ||
1177 | "tokio-util", | ||
1178 | "tower-service", | ||
1179 | "url", | ||
1180 | "wasm-bindgen", | ||
1181 | "wasm-bindgen-futures", | ||
1182 | "wasm-streams", | ||
1183 | "web-sys", | ||
1184 | "winreg", | ||
1185 | ] | ||
1186 | |||
1187 | [[package]] | ||
1188 | name = "rust-ini" | ||
1189 | version = "0.18.0" | ||
1190 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1191 | checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df" | ||
1192 | dependencies = [ | ||
1193 | "cfg-if", | ||
1194 | "ordered-multimap", | ||
1195 | ] | ||
1196 | |||
1197 | [[package]] | ||
1198 | name = "rust-s3" | ||
1199 | version = "0.33.0" | ||
1200 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1201 | checksum = "1b2ac5ff6acfbe74226fa701b5ef793aaa054055c13ebb7060ad36942956e027" | ||
1202 | dependencies = [ | ||
1203 | "async-trait", | ||
1204 | "aws-creds", | ||
1205 | "aws-region", | ||
1206 | "base64 0.13.1", | ||
1207 | "bytes", | ||
1208 | "cfg-if", | ||
1209 | "futures", | ||
1210 | "hex", | ||
1211 | "hmac", | ||
1212 | "http 0.2.11", | ||
1213 | "log", | ||
1214 | "maybe-async", | ||
1215 | "md5", | ||
1216 | "minidom", | ||
1217 | "percent-encoding", | ||
1218 | "quick-xml", | ||
1219 | "reqwest", | ||
1220 | "serde", | ||
1221 | "serde_derive", | ||
1222 | "sha2", | ||
1223 | "thiserror", | ||
1224 | "time", | ||
1225 | "tokio", | ||
1226 | "tokio-stream", | ||
1227 | "url", | ||
1228 | ] | ||
1229 | |||
1230 | [[package]] | ||
1231 | name = "rustc-demangle" | ||
1232 | version = "0.1.23" | ||
1233 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1234 | checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" | ||
1235 | |||
1236 | [[package]] | ||
1237 | name = "rustix" | ||
1238 | version = "0.38.30" | ||
1239 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1240 | checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" | ||
1241 | dependencies = [ | ||
1242 | "bitflags 2.4.2", | ||
1243 | "errno", | ||
1244 | "libc", | ||
1245 | "linux-raw-sys", | ||
1246 | "windows-sys 0.52.0", | ||
1247 | ] | ||
1248 | |||
1249 | [[package]] | ||
1250 | name = "rustversion" | ||
1251 | version = "1.0.14" | ||
1252 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1253 | checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" | ||
1254 | |||
1255 | [[package]] | ||
1256 | name = "rxml" | ||
1257 | version = "0.9.1" | ||
1258 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1259 | checksum = "a98f186c7a2f3abbffb802984b7f1dfd65dac8be1aafdaabbca4137f53f0dff7" | ||
1260 | dependencies = [ | ||
1261 | "bytes", | ||
1262 | "rxml_validation", | ||
1263 | "smartstring", | ||
1264 | ] | ||
1265 | |||
1266 | [[package]] | ||
1267 | name = "rxml_validation" | ||
1268 | version = "0.9.1" | ||
1269 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1270 | checksum = "22a197350ece202f19a166d1ad6d9d6de145e1d2a8ef47db299abe164dbd7530" | ||
1271 | |||
1272 | [[package]] | ||
1273 | name = "ryu" | ||
1274 | version = "1.0.16" | ||
1275 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1276 | checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" | ||
1277 | |||
1278 | [[package]] | ||
1279 | name = "schannel" | ||
1280 | version = "0.1.23" | ||
1281 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1282 | checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" | ||
1283 | dependencies = [ | ||
1284 | "windows-sys 0.52.0", | ||
1285 | ] | ||
1286 | |||
1287 | [[package]] | ||
1288 | name = "scopeguard" | ||
1289 | version = "1.2.0" | ||
1290 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1291 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" | ||
1292 | |||
1293 | [[package]] | ||
1294 | name = "security-framework" | ||
1295 | version = "2.9.2" | ||
1296 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1297 | checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" | ||
1298 | dependencies = [ | ||
1299 | "bitflags 1.3.2", | ||
1300 | "core-foundation", | ||
1301 | "core-foundation-sys", | ||
1302 | "libc", | ||
1303 | "security-framework-sys", | ||
1304 | ] | ||
1305 | |||
1306 | [[package]] | ||
1307 | name = "security-framework-sys" | ||
1308 | version = "2.9.1" | ||
1309 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1310 | checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" | ||
1311 | dependencies = [ | ||
1312 | "core-foundation-sys", | ||
1313 | "libc", | ||
1314 | ] | ||
1315 | |||
1316 | [[package]] | ||
1317 | name = "serde" | ||
1318 | version = "1.0.195" | ||
1319 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1320 | checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" | ||
1321 | dependencies = [ | ||
1322 | "serde_derive", | ||
1323 | ] | ||
1324 | |||
1325 | [[package]] | ||
1326 | name = "serde_derive" | ||
1327 | version = "1.0.195" | ||
1328 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1329 | checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" | ||
1330 | dependencies = [ | ||
1331 | "proc-macro2", | ||
1332 | "quote", | ||
1333 | "syn 2.0.48", | ||
1334 | ] | ||
1335 | |||
1336 | [[package]] | ||
1337 | name = "serde_json" | ||
1338 | version = "1.0.111" | ||
1339 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1340 | checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" | ||
1341 | dependencies = [ | ||
1342 | "itoa", | ||
1343 | "ryu", | ||
1344 | "serde", | ||
1345 | ] | ||
1346 | |||
1347 | [[package]] | ||
1348 | name = "serde_path_to_error" | ||
1349 | version = "0.1.15" | ||
1350 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1351 | checksum = "ebd154a240de39fdebcf5775d2675c204d7c13cf39a4c697be6493c8e734337c" | ||
1352 | dependencies = [ | ||
1353 | "itoa", | ||
1354 | "serde", | ||
1355 | ] | ||
1356 | |||
1357 | [[package]] | ||
1358 | name = "serde_urlencoded" | ||
1359 | version = "0.7.1" | ||
1360 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1361 | checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" | ||
1362 | dependencies = [ | ||
1363 | "form_urlencoded", | ||
1364 | "itoa", | ||
1365 | "ryu", | ||
1366 | "serde", | ||
1367 | ] | ||
1368 | |||
1369 | [[package]] | ||
1370 | name = "server" | ||
1371 | version = "0.1.0" | ||
1372 | dependencies = [ | ||
1373 | "aws-creds", | ||
1374 | "axum", | ||
1375 | "chrono", | ||
1376 | "common", | ||
1377 | "mime", | ||
1378 | "rust-s3", | ||
1379 | "serde", | ||
1380 | "tokio", | ||
1381 | "tower-service", | ||
1382 | ] | ||
1383 | |||
1384 | [[package]] | ||
1385 | name = "sha2" | ||
1386 | version = "0.10.8" | ||
1387 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1388 | checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" | ||
1389 | dependencies = [ | ||
1390 | "cfg-if", | ||
1391 | "cpufeatures", | ||
1392 | "digest", | ||
1393 | ] | ||
1394 | |||
1395 | [[package]] | ||
1396 | name = "signal-hook-registry" | ||
1397 | version = "1.4.1" | ||
1398 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1399 | checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" | ||
1400 | dependencies = [ | ||
1401 | "libc", | ||
1402 | ] | ||
1403 | |||
1404 | [[package]] | ||
1405 | name = "slab" | ||
1406 | version = "0.4.9" | ||
1407 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1408 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" | ||
1409 | dependencies = [ | ||
1410 | "autocfg", | ||
1411 | ] | ||
1412 | |||
1413 | [[package]] | ||
1414 | name = "smallvec" | ||
1415 | version = "1.11.2" | ||
1416 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1417 | checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" | ||
1418 | |||
1419 | [[package]] | ||
1420 | name = "smartstring" | ||
1421 | version = "1.0.1" | ||
1422 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1423 | checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29" | ||
1424 | dependencies = [ | ||
1425 | "autocfg", | ||
1426 | "static_assertions", | ||
1427 | "version_check", | ||
1428 | ] | ||
1429 | |||
1430 | [[package]] | ||
1431 | name = "socket2" | ||
1432 | version = "0.5.5" | ||
1433 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1434 | checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" | ||
1435 | dependencies = [ | ||
1436 | "libc", | ||
1437 | "windows-sys 0.48.0", | ||
1438 | ] | ||
1439 | |||
1440 | [[package]] | ||
1441 | name = "static_assertions" | ||
1442 | version = "1.1.0" | ||
1443 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1444 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" | ||
1445 | |||
1446 | [[package]] | ||
1447 | name = "subtle" | ||
1448 | version = "2.5.0" | ||
1449 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1450 | checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" | ||
1451 | |||
1452 | [[package]] | ||
1453 | name = "syn" | ||
1454 | version = "1.0.109" | ||
1455 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1456 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" | ||
1457 | dependencies = [ | ||
1458 | "proc-macro2", | ||
1459 | "quote", | ||
1460 | "unicode-ident", | ||
1461 | ] | ||
1462 | |||
1463 | [[package]] | ||
1464 | name = "syn" | ||
1465 | version = "2.0.48" | ||
1466 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1467 | checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" | ||
1468 | dependencies = [ | ||
1469 | "proc-macro2", | ||
1470 | "quote", | ||
1471 | "unicode-ident", | ||
1472 | ] | ||
1473 | |||
1474 | [[package]] | ||
1475 | name = "sync_wrapper" | ||
1476 | version = "0.1.2" | ||
1477 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1478 | checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" | ||
1479 | |||
1480 | [[package]] | ||
1481 | name = "system-configuration" | ||
1482 | version = "0.5.1" | ||
1483 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1484 | checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" | ||
1485 | dependencies = [ | ||
1486 | "bitflags 1.3.2", | ||
1487 | "core-foundation", | ||
1488 | "system-configuration-sys", | ||
1489 | ] | ||
1490 | |||
1491 | [[package]] | ||
1492 | name = "system-configuration-sys" | ||
1493 | version = "0.5.0" | ||
1494 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1495 | checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" | ||
1496 | dependencies = [ | ||
1497 | "core-foundation-sys", | ||
1498 | "libc", | ||
1499 | ] | ||
1500 | |||
1501 | [[package]] | ||
1502 | name = "tempfile" | ||
1503 | version = "3.9.0" | ||
1504 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1505 | checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" | ||
1506 | dependencies = [ | ||
1507 | "cfg-if", | ||
1508 | "fastrand", | ||
1509 | "redox_syscall", | ||
1510 | "rustix", | ||
1511 | "windows-sys 0.52.0", | ||
1512 | ] | ||
1513 | |||
1514 | [[package]] | ||
1515 | name = "thiserror" | ||
1516 | version = "1.0.56" | ||
1517 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1518 | checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" | ||
1519 | dependencies = [ | ||
1520 | "thiserror-impl", | ||
1521 | ] | ||
1522 | |||
1523 | [[package]] | ||
1524 | name = "thiserror-impl" | ||
1525 | version = "1.0.56" | ||
1526 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1527 | checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" | ||
1528 | dependencies = [ | ||
1529 | "proc-macro2", | ||
1530 | "quote", | ||
1531 | "syn 2.0.48", | ||
1532 | ] | ||
1533 | |||
1534 | [[package]] | ||
1535 | name = "time" | ||
1536 | version = "0.3.31" | ||
1537 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1538 | checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" | ||
1539 | dependencies = [ | ||
1540 | "deranged", | ||
1541 | "itoa", | ||
1542 | "powerfmt", | ||
1543 | "serde", | ||
1544 | "time-core", | ||
1545 | "time-macros", | ||
1546 | ] | ||
1547 | |||
1548 | [[package]] | ||
1549 | name = "time-core" | ||
1550 | version = "0.1.2" | ||
1551 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1552 | checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" | ||
1553 | |||
1554 | [[package]] | ||
1555 | name = "time-macros" | ||
1556 | version = "0.2.16" | ||
1557 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1558 | checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" | ||
1559 | dependencies = [ | ||
1560 | "time-core", | ||
1561 | ] | ||
1562 | |||
1563 | [[package]] | ||
1564 | name = "tinyvec" | ||
1565 | version = "1.6.0" | ||
1566 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1567 | checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" | ||
1568 | dependencies = [ | ||
1569 | "tinyvec_macros", | ||
1570 | ] | ||
1571 | |||
1572 | [[package]] | ||
1573 | name = "tinyvec_macros" | ||
1574 | version = "0.1.1" | ||
1575 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1576 | checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" | ||
1577 | |||
1578 | [[package]] | ||
1579 | name = "tokio" | ||
1580 | version = "1.35.1" | ||
1581 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1582 | checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" | ||
1583 | dependencies = [ | ||
1584 | "backtrace", | ||
1585 | "bytes", | ||
1586 | "libc", | ||
1587 | "mio", | ||
1588 | "num_cpus", | ||
1589 | "parking_lot", | ||
1590 | "pin-project-lite", | ||
1591 | "signal-hook-registry", | ||
1592 | "socket2", | ||
1593 | "tokio-macros", | ||
1594 | "windows-sys 0.48.0", | ||
1595 | ] | ||
1596 | |||
1597 | [[package]] | ||
1598 | name = "tokio-macros" | ||
1599 | version = "2.2.0" | ||
1600 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1601 | checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" | ||
1602 | dependencies = [ | ||
1603 | "proc-macro2", | ||
1604 | "quote", | ||
1605 | "syn 2.0.48", | ||
1606 | ] | ||
1607 | |||
1608 | [[package]] | ||
1609 | name = "tokio-native-tls" | ||
1610 | version = "0.3.1" | ||
1611 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1612 | checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" | ||
1613 | dependencies = [ | ||
1614 | "native-tls", | ||
1615 | "tokio", | ||
1616 | ] | ||
1617 | |||
1618 | [[package]] | ||
1619 | name = "tokio-stream" | ||
1620 | version = "0.1.14" | ||
1621 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1622 | checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" | ||
1623 | dependencies = [ | ||
1624 | "futures-core", | ||
1625 | "pin-project-lite", | ||
1626 | "tokio", | ||
1627 | ] | ||
1628 | |||
1629 | [[package]] | ||
1630 | name = "tokio-util" | ||
1631 | version = "0.7.10" | ||
1632 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1633 | checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" | ||
1634 | dependencies = [ | ||
1635 | "bytes", | ||
1636 | "futures-core", | ||
1637 | "futures-sink", | ||
1638 | "pin-project-lite", | ||
1639 | "tokio", | ||
1640 | "tracing", | ||
1641 | ] | ||
1642 | |||
1643 | [[package]] | ||
1644 | name = "tower" | ||
1645 | version = "0.4.13" | ||
1646 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1647 | checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" | ||
1648 | dependencies = [ | ||
1649 | "futures-core", | ||
1650 | "futures-util", | ||
1651 | "pin-project", | ||
1652 | "pin-project-lite", | ||
1653 | "tokio", | ||
1654 | "tower-layer", | ||
1655 | "tower-service", | ||
1656 | "tracing", | ||
1657 | ] | ||
1658 | |||
1659 | [[package]] | ||
1660 | name = "tower-layer" | ||
1661 | version = "0.3.2" | ||
1662 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1663 | checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" | ||
1664 | |||
1665 | [[package]] | ||
1666 | name = "tower-service" | ||
1667 | version = "0.3.2" | ||
1668 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1669 | checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" | ||
1670 | |||
1671 | [[package]] | ||
1672 | name = "tracing" | ||
1673 | version = "0.1.40" | ||
1674 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1675 | checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" | ||
1676 | dependencies = [ | ||
1677 | "log", | ||
1678 | "pin-project-lite", | ||
1679 | "tracing-core", | ||
1680 | ] | ||
1681 | |||
1682 | [[package]] | ||
1683 | name = "tracing-core" | ||
1684 | version = "0.1.32" | ||
1685 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1686 | checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" | ||
1687 | dependencies = [ | ||
1688 | "once_cell", | ||
1689 | ] | ||
1690 | |||
1691 | [[package]] | ||
1692 | name = "try-lock" | ||
1693 | version = "0.2.5" | ||
1694 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1695 | checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" | ||
1696 | |||
1697 | [[package]] | ||
1698 | name = "typenum" | ||
1699 | version = "1.17.0" | ||
1700 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1701 | checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" | ||
1702 | |||
1703 | [[package]] | ||
1704 | name = "unicode-bidi" | ||
1705 | version = "0.3.15" | ||
1706 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1707 | checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" | ||
1708 | |||
1709 | [[package]] | ||
1710 | name = "unicode-ident" | ||
1711 | version = "1.0.12" | ||
1712 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1713 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" | ||
1714 | |||
1715 | [[package]] | ||
1716 | name = "unicode-normalization" | ||
1717 | version = "0.1.22" | ||
1718 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1719 | checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" | ||
1720 | dependencies = [ | ||
1721 | "tinyvec", | ||
1722 | ] | ||
1723 | |||
1724 | [[package]] | ||
1725 | name = "url" | ||
1726 | version = "2.5.0" | ||
1727 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1728 | checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" | ||
1729 | dependencies = [ | ||
1730 | "form_urlencoded", | ||
1731 | "idna", | ||
1732 | "percent-encoding", | ||
1733 | ] | ||
1734 | |||
1735 | [[package]] | ||
1736 | name = "vcpkg" | ||
1737 | version = "0.2.15" | ||
1738 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1739 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" | ||
1740 | |||
1741 | [[package]] | ||
1742 | name = "version_check" | ||
1743 | version = "0.9.4" | ||
1744 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1745 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" | ||
1746 | |||
1747 | [[package]] | ||
1748 | name = "want" | ||
1749 | version = "0.3.1" | ||
1750 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1751 | checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" | ||
1752 | dependencies = [ | ||
1753 | "try-lock", | ||
1754 | ] | ||
1755 | |||
1756 | [[package]] | ||
1757 | name = "wasi" | ||
1758 | version = "0.11.0+wasi-snapshot-preview1" | ||
1759 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1760 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" | ||
1761 | |||
1762 | [[package]] | ||
1763 | name = "wasm-bindgen" | ||
1764 | version = "0.2.90" | ||
1765 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1766 | checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" | ||
1767 | dependencies = [ | ||
1768 | "cfg-if", | ||
1769 | "wasm-bindgen-macro", | ||
1770 | ] | ||
1771 | |||
1772 | [[package]] | ||
1773 | name = "wasm-bindgen-backend" | ||
1774 | version = "0.2.90" | ||
1775 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1776 | checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" | ||
1777 | dependencies = [ | ||
1778 | "bumpalo", | ||
1779 | "log", | ||
1780 | "once_cell", | ||
1781 | "proc-macro2", | ||
1782 | "quote", | ||
1783 | "syn 2.0.48", | ||
1784 | "wasm-bindgen-shared", | ||
1785 | ] | ||
1786 | |||
1787 | [[package]] | ||
1788 | name = "wasm-bindgen-futures" | ||
1789 | version = "0.4.40" | ||
1790 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1791 | checksum = "bde2032aeb86bdfaecc8b261eef3cba735cc426c1f3a3416d1e0791be95fc461" | ||
1792 | dependencies = [ | ||
1793 | "cfg-if", | ||
1794 | "js-sys", | ||
1795 | "wasm-bindgen", | ||
1796 | "web-sys", | ||
1797 | ] | ||
1798 | |||
1799 | [[package]] | ||
1800 | name = "wasm-bindgen-macro" | ||
1801 | version = "0.2.90" | ||
1802 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1803 | checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" | ||
1804 | dependencies = [ | ||
1805 | "quote", | ||
1806 | "wasm-bindgen-macro-support", | ||
1807 | ] | ||
1808 | |||
1809 | [[package]] | ||
1810 | name = "wasm-bindgen-macro-support" | ||
1811 | version = "0.2.90" | ||
1812 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1813 | checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" | ||
1814 | dependencies = [ | ||
1815 | "proc-macro2", | ||
1816 | "quote", | ||
1817 | "syn 2.0.48", | ||
1818 | "wasm-bindgen-backend", | ||
1819 | "wasm-bindgen-shared", | ||
1820 | ] | ||
1821 | |||
1822 | [[package]] | ||
1823 | name = "wasm-bindgen-shared" | ||
1824 | version = "0.2.90" | ||
1825 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1826 | checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" | ||
1827 | |||
1828 | [[package]] | ||
1829 | name = "wasm-streams" | ||
1830 | version = "0.3.0" | ||
1831 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1832 | checksum = "b4609d447824375f43e1ffbc051b50ad8f4b3ae8219680c94452ea05eb240ac7" | ||
1833 | dependencies = [ | ||
1834 | "futures-util", | ||
1835 | "js-sys", | ||
1836 | "wasm-bindgen", | ||
1837 | "wasm-bindgen-futures", | ||
1838 | "web-sys", | ||
1839 | ] | ||
1840 | |||
1841 | [[package]] | ||
1842 | name = "web-sys" | ||
1843 | version = "0.3.67" | ||
1844 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1845 | checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed" | ||
1846 | dependencies = [ | ||
1847 | "js-sys", | ||
1848 | "wasm-bindgen", | ||
1849 | ] | ||
1850 | |||
1851 | [[package]] | ||
1852 | name = "winapi" | ||
1853 | version = "0.3.9" | ||
1854 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1855 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" | ||
1856 | dependencies = [ | ||
1857 | "winapi-i686-pc-windows-gnu", | ||
1858 | "winapi-x86_64-pc-windows-gnu", | ||
1859 | ] | ||
1860 | |||
1861 | [[package]] | ||
1862 | name = "winapi-i686-pc-windows-gnu" | ||
1863 | version = "0.4.0" | ||
1864 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1865 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" | ||
1866 | |||
1867 | [[package]] | ||
1868 | name = "winapi-x86_64-pc-windows-gnu" | ||
1869 | version = "0.4.0" | ||
1870 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1871 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" | ||
1872 | |||
1873 | [[package]] | ||
1874 | name = "windows-core" | ||
1875 | version = "0.52.0" | ||
1876 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1877 | checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" | ||
1878 | dependencies = [ | ||
1879 | "windows-targets 0.52.0", | ||
1880 | ] | ||
1881 | |||
1882 | [[package]] | ||
1883 | name = "windows-sys" | ||
1884 | version = "0.48.0" | ||
1885 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1886 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" | ||
1887 | dependencies = [ | ||
1888 | "windows-targets 0.48.5", | ||
1889 | ] | ||
1890 | |||
1891 | [[package]] | ||
1892 | name = "windows-sys" | ||
1893 | version = "0.52.0" | ||
1894 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1895 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" | ||
1896 | dependencies = [ | ||
1897 | "windows-targets 0.52.0", | ||
1898 | ] | ||
1899 | |||
1900 | [[package]] | ||
1901 | name = "windows-targets" | ||
1902 | version = "0.48.5" | ||
1903 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1904 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" | ||
1905 | dependencies = [ | ||
1906 | "windows_aarch64_gnullvm 0.48.5", | ||
1907 | "windows_aarch64_msvc 0.48.5", | ||
1908 | "windows_i686_gnu 0.48.5", | ||
1909 | "windows_i686_msvc 0.48.5", | ||
1910 | "windows_x86_64_gnu 0.48.5", | ||
1911 | "windows_x86_64_gnullvm 0.48.5", | ||
1912 | "windows_x86_64_msvc 0.48.5", | ||
1913 | ] | ||
1914 | |||
1915 | [[package]] | ||
1916 | name = "windows-targets" | ||
1917 | version = "0.52.0" | ||
1918 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1919 | checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" | ||
1920 | dependencies = [ | ||
1921 | "windows_aarch64_gnullvm 0.52.0", | ||
1922 | "windows_aarch64_msvc 0.52.0", | ||
1923 | "windows_i686_gnu 0.52.0", | ||
1924 | "windows_i686_msvc 0.52.0", | ||
1925 | "windows_x86_64_gnu 0.52.0", | ||
1926 | "windows_x86_64_gnullvm 0.52.0", | ||
1927 | "windows_x86_64_msvc 0.52.0", | ||
1928 | ] | ||
1929 | |||
1930 | [[package]] | ||
1931 | name = "windows_aarch64_gnullvm" | ||
1932 | version = "0.48.5" | ||
1933 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1934 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" | ||
1935 | |||
1936 | [[package]] | ||
1937 | name = "windows_aarch64_gnullvm" | ||
1938 | version = "0.52.0" | ||
1939 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1940 | checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" | ||
1941 | |||
1942 | [[package]] | ||
1943 | name = "windows_aarch64_msvc" | ||
1944 | version = "0.48.5" | ||
1945 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1946 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" | ||
1947 | |||
1948 | [[package]] | ||
1949 | name = "windows_aarch64_msvc" | ||
1950 | version = "0.52.0" | ||
1951 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1952 | checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" | ||
1953 | |||
1954 | [[package]] | ||
1955 | name = "windows_i686_gnu" | ||
1956 | version = "0.48.5" | ||
1957 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1958 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" | ||
1959 | |||
1960 | [[package]] | ||
1961 | name = "windows_i686_gnu" | ||
1962 | version = "0.52.0" | ||
1963 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1964 | checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" | ||
1965 | |||
1966 | [[package]] | ||
1967 | name = "windows_i686_msvc" | ||
1968 | version = "0.48.5" | ||
1969 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1970 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" | ||
1971 | |||
1972 | [[package]] | ||
1973 | name = "windows_i686_msvc" | ||
1974 | version = "0.52.0" | ||
1975 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1976 | checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" | ||
1977 | |||
1978 | [[package]] | ||
1979 | name = "windows_x86_64_gnu" | ||
1980 | version = "0.48.5" | ||
1981 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1982 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" | ||
1983 | |||
1984 | [[package]] | ||
1985 | name = "windows_x86_64_gnu" | ||
1986 | version = "0.52.0" | ||
1987 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1988 | checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" | ||
1989 | |||
1990 | [[package]] | ||
1991 | name = "windows_x86_64_gnullvm" | ||
1992 | version = "0.48.5" | ||
1993 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1994 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" | ||
1995 | |||
1996 | [[package]] | ||
1997 | name = "windows_x86_64_gnullvm" | ||
1998 | version = "0.52.0" | ||
1999 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2000 | checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" | ||
2001 | |||
2002 | [[package]] | ||
2003 | name = "windows_x86_64_msvc" | ||
2004 | version = "0.48.5" | ||
2005 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2006 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" | ||
2007 | |||
2008 | [[package]] | ||
2009 | name = "windows_x86_64_msvc" | ||
2010 | version = "0.52.0" | ||
2011 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2012 | checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" | ||
2013 | |||
2014 | [[package]] | ||
2015 | name = "winreg" | ||
2016 | version = "0.50.0" | ||
2017 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2018 | checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" | ||
2019 | dependencies = [ | ||
2020 | "cfg-if", | ||
2021 | "windows-sys 0.48.0", | ||
2022 | ] | ||
diff --git a/rs/Cargo.toml b/rs/Cargo.toml new file mode 100644 index 0000000..ec75eb3 --- /dev/null +++ b/rs/Cargo.toml | |||
@@ -0,0 +1,7 @@ | |||
1 | [workspace] | ||
2 | resolver = "2" | ||
3 | members = [ | ||
4 | "common", | ||
5 | "git-lfs-authenticate", | ||
6 | "server", | ||
7 | ] | ||
diff --git a/rs/common/Cargo.toml b/rs/common/Cargo.toml new file mode 100644 index 0000000..20d9bdd --- /dev/null +++ b/rs/common/Cargo.toml | |||
@@ -0,0 +1,10 @@ | |||
1 | [package] | ||
2 | name = "common" | ||
3 | version = "0.1.0" | ||
4 | edition = "2021" | ||
5 | |||
6 | [dependencies] | ||
7 | chrono = "0.4" | ||
8 | hmac-sha256 = "1.1" | ||
9 | subtle = "2.5" | ||
10 | serde = { version = "1", features = ["derive"] } | ||
diff --git a/rs/common/src/lib.rs b/rs/common/src/lib.rs new file mode 100644 index 0000000..aafe7f1 --- /dev/null +++ b/rs/common/src/lib.rs | |||
@@ -0,0 +1,337 @@ | |||
1 | use chrono::{DateTime, Utc}; | ||
2 | use serde::de; | ||
3 | use serde::{Deserialize, Serialize}; | ||
4 | use std::fmt::Write; | ||
5 | use std::ops; | ||
6 | use std::{fmt, str::FromStr}; | ||
7 | use subtle::ConstantTimeEq; | ||
8 | |||
9 | #[derive(Debug, Eq, PartialEq, Copy, Clone, Serialize, Deserialize)] | ||
10 | #[repr(u8)] | ||
11 | pub enum Operation { | ||
12 | #[serde(rename = "download")] | ||
13 | Download = 1, | ||
14 | #[serde(rename = "upload")] | ||
15 | Upload = 2, | ||
16 | } | ||
17 | |||
18 | #[derive(Debug, PartialEq, Eq, Copy, Clone)] | ||
19 | pub struct ParseOperationError; | ||
20 | |||
21 | impl fmt::Display for ParseOperationError { | ||
22 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
23 | write!(f, "operation should be 'download' or 'upload'") | ||
24 | } | ||
25 | } | ||
26 | |||
27 | impl FromStr for Operation { | ||
28 | type Err = ParseOperationError; | ||
29 | |||
30 | fn from_str(s: &str) -> Result<Self, Self::Err> { | ||
31 | match s { | ||
32 | "upload" => Ok(Self::Upload), | ||
33 | "download" => Ok(Self::Download), | ||
34 | _ => Err(ParseOperationError), | ||
35 | } | ||
36 | } | ||
37 | } | ||
38 | |||
39 | #[repr(u8)] | ||
40 | pub enum AuthType { | ||
41 | GitLfsAuthenticate = 1, | ||
42 | } | ||
43 | |||
44 | /// None means out of range. | ||
45 | fn decode_nibble(c: u8) -> Option<u8> { | ||
46 | if c.is_ascii_digit() { | ||
47 | Some(c - b'0') | ||
48 | } else if (b'a'..=b'f').contains(&c) { | ||
49 | Some(c - b'a' + 10) | ||
50 | } else if (b'A'..=b'F').contains(&c) { | ||
51 | Some(c - b'A' + 10) | ||
52 | } else { | ||
53 | None | ||
54 | } | ||
55 | } | ||
56 | |||
57 | #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] | ||
58 | pub struct HexByte(pub u8); | ||
59 | |||
60 | impl<'de> Deserialize<'de> for HexByte { | ||
61 | fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> | ||
62 | where | ||
63 | D: serde::Deserializer<'de>, | ||
64 | { | ||
65 | let str = <&str>::deserialize(deserializer)?; | ||
66 | let &[b1, b2] = str.as_bytes() else { | ||
67 | return Err(de::Error::invalid_length( | ||
68 | str.len(), | ||
69 | &"two hexadecimal characters", | ||
70 | )); | ||
71 | }; | ||
72 | let (Some(b1), Some(b2)) = (decode_nibble(b1), decode_nibble(b2)) else { | ||
73 | return Err(de::Error::invalid_value( | ||
74 | de::Unexpected::Str(str), | ||
75 | &"two hexadecimal characters", | ||
76 | )); | ||
77 | }; | ||
78 | Ok(HexByte((b1 << 4) | b2)) | ||
79 | } | ||
80 | } | ||
81 | |||
82 | impl fmt::Display for HexByte { | ||
83 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
84 | let &HexByte(b) = self; | ||
85 | HexFmt(&[b]).fmt(f) | ||
86 | } | ||
87 | } | ||
88 | |||
89 | #[derive(Debug, PartialEq, Eq, Copy, Clone)] | ||
90 | pub enum ParseHexError { | ||
91 | UnevenNibbles, | ||
92 | InvalidCharacter, | ||
93 | TooShort, | ||
94 | TooLong, | ||
95 | } | ||
96 | |||
97 | impl fmt::Display for ParseHexError { | ||
98 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
99 | match self { | ||
100 | Self::UnevenNibbles => { | ||
101 | write!(f, "uneven amount of nibbles (chars in range [a-zA-Z0-9])") | ||
102 | } | ||
103 | Self::InvalidCharacter => write!(f, "non-hex character encountered"), | ||
104 | Self::TooShort => write!(f, "unexpected end of hex sequence"), | ||
105 | Self::TooLong => write!(f, "longer hex sequence than expected"), | ||
106 | } | ||
107 | } | ||
108 | } | ||
109 | |||
110 | #[derive(Debug)] | ||
111 | pub enum ReadHexError { | ||
112 | Io(std::io::Error), | ||
113 | Format(ParseHexError), | ||
114 | } | ||
115 | |||
116 | impl fmt::Display for ReadHexError { | ||
117 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
118 | match self { | ||
119 | Self::Io(e) => e.fmt(f), | ||
120 | Self::Format(e) => e.fmt(f), | ||
121 | } | ||
122 | } | ||
123 | } | ||
124 | |||
125 | fn parse_hex_exact(value: &str, buf: &mut [u8]) -> Result<(), ParseHexError> { | ||
126 | if value.bytes().len() % 2 == 1 { | ||
127 | return Err(ParseHexError::UnevenNibbles); | ||
128 | } | ||
129 | if value.bytes().len() < 2 * buf.len() { | ||
130 | return Err(ParseHexError::TooShort); | ||
131 | } | ||
132 | if value.bytes().len() > 2 * buf.len() { | ||
133 | return Err(ParseHexError::TooLong); | ||
134 | } | ||
135 | for (i, c) in value.bytes().enumerate() { | ||
136 | if let Some(b) = decode_nibble(c) { | ||
137 | if i % 2 == 0 { | ||
138 | buf[i / 2] |= b; | ||
139 | } else { | ||
140 | buf[i / 2] = b << 4; | ||
141 | } | ||
142 | } else { | ||
143 | return Err(ParseHexError::InvalidCharacter); | ||
144 | } | ||
145 | } | ||
146 | Ok(()) | ||
147 | } | ||
148 | |||
149 | pub struct SafeByteArray<const N: usize> { | ||
150 | inner: [u8; N], | ||
151 | } | ||
152 | |||
153 | impl<const N: usize> SafeByteArray<N> { | ||
154 | pub fn new() -> Self { | ||
155 | Self { inner: [0; N] } | ||
156 | } | ||
157 | } | ||
158 | |||
159 | impl<const N: usize> AsRef<[u8]> for SafeByteArray<N> { | ||
160 | fn as_ref(&self) -> &[u8] { | ||
161 | &self.inner | ||
162 | } | ||
163 | } | ||
164 | |||
165 | impl<const N: usize> AsMut<[u8]> for SafeByteArray<N> { | ||
166 | fn as_mut(&mut self) -> &mut [u8] { | ||
167 | &mut self.inner | ||
168 | } | ||
169 | } | ||
170 | |||
171 | impl<const N: usize> Drop for SafeByteArray<N> { | ||
172 | fn drop(&mut self) { | ||
173 | self.inner.fill(0) | ||
174 | } | ||
175 | } | ||
176 | |||
177 | impl<const N: usize> FromStr for SafeByteArray<N> { | ||
178 | type Err = ParseHexError; | ||
179 | |||
180 | fn from_str(value: &str) -> Result<Self, Self::Err> { | ||
181 | let mut sba = Self { inner: [0u8; N] }; | ||
182 | parse_hex_exact(value, &mut sba.inner)?; | ||
183 | Ok(sba) | ||
184 | } | ||
185 | } | ||
186 | |||
187 | pub struct Claims<'a> { | ||
188 | pub auth_type: AuthType, | ||
189 | pub repo_path: &'a str, | ||
190 | pub operation: Operation, | ||
191 | pub expires_at: DateTime<Utc>, | ||
192 | } | ||
193 | |||
194 | /// Returns None if the claims are invalid. Repo path length may be no more than 100 bytes. | ||
195 | pub fn generate_tag(claims: Claims, key: impl AsRef<[u8]>) -> Option<Digest<32>> { | ||
196 | if claims.repo_path.len() > 100 { | ||
197 | return None; | ||
198 | } | ||
199 | |||
200 | let mut hmac = hmac_sha256::HMAC::new(key); | ||
201 | hmac.update([claims.auth_type as u8]); | ||
202 | hmac.update([claims.repo_path.len() as u8]); | ||
203 | hmac.update(claims.repo_path.as_bytes()); | ||
204 | hmac.update([claims.operation as u8]); | ||
205 | hmac.update(claims.expires_at.timestamp().to_be_bytes()); | ||
206 | Some(hmac.finalize().into()) | ||
207 | } | ||
208 | |||
209 | pub struct HexFmt<B: AsRef<[u8]>>(pub B); | ||
210 | |||
211 | impl<B: AsRef<[u8]>> fmt::Display for HexFmt<B> { | ||
212 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
213 | let HexFmt(buf) = self; | ||
214 | for b in buf.as_ref() { | ||
215 | let (high, low) = (b >> 4, b & 0xF); | ||
216 | let highc = if high < 10 { b'0' + high } else { b'a' + high }; | ||
217 | let lowc = if low < 10 { b'0' + low } else { b'a' + low }; | ||
218 | f.write_char(highc as char)?; | ||
219 | f.write_char(lowc as char)?; | ||
220 | } | ||
221 | Ok(()) | ||
222 | } | ||
223 | } | ||
224 | |||
225 | pub struct EscJsonFmt<'a>(pub &'a str); | ||
226 | |||
227 | impl<'a> fmt::Display for EscJsonFmt<'a> { | ||
228 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
229 | let EscJsonFmt(buf) = self; | ||
230 | for c in buf.chars() { | ||
231 | match c { | ||
232 | '"' => f.write_str("\\\"")?, // quote | ||
233 | '\\' => f.write_str("\\\\")?, // backslash | ||
234 | '\x08' => f.write_str("\\b")?, // backspace | ||
235 | '\x0C' => f.write_str("\\f")?, // form feed | ||
236 | '\n' => f.write_str("\\n")?, // line feed | ||
237 | '\r' => f.write_str("\\r")?, // carriage return | ||
238 | '\t' => f.write_str("\\t")?, // horizontal tab | ||
239 | _ => f.write_char(c)?, | ||
240 | }; | ||
241 | } | ||
242 | Ok(()) | ||
243 | } | ||
244 | } | ||
245 | |||
246 | #[derive(Debug, Copy, Clone)] | ||
247 | pub struct Digest<const N: usize> { | ||
248 | inner: [u8; N], | ||
249 | } | ||
250 | |||
251 | impl<const N: usize> ops::Index<usize> for Digest<N> { | ||
252 | type Output = u8; | ||
253 | |||
254 | fn index(&self, index: usize) -> &Self::Output { | ||
255 | &self.inner[index] | ||
256 | } | ||
257 | } | ||
258 | |||
259 | impl<const N: usize> Digest<N> { | ||
260 | pub fn as_bytes(&self) -> &[u8; N] { | ||
261 | &self.inner | ||
262 | } | ||
263 | } | ||
264 | |||
265 | impl<const N: usize> fmt::Display for Digest<N> { | ||
266 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
267 | HexFmt(&self.inner).fmt(f) | ||
268 | } | ||
269 | } | ||
270 | |||
271 | impl<const N: usize> Digest<N> { | ||
272 | pub fn new(data: [u8; N]) -> Self { | ||
273 | Self { inner: data } | ||
274 | } | ||
275 | } | ||
276 | |||
277 | impl<const N: usize> From<[u8; N]> for Digest<N> { | ||
278 | fn from(value: [u8; N]) -> Self { | ||
279 | Self::new(value) | ||
280 | } | ||
281 | } | ||
282 | |||
283 | impl<const N: usize> Into<[u8; N]> for Digest<N> { | ||
284 | fn into(self) -> [u8; N] { | ||
285 | self.inner | ||
286 | } | ||
287 | } | ||
288 | |||
289 | impl<const N: usize> FromStr for Digest<N> { | ||
290 | type Err = ParseHexError; | ||
291 | |||
292 | fn from_str(value: &str) -> Result<Self, Self::Err> { | ||
293 | let mut buf = [0u8; N]; | ||
294 | parse_hex_exact(value, &mut buf)?; | ||
295 | Ok(buf.into()) | ||
296 | } | ||
297 | } | ||
298 | |||
299 | impl<const N: usize> ConstantTimeEq for Digest<N> { | ||
300 | fn ct_eq(&self, other: &Self) -> subtle::Choice { | ||
301 | self.inner.ct_eq(&other.inner) | ||
302 | } | ||
303 | } | ||
304 | |||
305 | impl<const N: usize> PartialEq for Digest<N> { | ||
306 | fn eq(&self, other: &Self) -> bool { | ||
307 | self.ct_eq(&other).into() | ||
308 | } | ||
309 | } | ||
310 | |||
311 | impl<const N: usize> Eq for Digest<N> {} | ||
312 | |||
313 | impl<'de, const N: usize> Deserialize<'de> for Digest<N> { | ||
314 | fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> | ||
315 | where | ||
316 | D: serde::Deserializer<'de>, | ||
317 | { | ||
318 | let hex = <&str>::deserialize(deserializer)?; | ||
319 | Digest::from_str(hex).map_err(de::Error::custom) | ||
320 | } | ||
321 | } | ||
322 | |||
323 | impl<const N: usize> Serialize for Digest<N> { | ||
324 | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | ||
325 | where | ||
326 | S: serde::Serializer, | ||
327 | { | ||
328 | serializer.serialize_str(&format!("{self}")) | ||
329 | } | ||
330 | } | ||
331 | |||
332 | pub type Key = SafeByteArray<64>; | ||
333 | |||
334 | pub fn load_key(path: &str) -> Result<Key, ReadHexError> { | ||
335 | let key_str = std::fs::read_to_string(path).map_err(ReadHexError::Io)?; | ||
336 | key_str.trim().parse().map_err(ReadHexError::Format) | ||
337 | } | ||
diff --git a/rs/git-lfs-authenticate/Cargo.toml b/rs/git-lfs-authenticate/Cargo.toml new file mode 100644 index 0000000..217250f --- /dev/null +++ b/rs/git-lfs-authenticate/Cargo.toml | |||
@@ -0,0 +1,8 @@ | |||
1 | [package] | ||
2 | name = "git-lfs-authenticate" | ||
3 | version = "0.1.0" | ||
4 | edition = "2021" | ||
5 | |||
6 | [dependencies] | ||
7 | chrono = "0.4" | ||
8 | common = { path = "../common" } | ||
diff --git a/rs/git-lfs-authenticate/src/main.rs b/rs/git-lfs-authenticate/src/main.rs new file mode 100644 index 0000000..76f2edd --- /dev/null +++ b/rs/git-lfs-authenticate/src/main.rs | |||
@@ -0,0 +1,239 @@ | |||
1 | use std::{fmt, process::ExitCode, time::Duration}; | ||
2 | |||
3 | use chrono::Utc; | ||
4 | use common::{Operation, ParseOperationError}; | ||
5 | |||
6 | fn help() { | ||
7 | eprintln!("Usage: git-lfs-authenticate <REPO> upload/download"); | ||
8 | } | ||
9 | |||
10 | #[derive(Debug, Eq, PartialEq, Copy, Clone)] | ||
11 | enum RepoNameError { | ||
12 | TooLong, | ||
13 | UnresolvedPath, | ||
14 | AbsolutePath, | ||
15 | MissingGitSuffix, | ||
16 | } | ||
17 | |||
18 | impl fmt::Display for RepoNameError { | ||
19 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
20 | match self { | ||
21 | Self::TooLong => write!(f, "too long (more than 100 characters)"), | ||
22 | Self::UnresolvedPath => { | ||
23 | write!(f, "contains path one or more path elements '.' and '..'") | ||
24 | } | ||
25 | Self::AbsolutePath => { | ||
26 | write!(f, "starts with '/', which is not allowed") | ||
27 | } | ||
28 | Self::MissingGitSuffix => write!(f, "misses '.git' suffix"), | ||
29 | } | ||
30 | } | ||
31 | } | ||
32 | |||
33 | // Using `Result<(), E>` here instead of `Option<E>` because `None` typically signifies some error | ||
34 | // state with no further details provided. If we were to return an `Option` type, the user would | ||
35 | // have to first transform it into a `Result` type in order to use the `?` operator, meaning that | ||
36 | // they would have to the following operation to get the type into the right shape: | ||
37 | // `validate_repo_path(path).map_or(Ok(()), Err)`. That would not be very ergonomic. | ||
38 | fn validate_repo_path(path: &str) -> Result<(), RepoNameError> { | ||
39 | if path.len() > 100 { | ||
40 | return Err(RepoNameError::TooLong); | ||
41 | } | ||
42 | if path.contains("//") | ||
43 | || path.contains("/./") | ||
44 | || path.contains("/../") | ||
45 | || path.starts_with("./") | ||
46 | || path.starts_with("../") | ||
47 | { | ||
48 | return Err(RepoNameError::UnresolvedPath); | ||
49 | } | ||
50 | if path.starts_with('/') { | ||
51 | return Err(RepoNameError::AbsolutePath); | ||
52 | } | ||
53 | if !path.ends_with(".git") { | ||
54 | return Err(RepoNameError::MissingGitSuffix); | ||
55 | } | ||
56 | Ok(()) | ||
57 | } | ||
58 | |||
59 | #[derive(Debug, Eq, PartialEq, Copy, Clone)] | ||
60 | enum ParseCmdlineError { | ||
61 | UnknownOperation(ParseOperationError), | ||
62 | InvalidRepoName(RepoNameError), | ||
63 | UnexpectedArgCount(ArgCountError), | ||
64 | } | ||
65 | |||
66 | impl From<RepoNameError> for ParseCmdlineError { | ||
67 | fn from(value: RepoNameError) -> Self { | ||
68 | Self::InvalidRepoName(value) | ||
69 | } | ||
70 | } | ||
71 | |||
72 | impl From<ParseOperationError> for ParseCmdlineError { | ||
73 | fn from(value: ParseOperationError) -> Self { | ||
74 | Self::UnknownOperation(value) | ||
75 | } | ||
76 | } | ||
77 | |||
78 | impl From<ArgCountError> for ParseCmdlineError { | ||
79 | fn from(value: ArgCountError) -> Self { | ||
80 | Self::UnexpectedArgCount(value) | ||
81 | } | ||
82 | } | ||
83 | |||
84 | impl fmt::Display for ParseCmdlineError { | ||
85 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
86 | match self { | ||
87 | Self::UnknownOperation(e) => write!(f, "unknown operation: {e}"), | ||
88 | Self::InvalidRepoName(e) => write!(f, "invalid repository name: {e}"), | ||
89 | Self::UnexpectedArgCount(e) => e.fmt(f), | ||
90 | } | ||
91 | } | ||
92 | } | ||
93 | |||
94 | #[derive(Debug, Eq, PartialEq, Copy, Clone)] | ||
95 | struct ArgCountError { | ||
96 | provided: usize, | ||
97 | expected: usize, | ||
98 | } | ||
99 | |||
100 | impl fmt::Display for ArgCountError { | ||
101 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
102 | write!( | ||
103 | f, | ||
104 | "got {} argument(s), expected {}", | ||
105 | self.provided, self.expected | ||
106 | ) | ||
107 | } | ||
108 | } | ||
109 | |||
110 | fn get_cmdline_args<const N: usize>() -> Result<[String; N], ArgCountError> { | ||
111 | let args = std::env::args(); | ||
112 | if args.len() - 1 != N { | ||
113 | return Err(ArgCountError { | ||
114 | provided: args.len() - 1, | ||
115 | expected: N, | ||
116 | }); | ||
117 | } | ||
118 | |||
119 | // Does not allocate. | ||
120 | const EMPTY_STRING: String = String::new(); | ||
121 | let mut values = [EMPTY_STRING; N]; | ||
122 | |||
123 | // Skip the first element; we do not care about the program name. | ||
124 | for (i, arg) in args.skip(1).enumerate() { | ||
125 | values[i] = arg | ||
126 | } | ||
127 | Ok(values) | ||
128 | } | ||
129 | |||
130 | fn parse_cmdline() -> Result<(String, Operation), ParseCmdlineError> { | ||
131 | let [repo_path, op_str] = get_cmdline_args::<2>()?; | ||
132 | let op: Operation = op_str.parse()?; | ||
133 | validate_repo_path(&repo_path)?; | ||
134 | Ok((repo_path.to_string(), op)) | ||
135 | } | ||
136 | |||
137 | fn repo_exists(name: &str) -> bool { | ||
138 | match std::fs::metadata(name) { | ||
139 | Ok(metadata) => metadata.is_dir(), | ||
140 | _ => false, | ||
141 | } | ||
142 | } | ||
143 | |||
144 | struct Config { | ||
145 | href_base: String, | ||
146 | key_path: String, | ||
147 | } | ||
148 | |||
149 | #[derive(Debug, Eq, PartialEq, Copy, Clone)] | ||
150 | enum LoadConfigError { | ||
151 | BaseUrlMissing, | ||
152 | BaseUrlSlashSuffixMissing, | ||
153 | KeyPathMissing, | ||
154 | } | ||
155 | |||
156 | impl fmt::Display for LoadConfigError { | ||
157 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
158 | match self { | ||
159 | Self::BaseUrlMissing => write!(f, "base URL not provided"), | ||
160 | Self::BaseUrlSlashSuffixMissing => write!(f, "base URL does not end with slash"), | ||
161 | Self::KeyPathMissing => write!(f, "key path not provided"), | ||
162 | } | ||
163 | } | ||
164 | } | ||
165 | |||
166 | fn load_config() -> Result<Config, LoadConfigError> { | ||
167 | let Ok(href_base) = std::env::var("GITOLFS3_HREF_BASE") else { | ||
168 | return Err(LoadConfigError::BaseUrlMissing); | ||
169 | }; | ||
170 | if !href_base.ends_with('/') { | ||
171 | return Err(LoadConfigError::BaseUrlSlashSuffixMissing); | ||
172 | } | ||
173 | let Ok(key_path) = std::env::var("GITOLFS3_KEY_PATH") else { | ||
174 | return Err(LoadConfigError::KeyPathMissing); | ||
175 | }; | ||
176 | Ok(Config { | ||
177 | href_base, | ||
178 | key_path, | ||
179 | }) | ||
180 | } | ||
181 | |||
182 | fn main() -> ExitCode { | ||
183 | let config = match load_config() { | ||
184 | Ok(config) => config, | ||
185 | Err(e) => { | ||
186 | eprintln!("Failed to load config: {e}"); | ||
187 | return ExitCode::FAILURE; | ||
188 | } | ||
189 | }; | ||
190 | let key = match common::load_key(&config.key_path) { | ||
191 | Ok(key) => key, | ||
192 | Err(e) => { | ||
193 | eprintln!("Failed to load key: {e}"); | ||
194 | return ExitCode::FAILURE; | ||
195 | } | ||
196 | }; | ||
197 | |||
198 | let (repo_name, op) = match parse_cmdline() { | ||
199 | Ok(args) => args, | ||
200 | Err(e) => { | ||
201 | eprintln!("Error: {e}\n"); | ||
202 | help(); | ||
203 | // Exit code 2 signifies bad usage of CLI. | ||
204 | return ExitCode::from(2); | ||
205 | } | ||
206 | }; | ||
207 | |||
208 | if !repo_exists(&repo_name) { | ||
209 | eprintln!("Error: repository does not exist"); | ||
210 | return ExitCode::FAILURE; | ||
211 | } | ||
212 | |||
213 | let expires_in = Duration::from_secs(5 * 60); | ||
214 | let expires_at = Utc::now() + expires_in; | ||
215 | |||
216 | let Some(tag) = common::generate_tag( | ||
217 | common::Claims { | ||
218 | auth_type: common::AuthType::GitLfsAuthenticate, | ||
219 | repo_path: &repo_name, | ||
220 | expires_at, | ||
221 | operation: op, | ||
222 | }, | ||
223 | key, | ||
224 | ) else { | ||
225 | eprintln!("Failed to generate validation tag"); | ||
226 | return ExitCode::FAILURE; | ||
227 | }; | ||
228 | |||
229 | println!( | ||
230 | "{{\"header\":{{\"Authorization\":\"Gitolfs3-Hmac-Sha256 {tag}\"}},\ | ||
231 | \"expires_at\":\"{}\",\"href\":\"{}{}/info/lfs?p=1&te={}\"}}", | ||
232 | common::EscJsonFmt(&expires_at.to_rfc3339_opts(chrono::SecondsFormat::Secs, true)), | ||
233 | common::EscJsonFmt(&config.href_base), | ||
234 | common::EscJsonFmt(&repo_name), | ||
235 | expires_at.timestamp() | ||
236 | ); | ||
237 | |||
238 | ExitCode::SUCCESS | ||
239 | } | ||
diff --git a/rs/server/Cargo.toml b/rs/server/Cargo.toml new file mode 100644 index 0000000..ac571af --- /dev/null +++ b/rs/server/Cargo.toml | |||
@@ -0,0 +1,15 @@ | |||
1 | [package] | ||
2 | name = "server" | ||
3 | version = "0.1.0" | ||
4 | edition = "2021" | ||
5 | |||
6 | [dependencies] | ||
7 | axum = "0.7" | ||
8 | aws-creds = "0.34" | ||
9 | chrono = { version = "0.4", features = ["serde"] } | ||
10 | common = { path = "../common" } | ||
11 | mime = "0.3" | ||
12 | rust-s3 = "0.33" | ||
13 | serde = { version = "1", features = ["derive"] } | ||
14 | tokio = { version = "1.35", features = ["full"] } | ||
15 | tower-service = "0.3" | ||
diff --git a/rs/server/src/main.rs b/rs/server/src/main.rs new file mode 100644 index 0000000..8fe1d16 --- /dev/null +++ b/rs/server/src/main.rs | |||
@@ -0,0 +1,347 @@ | |||
1 | use std::collections::HashMap; | ||
2 | |||
3 | use awscreds::Credentials; | ||
4 | use axum::extract::rejection; | ||
5 | use axum::extract::FromRequest; | ||
6 | use axum::extract::Path; | ||
7 | use axum::http::header; | ||
8 | use axum::http::HeaderMap; | ||
9 | use axum::http::HeaderValue; | ||
10 | use axum::response::Response; | ||
11 | use axum::Json; | ||
12 | use chrono::DateTime; | ||
13 | use chrono::Utc; | ||
14 | use common::HexByte; | ||
15 | use common::Operation; | ||
16 | use s3::Bucket; | ||
17 | use serde::de; | ||
18 | use serde::de::DeserializeOwned; | ||
19 | use serde::Deserialize; | ||
20 | use serde::Serialize; | ||
21 | use tower_service::Service; | ||
22 | |||
23 | use axum::{ | ||
24 | async_trait, | ||
25 | extract::{FromRequestParts, OriginalUri, Request}, | ||
26 | http::{request::Parts, StatusCode, Uri}, | ||
27 | response::IntoResponse, | ||
28 | routing::{any, get, post, put}, | ||
29 | Extension, Router, | ||
30 | }; | ||
31 | |||
32 | #[derive(Clone)] | ||
33 | struct RepositoryName(String); | ||
34 | |||
35 | struct RepositoryNameRejection; | ||
36 | |||
37 | impl IntoResponse for RepositoryNameRejection { | ||
38 | fn into_response(self) -> Response { | ||
39 | (StatusCode::INTERNAL_SERVER_ERROR, "Missing repository name").into_response() | ||
40 | } | ||
41 | } | ||
42 | |||
43 | #[async_trait] | ||
44 | impl<S: Send + Sync> FromRequestParts<S> for RepositoryName { | ||
45 | type Rejection = RepositoryNameRejection; | ||
46 | |||
47 | async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> { | ||
48 | let Ok(Extension(repo_name)) = Extension::<Self>::from_request_parts(parts, state).await | ||
49 | else { | ||
50 | return Err(RepositoryNameRejection); | ||
51 | }; | ||
52 | Ok(repo_name) | ||
53 | } | ||
54 | } | ||
55 | |||
56 | #[tokio::main] | ||
57 | async fn main() { | ||
58 | // run our app with hyper, listening globally on port 3000 | ||
59 | let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); | ||
60 | let mut app = Router::new() | ||
61 | .route("/batch", post(batch)) | ||
62 | .route("/:oid0/:oid1/:oid", get(obj_download)) | ||
63 | .route("/:oid0/:oid1/:oid", put(obj_upload)); | ||
64 | axum::serve( | ||
65 | listener, | ||
66 | any(|mut req: Request| async move { | ||
67 | let uri = req.uri(); | ||
68 | let original_uri = OriginalUri(uri.clone()); | ||
69 | |||
70 | let path_and_query = uri.path_and_query().unwrap(); | ||
71 | let Some((repo, path)) = path_and_query.path().split_once("/info/lfs/objects") else { | ||
72 | return Ok(StatusCode::NOT_FOUND.into_response()); | ||
73 | }; | ||
74 | let repo = repo | ||
75 | .trim_start_matches('/') | ||
76 | .trim_end_matches('/') | ||
77 | .to_string(); | ||
78 | if !path.starts_with("/") || !repo.ends_with(".git") { | ||
79 | return Ok(StatusCode::NOT_FOUND.into_response()); | ||
80 | } | ||
81 | |||
82 | let mut parts = uri.clone().into_parts(); | ||
83 | parts.path_and_query = match path_and_query.query() { | ||
84 | None => path.try_into().ok(), | ||
85 | Some(q) => format!("{path}?{q}").try_into().ok(), | ||
86 | }; | ||
87 | let new_uri = Uri::from_parts(parts).unwrap(); | ||
88 | |||
89 | *req.uri_mut() = new_uri; | ||
90 | req.extensions_mut().insert(original_uri); | ||
91 | req.extensions_mut().insert(RepositoryName(repo)); | ||
92 | |||
93 | app.call(req).await | ||
94 | }), | ||
95 | ) | ||
96 | .await | ||
97 | .unwrap(); | ||
98 | } | ||
99 | |||
100 | #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Copy)] | ||
101 | enum TransferAdapter { | ||
102 | #[serde(rename = "basic")] | ||
103 | Basic, | ||
104 | } | ||
105 | |||
106 | #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Copy)] | ||
107 | enum HashAlgo { | ||
108 | #[serde(rename = "sha256")] | ||
109 | Sha256, | ||
110 | } | ||
111 | |||
112 | impl Default for HashAlgo { | ||
113 | fn default() -> Self { | ||
114 | Self::Sha256 | ||
115 | } | ||
116 | } | ||
117 | |||
118 | type Oid = common::Digest<32>; | ||
119 | |||
120 | #[derive(Debug, Deserialize, Clone)] | ||
121 | struct BatchRequestObject { | ||
122 | oid: Oid, | ||
123 | size: i64, | ||
124 | } | ||
125 | |||
126 | #[derive(Debug, Serialize, Deserialize, Clone)] | ||
127 | struct BatchRef { | ||
128 | name: String, | ||
129 | } | ||
130 | |||
131 | fn default_transfers() -> Vec<TransferAdapter> { | ||
132 | vec![TransferAdapter::Basic] | ||
133 | } | ||
134 | |||
135 | #[derive(Deserialize)] | ||
136 | struct BatchRequest { | ||
137 | operation: common::Operation, | ||
138 | #[serde(default = "default_transfers")] | ||
139 | transfers: Vec<TransferAdapter>, | ||
140 | #[serde(rename = "ref")] | ||
141 | reference: Option<BatchRef>, | ||
142 | objects: Vec<BatchRequestObject>, | ||
143 | #[serde(default)] | ||
144 | hash_algo: HashAlgo, | ||
145 | } | ||
146 | |||
147 | #[derive(Clone)] | ||
148 | struct GitLfsJson<T>(Json<T>); | ||
149 | |||
150 | const LFS_MIME: &'static str = "application/vnd.git-lfs+json"; | ||
151 | |||
152 | enum GitLfsJsonRejection { | ||
153 | Json(rejection::JsonRejection), | ||
154 | MissingGitLfsJsonContentType, | ||
155 | } | ||
156 | |||
157 | impl IntoResponse for GitLfsJsonRejection { | ||
158 | fn into_response(self) -> Response { | ||
159 | ( | ||
160 | StatusCode::UNSUPPORTED_MEDIA_TYPE, | ||
161 | format!("Expected request with `Content-Type: {LFS_MIME}`"), | ||
162 | ) | ||
163 | .into_response() | ||
164 | } | ||
165 | } | ||
166 | |||
167 | fn is_git_lfs_json_mimetype(mimetype: &str) -> bool { | ||
168 | let Ok(mime) = mimetype.parse::<mime::Mime>() else { | ||
169 | return false; | ||
170 | }; | ||
171 | if mime.type_() != mime::APPLICATION | ||
172 | || mime.subtype() != "vnd.git-lfs" | ||
173 | || mime.suffix() != Some(mime::JSON) | ||
174 | { | ||
175 | return false; | ||
176 | } | ||
177 | match mime.get_param(mime::CHARSET) { | ||
178 | Some(mime::UTF_8) | None => true, | ||
179 | Some(_) => false, | ||
180 | } | ||
181 | } | ||
182 | |||
183 | fn has_git_lfs_json_content_type(req: &Request) -> bool { | ||
184 | let Some(content_type) = req.headers().get(header::CONTENT_TYPE) else { | ||
185 | return false; | ||
186 | }; | ||
187 | let Ok(content_type) = content_type.to_str() else { | ||
188 | return false; | ||
189 | }; | ||
190 | return is_git_lfs_json_mimetype(content_type); | ||
191 | } | ||
192 | |||
193 | #[async_trait] | ||
194 | impl<T, S> FromRequest<S> for GitLfsJson<T> | ||
195 | where | ||
196 | T: DeserializeOwned, | ||
197 | S: Send + Sync, | ||
198 | { | ||
199 | type Rejection = GitLfsJsonRejection; | ||
200 | |||
201 | async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> { | ||
202 | if !has_git_lfs_json_content_type(&req) { | ||
203 | return Err(GitLfsJsonRejection::MissingGitLfsJsonContentType); | ||
204 | } | ||
205 | Json::<T>::from_request(req, state) | ||
206 | .await | ||
207 | .map(GitLfsJson) | ||
208 | .map_err(GitLfsJsonRejection::Json) | ||
209 | } | ||
210 | } | ||
211 | |||
212 | impl<T: Serialize> IntoResponse for GitLfsJson<T> { | ||
213 | fn into_response(self) -> Response { | ||
214 | let GitLfsJson(json) = self; | ||
215 | let mut resp = json.into_response(); | ||
216 | resp.headers_mut().insert( | ||
217 | header::CONTENT_TYPE, | ||
218 | HeaderValue::from_static("application/vnd.git-lfs+json"), | ||
219 | ); | ||
220 | resp | ||
221 | } | ||
222 | } | ||
223 | |||
224 | #[derive(Debug, Serialize, Clone)] | ||
225 | struct BatchResponseObjectAction { | ||
226 | href: String, | ||
227 | #[serde(skip_serializing_if = "HashMap::is_empty")] | ||
228 | header: HashMap<String, String>, | ||
229 | expires_at: DateTime<Utc>, | ||
230 | } | ||
231 | |||
232 | #[derive(Debug, Serialize, Clone)] | ||
233 | struct BatchResponseObjectActions { | ||
234 | #[serde(skip_serializing_if = "Option::is_none")] | ||
235 | upload: Option<BatchResponseObjectAction>, | ||
236 | #[serde(skip_serializing_if = "Option::is_none")] | ||
237 | download: Option<BatchResponseObjectAction>, | ||
238 | #[serde(skip_serializing_if = "Option::is_none")] | ||
239 | verify: Option<BatchResponseObjectAction>, | ||
240 | } | ||
241 | |||
242 | #[derive(Debug, Serialize, Clone)] | ||
243 | struct BatchResponseObject { | ||
244 | oid: Oid, | ||
245 | size: i64, | ||
246 | #[serde(skip_serializing_if = "Option::is_none")] | ||
247 | authenticated: Option<bool>, | ||
248 | } | ||
249 | |||
250 | #[derive(Debug, Serialize, Clone)] | ||
251 | struct BatchResponse { | ||
252 | transfer: TransferAdapter, | ||
253 | objects: Vec<BatchResponseObject>, | ||
254 | hash_algo: HashAlgo, | ||
255 | } | ||
256 | |||
257 | //fn handle_download_object(repo: &str, obj: &BatchRequestObject) { | ||
258 | // let (oid0, oid1) = (HexByte(obj.oid[0]), HexByte(obj.oid[1])); | ||
259 | // let full_path = format!("{repo}/lfs/objects/{}/{}/{}", oid0, oid1, obj.oid); | ||
260 | // | ||
261 | // let bucket_anme = "asdfasdf"; | ||
262 | // let region = s3::Region::Custom { | ||
263 | // region: "nl-ams".to_string(), | ||
264 | // endpoint: "rg.nl-ams.swc.cloud".to_string() | ||
265 | // }; | ||
266 | // let credentials = Credentials::new(None, None, None, None, None).unwrap(); | ||
267 | // let bucket = Bucket::new(bucket_anme, region, credentials).unwrap(); | ||
268 | //} | ||
269 | |||
270 | async fn batch( | ||
271 | header: HeaderMap, | ||
272 | RepositoryName(repo): RepositoryName, | ||
273 | GitLfsJson(Json(payload)): GitLfsJson<BatchRequest>, | ||
274 | ) -> Response { | ||
275 | if !header | ||
276 | .get_all("Accept") | ||
277 | .iter() | ||
278 | .filter_map(|v| v.to_str().ok()) | ||
279 | .any(is_git_lfs_json_mimetype) | ||
280 | { | ||
281 | return ( | ||
282 | StatusCode::NOT_ACCEPTABLE, | ||
283 | format!("Expected `{LFS_MIME}` (with UTF-8 charset) in list of acceptable response media types"), | ||
284 | ).into_response(); | ||
285 | } | ||
286 | |||
287 | if payload.hash_algo != HashAlgo::Sha256 { | ||
288 | return ( | ||
289 | StatusCode::CONFLICT, | ||
290 | "Unsupported hashing algorithm speicifed", | ||
291 | ) | ||
292 | .into_response(); | ||
293 | } | ||
294 | if !payload.transfers.is_empty() && !payload.transfers.contains(&TransferAdapter::Basic) { | ||
295 | return ( | ||
296 | StatusCode::CONFLICT, | ||
297 | "Unsupported transfer adapter specified (supported: basic)", | ||
298 | ) | ||
299 | .into_response(); | ||
300 | } | ||
301 | |||
302 | let resp: BatchResponse; | ||
303 | for obj in payload.objects { | ||
304 | // match payload.operation { | ||
305 | // Operation::Download => resp.objects.push(handle_download_object(repo, obj));, | ||
306 | // Operation::Upload => resp.objects.push(handle_upload_object(repo, obj)), | ||
307 | // }; | ||
308 | } | ||
309 | |||
310 | format!("hi from {repo}\n").into_response() | ||
311 | } | ||
312 | |||
313 | #[derive(Deserialize, Copy, Clone)] | ||
314 | #[serde(remote = "Self")] | ||
315 | struct FileParams { | ||
316 | oid0: HexByte, | ||
317 | oid1: HexByte, | ||
318 | oid: Oid, | ||
319 | } | ||
320 | |||
321 | impl<'de> Deserialize<'de> for FileParams { | ||
322 | fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> | ||
323 | where | ||
324 | D: serde::Deserializer<'de>, | ||
325 | { | ||
326 | let unchecked @ FileParams { | ||
327 | oid0: HexByte(oid0), | ||
328 | oid1: HexByte(oid1), | ||
329 | oid, | ||
330 | } = FileParams::deserialize(deserializer)?; | ||
331 | if oid0 != oid.as_bytes()[0] { | ||
332 | return Err(de::Error::custom( | ||
333 | "first OID path part does not match first byte of full OID", | ||
334 | )); | ||
335 | } | ||
336 | if oid1 != oid.as_bytes()[1] { | ||
337 | return Err(de::Error::custom( | ||
338 | "second OID path part does not match first byte of full OID", | ||
339 | )); | ||
340 | } | ||
341 | Ok(unchecked) | ||
342 | } | ||
343 | } | ||
344 | |||
345 | async fn obj_download(Path(FileParams { oid0, oid1, oid }): Path<FileParams>) {} | ||
346 | |||
347 | async fn obj_upload(Path(FileParams { oid0, oid1, oid }): Path<FileParams>) {} | ||