+8
.gitignore
+8
.gitignore
+70
CLAUDE.md
+70
CLAUDE.md
···
1
+
# CLAUDE.md
2
+
3
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+
## Project Overview
6
+
7
+
This is `atproto-identity`, a comprehensive Rust library for AT Protocol identity management. The project provides full functionality for DID resolution, handle resolution, and identity document management across multiple DID methods.
8
+
9
+
## Common Commands
10
+
11
+
- **Build**: `cargo build`
12
+
- **Run tests**: `cargo test`
13
+
- **Run specific test**: `cargo test test_name`
14
+
- **Check code**: `cargo check`
15
+
- **Format code**: `cargo fmt`
16
+
- **Lint**: `cargo clippy`
17
+
- **Run CLI tool**: `cargo run --bin atproto-identity-resolve -- <handle_or_did>`
18
+
- **Run CLI with DID document**: `cargo run --bin atproto-identity-resolve -- --did-document <handle_or_did>`
19
+
20
+
## Architecture
21
+
22
+
A comprehensive Rust library with:
23
+
- Modular architecture with 7 core modules (resolve, plc, web, model, validation, config, errors)
24
+
- Complete CLI tool for identity resolution (`atproto-identity-resolve`)
25
+
- Rust edition 2021 with modern async/await patterns
26
+
- Comprehensive error handling with structured error types
27
+
- Multiple external dependencies for HTTP, DNS, JSON, and cryptographic operations
28
+
- Full test coverage with unit tests for all modules
29
+
30
+
## Error Handling
31
+
32
+
All error strings must use this format:
33
+
34
+
error-atproto-identity-<domain>-<number> <message>: <details>
35
+
36
+
Example errors:
37
+
38
+
* error-atproto-identity-resolve-1 Multiple DIDs resolved for method
39
+
* error-atproto-identity-plc-1 HTTP request failed: https://google.com/ Not Found
40
+
* error-did-web-1 Invalid DID format: missing 'did:web:' prefix
41
+
42
+
Errors should be represented as enums using the `thiserror` library when possible using `src/errors.rs` as a reference and example.
43
+
44
+
Avoid creating new errors with the `anyhow!(...)` macro.
45
+
46
+
When a function call would return `anyhow::Error`, use the following pattern to log the error in addition to any code specific handling that must occur
47
+
48
+
```
49
+
If let Err(err) = result {
50
+
tracing::error!(error = ?error, "Helpful contextual log line.");
51
+
}
52
+
```
53
+
54
+
### Logging
55
+
56
+
Use tracing for structured logging.
57
+
58
+
Async calls should be instrumented using the `.instrument()` that references the `use tracing::Instrument;` trait.
59
+
60
+
## Module Structure
61
+
62
+
- **`src/lib.rs`**: Main library exports
63
+
- **`src/resolve.rs`**: Core resolution logic for handles and DIDs, DNS/HTTP resolution
64
+
- **`src/plc.rs`**: PLC directory client for did:plc resolution
65
+
- **`src/web.rs`**: Web DID client for did:web resolution and URL conversion
66
+
- **`src/model.rs`**: Data structures for DID documents and AT Protocol entities
67
+
- **`src/validation.rs`**: Input validation for handles and DIDs
68
+
- **`src/config.rs`**: Configuration management and environment variable handling
69
+
- **`src/errors.rs`**: Structured error types following project conventions
70
+
- **`src/bin/atproto-identity-resolve.rs`**: CLI tool for identity resolution
+2463
Cargo.lock
+2463
Cargo.lock
···
1
+
# This file is automatically @generated by Cargo.
2
+
# It is not intended for manual editing.
3
+
version = 4
4
+
5
+
[[package]]
6
+
name = "addr2line"
7
+
version = "0.24.2"
8
+
source = "registry+https://github.com/rust-lang/crates.io-index"
9
+
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
10
+
dependencies = [
11
+
"gimli",
12
+
]
13
+
14
+
[[package]]
15
+
name = "adler2"
16
+
version = "2.0.0"
17
+
source = "registry+https://github.com/rust-lang/crates.io-index"
18
+
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
19
+
20
+
[[package]]
21
+
name = "aho-corasick"
22
+
version = "1.1.3"
23
+
source = "registry+https://github.com/rust-lang/crates.io-index"
24
+
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
25
+
dependencies = [
26
+
"memchr",
27
+
]
28
+
29
+
[[package]]
30
+
name = "anyhow"
31
+
version = "1.0.98"
32
+
source = "registry+https://github.com/rust-lang/crates.io-index"
33
+
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
34
+
35
+
[[package]]
36
+
name = "async-trait"
37
+
version = "0.1.88"
38
+
source = "registry+https://github.com/rust-lang/crates.io-index"
39
+
checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5"
40
+
dependencies = [
41
+
"proc-macro2",
42
+
"quote",
43
+
"syn",
44
+
]
45
+
46
+
[[package]]
47
+
name = "atomic-waker"
48
+
version = "1.1.2"
49
+
source = "registry+https://github.com/rust-lang/crates.io-index"
50
+
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
51
+
52
+
[[package]]
53
+
name = "atproto-identity"
54
+
version = "0.1.0"
55
+
dependencies = [
56
+
"anyhow",
57
+
"futures-util",
58
+
"hickory-resolver",
59
+
"reqwest",
60
+
"serde",
61
+
"serde_json",
62
+
"thiserror 2.0.12",
63
+
"tokio",
64
+
"tracing",
65
+
]
66
+
67
+
[[package]]
68
+
name = "autocfg"
69
+
version = "1.4.0"
70
+
source = "registry+https://github.com/rust-lang/crates.io-index"
71
+
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
72
+
73
+
[[package]]
74
+
name = "backtrace"
75
+
version = "0.3.75"
76
+
source = "registry+https://github.com/rust-lang/crates.io-index"
77
+
checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
78
+
dependencies = [
79
+
"addr2line",
80
+
"cfg-if",
81
+
"libc",
82
+
"miniz_oxide",
83
+
"object",
84
+
"rustc-demangle",
85
+
"windows-targets 0.52.6",
86
+
]
87
+
88
+
[[package]]
89
+
name = "base64"
90
+
version = "0.22.1"
91
+
source = "registry+https://github.com/rust-lang/crates.io-index"
92
+
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
93
+
94
+
[[package]]
95
+
name = "bitflags"
96
+
version = "2.9.1"
97
+
source = "registry+https://github.com/rust-lang/crates.io-index"
98
+
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
99
+
100
+
[[package]]
101
+
name = "bumpalo"
102
+
version = "3.17.0"
103
+
source = "registry+https://github.com/rust-lang/crates.io-index"
104
+
checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
105
+
106
+
[[package]]
107
+
name = "bytes"
108
+
version = "1.10.1"
109
+
source = "registry+https://github.com/rust-lang/crates.io-index"
110
+
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
111
+
112
+
[[package]]
113
+
name = "cc"
114
+
version = "1.2.24"
115
+
source = "registry+https://github.com/rust-lang/crates.io-index"
116
+
checksum = "16595d3be041c03b09d08d0858631facccee9221e579704070e6e9e4915d3bc7"
117
+
dependencies = [
118
+
"shlex",
119
+
]
120
+
121
+
[[package]]
122
+
name = "cfg-if"
123
+
version = "1.0.0"
124
+
source = "registry+https://github.com/rust-lang/crates.io-index"
125
+
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
126
+
127
+
[[package]]
128
+
name = "cfg_aliases"
129
+
version = "0.2.1"
130
+
source = "registry+https://github.com/rust-lang/crates.io-index"
131
+
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
132
+
133
+
[[package]]
134
+
name = "core-foundation"
135
+
version = "0.9.4"
136
+
source = "registry+https://github.com/rust-lang/crates.io-index"
137
+
checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
138
+
dependencies = [
139
+
"core-foundation-sys",
140
+
"libc",
141
+
]
142
+
143
+
[[package]]
144
+
name = "core-foundation-sys"
145
+
version = "0.8.7"
146
+
source = "registry+https://github.com/rust-lang/crates.io-index"
147
+
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
148
+
149
+
[[package]]
150
+
name = "critical-section"
151
+
version = "1.2.0"
152
+
source = "registry+https://github.com/rust-lang/crates.io-index"
153
+
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
154
+
155
+
[[package]]
156
+
name = "crossbeam-channel"
157
+
version = "0.5.15"
158
+
source = "registry+https://github.com/rust-lang/crates.io-index"
159
+
checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
160
+
dependencies = [
161
+
"crossbeam-utils",
162
+
]
163
+
164
+
[[package]]
165
+
name = "crossbeam-epoch"
166
+
version = "0.9.18"
167
+
source = "registry+https://github.com/rust-lang/crates.io-index"
168
+
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
169
+
dependencies = [
170
+
"crossbeam-utils",
171
+
]
172
+
173
+
[[package]]
174
+
name = "crossbeam-utils"
175
+
version = "0.8.21"
176
+
source = "registry+https://github.com/rust-lang/crates.io-index"
177
+
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
178
+
179
+
[[package]]
180
+
name = "data-encoding"
181
+
version = "2.9.0"
182
+
source = "registry+https://github.com/rust-lang/crates.io-index"
183
+
checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476"
184
+
185
+
[[package]]
186
+
name = "displaydoc"
187
+
version = "0.2.5"
188
+
source = "registry+https://github.com/rust-lang/crates.io-index"
189
+
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
190
+
dependencies = [
191
+
"proc-macro2",
192
+
"quote",
193
+
"syn",
194
+
]
195
+
196
+
[[package]]
197
+
name = "encoding_rs"
198
+
version = "0.8.35"
199
+
source = "registry+https://github.com/rust-lang/crates.io-index"
200
+
checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
201
+
dependencies = [
202
+
"cfg-if",
203
+
]
204
+
205
+
[[package]]
206
+
name = "enum-as-inner"
207
+
version = "0.6.1"
208
+
source = "registry+https://github.com/rust-lang/crates.io-index"
209
+
checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc"
210
+
dependencies = [
211
+
"heck",
212
+
"proc-macro2",
213
+
"quote",
214
+
"syn",
215
+
]
216
+
217
+
[[package]]
218
+
name = "equivalent"
219
+
version = "1.0.2"
220
+
source = "registry+https://github.com/rust-lang/crates.io-index"
221
+
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
222
+
223
+
[[package]]
224
+
name = "errno"
225
+
version = "0.3.12"
226
+
source = "registry+https://github.com/rust-lang/crates.io-index"
227
+
checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18"
228
+
dependencies = [
229
+
"libc",
230
+
"windows-sys 0.59.0",
231
+
]
232
+
233
+
[[package]]
234
+
name = "fastrand"
235
+
version = "2.3.0"
236
+
source = "registry+https://github.com/rust-lang/crates.io-index"
237
+
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
238
+
239
+
[[package]]
240
+
name = "fnv"
241
+
version = "1.0.7"
242
+
source = "registry+https://github.com/rust-lang/crates.io-index"
243
+
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
244
+
245
+
[[package]]
246
+
name = "foreign-types"
247
+
version = "0.3.2"
248
+
source = "registry+https://github.com/rust-lang/crates.io-index"
249
+
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
250
+
dependencies = [
251
+
"foreign-types-shared",
252
+
]
253
+
254
+
[[package]]
255
+
name = "foreign-types-shared"
256
+
version = "0.1.1"
257
+
source = "registry+https://github.com/rust-lang/crates.io-index"
258
+
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
259
+
260
+
[[package]]
261
+
name = "form_urlencoded"
262
+
version = "1.2.1"
263
+
source = "registry+https://github.com/rust-lang/crates.io-index"
264
+
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
265
+
dependencies = [
266
+
"percent-encoding",
267
+
]
268
+
269
+
[[package]]
270
+
name = "futures-channel"
271
+
version = "0.3.31"
272
+
source = "registry+https://github.com/rust-lang/crates.io-index"
273
+
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
274
+
dependencies = [
275
+
"futures-core",
276
+
]
277
+
278
+
[[package]]
279
+
name = "futures-core"
280
+
version = "0.3.31"
281
+
source = "registry+https://github.com/rust-lang/crates.io-index"
282
+
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
283
+
284
+
[[package]]
285
+
name = "futures-io"
286
+
version = "0.3.31"
287
+
source = "registry+https://github.com/rust-lang/crates.io-index"
288
+
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
289
+
290
+
[[package]]
291
+
name = "futures-macro"
292
+
version = "0.3.31"
293
+
source = "registry+https://github.com/rust-lang/crates.io-index"
294
+
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
295
+
dependencies = [
296
+
"proc-macro2",
297
+
"quote",
298
+
"syn",
299
+
]
300
+
301
+
[[package]]
302
+
name = "futures-sink"
303
+
version = "0.3.31"
304
+
source = "registry+https://github.com/rust-lang/crates.io-index"
305
+
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
306
+
307
+
[[package]]
308
+
name = "futures-task"
309
+
version = "0.3.31"
310
+
source = "registry+https://github.com/rust-lang/crates.io-index"
311
+
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
312
+
313
+
[[package]]
314
+
name = "futures-util"
315
+
version = "0.3.31"
316
+
source = "registry+https://github.com/rust-lang/crates.io-index"
317
+
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
318
+
dependencies = [
319
+
"futures-core",
320
+
"futures-macro",
321
+
"futures-task",
322
+
"pin-project-lite",
323
+
"pin-utils",
324
+
"slab",
325
+
]
326
+
327
+
[[package]]
328
+
name = "generator"
329
+
version = "0.8.5"
330
+
source = "registry+https://github.com/rust-lang/crates.io-index"
331
+
checksum = "d18470a76cb7f8ff746cf1f7470914f900252ec36bbc40b569d74b1258446827"
332
+
dependencies = [
333
+
"cc",
334
+
"cfg-if",
335
+
"libc",
336
+
"log",
337
+
"rustversion",
338
+
"windows",
339
+
]
340
+
341
+
[[package]]
342
+
name = "getrandom"
343
+
version = "0.2.16"
344
+
source = "registry+https://github.com/rust-lang/crates.io-index"
345
+
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
346
+
dependencies = [
347
+
"cfg-if",
348
+
"js-sys",
349
+
"libc",
350
+
"wasi 0.11.0+wasi-snapshot-preview1",
351
+
"wasm-bindgen",
352
+
]
353
+
354
+
[[package]]
355
+
name = "getrandom"
356
+
version = "0.3.3"
357
+
source = "registry+https://github.com/rust-lang/crates.io-index"
358
+
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
359
+
dependencies = [
360
+
"cfg-if",
361
+
"js-sys",
362
+
"libc",
363
+
"r-efi",
364
+
"wasi 0.14.2+wasi-0.2.4",
365
+
"wasm-bindgen",
366
+
]
367
+
368
+
[[package]]
369
+
name = "gimli"
370
+
version = "0.31.1"
371
+
source = "registry+https://github.com/rust-lang/crates.io-index"
372
+
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
373
+
374
+
[[package]]
375
+
name = "h2"
376
+
version = "0.4.10"
377
+
source = "registry+https://github.com/rust-lang/crates.io-index"
378
+
checksum = "a9421a676d1b147b16b82c9225157dc629087ef8ec4d5e2960f9437a90dac0a5"
379
+
dependencies = [
380
+
"atomic-waker",
381
+
"bytes",
382
+
"fnv",
383
+
"futures-core",
384
+
"futures-sink",
385
+
"http",
386
+
"indexmap",
387
+
"slab",
388
+
"tokio",
389
+
"tokio-util",
390
+
"tracing",
391
+
]
392
+
393
+
[[package]]
394
+
name = "hashbrown"
395
+
version = "0.15.3"
396
+
source = "registry+https://github.com/rust-lang/crates.io-index"
397
+
checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3"
398
+
399
+
[[package]]
400
+
name = "heck"
401
+
version = "0.5.0"
402
+
source = "registry+https://github.com/rust-lang/crates.io-index"
403
+
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
404
+
405
+
[[package]]
406
+
name = "hickory-proto"
407
+
version = "0.25.2"
408
+
source = "registry+https://github.com/rust-lang/crates.io-index"
409
+
checksum = "f8a6fe56c0038198998a6f217ca4e7ef3a5e51f46163bd6dd60b5c71ca6c6502"
410
+
dependencies = [
411
+
"async-trait",
412
+
"cfg-if",
413
+
"data-encoding",
414
+
"enum-as-inner",
415
+
"futures-channel",
416
+
"futures-io",
417
+
"futures-util",
418
+
"idna",
419
+
"ipnet",
420
+
"once_cell",
421
+
"rand",
422
+
"ring",
423
+
"thiserror 2.0.12",
424
+
"tinyvec",
425
+
"tokio",
426
+
"tracing",
427
+
"url",
428
+
]
429
+
430
+
[[package]]
431
+
name = "hickory-resolver"
432
+
version = "0.25.2"
433
+
source = "registry+https://github.com/rust-lang/crates.io-index"
434
+
checksum = "dc62a9a99b0bfb44d2ab95a7208ac952d31060efc16241c87eaf36406fecf87a"
435
+
dependencies = [
436
+
"cfg-if",
437
+
"futures-util",
438
+
"hickory-proto",
439
+
"ipconfig",
440
+
"moka",
441
+
"once_cell",
442
+
"parking_lot",
443
+
"rand",
444
+
"resolv-conf",
445
+
"smallvec",
446
+
"thiserror 2.0.12",
447
+
"tokio",
448
+
"tracing",
449
+
]
450
+
451
+
[[package]]
452
+
name = "http"
453
+
version = "1.3.1"
454
+
source = "registry+https://github.com/rust-lang/crates.io-index"
455
+
checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565"
456
+
dependencies = [
457
+
"bytes",
458
+
"fnv",
459
+
"itoa",
460
+
]
461
+
462
+
[[package]]
463
+
name = "http-body"
464
+
version = "1.0.1"
465
+
source = "registry+https://github.com/rust-lang/crates.io-index"
466
+
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
467
+
dependencies = [
468
+
"bytes",
469
+
"http",
470
+
]
471
+
472
+
[[package]]
473
+
name = "http-body-util"
474
+
version = "0.1.3"
475
+
source = "registry+https://github.com/rust-lang/crates.io-index"
476
+
checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
477
+
dependencies = [
478
+
"bytes",
479
+
"futures-core",
480
+
"http",
481
+
"http-body",
482
+
"pin-project-lite",
483
+
]
484
+
485
+
[[package]]
486
+
name = "httparse"
487
+
version = "1.10.1"
488
+
source = "registry+https://github.com/rust-lang/crates.io-index"
489
+
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
490
+
491
+
[[package]]
492
+
name = "hyper"
493
+
version = "1.6.0"
494
+
source = "registry+https://github.com/rust-lang/crates.io-index"
495
+
checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80"
496
+
dependencies = [
497
+
"bytes",
498
+
"futures-channel",
499
+
"futures-util",
500
+
"h2",
501
+
"http",
502
+
"http-body",
503
+
"httparse",
504
+
"itoa",
505
+
"pin-project-lite",
506
+
"smallvec",
507
+
"tokio",
508
+
"want",
509
+
]
510
+
511
+
[[package]]
512
+
name = "hyper-rustls"
513
+
version = "0.27.6"
514
+
source = "registry+https://github.com/rust-lang/crates.io-index"
515
+
checksum = "03a01595e11bdcec50946522c32dde3fc6914743000a68b93000965f2f02406d"
516
+
dependencies = [
517
+
"http",
518
+
"hyper",
519
+
"hyper-util",
520
+
"rustls",
521
+
"rustls-pki-types",
522
+
"tokio",
523
+
"tokio-rustls",
524
+
"tower-service",
525
+
"webpki-roots",
526
+
]
527
+
528
+
[[package]]
529
+
name = "hyper-tls"
530
+
version = "0.6.0"
531
+
source = "registry+https://github.com/rust-lang/crates.io-index"
532
+
checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
533
+
dependencies = [
534
+
"bytes",
535
+
"http-body-util",
536
+
"hyper",
537
+
"hyper-util",
538
+
"native-tls",
539
+
"tokio",
540
+
"tokio-native-tls",
541
+
"tower-service",
542
+
]
543
+
544
+
[[package]]
545
+
name = "hyper-util"
546
+
version = "0.1.13"
547
+
source = "registry+https://github.com/rust-lang/crates.io-index"
548
+
checksum = "b1c293b6b3d21eca78250dc7dbebd6b9210ec5530e038cbfe0661b5c47ab06e8"
549
+
dependencies = [
550
+
"base64",
551
+
"bytes",
552
+
"futures-channel",
553
+
"futures-core",
554
+
"futures-util",
555
+
"http",
556
+
"http-body",
557
+
"hyper",
558
+
"ipnet",
559
+
"libc",
560
+
"percent-encoding",
561
+
"pin-project-lite",
562
+
"socket2",
563
+
"system-configuration",
564
+
"tokio",
565
+
"tower-service",
566
+
"tracing",
567
+
"windows-registry",
568
+
]
569
+
570
+
[[package]]
571
+
name = "icu_collections"
572
+
version = "2.0.0"
573
+
source = "registry+https://github.com/rust-lang/crates.io-index"
574
+
checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47"
575
+
dependencies = [
576
+
"displaydoc",
577
+
"potential_utf",
578
+
"yoke",
579
+
"zerofrom",
580
+
"zerovec",
581
+
]
582
+
583
+
[[package]]
584
+
name = "icu_locale_core"
585
+
version = "2.0.0"
586
+
source = "registry+https://github.com/rust-lang/crates.io-index"
587
+
checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a"
588
+
dependencies = [
589
+
"displaydoc",
590
+
"litemap",
591
+
"tinystr",
592
+
"writeable",
593
+
"zerovec",
594
+
]
595
+
596
+
[[package]]
597
+
name = "icu_normalizer"
598
+
version = "2.0.0"
599
+
source = "registry+https://github.com/rust-lang/crates.io-index"
600
+
checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979"
601
+
dependencies = [
602
+
"displaydoc",
603
+
"icu_collections",
604
+
"icu_normalizer_data",
605
+
"icu_properties",
606
+
"icu_provider",
607
+
"smallvec",
608
+
"zerovec",
609
+
]
610
+
611
+
[[package]]
612
+
name = "icu_normalizer_data"
613
+
version = "2.0.0"
614
+
source = "registry+https://github.com/rust-lang/crates.io-index"
615
+
checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3"
616
+
617
+
[[package]]
618
+
name = "icu_properties"
619
+
version = "2.0.1"
620
+
source = "registry+https://github.com/rust-lang/crates.io-index"
621
+
checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b"
622
+
dependencies = [
623
+
"displaydoc",
624
+
"icu_collections",
625
+
"icu_locale_core",
626
+
"icu_properties_data",
627
+
"icu_provider",
628
+
"potential_utf",
629
+
"zerotrie",
630
+
"zerovec",
631
+
]
632
+
633
+
[[package]]
634
+
name = "icu_properties_data"
635
+
version = "2.0.1"
636
+
source = "registry+https://github.com/rust-lang/crates.io-index"
637
+
checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632"
638
+
639
+
[[package]]
640
+
name = "icu_provider"
641
+
version = "2.0.0"
642
+
source = "registry+https://github.com/rust-lang/crates.io-index"
643
+
checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af"
644
+
dependencies = [
645
+
"displaydoc",
646
+
"icu_locale_core",
647
+
"stable_deref_trait",
648
+
"tinystr",
649
+
"writeable",
650
+
"yoke",
651
+
"zerofrom",
652
+
"zerotrie",
653
+
"zerovec",
654
+
]
655
+
656
+
[[package]]
657
+
name = "idna"
658
+
version = "1.0.3"
659
+
source = "registry+https://github.com/rust-lang/crates.io-index"
660
+
checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
661
+
dependencies = [
662
+
"idna_adapter",
663
+
"smallvec",
664
+
"utf8_iter",
665
+
]
666
+
667
+
[[package]]
668
+
name = "idna_adapter"
669
+
version = "1.2.1"
670
+
source = "registry+https://github.com/rust-lang/crates.io-index"
671
+
checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
672
+
dependencies = [
673
+
"icu_normalizer",
674
+
"icu_properties",
675
+
]
676
+
677
+
[[package]]
678
+
name = "indexmap"
679
+
version = "2.9.0"
680
+
source = "registry+https://github.com/rust-lang/crates.io-index"
681
+
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
682
+
dependencies = [
683
+
"equivalent",
684
+
"hashbrown",
685
+
]
686
+
687
+
[[package]]
688
+
name = "ipconfig"
689
+
version = "0.3.2"
690
+
source = "registry+https://github.com/rust-lang/crates.io-index"
691
+
checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f"
692
+
dependencies = [
693
+
"socket2",
694
+
"widestring",
695
+
"windows-sys 0.48.0",
696
+
"winreg",
697
+
]
698
+
699
+
[[package]]
700
+
name = "ipnet"
701
+
version = "2.11.0"
702
+
source = "registry+https://github.com/rust-lang/crates.io-index"
703
+
checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
704
+
705
+
[[package]]
706
+
name = "iri-string"
707
+
version = "0.7.8"
708
+
source = "registry+https://github.com/rust-lang/crates.io-index"
709
+
checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2"
710
+
dependencies = [
711
+
"memchr",
712
+
"serde",
713
+
]
714
+
715
+
[[package]]
716
+
name = "itoa"
717
+
version = "1.0.15"
718
+
source = "registry+https://github.com/rust-lang/crates.io-index"
719
+
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
720
+
721
+
[[package]]
722
+
name = "js-sys"
723
+
version = "0.3.77"
724
+
source = "registry+https://github.com/rust-lang/crates.io-index"
725
+
checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
726
+
dependencies = [
727
+
"once_cell",
728
+
"wasm-bindgen",
729
+
]
730
+
731
+
[[package]]
732
+
name = "lazy_static"
733
+
version = "1.5.0"
734
+
source = "registry+https://github.com/rust-lang/crates.io-index"
735
+
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
736
+
737
+
[[package]]
738
+
name = "libc"
739
+
version = "0.2.172"
740
+
source = "registry+https://github.com/rust-lang/crates.io-index"
741
+
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
742
+
743
+
[[package]]
744
+
name = "linux-raw-sys"
745
+
version = "0.9.4"
746
+
source = "registry+https://github.com/rust-lang/crates.io-index"
747
+
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
748
+
749
+
[[package]]
750
+
name = "litemap"
751
+
version = "0.8.0"
752
+
source = "registry+https://github.com/rust-lang/crates.io-index"
753
+
checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
754
+
755
+
[[package]]
756
+
name = "lock_api"
757
+
version = "0.4.13"
758
+
source = "registry+https://github.com/rust-lang/crates.io-index"
759
+
checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
760
+
dependencies = [
761
+
"autocfg",
762
+
"scopeguard",
763
+
]
764
+
765
+
[[package]]
766
+
name = "log"
767
+
version = "0.4.27"
768
+
source = "registry+https://github.com/rust-lang/crates.io-index"
769
+
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
770
+
771
+
[[package]]
772
+
name = "loom"
773
+
version = "0.7.2"
774
+
source = "registry+https://github.com/rust-lang/crates.io-index"
775
+
checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca"
776
+
dependencies = [
777
+
"cfg-if",
778
+
"generator",
779
+
"scoped-tls",
780
+
"tracing",
781
+
"tracing-subscriber",
782
+
]
783
+
784
+
[[package]]
785
+
name = "lru-slab"
786
+
version = "0.1.2"
787
+
source = "registry+https://github.com/rust-lang/crates.io-index"
788
+
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
789
+
790
+
[[package]]
791
+
name = "matchers"
792
+
version = "0.1.0"
793
+
source = "registry+https://github.com/rust-lang/crates.io-index"
794
+
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
795
+
dependencies = [
796
+
"regex-automata 0.1.10",
797
+
]
798
+
799
+
[[package]]
800
+
name = "memchr"
801
+
version = "2.7.4"
802
+
source = "registry+https://github.com/rust-lang/crates.io-index"
803
+
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
804
+
805
+
[[package]]
806
+
name = "mime"
807
+
version = "0.3.17"
808
+
source = "registry+https://github.com/rust-lang/crates.io-index"
809
+
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
810
+
811
+
[[package]]
812
+
name = "miniz_oxide"
813
+
version = "0.8.8"
814
+
source = "registry+https://github.com/rust-lang/crates.io-index"
815
+
checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
816
+
dependencies = [
817
+
"adler2",
818
+
]
819
+
820
+
[[package]]
821
+
name = "mio"
822
+
version = "1.0.4"
823
+
source = "registry+https://github.com/rust-lang/crates.io-index"
824
+
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
825
+
dependencies = [
826
+
"libc",
827
+
"wasi 0.11.0+wasi-snapshot-preview1",
828
+
"windows-sys 0.59.0",
829
+
]
830
+
831
+
[[package]]
832
+
name = "moka"
833
+
version = "0.12.10"
834
+
source = "registry+https://github.com/rust-lang/crates.io-index"
835
+
checksum = "a9321642ca94a4282428e6ea4af8cc2ca4eac48ac7a6a4ea8f33f76d0ce70926"
836
+
dependencies = [
837
+
"crossbeam-channel",
838
+
"crossbeam-epoch",
839
+
"crossbeam-utils",
840
+
"loom",
841
+
"parking_lot",
842
+
"portable-atomic",
843
+
"rustc_version",
844
+
"smallvec",
845
+
"tagptr",
846
+
"thiserror 1.0.69",
847
+
"uuid",
848
+
]
849
+
850
+
[[package]]
851
+
name = "native-tls"
852
+
version = "0.2.14"
853
+
source = "registry+https://github.com/rust-lang/crates.io-index"
854
+
checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e"
855
+
dependencies = [
856
+
"libc",
857
+
"log",
858
+
"openssl",
859
+
"openssl-probe",
860
+
"openssl-sys",
861
+
"schannel",
862
+
"security-framework",
863
+
"security-framework-sys",
864
+
"tempfile",
865
+
]
866
+
867
+
[[package]]
868
+
name = "nu-ansi-term"
869
+
version = "0.46.0"
870
+
source = "registry+https://github.com/rust-lang/crates.io-index"
871
+
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
872
+
dependencies = [
873
+
"overload",
874
+
"winapi",
875
+
]
876
+
877
+
[[package]]
878
+
name = "object"
879
+
version = "0.36.7"
880
+
source = "registry+https://github.com/rust-lang/crates.io-index"
881
+
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
882
+
dependencies = [
883
+
"memchr",
884
+
]
885
+
886
+
[[package]]
887
+
name = "once_cell"
888
+
version = "1.21.3"
889
+
source = "registry+https://github.com/rust-lang/crates.io-index"
890
+
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
891
+
dependencies = [
892
+
"critical-section",
893
+
"portable-atomic",
894
+
]
895
+
896
+
[[package]]
897
+
name = "openssl"
898
+
version = "0.10.73"
899
+
source = "registry+https://github.com/rust-lang/crates.io-index"
900
+
checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8"
901
+
dependencies = [
902
+
"bitflags",
903
+
"cfg-if",
904
+
"foreign-types",
905
+
"libc",
906
+
"once_cell",
907
+
"openssl-macros",
908
+
"openssl-sys",
909
+
]
910
+
911
+
[[package]]
912
+
name = "openssl-macros"
913
+
version = "0.1.1"
914
+
source = "registry+https://github.com/rust-lang/crates.io-index"
915
+
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
916
+
dependencies = [
917
+
"proc-macro2",
918
+
"quote",
919
+
"syn",
920
+
]
921
+
922
+
[[package]]
923
+
name = "openssl-probe"
924
+
version = "0.1.6"
925
+
source = "registry+https://github.com/rust-lang/crates.io-index"
926
+
checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
927
+
928
+
[[package]]
929
+
name = "openssl-sys"
930
+
version = "0.9.109"
931
+
source = "registry+https://github.com/rust-lang/crates.io-index"
932
+
checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571"
933
+
dependencies = [
934
+
"cc",
935
+
"libc",
936
+
"pkg-config",
937
+
"vcpkg",
938
+
]
939
+
940
+
[[package]]
941
+
name = "overload"
942
+
version = "0.1.1"
943
+
source = "registry+https://github.com/rust-lang/crates.io-index"
944
+
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
945
+
946
+
[[package]]
947
+
name = "parking_lot"
948
+
version = "0.12.4"
949
+
source = "registry+https://github.com/rust-lang/crates.io-index"
950
+
checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13"
951
+
dependencies = [
952
+
"lock_api",
953
+
"parking_lot_core",
954
+
]
955
+
956
+
[[package]]
957
+
name = "parking_lot_core"
958
+
version = "0.9.11"
959
+
source = "registry+https://github.com/rust-lang/crates.io-index"
960
+
checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
961
+
dependencies = [
962
+
"cfg-if",
963
+
"libc",
964
+
"redox_syscall",
965
+
"smallvec",
966
+
"windows-targets 0.52.6",
967
+
]
968
+
969
+
[[package]]
970
+
name = "percent-encoding"
971
+
version = "2.3.1"
972
+
source = "registry+https://github.com/rust-lang/crates.io-index"
973
+
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
974
+
975
+
[[package]]
976
+
name = "pin-project-lite"
977
+
version = "0.2.16"
978
+
source = "registry+https://github.com/rust-lang/crates.io-index"
979
+
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
980
+
981
+
[[package]]
982
+
name = "pin-utils"
983
+
version = "0.1.0"
984
+
source = "registry+https://github.com/rust-lang/crates.io-index"
985
+
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
986
+
987
+
[[package]]
988
+
name = "pkg-config"
989
+
version = "0.3.32"
990
+
source = "registry+https://github.com/rust-lang/crates.io-index"
991
+
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
992
+
993
+
[[package]]
994
+
name = "portable-atomic"
995
+
version = "1.11.0"
996
+
source = "registry+https://github.com/rust-lang/crates.io-index"
997
+
checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e"
998
+
999
+
[[package]]
1000
+
name = "potential_utf"
1001
+
version = "0.1.2"
1002
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1003
+
checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585"
1004
+
dependencies = [
1005
+
"zerovec",
1006
+
]
1007
+
1008
+
[[package]]
1009
+
name = "ppv-lite86"
1010
+
version = "0.2.21"
1011
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1012
+
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
1013
+
dependencies = [
1014
+
"zerocopy",
1015
+
]
1016
+
1017
+
[[package]]
1018
+
name = "proc-macro2"
1019
+
version = "1.0.95"
1020
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1021
+
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
1022
+
dependencies = [
1023
+
"unicode-ident",
1024
+
]
1025
+
1026
+
[[package]]
1027
+
name = "quinn"
1028
+
version = "0.11.8"
1029
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1030
+
checksum = "626214629cda6781b6dc1d316ba307189c85ba657213ce642d9c77670f8202c8"
1031
+
dependencies = [
1032
+
"bytes",
1033
+
"cfg_aliases",
1034
+
"pin-project-lite",
1035
+
"quinn-proto",
1036
+
"quinn-udp",
1037
+
"rustc-hash",
1038
+
"rustls",
1039
+
"socket2",
1040
+
"thiserror 2.0.12",
1041
+
"tokio",
1042
+
"tracing",
1043
+
"web-time",
1044
+
]
1045
+
1046
+
[[package]]
1047
+
name = "quinn-proto"
1048
+
version = "0.11.12"
1049
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1050
+
checksum = "49df843a9161c85bb8aae55f101bc0bac8bcafd637a620d9122fd7e0b2f7422e"
1051
+
dependencies = [
1052
+
"bytes",
1053
+
"getrandom 0.3.3",
1054
+
"lru-slab",
1055
+
"rand",
1056
+
"ring",
1057
+
"rustc-hash",
1058
+
"rustls",
1059
+
"rustls-pki-types",
1060
+
"slab",
1061
+
"thiserror 2.0.12",
1062
+
"tinyvec",
1063
+
"tracing",
1064
+
"web-time",
1065
+
]
1066
+
1067
+
[[package]]
1068
+
name = "quinn-udp"
1069
+
version = "0.5.12"
1070
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1071
+
checksum = "ee4e529991f949c5e25755532370b8af5d114acae52326361d68d47af64aa842"
1072
+
dependencies = [
1073
+
"cfg_aliases",
1074
+
"libc",
1075
+
"once_cell",
1076
+
"socket2",
1077
+
"tracing",
1078
+
"windows-sys 0.59.0",
1079
+
]
1080
+
1081
+
[[package]]
1082
+
name = "quote"
1083
+
version = "1.0.40"
1084
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1085
+
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
1086
+
dependencies = [
1087
+
"proc-macro2",
1088
+
]
1089
+
1090
+
[[package]]
1091
+
name = "r-efi"
1092
+
version = "5.2.0"
1093
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1094
+
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
1095
+
1096
+
[[package]]
1097
+
name = "rand"
1098
+
version = "0.9.1"
1099
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1100
+
checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
1101
+
dependencies = [
1102
+
"rand_chacha",
1103
+
"rand_core",
1104
+
]
1105
+
1106
+
[[package]]
1107
+
name = "rand_chacha"
1108
+
version = "0.9.0"
1109
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1110
+
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
1111
+
dependencies = [
1112
+
"ppv-lite86",
1113
+
"rand_core",
1114
+
]
1115
+
1116
+
[[package]]
1117
+
name = "rand_core"
1118
+
version = "0.9.3"
1119
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1120
+
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
1121
+
dependencies = [
1122
+
"getrandom 0.3.3",
1123
+
]
1124
+
1125
+
[[package]]
1126
+
name = "redox_syscall"
1127
+
version = "0.5.12"
1128
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1129
+
checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af"
1130
+
dependencies = [
1131
+
"bitflags",
1132
+
]
1133
+
1134
+
[[package]]
1135
+
name = "regex"
1136
+
version = "1.11.1"
1137
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1138
+
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
1139
+
dependencies = [
1140
+
"aho-corasick",
1141
+
"memchr",
1142
+
"regex-automata 0.4.9",
1143
+
"regex-syntax 0.8.5",
1144
+
]
1145
+
1146
+
[[package]]
1147
+
name = "regex-automata"
1148
+
version = "0.1.10"
1149
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1150
+
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
1151
+
dependencies = [
1152
+
"regex-syntax 0.6.29",
1153
+
]
1154
+
1155
+
[[package]]
1156
+
name = "regex-automata"
1157
+
version = "0.4.9"
1158
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1159
+
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
1160
+
dependencies = [
1161
+
"aho-corasick",
1162
+
"memchr",
1163
+
"regex-syntax 0.8.5",
1164
+
]
1165
+
1166
+
[[package]]
1167
+
name = "regex-syntax"
1168
+
version = "0.6.29"
1169
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1170
+
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
1171
+
1172
+
[[package]]
1173
+
name = "regex-syntax"
1174
+
version = "0.8.5"
1175
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1176
+
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
1177
+
1178
+
[[package]]
1179
+
name = "reqwest"
1180
+
version = "0.12.18"
1181
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1182
+
checksum = "e98ff6b0dbbe4d5a37318f433d4fc82babd21631f194d370409ceb2e40b2f0b5"
1183
+
dependencies = [
1184
+
"base64",
1185
+
"bytes",
1186
+
"encoding_rs",
1187
+
"futures-core",
1188
+
"h2",
1189
+
"http",
1190
+
"http-body",
1191
+
"http-body-util",
1192
+
"hyper",
1193
+
"hyper-rustls",
1194
+
"hyper-tls",
1195
+
"hyper-util",
1196
+
"ipnet",
1197
+
"js-sys",
1198
+
"log",
1199
+
"mime",
1200
+
"native-tls",
1201
+
"once_cell",
1202
+
"percent-encoding",
1203
+
"pin-project-lite",
1204
+
"quinn",
1205
+
"rustls",
1206
+
"rustls-pki-types",
1207
+
"serde",
1208
+
"serde_json",
1209
+
"serde_urlencoded",
1210
+
"sync_wrapper",
1211
+
"tokio",
1212
+
"tokio-native-tls",
1213
+
"tokio-rustls",
1214
+
"tower",
1215
+
"tower-http",
1216
+
"tower-service",
1217
+
"url",
1218
+
"wasm-bindgen",
1219
+
"wasm-bindgen-futures",
1220
+
"web-sys",
1221
+
"webpki-roots",
1222
+
]
1223
+
1224
+
[[package]]
1225
+
name = "resolv-conf"
1226
+
version = "0.7.4"
1227
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1228
+
checksum = "95325155c684b1c89f7765e30bc1c42e4a6da51ca513615660cb8a62ef9a88e3"
1229
+
1230
+
[[package]]
1231
+
name = "ring"
1232
+
version = "0.17.14"
1233
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1234
+
checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
1235
+
dependencies = [
1236
+
"cc",
1237
+
"cfg-if",
1238
+
"getrandom 0.2.16",
1239
+
"libc",
1240
+
"untrusted",
1241
+
"windows-sys 0.52.0",
1242
+
]
1243
+
1244
+
[[package]]
1245
+
name = "rustc-demangle"
1246
+
version = "0.1.24"
1247
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1248
+
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
1249
+
1250
+
[[package]]
1251
+
name = "rustc-hash"
1252
+
version = "2.1.1"
1253
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1254
+
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
1255
+
1256
+
[[package]]
1257
+
name = "rustc_version"
1258
+
version = "0.4.1"
1259
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1260
+
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
1261
+
dependencies = [
1262
+
"semver",
1263
+
]
1264
+
1265
+
[[package]]
1266
+
name = "rustix"
1267
+
version = "1.0.7"
1268
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1269
+
checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
1270
+
dependencies = [
1271
+
"bitflags",
1272
+
"errno",
1273
+
"libc",
1274
+
"linux-raw-sys",
1275
+
"windows-sys 0.59.0",
1276
+
]
1277
+
1278
+
[[package]]
1279
+
name = "rustls"
1280
+
version = "0.23.27"
1281
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1282
+
checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321"
1283
+
dependencies = [
1284
+
"once_cell",
1285
+
"ring",
1286
+
"rustls-pki-types",
1287
+
"rustls-webpki",
1288
+
"subtle",
1289
+
"zeroize",
1290
+
]
1291
+
1292
+
[[package]]
1293
+
name = "rustls-pki-types"
1294
+
version = "1.12.0"
1295
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1296
+
checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79"
1297
+
dependencies = [
1298
+
"web-time",
1299
+
"zeroize",
1300
+
]
1301
+
1302
+
[[package]]
1303
+
name = "rustls-webpki"
1304
+
version = "0.103.3"
1305
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1306
+
checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435"
1307
+
dependencies = [
1308
+
"ring",
1309
+
"rustls-pki-types",
1310
+
"untrusted",
1311
+
]
1312
+
1313
+
[[package]]
1314
+
name = "rustversion"
1315
+
version = "1.0.21"
1316
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1317
+
checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
1318
+
1319
+
[[package]]
1320
+
name = "ryu"
1321
+
version = "1.0.20"
1322
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1323
+
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
1324
+
1325
+
[[package]]
1326
+
name = "schannel"
1327
+
version = "0.1.27"
1328
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1329
+
checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d"
1330
+
dependencies = [
1331
+
"windows-sys 0.59.0",
1332
+
]
1333
+
1334
+
[[package]]
1335
+
name = "scoped-tls"
1336
+
version = "1.0.1"
1337
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1338
+
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
1339
+
1340
+
[[package]]
1341
+
name = "scopeguard"
1342
+
version = "1.2.0"
1343
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1344
+
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
1345
+
1346
+
[[package]]
1347
+
name = "security-framework"
1348
+
version = "2.11.1"
1349
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1350
+
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
1351
+
dependencies = [
1352
+
"bitflags",
1353
+
"core-foundation",
1354
+
"core-foundation-sys",
1355
+
"libc",
1356
+
"security-framework-sys",
1357
+
]
1358
+
1359
+
[[package]]
1360
+
name = "security-framework-sys"
1361
+
version = "2.14.0"
1362
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1363
+
checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32"
1364
+
dependencies = [
1365
+
"core-foundation-sys",
1366
+
"libc",
1367
+
]
1368
+
1369
+
[[package]]
1370
+
name = "semver"
1371
+
version = "1.0.26"
1372
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1373
+
checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
1374
+
1375
+
[[package]]
1376
+
name = "serde"
1377
+
version = "1.0.219"
1378
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1379
+
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
1380
+
dependencies = [
1381
+
"serde_derive",
1382
+
]
1383
+
1384
+
[[package]]
1385
+
name = "serde_derive"
1386
+
version = "1.0.219"
1387
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1388
+
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
1389
+
dependencies = [
1390
+
"proc-macro2",
1391
+
"quote",
1392
+
"syn",
1393
+
]
1394
+
1395
+
[[package]]
1396
+
name = "serde_json"
1397
+
version = "1.0.140"
1398
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1399
+
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
1400
+
dependencies = [
1401
+
"itoa",
1402
+
"memchr",
1403
+
"ryu",
1404
+
"serde",
1405
+
]
1406
+
1407
+
[[package]]
1408
+
name = "serde_urlencoded"
1409
+
version = "0.7.1"
1410
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1411
+
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
1412
+
dependencies = [
1413
+
"form_urlencoded",
1414
+
"itoa",
1415
+
"ryu",
1416
+
"serde",
1417
+
]
1418
+
1419
+
[[package]]
1420
+
name = "sharded-slab"
1421
+
version = "0.1.7"
1422
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1423
+
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
1424
+
dependencies = [
1425
+
"lazy_static",
1426
+
]
1427
+
1428
+
[[package]]
1429
+
name = "shlex"
1430
+
version = "1.3.0"
1431
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1432
+
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
1433
+
1434
+
[[package]]
1435
+
name = "slab"
1436
+
version = "0.4.9"
1437
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1438
+
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
1439
+
dependencies = [
1440
+
"autocfg",
1441
+
]
1442
+
1443
+
[[package]]
1444
+
name = "smallvec"
1445
+
version = "1.15.0"
1446
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1447
+
checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9"
1448
+
1449
+
[[package]]
1450
+
name = "socket2"
1451
+
version = "0.5.10"
1452
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1453
+
checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678"
1454
+
dependencies = [
1455
+
"libc",
1456
+
"windows-sys 0.52.0",
1457
+
]
1458
+
1459
+
[[package]]
1460
+
name = "stable_deref_trait"
1461
+
version = "1.2.0"
1462
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1463
+
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
1464
+
1465
+
[[package]]
1466
+
name = "subtle"
1467
+
version = "2.6.1"
1468
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1469
+
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
1470
+
1471
+
[[package]]
1472
+
name = "syn"
1473
+
version = "2.0.101"
1474
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1475
+
checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
1476
+
dependencies = [
1477
+
"proc-macro2",
1478
+
"quote",
1479
+
"unicode-ident",
1480
+
]
1481
+
1482
+
[[package]]
1483
+
name = "sync_wrapper"
1484
+
version = "1.0.2"
1485
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1486
+
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
1487
+
dependencies = [
1488
+
"futures-core",
1489
+
]
1490
+
1491
+
[[package]]
1492
+
name = "synstructure"
1493
+
version = "0.13.2"
1494
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1495
+
checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
1496
+
dependencies = [
1497
+
"proc-macro2",
1498
+
"quote",
1499
+
"syn",
1500
+
]
1501
+
1502
+
[[package]]
1503
+
name = "system-configuration"
1504
+
version = "0.6.1"
1505
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1506
+
checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
1507
+
dependencies = [
1508
+
"bitflags",
1509
+
"core-foundation",
1510
+
"system-configuration-sys",
1511
+
]
1512
+
1513
+
[[package]]
1514
+
name = "system-configuration-sys"
1515
+
version = "0.6.0"
1516
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1517
+
checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
1518
+
dependencies = [
1519
+
"core-foundation-sys",
1520
+
"libc",
1521
+
]
1522
+
1523
+
[[package]]
1524
+
name = "tagptr"
1525
+
version = "0.2.0"
1526
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1527
+
checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417"
1528
+
1529
+
[[package]]
1530
+
name = "tempfile"
1531
+
version = "3.20.0"
1532
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1533
+
checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1"
1534
+
dependencies = [
1535
+
"fastrand",
1536
+
"getrandom 0.3.3",
1537
+
"once_cell",
1538
+
"rustix",
1539
+
"windows-sys 0.59.0",
1540
+
]
1541
+
1542
+
[[package]]
1543
+
name = "thiserror"
1544
+
version = "1.0.69"
1545
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1546
+
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
1547
+
dependencies = [
1548
+
"thiserror-impl 1.0.69",
1549
+
]
1550
+
1551
+
[[package]]
1552
+
name = "thiserror"
1553
+
version = "2.0.12"
1554
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1555
+
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
1556
+
dependencies = [
1557
+
"thiserror-impl 2.0.12",
1558
+
]
1559
+
1560
+
[[package]]
1561
+
name = "thiserror-impl"
1562
+
version = "1.0.69"
1563
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1564
+
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
1565
+
dependencies = [
1566
+
"proc-macro2",
1567
+
"quote",
1568
+
"syn",
1569
+
]
1570
+
1571
+
[[package]]
1572
+
name = "thiserror-impl"
1573
+
version = "2.0.12"
1574
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1575
+
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
1576
+
dependencies = [
1577
+
"proc-macro2",
1578
+
"quote",
1579
+
"syn",
1580
+
]
1581
+
1582
+
[[package]]
1583
+
name = "thread_local"
1584
+
version = "1.1.8"
1585
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1586
+
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
1587
+
dependencies = [
1588
+
"cfg-if",
1589
+
"once_cell",
1590
+
]
1591
+
1592
+
[[package]]
1593
+
name = "tinystr"
1594
+
version = "0.8.1"
1595
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1596
+
checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b"
1597
+
dependencies = [
1598
+
"displaydoc",
1599
+
"zerovec",
1600
+
]
1601
+
1602
+
[[package]]
1603
+
name = "tinyvec"
1604
+
version = "1.9.0"
1605
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1606
+
checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71"
1607
+
dependencies = [
1608
+
"tinyvec_macros",
1609
+
]
1610
+
1611
+
[[package]]
1612
+
name = "tinyvec_macros"
1613
+
version = "0.1.1"
1614
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1615
+
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
1616
+
1617
+
[[package]]
1618
+
name = "tokio"
1619
+
version = "1.45.1"
1620
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1621
+
checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779"
1622
+
dependencies = [
1623
+
"backtrace",
1624
+
"bytes",
1625
+
"libc",
1626
+
"mio",
1627
+
"pin-project-lite",
1628
+
"socket2",
1629
+
"tokio-macros",
1630
+
"windows-sys 0.52.0",
1631
+
]
1632
+
1633
+
[[package]]
1634
+
name = "tokio-macros"
1635
+
version = "2.5.0"
1636
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1637
+
checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
1638
+
dependencies = [
1639
+
"proc-macro2",
1640
+
"quote",
1641
+
"syn",
1642
+
]
1643
+
1644
+
[[package]]
1645
+
name = "tokio-native-tls"
1646
+
version = "0.3.1"
1647
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1648
+
checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
1649
+
dependencies = [
1650
+
"native-tls",
1651
+
"tokio",
1652
+
]
1653
+
1654
+
[[package]]
1655
+
name = "tokio-rustls"
1656
+
version = "0.26.2"
1657
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1658
+
checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b"
1659
+
dependencies = [
1660
+
"rustls",
1661
+
"tokio",
1662
+
]
1663
+
1664
+
[[package]]
1665
+
name = "tokio-util"
1666
+
version = "0.7.15"
1667
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1668
+
checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df"
1669
+
dependencies = [
1670
+
"bytes",
1671
+
"futures-core",
1672
+
"futures-sink",
1673
+
"pin-project-lite",
1674
+
"tokio",
1675
+
]
1676
+
1677
+
[[package]]
1678
+
name = "tower"
1679
+
version = "0.5.2"
1680
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1681
+
checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9"
1682
+
dependencies = [
1683
+
"futures-core",
1684
+
"futures-util",
1685
+
"pin-project-lite",
1686
+
"sync_wrapper",
1687
+
"tokio",
1688
+
"tower-layer",
1689
+
"tower-service",
1690
+
]
1691
+
1692
+
[[package]]
1693
+
name = "tower-http"
1694
+
version = "0.6.4"
1695
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1696
+
checksum = "0fdb0c213ca27a9f57ab69ddb290fd80d970922355b83ae380b395d3986b8a2e"
1697
+
dependencies = [
1698
+
"bitflags",
1699
+
"bytes",
1700
+
"futures-util",
1701
+
"http",
1702
+
"http-body",
1703
+
"iri-string",
1704
+
"pin-project-lite",
1705
+
"tower",
1706
+
"tower-layer",
1707
+
"tower-service",
1708
+
]
1709
+
1710
+
[[package]]
1711
+
name = "tower-layer"
1712
+
version = "0.3.3"
1713
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1714
+
checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
1715
+
1716
+
[[package]]
1717
+
name = "tower-service"
1718
+
version = "0.3.3"
1719
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1720
+
checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
1721
+
1722
+
[[package]]
1723
+
name = "tracing"
1724
+
version = "0.1.41"
1725
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1726
+
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
1727
+
dependencies = [
1728
+
"pin-project-lite",
1729
+
"tracing-attributes",
1730
+
"tracing-core",
1731
+
]
1732
+
1733
+
[[package]]
1734
+
name = "tracing-attributes"
1735
+
version = "0.1.28"
1736
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1737
+
checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
1738
+
dependencies = [
1739
+
"proc-macro2",
1740
+
"quote",
1741
+
"syn",
1742
+
]
1743
+
1744
+
[[package]]
1745
+
name = "tracing-core"
1746
+
version = "0.1.33"
1747
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1748
+
checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
1749
+
dependencies = [
1750
+
"once_cell",
1751
+
"valuable",
1752
+
]
1753
+
1754
+
[[package]]
1755
+
name = "tracing-log"
1756
+
version = "0.2.0"
1757
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1758
+
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
1759
+
dependencies = [
1760
+
"log",
1761
+
"once_cell",
1762
+
"tracing-core",
1763
+
]
1764
+
1765
+
[[package]]
1766
+
name = "tracing-subscriber"
1767
+
version = "0.3.19"
1768
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1769
+
checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
1770
+
dependencies = [
1771
+
"matchers",
1772
+
"nu-ansi-term",
1773
+
"once_cell",
1774
+
"regex",
1775
+
"sharded-slab",
1776
+
"smallvec",
1777
+
"thread_local",
1778
+
"tracing",
1779
+
"tracing-core",
1780
+
"tracing-log",
1781
+
]
1782
+
1783
+
[[package]]
1784
+
name = "try-lock"
1785
+
version = "0.2.5"
1786
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1787
+
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
1788
+
1789
+
[[package]]
1790
+
name = "unicode-ident"
1791
+
version = "1.0.18"
1792
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1793
+
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
1794
+
1795
+
[[package]]
1796
+
name = "untrusted"
1797
+
version = "0.9.0"
1798
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1799
+
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
1800
+
1801
+
[[package]]
1802
+
name = "url"
1803
+
version = "2.5.4"
1804
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1805
+
checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
1806
+
dependencies = [
1807
+
"form_urlencoded",
1808
+
"idna",
1809
+
"percent-encoding",
1810
+
]
1811
+
1812
+
[[package]]
1813
+
name = "utf8_iter"
1814
+
version = "1.0.4"
1815
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1816
+
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
1817
+
1818
+
[[package]]
1819
+
name = "uuid"
1820
+
version = "1.17.0"
1821
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1822
+
checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d"
1823
+
dependencies = [
1824
+
"getrandom 0.3.3",
1825
+
"js-sys",
1826
+
"wasm-bindgen",
1827
+
]
1828
+
1829
+
[[package]]
1830
+
name = "valuable"
1831
+
version = "0.1.1"
1832
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1833
+
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
1834
+
1835
+
[[package]]
1836
+
name = "vcpkg"
1837
+
version = "0.2.15"
1838
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1839
+
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
1840
+
1841
+
[[package]]
1842
+
name = "want"
1843
+
version = "0.3.1"
1844
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1845
+
checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
1846
+
dependencies = [
1847
+
"try-lock",
1848
+
]
1849
+
1850
+
[[package]]
1851
+
name = "wasi"
1852
+
version = "0.11.0+wasi-snapshot-preview1"
1853
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1854
+
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
1855
+
1856
+
[[package]]
1857
+
name = "wasi"
1858
+
version = "0.14.2+wasi-0.2.4"
1859
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1860
+
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
1861
+
dependencies = [
1862
+
"wit-bindgen-rt",
1863
+
]
1864
+
1865
+
[[package]]
1866
+
name = "wasm-bindgen"
1867
+
version = "0.2.100"
1868
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1869
+
checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
1870
+
dependencies = [
1871
+
"cfg-if",
1872
+
"once_cell",
1873
+
"rustversion",
1874
+
"wasm-bindgen-macro",
1875
+
]
1876
+
1877
+
[[package]]
1878
+
name = "wasm-bindgen-backend"
1879
+
version = "0.2.100"
1880
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1881
+
checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
1882
+
dependencies = [
1883
+
"bumpalo",
1884
+
"log",
1885
+
"proc-macro2",
1886
+
"quote",
1887
+
"syn",
1888
+
"wasm-bindgen-shared",
1889
+
]
1890
+
1891
+
[[package]]
1892
+
name = "wasm-bindgen-futures"
1893
+
version = "0.4.50"
1894
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1895
+
checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61"
1896
+
dependencies = [
1897
+
"cfg-if",
1898
+
"js-sys",
1899
+
"once_cell",
1900
+
"wasm-bindgen",
1901
+
"web-sys",
1902
+
]
1903
+
1904
+
[[package]]
1905
+
name = "wasm-bindgen-macro"
1906
+
version = "0.2.100"
1907
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1908
+
checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
1909
+
dependencies = [
1910
+
"quote",
1911
+
"wasm-bindgen-macro-support",
1912
+
]
1913
+
1914
+
[[package]]
1915
+
name = "wasm-bindgen-macro-support"
1916
+
version = "0.2.100"
1917
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1918
+
checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
1919
+
dependencies = [
1920
+
"proc-macro2",
1921
+
"quote",
1922
+
"syn",
1923
+
"wasm-bindgen-backend",
1924
+
"wasm-bindgen-shared",
1925
+
]
1926
+
1927
+
[[package]]
1928
+
name = "wasm-bindgen-shared"
1929
+
version = "0.2.100"
1930
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1931
+
checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
1932
+
dependencies = [
1933
+
"unicode-ident",
1934
+
]
1935
+
1936
+
[[package]]
1937
+
name = "web-sys"
1938
+
version = "0.3.77"
1939
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1940
+
checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2"
1941
+
dependencies = [
1942
+
"js-sys",
1943
+
"wasm-bindgen",
1944
+
]
1945
+
1946
+
[[package]]
1947
+
name = "web-time"
1948
+
version = "1.1.0"
1949
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1950
+
checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
1951
+
dependencies = [
1952
+
"js-sys",
1953
+
"wasm-bindgen",
1954
+
]
1955
+
1956
+
[[package]]
1957
+
name = "webpki-roots"
1958
+
version = "1.0.0"
1959
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1960
+
checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb"
1961
+
dependencies = [
1962
+
"rustls-pki-types",
1963
+
]
1964
+
1965
+
[[package]]
1966
+
name = "widestring"
1967
+
version = "1.2.0"
1968
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1969
+
checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d"
1970
+
1971
+
[[package]]
1972
+
name = "winapi"
1973
+
version = "0.3.9"
1974
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1975
+
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
1976
+
dependencies = [
1977
+
"winapi-i686-pc-windows-gnu",
1978
+
"winapi-x86_64-pc-windows-gnu",
1979
+
]
1980
+
1981
+
[[package]]
1982
+
name = "winapi-i686-pc-windows-gnu"
1983
+
version = "0.4.0"
1984
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1985
+
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
1986
+
1987
+
[[package]]
1988
+
name = "winapi-x86_64-pc-windows-gnu"
1989
+
version = "0.4.0"
1990
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1991
+
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
1992
+
1993
+
[[package]]
1994
+
name = "windows"
1995
+
version = "0.61.1"
1996
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1997
+
checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419"
1998
+
dependencies = [
1999
+
"windows-collections",
2000
+
"windows-core",
2001
+
"windows-future",
2002
+
"windows-link",
2003
+
"windows-numerics",
2004
+
]
2005
+
2006
+
[[package]]
2007
+
name = "windows-collections"
2008
+
version = "0.2.0"
2009
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2010
+
checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
2011
+
dependencies = [
2012
+
"windows-core",
2013
+
]
2014
+
2015
+
[[package]]
2016
+
name = "windows-core"
2017
+
version = "0.61.2"
2018
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2019
+
checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
2020
+
dependencies = [
2021
+
"windows-implement",
2022
+
"windows-interface",
2023
+
"windows-link",
2024
+
"windows-result",
2025
+
"windows-strings 0.4.2",
2026
+
]
2027
+
2028
+
[[package]]
2029
+
name = "windows-future"
2030
+
version = "0.2.1"
2031
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2032
+
checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e"
2033
+
dependencies = [
2034
+
"windows-core",
2035
+
"windows-link",
2036
+
"windows-threading",
2037
+
]
2038
+
2039
+
[[package]]
2040
+
name = "windows-implement"
2041
+
version = "0.60.0"
2042
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2043
+
checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
2044
+
dependencies = [
2045
+
"proc-macro2",
2046
+
"quote",
2047
+
"syn",
2048
+
]
2049
+
2050
+
[[package]]
2051
+
name = "windows-interface"
2052
+
version = "0.59.1"
2053
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2054
+
checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
2055
+
dependencies = [
2056
+
"proc-macro2",
2057
+
"quote",
2058
+
"syn",
2059
+
]
2060
+
2061
+
[[package]]
2062
+
name = "windows-link"
2063
+
version = "0.1.1"
2064
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2065
+
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
2066
+
2067
+
[[package]]
2068
+
name = "windows-numerics"
2069
+
version = "0.2.0"
2070
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2071
+
checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
2072
+
dependencies = [
2073
+
"windows-core",
2074
+
"windows-link",
2075
+
]
2076
+
2077
+
[[package]]
2078
+
name = "windows-registry"
2079
+
version = "0.4.0"
2080
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2081
+
checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3"
2082
+
dependencies = [
2083
+
"windows-result",
2084
+
"windows-strings 0.3.1",
2085
+
"windows-targets 0.53.0",
2086
+
]
2087
+
2088
+
[[package]]
2089
+
name = "windows-result"
2090
+
version = "0.3.4"
2091
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2092
+
checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
2093
+
dependencies = [
2094
+
"windows-link",
2095
+
]
2096
+
2097
+
[[package]]
2098
+
name = "windows-strings"
2099
+
version = "0.3.1"
2100
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2101
+
checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319"
2102
+
dependencies = [
2103
+
"windows-link",
2104
+
]
2105
+
2106
+
[[package]]
2107
+
name = "windows-strings"
2108
+
version = "0.4.2"
2109
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2110
+
checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
2111
+
dependencies = [
2112
+
"windows-link",
2113
+
]
2114
+
2115
+
[[package]]
2116
+
name = "windows-sys"
2117
+
version = "0.48.0"
2118
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2119
+
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
2120
+
dependencies = [
2121
+
"windows-targets 0.48.5",
2122
+
]
2123
+
2124
+
[[package]]
2125
+
name = "windows-sys"
2126
+
version = "0.52.0"
2127
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2128
+
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
2129
+
dependencies = [
2130
+
"windows-targets 0.52.6",
2131
+
]
2132
+
2133
+
[[package]]
2134
+
name = "windows-sys"
2135
+
version = "0.59.0"
2136
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2137
+
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
2138
+
dependencies = [
2139
+
"windows-targets 0.52.6",
2140
+
]
2141
+
2142
+
[[package]]
2143
+
name = "windows-targets"
2144
+
version = "0.48.5"
2145
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2146
+
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
2147
+
dependencies = [
2148
+
"windows_aarch64_gnullvm 0.48.5",
2149
+
"windows_aarch64_msvc 0.48.5",
2150
+
"windows_i686_gnu 0.48.5",
2151
+
"windows_i686_msvc 0.48.5",
2152
+
"windows_x86_64_gnu 0.48.5",
2153
+
"windows_x86_64_gnullvm 0.48.5",
2154
+
"windows_x86_64_msvc 0.48.5",
2155
+
]
2156
+
2157
+
[[package]]
2158
+
name = "windows-targets"
2159
+
version = "0.52.6"
2160
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2161
+
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
2162
+
dependencies = [
2163
+
"windows_aarch64_gnullvm 0.52.6",
2164
+
"windows_aarch64_msvc 0.52.6",
2165
+
"windows_i686_gnu 0.52.6",
2166
+
"windows_i686_gnullvm 0.52.6",
2167
+
"windows_i686_msvc 0.52.6",
2168
+
"windows_x86_64_gnu 0.52.6",
2169
+
"windows_x86_64_gnullvm 0.52.6",
2170
+
"windows_x86_64_msvc 0.52.6",
2171
+
]
2172
+
2173
+
[[package]]
2174
+
name = "windows-targets"
2175
+
version = "0.53.0"
2176
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2177
+
checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b"
2178
+
dependencies = [
2179
+
"windows_aarch64_gnullvm 0.53.0",
2180
+
"windows_aarch64_msvc 0.53.0",
2181
+
"windows_i686_gnu 0.53.0",
2182
+
"windows_i686_gnullvm 0.53.0",
2183
+
"windows_i686_msvc 0.53.0",
2184
+
"windows_x86_64_gnu 0.53.0",
2185
+
"windows_x86_64_gnullvm 0.53.0",
2186
+
"windows_x86_64_msvc 0.53.0",
2187
+
]
2188
+
2189
+
[[package]]
2190
+
name = "windows-threading"
2191
+
version = "0.1.0"
2192
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2193
+
checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6"
2194
+
dependencies = [
2195
+
"windows-link",
2196
+
]
2197
+
2198
+
[[package]]
2199
+
name = "windows_aarch64_gnullvm"
2200
+
version = "0.48.5"
2201
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2202
+
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
2203
+
2204
+
[[package]]
2205
+
name = "windows_aarch64_gnullvm"
2206
+
version = "0.52.6"
2207
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2208
+
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
2209
+
2210
+
[[package]]
2211
+
name = "windows_aarch64_gnullvm"
2212
+
version = "0.53.0"
2213
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2214
+
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
2215
+
2216
+
[[package]]
2217
+
name = "windows_aarch64_msvc"
2218
+
version = "0.48.5"
2219
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2220
+
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
2221
+
2222
+
[[package]]
2223
+
name = "windows_aarch64_msvc"
2224
+
version = "0.52.6"
2225
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2226
+
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
2227
+
2228
+
[[package]]
2229
+
name = "windows_aarch64_msvc"
2230
+
version = "0.53.0"
2231
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2232
+
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
2233
+
2234
+
[[package]]
2235
+
name = "windows_i686_gnu"
2236
+
version = "0.48.5"
2237
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2238
+
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
2239
+
2240
+
[[package]]
2241
+
name = "windows_i686_gnu"
2242
+
version = "0.52.6"
2243
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2244
+
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
2245
+
2246
+
[[package]]
2247
+
name = "windows_i686_gnu"
2248
+
version = "0.53.0"
2249
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2250
+
checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
2251
+
2252
+
[[package]]
2253
+
name = "windows_i686_gnullvm"
2254
+
version = "0.52.6"
2255
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2256
+
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
2257
+
2258
+
[[package]]
2259
+
name = "windows_i686_gnullvm"
2260
+
version = "0.53.0"
2261
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2262
+
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
2263
+
2264
+
[[package]]
2265
+
name = "windows_i686_msvc"
2266
+
version = "0.48.5"
2267
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2268
+
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
2269
+
2270
+
[[package]]
2271
+
name = "windows_i686_msvc"
2272
+
version = "0.52.6"
2273
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2274
+
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
2275
+
2276
+
[[package]]
2277
+
name = "windows_i686_msvc"
2278
+
version = "0.53.0"
2279
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2280
+
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
2281
+
2282
+
[[package]]
2283
+
name = "windows_x86_64_gnu"
2284
+
version = "0.48.5"
2285
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2286
+
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
2287
+
2288
+
[[package]]
2289
+
name = "windows_x86_64_gnu"
2290
+
version = "0.52.6"
2291
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2292
+
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
2293
+
2294
+
[[package]]
2295
+
name = "windows_x86_64_gnu"
2296
+
version = "0.53.0"
2297
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2298
+
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
2299
+
2300
+
[[package]]
2301
+
name = "windows_x86_64_gnullvm"
2302
+
version = "0.48.5"
2303
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2304
+
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
2305
+
2306
+
[[package]]
2307
+
name = "windows_x86_64_gnullvm"
2308
+
version = "0.52.6"
2309
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2310
+
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
2311
+
2312
+
[[package]]
2313
+
name = "windows_x86_64_gnullvm"
2314
+
version = "0.53.0"
2315
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2316
+
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
2317
+
2318
+
[[package]]
2319
+
name = "windows_x86_64_msvc"
2320
+
version = "0.48.5"
2321
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2322
+
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
2323
+
2324
+
[[package]]
2325
+
name = "windows_x86_64_msvc"
2326
+
version = "0.52.6"
2327
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2328
+
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
2329
+
2330
+
[[package]]
2331
+
name = "windows_x86_64_msvc"
2332
+
version = "0.53.0"
2333
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2334
+
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
2335
+
2336
+
[[package]]
2337
+
name = "winreg"
2338
+
version = "0.50.0"
2339
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2340
+
checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
2341
+
dependencies = [
2342
+
"cfg-if",
2343
+
"windows-sys 0.48.0",
2344
+
]
2345
+
2346
+
[[package]]
2347
+
name = "wit-bindgen-rt"
2348
+
version = "0.39.0"
2349
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2350
+
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
2351
+
dependencies = [
2352
+
"bitflags",
2353
+
]
2354
+
2355
+
[[package]]
2356
+
name = "writeable"
2357
+
version = "0.6.1"
2358
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2359
+
checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
2360
+
2361
+
[[package]]
2362
+
name = "yoke"
2363
+
version = "0.8.0"
2364
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2365
+
checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc"
2366
+
dependencies = [
2367
+
"serde",
2368
+
"stable_deref_trait",
2369
+
"yoke-derive",
2370
+
"zerofrom",
2371
+
]
2372
+
2373
+
[[package]]
2374
+
name = "yoke-derive"
2375
+
version = "0.8.0"
2376
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2377
+
checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
2378
+
dependencies = [
2379
+
"proc-macro2",
2380
+
"quote",
2381
+
"syn",
2382
+
"synstructure",
2383
+
]
2384
+
2385
+
[[package]]
2386
+
name = "zerocopy"
2387
+
version = "0.8.25"
2388
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2389
+
checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb"
2390
+
dependencies = [
2391
+
"zerocopy-derive",
2392
+
]
2393
+
2394
+
[[package]]
2395
+
name = "zerocopy-derive"
2396
+
version = "0.8.25"
2397
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2398
+
checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef"
2399
+
dependencies = [
2400
+
"proc-macro2",
2401
+
"quote",
2402
+
"syn",
2403
+
]
2404
+
2405
+
[[package]]
2406
+
name = "zerofrom"
2407
+
version = "0.1.6"
2408
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2409
+
checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
2410
+
dependencies = [
2411
+
"zerofrom-derive",
2412
+
]
2413
+
2414
+
[[package]]
2415
+
name = "zerofrom-derive"
2416
+
version = "0.1.6"
2417
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2418
+
checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
2419
+
dependencies = [
2420
+
"proc-macro2",
2421
+
"quote",
2422
+
"syn",
2423
+
"synstructure",
2424
+
]
2425
+
2426
+
[[package]]
2427
+
name = "zeroize"
2428
+
version = "1.8.1"
2429
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2430
+
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
2431
+
2432
+
[[package]]
2433
+
name = "zerotrie"
2434
+
version = "0.2.2"
2435
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2436
+
checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595"
2437
+
dependencies = [
2438
+
"displaydoc",
2439
+
"yoke",
2440
+
"zerofrom",
2441
+
]
2442
+
2443
+
[[package]]
2444
+
name = "zerovec"
2445
+
version = "0.11.2"
2446
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2447
+
checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428"
2448
+
dependencies = [
2449
+
"yoke",
2450
+
"zerofrom",
2451
+
"zerovec-derive",
2452
+
]
2453
+
2454
+
[[package]]
2455
+
name = "zerovec-derive"
2456
+
version = "0.11.1"
2457
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2458
+
checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
2459
+
dependencies = [
2460
+
"proc-macro2",
2461
+
"quote",
2462
+
"syn",
2463
+
]
+26
Cargo.toml
+26
Cargo.toml
···
1
+
[package]
2
+
name = "atproto-identity"
3
+
version = "0.1.0"
4
+
edition = "2021"
5
+
rust-version = "1.83"
6
+
authors = ["Nick Gerakines <nick.gerakines@gmail.com>"]
7
+
description = "An ATProtocol identity library"
8
+
readme = "README.md"
9
+
repository = "https://tangled.sh/@smokesignal.events/atproto-identity-rs"
10
+
license = "MIT"
11
+
12
+
[[bin]]
13
+
name = "atproto-identity-resolve"
14
+
test = false
15
+
bench = false
16
+
17
+
[dependencies]
18
+
anyhow = "1.0"
19
+
futures-util = "0.3"
20
+
serde = { version = "1.0", features = ["derive"] }
21
+
serde_json = "1.0"
22
+
thiserror = "2.0"
23
+
hickory-resolver = { version = "0.25" }
24
+
reqwest = { version = "0.12", features = ["json", "rustls-tls"] }
25
+
tracing = { version = "0.1", features = ["async-await"] }
26
+
tokio = { version = "1.41", features = ["macros", "rt", "rt-multi-thread"] }
+170
README.md
+170
README.md
···
1
+
# atproto-identity
2
+
3
+
A Rust library for AT Protocol identity resolution and management.
4
+
5
+
## Overview
6
+
7
+
`atproto-identity` provides comprehensive support for resolving and managing identities in the AT Protocol ecosystem. This library handles multiple DID (Decentralized Identifier) methods including `did:plc` and `did:web`, as well as AT Protocol handle resolution via both DNS and HTTP methods.
8
+
9
+
This project was extracted from the open-sourced [Smokesignal](https://tangled.sh/@smokesignal.events/smokesignal) project and is designed to be a standalone, reusable library for AT Protocol identity operations.
10
+
11
+
## Features
12
+
13
+
- **Handle Resolution**: Resolve AT Protocol handles to DIDs using DNS TXT records and HTTP well-known endpoints
14
+
- **DID Document Retrieval**: Fetch and parse DID documents for `did:plc` and `did:web` identifiers
15
+
- **Multiple Resolution Methods**: Supports both DNS and HTTP-based handle resolution with conflict detection
16
+
- **Configurable DNS**: Custom DNS nameserver support with fallback to system defaults
17
+
- **Structured Logging**: Built-in tracing support for debugging and monitoring
18
+
- **Type Safety**: Comprehensive error handling with structured error types
19
+
20
+
## Supported DID Methods
21
+
22
+
- **did-method-plc**: Public Ledger of Credentials DIDs via PLC directory
23
+
- **did-method-web**: Web-based DIDs following the did:web specification with URL conversion utilities
24
+
- **ATProtocol Handle Resolution**: AT Protocol handles (e.g., `ngerakines.me`) can be resolved to DIDs
25
+
26
+
## Installation
27
+
28
+
Add this to your `Cargo.toml`:
29
+
30
+
```toml
31
+
[dependencies]
32
+
atproto-identity = "0.1.0"
33
+
```
34
+
35
+
## Usage
36
+
37
+
### Basic Handle Resolution
38
+
39
+
```rust
40
+
use atproto_identity::resolve::{resolve_subject, create_resolver};
41
+
42
+
#[tokio::main]
43
+
async fn main() -> anyhow::Result<()> {
44
+
let http_client = reqwest::Client::new();
45
+
let dns_resolver = create_resolver(&[]);
46
+
47
+
let did = resolve_subject(&http_client, &dns_resolver, "ngerakines.me").await?;
48
+
println!("Resolved DID: {}", did);
49
+
50
+
Ok(())
51
+
}
52
+
```
53
+
54
+
### DID Document Retrieval
55
+
56
+
```rust
57
+
use atproto_identity::{plc, web};
58
+
59
+
#[tokio::main]
60
+
async fn main() -> anyhow::Result<()> {
61
+
let http_client = reqwest::Client::new();
62
+
63
+
// Query PLC DID document
64
+
let plc_doc = plc::query(&http_client, "plc.directory", "did:plc:example123").await?;
65
+
66
+
// Query Web DID document
67
+
let web_doc = web::query(&http_client, "did:web:example.com").await?;
68
+
69
+
// Convert Web DID to URL (for custom processing)
70
+
let did_url = web::did_web_to_url("did:web:example.com")?;
71
+
println!("DID document URL: {}", did_url);
72
+
73
+
Ok(())
74
+
}
75
+
```
76
+
77
+
### Web DID URL Conversion
78
+
79
+
The `web` module provides utilities for converting DID identifiers to their HTTPS document URLs:
80
+
81
+
```rust
82
+
use atproto_identity::web;
83
+
84
+
fn main() -> anyhow::Result<()> {
85
+
// Convert simple hostname DID
86
+
let url = web::did_web_to_url("did:web:example.com")?;
87
+
// Returns: "https://example.com/.well-known/did.json"
88
+
89
+
// Convert DID with path components
90
+
let url = web::did_web_to_url("did:web:example.com:path:subpath")?;
91
+
// Returns: "https://example.com/path/subpath/did.json"
92
+
93
+
Ok(())
94
+
}
95
+
```
96
+
97
+
### Configuration
98
+
99
+
The library supports various configuration options through environment variables:
100
+
101
+
```bash
102
+
# Custom PLC directory hostname
103
+
export PLC_HOSTNAME=plc.directory
104
+
105
+
# Custom DNS nameservers (semicolon-separated)
106
+
export DNS_NAMESERVERS=8.8.8.8;1.1.1.1
107
+
108
+
# Custom CA certificate bundles (semicolon-separated paths)
109
+
export CERTIFICATE_BUNDLES=/path/to/cert1.pem;/path/to/cert2.pem
110
+
111
+
# Custom User-Agent string
112
+
export USER_AGENT="my-app/1.0"
113
+
```
114
+
115
+
## Command Line Tool
116
+
117
+
The library includes a command-line tool for testing and resolution:
118
+
119
+
```bash
120
+
# Install the binary
121
+
cargo install --path .
122
+
123
+
# Resolve a handle to DID
124
+
atproto-identity-resolve ngerakines.me
125
+
126
+
# Get full DID document
127
+
atproto-identity-resolve --did-document ngerakines.me
128
+
```
129
+
130
+
## Architecture
131
+
132
+
The library is organized into several modules:
133
+
134
+
- **resolve**: Core resolution logic for handles and DIDs
135
+
- **plc**: PLC directory client for `did:plc` resolution
136
+
- **web**: Web DID client for `did:web` resolution and URL conversion
137
+
- **model**: Data structures for DID documents and AT Protocol entities
138
+
- **validation**: Input validation for handles and DIDs
139
+
- **config**: Configuration management and environment variable handling
140
+
- **errors**: Structured error types following project conventions
141
+
142
+
## Error Handling
143
+
144
+
All errors follow a structured format:
145
+
146
+
```
147
+
error-atproto-identity-<domain>-<number> <message>: <details>
148
+
```
149
+
150
+
Examples:
151
+
- `error-atproto-identity-resolve-1 Multiple DIDs resolved for method`
152
+
- `error-atproto-identity-plc-1 HTTP request failed: https://plc.directory/did:plc:example Not Found`
153
+
- `error-did-web-1 Invalid DID format: missing 'did:web:' prefix`
154
+
155
+
## Contributing
156
+
157
+
Contributions are welcome! Please ensure that:
158
+
159
+
1. All tests pass: `cargo test`
160
+
2. Code is properly formatted: `cargo fmt`
161
+
3. No linting issues: `cargo clippy`
162
+
4. New functionality includes appropriate tests
163
+
164
+
## License
165
+
166
+
This project is licensed under the MIT License. See the LICENSE file for details.
167
+
168
+
## Acknowledgments
169
+
170
+
This library was extracted from the [Smokesignal](https://tangled.sh/@smokesignal.events/smokesignal) project, an open-source event and RSVP management and discovery application.
+90
src/bin/atproto-identity-resolve.rs
+90
src/bin/atproto-identity-resolve.rs
···
1
+
use std::env;
2
+
3
+
use anyhow::Result;
4
+
use atproto_identity::{
5
+
config::{default_env, optional_env, version, CertificateBundles, DnsNameservers},
6
+
plc::query as plc_query,
7
+
resolve::{create_resolver, parse_input, resolve_subject, InputType},
8
+
web::query as web_query,
9
+
};
10
+
11
+
#[tokio::main]
12
+
async fn main() -> Result<()> {
13
+
let plc_hostname = default_env("PLC_HOSTNAME", "plc.directory");
14
+
let certificate_bundles: CertificateBundles = optional_env("CERTIFICATE_BUNDLES").try_into()?;
15
+
let default_user_agent = format!(
16
+
"atproto-identity-rs ({}; +https://tangled.sh/@smokesignal.events/atproto-identity-rs)",
17
+
version()?
18
+
);
19
+
let user_agent = default_env("USER_AGENT", &default_user_agent);
20
+
let dns_nameservers: DnsNameservers = optional_env("DNS_NAMESERVERS").try_into()?;
21
+
22
+
let mut client_builder = reqwest::Client::builder();
23
+
for ca_certificate in certificate_bundles.as_ref() {
24
+
let cert = std::fs::read(ca_certificate)?;
25
+
let cert = reqwest::Certificate::from_pem(&cert)?;
26
+
client_builder = client_builder.add_root_certificate(cert);
27
+
}
28
+
29
+
client_builder = client_builder.user_agent(user_agent);
30
+
let http_client = client_builder.build()?;
31
+
32
+
let dns_resolver = create_resolver(dns_nameservers.as_ref());
33
+
34
+
let mut subjects = vec![];
35
+
let mut get_did_document = false;
36
+
37
+
for subject in env::args().skip(1) {
38
+
match subject.as_str() {
39
+
"--did-document" => {
40
+
get_did_document = true;
41
+
}
42
+
_ => {
43
+
subjects.push(subject);
44
+
}
45
+
}
46
+
}
47
+
48
+
for subject in subjects {
49
+
let resolved_did = resolve_subject(&http_client, &dns_resolver, &subject).await;
50
+
let resolved_did = match resolved_did {
51
+
Ok(value) => {
52
+
println!("{value}");
53
+
value
54
+
}
55
+
Err(err) => {
56
+
eprintln!("{err}");
57
+
continue;
58
+
}
59
+
};
60
+
61
+
if !get_did_document {
62
+
continue;
63
+
}
64
+
65
+
let did_document = match parse_input(&resolved_did) {
66
+
Ok(InputType::Plc(did)) => plc_query(&http_client, &plc_hostname, &did).await,
67
+
Ok(InputType::Web(did)) => web_query(&http_client, &did).await,
68
+
Ok(InputType::Handle(_)) => {
69
+
eprintln!("error: subject resolved to handle");
70
+
continue;
71
+
}
72
+
Err(err) => {
73
+
eprintln!("{err}");
74
+
continue;
75
+
}
76
+
};
77
+
78
+
match did_document {
79
+
Ok(value) => {
80
+
println!("{value:?}")
81
+
}
82
+
Err(err) => {
83
+
eprintln!("{err}");
84
+
continue;
85
+
}
86
+
}
87
+
}
88
+
89
+
Ok(())
90
+
}
+95
src/config.rs
+95
src/config.rs
···
1
+
use crate::errors::ConfigError;
2
+
use anyhow::Result;
3
+
4
+
/// Certificate bundle paths for TLS verification.
5
+
/// Contains a collection of file paths to certificate bundles.
6
+
#[derive(Clone)]
7
+
pub struct CertificateBundles(Vec<String>);
8
+
9
+
/// DNS nameserver IP addresses for domain resolution.
10
+
/// Contains a collection of IP addresses for custom DNS resolution.
11
+
#[derive(Clone)]
12
+
pub struct DnsNameservers(Vec<std::net::IpAddr>);
13
+
14
+
/// Gets a required environment variable value.
15
+
/// Returns an error if the variable is not set.
16
+
pub fn require_env(name: &str) -> Result<String, ConfigError> {
17
+
std::env::var(name).map_err(|_| ConfigError::MissingEnvironmentVariable {
18
+
name: name.to_string(),
19
+
})
20
+
}
21
+
22
+
/// Gets an optional environment variable value.
23
+
/// Returns empty string if the variable is not set.
24
+
pub fn optional_env(name: &str) -> String {
25
+
std::env::var(name).unwrap_or("".to_string())
26
+
}
27
+
28
+
/// Gets an environment variable value with a fallback default.
29
+
/// Returns the default value if the variable is not set.
30
+
pub fn default_env(name: &str, default_value: &str) -> String {
31
+
std::env::var(name).unwrap_or(default_value.to_string())
32
+
}
33
+
34
+
/// Gets the application version from git hash or package version.
35
+
/// Returns the git hash if available, otherwise falls back to cargo package version.
36
+
pub fn version() -> Result<String, ConfigError> {
37
+
option_env!("GIT_HASH")
38
+
.or(option_env!("CARGO_PKG_VERSION"))
39
+
.map(|val| val.to_string())
40
+
.ok_or(ConfigError::VersionNotAvailable)
41
+
}
42
+
43
+
impl TryFrom<String> for CertificateBundles {
44
+
type Error = anyhow::Error;
45
+
fn try_from(value: String) -> Result<Self, Self::Error> {
46
+
Ok(Self(
47
+
value
48
+
.split(';')
49
+
.filter_map(|s| {
50
+
if s.is_empty() {
51
+
None
52
+
} else {
53
+
Some(s.to_string())
54
+
}
55
+
})
56
+
.collect::<Vec<String>>(),
57
+
))
58
+
}
59
+
}
60
+
impl AsRef<Vec<String>> for CertificateBundles {
61
+
fn as_ref(&self) -> &Vec<String> {
62
+
&self.0
63
+
}
64
+
}
65
+
66
+
impl TryFrom<String> for DnsNameservers {
67
+
type Error = anyhow::Error;
68
+
fn try_from(value: String) -> Result<Self, Self::Error> {
69
+
// Allow empty value for default DNS configuration
70
+
if value.is_empty() {
71
+
return Ok(Self(Vec::new()));
72
+
}
73
+
74
+
let nameservers = value
75
+
.split(';')
76
+
.map(|s| s.trim())
77
+
.filter(|s| !s.is_empty())
78
+
.map(|s| {
79
+
s.parse::<std::net::IpAddr>().map_err(|_| {
80
+
anyhow::Error::from(ConfigError::InvalidNameserverIP {
81
+
value: s.to_string(),
82
+
})
83
+
})
84
+
})
85
+
.collect::<Result<Vec<std::net::IpAddr>, _>>()?;
86
+
87
+
Ok(Self(nameservers))
88
+
}
89
+
}
90
+
91
+
impl AsRef<Vec<std::net::IpAddr>> for DnsNameservers {
92
+
fn as_ref(&self) -> &Vec<std::net::IpAddr> {
93
+
&self.0
94
+
}
95
+
}
+101
src/errors.rs
+101
src/errors.rs
···
1
+
use thiserror::Error;
2
+
3
+
/// Error types that can occur when working with Web DIDs
4
+
#[derive(Debug, Error)]
5
+
pub enum WebDIDError {
6
+
/// Occurs when the DID is missing the 'did:web:' prefix
7
+
#[error("error-did-web-1 Invalid DID format: missing 'did:web:' prefix")]
8
+
InvalidDIDPrefix,
9
+
10
+
/// Occurs when the DID is missing a hostname component
11
+
#[error("error-did-web-2 Invalid DID format: missing hostname component")]
12
+
MissingHostname,
13
+
14
+
/// Occurs when the HTTP request to fetch the DID document fails
15
+
#[error("error-did-web-3 HTTP request failed: {url} {error}")]
16
+
HttpRequestFailed {
17
+
/// The URL that was requested
18
+
url: String,
19
+
/// The underlying HTTP error
20
+
error: reqwest::Error,
21
+
},
22
+
23
+
/// Occurs when the DID document cannot be parsed from the HTTP response
24
+
#[error("error-did-web-4 Failed to parse DID document: {url} {error}")]
25
+
DocumentParseFailed {
26
+
/// The URL that was requested
27
+
url: String,
28
+
/// The underlying parse error
29
+
error: reqwest::Error,
30
+
},
31
+
}
32
+
33
+
/// Error types that can occur when working with configuration
34
+
#[derive(Debug, Error)]
35
+
pub enum ConfigError {
36
+
/// Occurs when a required environment variable is not set
37
+
#[error("error-atproto-identity-config-1 Required environment variable not found: {name}")]
38
+
MissingEnvironmentVariable { name: String },
39
+
40
+
/// Occurs when parsing an IP address from nameserver configuration fails
41
+
#[error("error-atproto-identity-config-2 Unable to parse nameserver IP: {value}")]
42
+
InvalidNameserverIP { value: String },
43
+
44
+
/// Occurs when version information cannot be determined
45
+
#[error("error-atproto-identity-config-3 Version information not available: GIT_HASH or CARGO_PKG_VERSION must be set")]
46
+
VersionNotAvailable,
47
+
}
48
+
49
+
/// Error types that can occur when resolving AT Protocol identities
50
+
#[derive(Debug, Error)]
51
+
pub enum ResolveError {
52
+
/// Occurs when multiple different DIDs are found via DNS TXT record lookup
53
+
#[error("error-atproto-identity-resolve-1 Multiple DIDs resolved for method")]
54
+
MultipleDIDsFound,
55
+
56
+
/// Occurs when no DIDs are found via either DNS or HTTP resolution methods
57
+
#[error("error-atproto-identity-resolve-2 No DIDs resolved for method")]
58
+
NoDIDsFound,
59
+
60
+
/// Occurs when DNS and HTTP resolution return different DIDs for the same handle
61
+
#[error("error-atproto-identity-resolve-3 Conflicting DIDs found for method")]
62
+
ConflictingDIDsFound,
63
+
64
+
/// Occurs when DNS TXT record lookup fails
65
+
#[error("error-atproto-identity-resolve-4 DNS resolution failed: {0:?}")]
66
+
DNSResolutionFailed(hickory_resolver::ResolveError),
67
+
68
+
/// Occurs when HTTP request to .well-known/atproto-did endpoint fails
69
+
#[error("error-atproto-identity-resolve-5 HTTP resolution failed: {0:?}")]
70
+
HTTPResolutionFailed(reqwest::Error),
71
+
72
+
/// Occurs when HTTP response from .well-known/atproto-did doesn't start with "did:"
73
+
#[error("error-atproto-identity-resolve-6 Invalid HTTP resolution response")]
74
+
InvalidHTTPResolutionResponse,
75
+
76
+
/// Occurs when input cannot be parsed as a valid handle or DID
77
+
#[error("error-atproto-identity-resolve-8 Invalid input")]
78
+
InvalidInput,
79
+
}
80
+
81
+
/// Error types that can occur when working with PLC DIDs
82
+
#[derive(Debug, Error)]
83
+
pub enum PLCDIDError {
84
+
/// Occurs when the HTTP request to the PLC directory fails
85
+
#[error("error-atproto-identity-plc-1 HTTP request failed: {url} {error}")]
86
+
HttpRequestFailed {
87
+
/// The URL that was requested
88
+
url: String,
89
+
/// The underlying HTTP error
90
+
error: reqwest::Error,
91
+
},
92
+
93
+
/// Occurs when the DID document cannot be parsed from the PLC directory response
94
+
#[error("error-atproto-identity-plc-2 Failed to parse DID document: {url} {error}")]
95
+
DocumentParseFailed {
96
+
/// The URL that was requested
97
+
url: String,
98
+
/// The underlying parse error
99
+
error: reqwest::Error,
100
+
},
101
+
}
+7
src/lib.rs
+7
src/lib.rs
+160
src/model.rs
+160
src/model.rs
···
1
+
use serde::{Deserialize, Serialize};
2
+
use serde_json::Value;
3
+
use std::collections::HashMap;
4
+
5
+
/// AT Protocol service configuration from a DID document.
6
+
/// Represents services like Personal Data Servers (PDS).
7
+
#[derive(Clone, Deserialize, Debug)]
8
+
#[serde(rename_all = "camelCase")]
9
+
pub struct Service {
10
+
/// Unique identifier for the service.
11
+
pub id: String,
12
+
/// Service type (e.g., "AtprotoPersonalDataServer").
13
+
pub r#type: String,
14
+
/// URL endpoint where the service can be reached.
15
+
pub service_endpoint: String,
16
+
17
+
/// Additional service properties not explicitly defined.
18
+
#[serde(flatten)]
19
+
pub extra: HashMap<String, Value>,
20
+
}
21
+
22
+
/// Cryptographic verification method from a DID document.
23
+
/// Used to verify signatures and authenticate identity operations.
24
+
#[derive(Clone, Deserialize, Debug)]
25
+
#[serde(tag = "type")]
26
+
pub enum VerificationMethod {
27
+
/// Multikey verification method with multibase-encoded public key.
28
+
Multikey {
29
+
/// Unique identifier for this verification method.
30
+
id: String,
31
+
/// DID that controls this verification method.
32
+
controller: String,
33
+
34
+
/// Public key encoded in multibase format.
35
+
#[serde(rename = "publicKeyMultibase")]
36
+
public_key_multibase: String,
37
+
38
+
/// Additional verification method properties.
39
+
#[serde(flatten)]
40
+
extra: HashMap<String, Value>,
41
+
},
42
+
43
+
/// Other verification method types not explicitly supported.
44
+
#[serde(untagged)]
45
+
Other {
46
+
/// All properties of the unsupported verification method.
47
+
#[serde(flatten)]
48
+
extra: HashMap<String, Value>,
49
+
},
50
+
}
51
+
52
+
/// Complete DID document containing identity information.
53
+
/// Contains services, verification methods, and aliases for a DID.
54
+
#[derive(Clone, Deserialize, Debug)]
55
+
#[serde(rename_all = "camelCase")]
56
+
pub struct Document {
57
+
/// The DID identifier (e.g., "did:plc:abc123").
58
+
pub id: String,
59
+
/// Alternative identifiers like handles and domains.
60
+
pub also_known_as: Vec<String>,
61
+
/// Available services for this identity.
62
+
pub service: Vec<Service>,
63
+
64
+
/// Cryptographic verification methods.
65
+
#[serde(alias = "verificationMethod")]
66
+
pub verification_method: Vec<VerificationMethod>,
67
+
68
+
/// Additional document properties not explicitly defined.
69
+
#[serde(flatten)]
70
+
pub extra: HashMap<String, Value>,
71
+
}
72
+
73
+
impl Document {
74
+
/// Extracts Personal Data Server endpoints from services.
75
+
/// Returns URLs of all AtprotoPersonalDataServer services.
76
+
pub fn pds_endpoints(&self) -> Vec<&str> {
77
+
self.service
78
+
.iter()
79
+
.filter_map(|service| {
80
+
if service.r#type == "AtprotoPersonalDataServer" {
81
+
Some(service.service_endpoint.as_str())
82
+
} else {
83
+
None
84
+
}
85
+
})
86
+
.collect()
87
+
}
88
+
89
+
/// Gets the primary handle from alsoKnownAs aliases.
90
+
/// Returns the first alias with "at://" prefix stripped if present.
91
+
pub fn handles(&self) -> Option<&str> {
92
+
self.also_known_as.first().map(|handle| {
93
+
if let Some(trimmed) = handle.strip_prefix("at://") {
94
+
trimmed
95
+
} else {
96
+
handle.as_str()
97
+
}
98
+
})
99
+
}
100
+
101
+
/// Extracts multibase public keys from verification methods.
102
+
/// Returns public keys from Multikey verification methods only.
103
+
pub fn did_keys(&self) -> Vec<&str> {
104
+
self.verification_method
105
+
.iter()
106
+
.filter_map(|verification_method| match verification_method {
107
+
VerificationMethod::Multikey {
108
+
public_key_multibase,
109
+
..
110
+
} => Some(public_key_multibase.as_str()),
111
+
VerificationMethod::Other { extra: _ } => None,
112
+
})
113
+
.collect()
114
+
}
115
+
}
116
+
117
+
/// Resolved handle information linking DID to human-readable identifier.
118
+
/// Contains the complete identity resolution result.
119
+
#[derive(Clone, Deserialize, Serialize, Debug)]
120
+
pub struct Handle {
121
+
/// The resolved DID identifier.
122
+
pub did: String,
123
+
/// Human-readable handle (e.g., "alice.bsky.social").
124
+
pub handle: String,
125
+
/// Personal Data Server URL hosting the identity.
126
+
pub pds: String,
127
+
/// Available cryptographic verification methods.
128
+
pub verification_methods: Vec<String>,
129
+
}
130
+
131
+
#[cfg(test)]
132
+
mod tests {
133
+
use crate::model::Document;
134
+
135
+
#[test]
136
+
fn test_deserialize() {
137
+
let document = serde_json::from_str::<Document>(
138
+
r##"{"@context":["https://www.w3.org/ns/did/v1","https://w3id.org/security/multikey/v1","https://w3id.org/security/suites/secp256k1-2019/v1"],"id":"did:plc:cbkjy5n7bk3ax2wplmtjofq2","alsoKnownAs":["at://ngerakines.me","at://nick.gerakines.net","at://nick.thegem.city","https://github.com/ngerakines","https://ngerakines.me/","dns:ngerakines.me"],"verificationMethod":[{"id":"did:plc:cbkjy5n7bk3ax2wplmtjofq2#atproto","type":"Multikey","controller":"did:plc:cbkjy5n7bk3ax2wplmtjofq2","publicKeyMultibase":"zQ3shXvCK2RyPrSLYQjBEw5CExZkUhJH3n1K2Mb9sC7JbvRMF"}],"service":[{"id":"#atproto_pds","type":"AtprotoPersonalDataServer","serviceEndpoint":"https://pds.cauda.cloud"}]}"##,
139
+
);
140
+
assert!(document.is_ok());
141
+
142
+
let document = document.unwrap();
143
+
assert_eq!(document.id, "did:plc:cbkjy5n7bk3ax2wplmtjofq2");
144
+
}
145
+
146
+
#[test]
147
+
fn test_deserialize_unsupported_verification_method() {
148
+
let documents = vec![
149
+
r##"{"@context":["https://www.w3.org/ns/did/v1","https://w3id.org/security/multikey/v1","https://w3id.org/security/suites/secp256k1-2019/v1"],"id":"did:plc:cbkjy5n7bk3ax2wplmtjofq2","alsoKnownAs":["at://ngerakines.me","at://nick.gerakines.net","at://nick.thegem.city","https://github.com/ngerakines","https://ngerakines.me/","dns:ngerakines.me"],"verificationMethod":[{"id":"did:plc:cbkjy5n7bk3ax2wplmtjofq2#atproto","type":"Ed25519VerificationKey2020","controller":"did:plc:cbkjy5n7bk3ax2wplmtjofq2","publicKeyMultibase":"zQ3shXvCK2RyPrSLYQjBEw5CExZkUhJH3n1K2Mb9sC7JbvRMF"}],"service":[{"id":"#atproto_pds","type":"AtprotoPersonalDataServer","serviceEndpoint":"https://pds.cauda.cloud"}]}"##,
150
+
r##"{"@context":["https://www.w3.org/ns/did/v1","https://w3id.org/security/multikey/v1","https://w3id.org/security/suites/secp256k1-2019/v1"],"id":"did:plc:cbkjy5n7bk3ax2wplmtjofq2","alsoKnownAs":["at://ngerakines.me","at://nick.gerakines.net","at://nick.thegem.city","https://github.com/ngerakines","https://ngerakines.me/","dns:ngerakines.me"],"verificationMethod":[{"id": "did:example:123#_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A","type": "JsonWebKey2020","controller": "did:example:123","publicKeyJwk": {"crv": "Ed25519","x": "VCpo2LMLhn6iWku8MKvSLg2ZAoC-nlOyPVQaO3FxVeQ","kty": "OKP","kid": "_Qq0UL2Fq651Q0Fjd6TvnYE-faHiOpRlPVQcY_-tA4A"}}],"service":[{"id":"#atproto_pds","type":"AtprotoPersonalDataServer","serviceEndpoint":"https://pds.cauda.cloud"}]}"##,
151
+
];
152
+
for document in documents {
153
+
let document = serde_json::from_str::<Document>(document);
154
+
assert!(document.is_ok());
155
+
156
+
let document = document.unwrap();
157
+
assert_eq!(document.id, "did:plc:cbkjy5n7bk3ax2wplmtjofq2");
158
+
}
159
+
}
160
+
}
+27
src/plc.rs
+27
src/plc.rs
···
1
+
use anyhow::Result;
2
+
3
+
use super::errors::PLCDIDError;
4
+
use super::model::Document;
5
+
6
+
/// Queries a PLC directory for a DID document.
7
+
/// Fetches the complete DID document from the specified PLC hostname.
8
+
pub async fn query(
9
+
http_client: &reqwest::Client,
10
+
plc_hostname: &str,
11
+
did: &str,
12
+
) -> Result<Document> {
13
+
let url = format!("https://{}/{}", plc_hostname, did);
14
+
15
+
http_client
16
+
.get(&url)
17
+
.send()
18
+
.await
19
+
.map_err(|error| PLCDIDError::HttpRequestFailed {
20
+
url: url.clone(),
21
+
error,
22
+
})?
23
+
.json::<Document>()
24
+
.await
25
+
.map_err(|error| PLCDIDError::DocumentParseFailed { url, error })
26
+
.map_err(Into::into)
27
+
}
+169
src/resolve.rs
+169
src/resolve.rs
···
1
+
use anyhow::Result;
2
+
use futures_util::future::join;
3
+
use hickory_resolver::{
4
+
config::{NameServerConfigGroup, ResolverConfig},
5
+
name_server::TokioConnectionProvider,
6
+
Resolver, TokioResolver,
7
+
};
8
+
use std::collections::HashSet;
9
+
use std::time::Duration;
10
+
11
+
use crate::errors::ResolveError;
12
+
use crate::validation::{is_valid_did_method_plc, is_valid_handle};
13
+
14
+
/// Type of input identifier for resolution.
15
+
/// Distinguishes between handles and different DID methods.
16
+
pub enum InputType {
17
+
/// AT Protocol handle (e.g., "alice.bsky.social").
18
+
Handle(String),
19
+
/// PLC DID identifier (e.g., "did:plc:abc123").
20
+
Plc(String),
21
+
/// Web DID identifier (e.g., "did:web:example.com").
22
+
Web(String),
23
+
}
24
+
25
+
/// Resolves a handle to DID using DNS TXT records.
26
+
/// Looks up _atproto.{handle} TXT record for DID value.
27
+
pub async fn resolve_handle_dns(
28
+
dns_resolver: &TokioResolver,
29
+
lookup_dns: &str,
30
+
) -> Result<String, ResolveError> {
31
+
let lookup = dns_resolver
32
+
.txt_lookup(&format!("_atproto.{}", lookup_dns))
33
+
.await
34
+
.map_err(ResolveError::DNSResolutionFailed)?;
35
+
36
+
let dids = lookup
37
+
.iter()
38
+
.filter_map(|record| {
39
+
record
40
+
.to_string()
41
+
.strip_prefix("did=")
42
+
.map(|did| did.to_string())
43
+
})
44
+
.collect::<HashSet<String>>();
45
+
46
+
if dids.len() > 1 {
47
+
return Err(ResolveError::MultipleDIDsFound);
48
+
}
49
+
50
+
dids.iter().next().cloned().ok_or(ResolveError::NoDIDsFound)
51
+
}
52
+
53
+
/// Resolves a handle to DID using HTTPS well-known endpoint.
54
+
/// Fetches DID from https://{handle}/.well-known/atproto-did
55
+
pub async fn resolve_handle_http(
56
+
http_client: &reqwest::Client,
57
+
handle: &str,
58
+
) -> Result<String, ResolveError> {
59
+
let lookup_url = format!("https://{}/.well-known/atproto-did", handle);
60
+
61
+
http_client
62
+
.get(lookup_url.clone())
63
+
.timeout(Duration::from_secs(10))
64
+
.send()
65
+
.await
66
+
.map_err(ResolveError::HTTPResolutionFailed)?
67
+
.text()
68
+
.await
69
+
.map_err(ResolveError::HTTPResolutionFailed)
70
+
.and_then(|body| {
71
+
if body.starts_with("did:") {
72
+
Ok(body.trim().to_string())
73
+
} else {
74
+
Err(ResolveError::InvalidHTTPResolutionResponse)
75
+
}
76
+
})
77
+
}
78
+
79
+
/// Parses input string into appropriate identifier type.
80
+
/// Handles prefixes like "at://", "@", and DID formats.
81
+
pub fn parse_input(input: &str) -> Result<InputType, ResolveError> {
82
+
let trimmed = {
83
+
if let Some(value) = input.trim().strip_prefix("at://") {
84
+
value.trim()
85
+
} else if let Some(value) = input.trim().strip_prefix('@') {
86
+
value.trim()
87
+
} else {
88
+
input.trim()
89
+
}
90
+
};
91
+
if trimmed.is_empty() {
92
+
return Err(ResolveError::InvalidInput);
93
+
}
94
+
if trimmed.starts_with("did:web:") {
95
+
Ok(InputType::Web(trimmed.to_string()))
96
+
} else if trimmed.starts_with("did:plc:") && is_valid_did_method_plc(trimmed) {
97
+
Ok(InputType::Plc(trimmed.to_string()))
98
+
} else {
99
+
is_valid_handle(trimmed)
100
+
.map(InputType::Handle)
101
+
.ok_or(ResolveError::InvalidInput)
102
+
}
103
+
}
104
+
105
+
/// Resolves a handle to DID using both DNS and HTTP methods.
106
+
/// Returns DID if both methods agree, or error if conflicting.
107
+
pub async fn resolve_handle(
108
+
http_client: &reqwest::Client,
109
+
dns_resolver: &TokioResolver,
110
+
handle: &str,
111
+
) -> Result<String, ResolveError> {
112
+
let trimmed = {
113
+
if let Some(value) = handle.trim().strip_prefix("at://") {
114
+
value
115
+
} else if let Some(value) = handle.trim().strip_prefix('@') {
116
+
value
117
+
} else {
118
+
handle.trim()
119
+
}
120
+
};
121
+
122
+
let (dns_lookup, http_lookup) = join(
123
+
resolve_handle_dns(dns_resolver, trimmed),
124
+
resolve_handle_http(http_client, trimmed),
125
+
)
126
+
.await;
127
+
128
+
let results = vec![dns_lookup, http_lookup]
129
+
.into_iter()
130
+
.filter_map(|result| result.ok())
131
+
.collect::<Vec<String>>();
132
+
if results.is_empty() {
133
+
return Err(ResolveError::NoDIDsFound);
134
+
}
135
+
136
+
let first = results[0].clone();
137
+
if results.iter().all(|result| result == &first) {
138
+
return Ok(first);
139
+
}
140
+
Err(ResolveError::ConflictingDIDsFound)
141
+
}
142
+
143
+
/// Resolves any subject (handle or DID) to a canonical DID.
144
+
/// Handles all supported identifier formats automatically.
145
+
pub async fn resolve_subject(
146
+
http_client: &reqwest::Client,
147
+
dns_resolver: &TokioResolver,
148
+
subject: &str,
149
+
) -> Result<String, ResolveError> {
150
+
match parse_input(subject)? {
151
+
InputType::Handle(handle) => resolve_handle(http_client, dns_resolver, &handle).await,
152
+
InputType::Plc(did) | InputType::Web(did) => Ok(did),
153
+
}
154
+
}
155
+
156
+
/// Creates a DNS resolver with custom or system nameservers.
157
+
/// Uses custom nameservers if provided, otherwise system defaults.
158
+
pub fn create_resolver(nameservers: &[std::net::IpAddr]) -> TokioResolver {
159
+
// Initialize the DNS resolver with custom nameservers if configured
160
+
if !nameservers.is_empty() {
161
+
tracing::debug!("Using custom DNS nameservers: {:?}", nameservers);
162
+
let nameserver_group = NameServerConfigGroup::from_ips_clear(nameservers, 53, true);
163
+
let resolver_config = ResolverConfig::from_parts(None, vec![], nameserver_group);
164
+
Resolver::builder_with_config(resolver_config, TokioConnectionProvider::default()).build()
165
+
} else {
166
+
tracing::debug!("Using system default DNS nameservers");
167
+
Resolver::builder_tokio().unwrap().build()
168
+
}
169
+
}
+84
src/validation.rs
+84
src/validation.rs
···
1
+
/// Maximum length for a valid hostname as defined in RFC 1035
2
+
const MAX_HOSTNAME_LENGTH: usize = 253;
3
+
4
+
/// Maximum length for a DNS label (component between dots) as defined in RFC 1035
5
+
const MAX_LABEL_LENGTH: usize = 63;
6
+
7
+
/// List of reserved top-level domains that are not valid for AT Protocol handles
8
+
const RESERVED_TLDS: [&str; 4] = [".localhost", ".internal", ".arpa", ".local"];
9
+
10
+
/// Validates if a string is a valid hostname according to RFC 1035.
11
+
/// Checks length limits, reserved TLDs, and character validity.
12
+
pub fn is_valid_hostname(hostname: &str) -> bool {
13
+
// Empty hostnames are invalid
14
+
if hostname.is_empty() || hostname.len() > MAX_HOSTNAME_LENGTH {
15
+
return false;
16
+
}
17
+
18
+
// Check if hostname uses any reserved TLDs
19
+
if RESERVED_TLDS.iter().any(|tld| hostname.ends_with(tld)) {
20
+
return false;
21
+
}
22
+
23
+
// Ensure all characters are valid hostname characters
24
+
if hostname.bytes().any(|byte| !is_valid_hostname_char(byte)) {
25
+
return false;
26
+
}
27
+
28
+
// Validate each DNS label in the hostname
29
+
if hostname.split('.').any(|label| !is_valid_dns_label(label)) {
30
+
return false;
31
+
}
32
+
33
+
true
34
+
}
35
+
36
+
fn is_valid_hostname_char(byte: u8) -> bool {
37
+
byte.is_ascii_lowercase()
38
+
|| byte.is_ascii_uppercase()
39
+
|| byte.is_ascii_digit()
40
+
|| byte == b'-'
41
+
|| byte == b'.'
42
+
}
43
+
44
+
fn is_valid_dns_label(label: &str) -> bool {
45
+
!(label.is_empty()
46
+
|| label.len() > MAX_LABEL_LENGTH
47
+
|| label.starts_with('-')
48
+
|| label.ends_with('-'))
49
+
}
50
+
51
+
/// Validates and normalizes an AT Protocol handle.
52
+
/// Returns the normalized handle if valid, None otherwise.
53
+
pub fn is_valid_handle(handle: &str) -> Option<String> {
54
+
// Strip optional prefixes to get the core handle
55
+
let trimmed = strip_handle_prefixes(handle);
56
+
57
+
// A valid handle must be a valid hostname with at least one period
58
+
if is_valid_hostname(trimmed) && trimmed.contains('.') {
59
+
Some(trimmed.to_string())
60
+
} else {
61
+
None
62
+
}
63
+
}
64
+
65
+
fn strip_handle_prefixes(handle: &str) -> &str {
66
+
if let Some(value) = handle.strip_prefix("at://") {
67
+
value
68
+
} else if let Some(value) = handle.strip_prefix('@') {
69
+
value
70
+
} else {
71
+
handle
72
+
}
73
+
}
74
+
75
+
/// Validates if a string is a properly formatted PLC DID.
76
+
/// Checks for correct prefix and 24-character identifier length.
77
+
pub fn is_valid_did_method_plc(did: &str) -> bool {
78
+
let did_value = match did.strip_prefix("did:plc:") {
79
+
Some(value) => value,
80
+
None => return false,
81
+
};
82
+
83
+
did_value.len() == 24
84
+
}
+107
src/web.rs
+107
src/web.rs
···
1
+
use anyhow::Result;
2
+
3
+
use super::errors::WebDIDError;
4
+
use super::model::Document;
5
+
6
+
/// Converts a did:web DID to its corresponding HTTPS URL.
7
+
/// Transforms DID format to the expected well-known document location.
8
+
pub fn did_web_to_url(did: &str) -> Result<String, WebDIDError> {
9
+
let parts = did
10
+
.strip_prefix("did:web:")
11
+
.ok_or(WebDIDError::InvalidDIDPrefix)?
12
+
.split(':')
13
+
.collect::<Vec<&str>>();
14
+
15
+
let hostname = parts.first().ok_or(WebDIDError::MissingHostname)?;
16
+
if hostname.is_empty() {
17
+
return Err(WebDIDError::MissingHostname);
18
+
}
19
+
let path_parts = &parts[1..];
20
+
21
+
let url = if path_parts.is_empty() {
22
+
format!("https://{}/.well-known/did.json", hostname)
23
+
} else {
24
+
format!("https://{}/{}/did.json", hostname, path_parts.join("/"))
25
+
};
26
+
27
+
Ok(url)
28
+
}
29
+
30
+
/// Queries a did:web DID document from its hosting location.
31
+
/// Resolves the DID to HTTPS URL and fetches the JSON document.
32
+
pub async fn query(http_client: &reqwest::Client, did: &str) -> Result<Document> {
33
+
let url = did_web_to_url(did)?;
34
+
35
+
http_client
36
+
.get(&url)
37
+
.send()
38
+
.await
39
+
.map_err(|error| WebDIDError::HttpRequestFailed {
40
+
url: url.clone(),
41
+
error,
42
+
})?
43
+
.json::<Document>()
44
+
.await
45
+
.map_err(|error| WebDIDError::DocumentParseFailed { url, error })
46
+
.map_err(Into::into)
47
+
}
48
+
49
+
/// Queries a DID document directly from a hostname's well-known location.
50
+
/// Fetches from https://{hostname}/.well-known/did.json
51
+
pub async fn query_hostname(http_client: &reqwest::Client, hostname: &str) -> Result<Document> {
52
+
let url = format!("https://{}/.well-known/did.json", hostname);
53
+
54
+
http_client
55
+
.get(&url)
56
+
.send()
57
+
.await
58
+
.map_err(|error| WebDIDError::HttpRequestFailed {
59
+
url: url.clone(),
60
+
error,
61
+
})?
62
+
.json::<Document>()
63
+
.await
64
+
.map_err(|error| WebDIDError::DocumentParseFailed { url, error })
65
+
.map_err(Into::into)
66
+
}
67
+
68
+
#[cfg(test)]
69
+
mod tests {
70
+
use super::*;
71
+
72
+
#[test]
73
+
fn test_did_web_to_url_simple_hostname() {
74
+
let result = did_web_to_url("did:web:example.com");
75
+
assert_eq!(result.unwrap(), "https://example.com/.well-known/did.json");
76
+
}
77
+
78
+
#[test]
79
+
fn test_did_web_to_url_with_path() {
80
+
let result = did_web_to_url("did:web:example.com:path");
81
+
assert_eq!(result.unwrap(), "https://example.com/path/did.json");
82
+
}
83
+
84
+
#[test]
85
+
fn test_did_web_to_url_with_nested_path() {
86
+
let result = did_web_to_url("did:web:example.com:path:subpath");
87
+
assert_eq!(result.unwrap(), "https://example.com/path/subpath/did.json");
88
+
}
89
+
90
+
#[test]
91
+
fn test_did_web_to_url_invalid_prefix() {
92
+
let result = did_web_to_url("did:plc:example.com");
93
+
assert!(matches!(result, Err(WebDIDError::InvalidDIDPrefix)));
94
+
}
95
+
96
+
#[test]
97
+
fn test_did_web_to_url_missing_hostname() {
98
+
let result = did_web_to_url("did:web:");
99
+
assert!(matches!(result, Err(WebDIDError::MissingHostname)));
100
+
}
101
+
102
+
#[test]
103
+
fn test_did_web_to_url_no_prefix() {
104
+
let result = did_web_to_url("example.com");
105
+
assert!(matches!(result, Err(WebDIDError::InvalidDIDPrefix)));
106
+
}
107
+
}