├── .env.sample ├── .gitignore ├── .npmignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── bin └── dynamodb-email-indexer.ts ├── cdk.json ├── examples ├── benchmark.rs ├── package.rs └── search.rs ├── lib └── dynamodb-email-indexer-stack.ts ├── loadtest └── index.js ├── package-lock.json ├── package.json ├── src ├── attribute_helper.rs ├── bin │ ├── email_index_reader.rs │ └── email_index_writer.rs ├── email.rs ├── email_index_schema.rs ├── lib.rs ├── search_request.rs └── search_response.rs └── tsconfig.json /.env.sample: -------------------------------------------------------------------------------- 1 | CDK_DEFAULT_ACCOUNT= 2 | CDK_DEFAULT_REGION= -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | cdk.out/ 4 | outputs.json 5 | /target 6 | /build 7 | cdk.context.json 8 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.ts 2 | !*.d.ts 3 | 4 | # CDK asset staging directory 5 | .cdk.staging 6 | cdk.out 7 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "adler" 7 | version = "1.0.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 10 | 11 | [[package]] 12 | name = "ahash" 13 | version = "0.7.6" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" 16 | dependencies = [ 17 | "getrandom", 18 | "once_cell", 19 | "version_check", 20 | ] 21 | 22 | [[package]] 23 | name = "aho-corasick" 24 | version = "0.7.18" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" 27 | dependencies = [ 28 | "memchr", 29 | ] 30 | 31 | [[package]] 32 | name = "ansi_term" 33 | version = "0.12.1" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" 36 | dependencies = [ 37 | "winapi", 38 | ] 39 | 40 | [[package]] 41 | name = "anyhow" 42 | version = "1.0.56" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27" 45 | 46 | [[package]] 47 | name = "async-stream" 48 | version = "0.3.3" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e" 51 | dependencies = [ 52 | "async-stream-impl", 53 | "futures-core", 54 | ] 55 | 56 | [[package]] 57 | name = "async-stream-impl" 58 | version = "0.3.3" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" 61 | dependencies = [ 62 | "proc-macro2", 63 | "quote", 64 | "syn", 65 | ] 66 | 67 | [[package]] 68 | name = "async-trait" 69 | version = "0.1.52" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | checksum = "061a7acccaa286c011ddc30970520b98fa40e00c9d644633fb26b5fc63a265e3" 72 | dependencies = [ 73 | "proc-macro2", 74 | "quote", 75 | "syn", 76 | ] 77 | 78 | [[package]] 79 | name = "atty" 80 | version = "0.2.14" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 83 | dependencies = [ 84 | "hermit-abi", 85 | "libc", 86 | "winapi", 87 | ] 88 | 89 | [[package]] 90 | name = "autocfg" 91 | version = "1.1.0" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 94 | 95 | [[package]] 96 | name = "aws-config" 97 | version = "0.9.0" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "2da63196d2d0dd38667b404459a35d32562a8d83c1f46c5b789ab89ab176fd53" 100 | dependencies = [ 101 | "aws-http", 102 | "aws-sdk-sso", 103 | "aws-sdk-sts", 104 | "aws-smithy-async", 105 | "aws-smithy-client", 106 | "aws-smithy-http", 107 | "aws-smithy-http-tower", 108 | "aws-smithy-json", 109 | "aws-smithy-types", 110 | "aws-types", 111 | "bytes", 112 | "hex", 113 | "http", 114 | "hyper", 115 | "ring", 116 | "tokio", 117 | "tower", 118 | "tracing", 119 | "zeroize", 120 | ] 121 | 122 | [[package]] 123 | name = "aws-endpoint" 124 | version = "0.9.0" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "5279590d48e92b287f864e099c7e851af03a5e184a57cec0959872cee297c7a0" 127 | dependencies = [ 128 | "aws-smithy-http", 129 | "aws-types", 130 | "http", 131 | "regex", 132 | "tracing", 133 | ] 134 | 135 | [[package]] 136 | name = "aws-http" 137 | version = "0.9.0" 138 | source = "registry+https://github.com/rust-lang/crates.io-index" 139 | checksum = "e7046bdd807c70caf28d6dbc69b9d6d8dda1728577866d3ff3862de585b8b0eb" 140 | dependencies = [ 141 | "aws-smithy-http", 142 | "aws-smithy-types", 143 | "aws-types", 144 | "http", 145 | "lazy_static", 146 | "percent-encoding", 147 | "tracing", 148 | ] 149 | 150 | [[package]] 151 | name = "aws-sdk-dynamodb" 152 | version = "0.9.0" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | checksum = "762a92e68a3a99d43593ff9b4c00dac630c1e5cfd04ed15e33f235c9c9125a09" 155 | dependencies = [ 156 | "aws-endpoint", 157 | "aws-http", 158 | "aws-sig-auth", 159 | "aws-smithy-async", 160 | "aws-smithy-client", 161 | "aws-smithy-http", 162 | "aws-smithy-http-tower", 163 | "aws-smithy-json", 164 | "aws-smithy-types", 165 | "aws-types", 166 | "bytes", 167 | "fastrand", 168 | "http", 169 | "tokio-stream", 170 | "tower", 171 | ] 172 | 173 | [[package]] 174 | name = "aws-sdk-sso" 175 | version = "0.9.0" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | checksum = "96f9038b498944025a39e426ae38f64e3e8481a9d675469580e1de7397b46ed5" 178 | dependencies = [ 179 | "aws-endpoint", 180 | "aws-http", 181 | "aws-sig-auth", 182 | "aws-smithy-async", 183 | "aws-smithy-client", 184 | "aws-smithy-http", 185 | "aws-smithy-http-tower", 186 | "aws-smithy-json", 187 | "aws-smithy-types", 188 | "aws-types", 189 | "bytes", 190 | "http", 191 | "tokio-stream", 192 | "tower", 193 | ] 194 | 195 | [[package]] 196 | name = "aws-sdk-sts" 197 | version = "0.9.0" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "e717e67debcd7f9d87563d08e7d40e3c5c28634a8badc491650d5ad2305befd3" 200 | dependencies = [ 201 | "aws-endpoint", 202 | "aws-http", 203 | "aws-sig-auth", 204 | "aws-smithy-async", 205 | "aws-smithy-client", 206 | "aws-smithy-http", 207 | "aws-smithy-http-tower", 208 | "aws-smithy-query", 209 | "aws-smithy-types", 210 | "aws-smithy-xml", 211 | "aws-types", 212 | "bytes", 213 | "http", 214 | "tower", 215 | ] 216 | 217 | [[package]] 218 | name = "aws-sig-auth" 219 | version = "0.9.0" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | checksum = "a0e6e4ba09f502057ad6a4ebf3627f9dae8402e366cf7b36ca1c09cbff8b5834" 222 | dependencies = [ 223 | "aws-sigv4", 224 | "aws-smithy-http", 225 | "aws-types", 226 | "http", 227 | "thiserror", 228 | "tracing", 229 | ] 230 | 231 | [[package]] 232 | name = "aws-sigv4" 233 | version = "0.9.0" 234 | source = "registry+https://github.com/rust-lang/crates.io-index" 235 | checksum = "ea07a5a108ee538793d681d608057218df95c5575f6c0699a1973c27a09334b2" 236 | dependencies = [ 237 | "aws-smithy-http", 238 | "form_urlencoded", 239 | "hex", 240 | "http", 241 | "once_cell", 242 | "percent-encoding", 243 | "regex", 244 | "ring", 245 | "time 0.3.7", 246 | "tracing", 247 | ] 248 | 249 | [[package]] 250 | name = "aws-smithy-async" 251 | version = "0.39.0" 252 | source = "registry+https://github.com/rust-lang/crates.io-index" 253 | checksum = "66ab5373d24e1651860240f122a8d956f7a2094d4553c78979617a7fac640030" 254 | dependencies = [ 255 | "futures-util", 256 | "pin-project-lite", 257 | "tokio", 258 | "tokio-stream", 259 | ] 260 | 261 | [[package]] 262 | name = "aws-smithy-client" 263 | version = "0.39.0" 264 | source = "registry+https://github.com/rust-lang/crates.io-index" 265 | checksum = "88e8a92747322eace67f666402a5949da27675f60a2b9098b84b63edef8e6980" 266 | dependencies = [ 267 | "aws-smithy-async", 268 | "aws-smithy-http", 269 | "aws-smithy-http-tower", 270 | "aws-smithy-types", 271 | "bytes", 272 | "fastrand", 273 | "http", 274 | "http-body", 275 | "hyper", 276 | "hyper-rustls", 277 | "lazy_static", 278 | "pin-project", 279 | "pin-project-lite", 280 | "tokio", 281 | "tower", 282 | "tracing", 283 | ] 284 | 285 | [[package]] 286 | name = "aws-smithy-http" 287 | version = "0.39.0" 288 | source = "registry+https://github.com/rust-lang/crates.io-index" 289 | checksum = "579d0c2ae96c700499c5330f082c4170b0535835f01eb845056324aa0abd04b4" 290 | dependencies = [ 291 | "aws-smithy-types", 292 | "bytes", 293 | "bytes-utils", 294 | "futures-core", 295 | "http", 296 | "http-body", 297 | "hyper", 298 | "once_cell", 299 | "percent-encoding", 300 | "pin-project", 301 | "tokio", 302 | "tokio-util 0.6.9", 303 | "tracing", 304 | ] 305 | 306 | [[package]] 307 | name = "aws-smithy-http-tower" 308 | version = "0.39.0" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "101a2e213acebe624cfb9bfc944de5e33c849e0df0f09c3d3aa3b54368dbe7af" 311 | dependencies = [ 312 | "aws-smithy-http", 313 | "bytes", 314 | "http", 315 | "http-body", 316 | "pin-project", 317 | "tower", 318 | "tracing", 319 | ] 320 | 321 | [[package]] 322 | name = "aws-smithy-json" 323 | version = "0.39.0" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | checksum = "bd21f28535a2538b77274aa590abfb6d37aece3281dfc4c9411c1625d3b9239e" 326 | dependencies = [ 327 | "aws-smithy-types", 328 | ] 329 | 330 | [[package]] 331 | name = "aws-smithy-query" 332 | version = "0.39.0" 333 | source = "registry+https://github.com/rust-lang/crates.io-index" 334 | checksum = "eb5a2c90311b0d20cf23212a15961cad2b76480863b1f7ce0608d9ece8dacdfb" 335 | dependencies = [ 336 | "aws-smithy-types", 337 | "urlencoding", 338 | ] 339 | 340 | [[package]] 341 | name = "aws-smithy-types" 342 | version = "0.39.0" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | checksum = "962f2da621cd29f272636eebce39ca321c91e02bbb7eb848c4587ac14933d339" 345 | dependencies = [ 346 | "itoa", 347 | "num-integer", 348 | "ryu", 349 | "time 0.3.7", 350 | ] 351 | 352 | [[package]] 353 | name = "aws-smithy-xml" 354 | version = "0.39.0" 355 | source = "registry+https://github.com/rust-lang/crates.io-index" 356 | checksum = "829c7efd92b7a6d0536ceb48fd93a289ddf8763c67bffe875d82eae3f9886546" 357 | dependencies = [ 358 | "thiserror", 359 | "xmlparser", 360 | ] 361 | 362 | [[package]] 363 | name = "aws-types" 364 | version = "0.9.0" 365 | source = "registry+https://github.com/rust-lang/crates.io-index" 366 | checksum = "68159725aa77553dbc6028f36d8378563cd45b18ef9cf03d1515ac469efacf13" 367 | dependencies = [ 368 | "aws-smithy-async", 369 | "aws-smithy-client", 370 | "aws-smithy-types", 371 | "rustc_version", 372 | "tracing", 373 | "zeroize", 374 | ] 375 | 376 | [[package]] 377 | name = "aws_lambda_events" 378 | version = "0.6.1" 379 | source = "registry+https://github.com/rust-lang/crates.io-index" 380 | checksum = "4c1e41ff8f5750dabfd3e940de42a12bdb842369d0985630a424db0a96721c6e" 381 | dependencies = [ 382 | "base64", 383 | "bytes", 384 | "chrono", 385 | "http", 386 | "http-body", 387 | "http-serde", 388 | "query_map", 389 | "serde", 390 | "serde_derive", 391 | "serde_json", 392 | ] 393 | 394 | [[package]] 395 | name = "base64" 396 | version = "0.13.0" 397 | source = "registry+https://github.com/rust-lang/crates.io-index" 398 | checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" 399 | 400 | [[package]] 401 | name = "bitflags" 402 | version = "1.3.2" 403 | source = "registry+https://github.com/rust-lang/crates.io-index" 404 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 405 | 406 | [[package]] 407 | name = "bitpacking" 408 | version = "0.8.4" 409 | source = "registry+https://github.com/rust-lang/crates.io-index" 410 | checksum = "a8c7d2ac73c167c06af4a5f37e6e59d84148d57ccbe4480b76f0273eefea82d7" 411 | dependencies = [ 412 | "crunchy", 413 | ] 414 | 415 | [[package]] 416 | name = "bumpalo" 417 | version = "3.9.1" 418 | source = "registry+https://github.com/rust-lang/crates.io-index" 419 | checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" 420 | 421 | [[package]] 422 | name = "byteorder" 423 | version = "1.4.3" 424 | source = "registry+https://github.com/rust-lang/crates.io-index" 425 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 426 | 427 | [[package]] 428 | name = "bytes" 429 | version = "1.1.0" 430 | source = "registry+https://github.com/rust-lang/crates.io-index" 431 | checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" 432 | dependencies = [ 433 | "serde", 434 | ] 435 | 436 | [[package]] 437 | name = "bytes-utils" 438 | version = "0.1.1" 439 | source = "registry+https://github.com/rust-lang/crates.io-index" 440 | checksum = "4e314712951c43123e5920a446464929adc667a5eade7f8fb3997776c9df6e54" 441 | dependencies = [ 442 | "bytes", 443 | "either", 444 | ] 445 | 446 | [[package]] 447 | name = "cc" 448 | version = "1.0.73" 449 | source = "registry+https://github.com/rust-lang/crates.io-index" 450 | checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" 451 | 452 | [[package]] 453 | name = "census" 454 | version = "0.4.0" 455 | source = "registry+https://github.com/rust-lang/crates.io-index" 456 | checksum = "5927edd8345aef08578bcbb4aea7314f340d80c7f4931f99fbeb40b99d8f5060" 457 | 458 | [[package]] 459 | name = "cfg-if" 460 | version = "1.0.0" 461 | source = "registry+https://github.com/rust-lang/crates.io-index" 462 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 463 | 464 | [[package]] 465 | name = "chrono" 466 | version = "0.4.19" 467 | source = "registry+https://github.com/rust-lang/crates.io-index" 468 | checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" 469 | dependencies = [ 470 | "js-sys", 471 | "libc", 472 | "num-integer", 473 | "num-traits", 474 | "serde", 475 | "time 0.1.44", 476 | "wasm-bindgen", 477 | "winapi", 478 | ] 479 | 480 | [[package]] 481 | name = "clap" 482 | version = "2.34.0" 483 | source = "registry+https://github.com/rust-lang/crates.io-index" 484 | checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" 485 | dependencies = [ 486 | "ansi_term", 487 | "atty", 488 | "bitflags", 489 | "strsim 0.8.0", 490 | "textwrap", 491 | "unicode-width", 492 | "vec_map", 493 | ] 494 | 495 | [[package]] 496 | name = "combine" 497 | version = "4.6.3" 498 | source = "registry+https://github.com/rust-lang/crates.io-index" 499 | checksum = "50b727aacc797f9fc28e355d21f34709ac4fc9adecfe470ad07b8f4464f53062" 500 | dependencies = [ 501 | "memchr", 502 | ] 503 | 504 | [[package]] 505 | name = "core-foundation" 506 | version = "0.9.3" 507 | source = "registry+https://github.com/rust-lang/crates.io-index" 508 | checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" 509 | dependencies = [ 510 | "core-foundation-sys", 511 | "libc", 512 | ] 513 | 514 | [[package]] 515 | name = "core-foundation-sys" 516 | version = "0.8.3" 517 | source = "registry+https://github.com/rust-lang/crates.io-index" 518 | checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" 519 | 520 | [[package]] 521 | name = "crc32fast" 522 | version = "1.3.2" 523 | source = "registry+https://github.com/rust-lang/crates.io-index" 524 | checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" 525 | dependencies = [ 526 | "cfg-if", 527 | ] 528 | 529 | [[package]] 530 | name = "crossbeam" 531 | version = "0.8.1" 532 | source = "registry+https://github.com/rust-lang/crates.io-index" 533 | checksum = "4ae5588f6b3c3cb05239e90bd110f257254aecd01e4635400391aeae07497845" 534 | dependencies = [ 535 | "cfg-if", 536 | "crossbeam-channel", 537 | "crossbeam-deque", 538 | "crossbeam-epoch", 539 | "crossbeam-queue", 540 | "crossbeam-utils", 541 | ] 542 | 543 | [[package]] 544 | name = "crossbeam-channel" 545 | version = "0.5.3" 546 | source = "registry+https://github.com/rust-lang/crates.io-index" 547 | checksum = "fdbfe11fe19ff083c48923cf179540e8cd0535903dc35e178a1fdeeb59aef51f" 548 | dependencies = [ 549 | "cfg-if", 550 | "crossbeam-utils", 551 | ] 552 | 553 | [[package]] 554 | name = "crossbeam-deque" 555 | version = "0.8.1" 556 | source = "registry+https://github.com/rust-lang/crates.io-index" 557 | checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" 558 | dependencies = [ 559 | "cfg-if", 560 | "crossbeam-epoch", 561 | "crossbeam-utils", 562 | ] 563 | 564 | [[package]] 565 | name = "crossbeam-epoch" 566 | version = "0.9.8" 567 | source = "registry+https://github.com/rust-lang/crates.io-index" 568 | checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c" 569 | dependencies = [ 570 | "autocfg", 571 | "cfg-if", 572 | "crossbeam-utils", 573 | "lazy_static", 574 | "memoffset", 575 | "scopeguard", 576 | ] 577 | 578 | [[package]] 579 | name = "crossbeam-queue" 580 | version = "0.3.5" 581 | source = "registry+https://github.com/rust-lang/crates.io-index" 582 | checksum = "1f25d8400f4a7a5778f0e4e52384a48cbd9b5c495d110786187fc750075277a2" 583 | dependencies = [ 584 | "cfg-if", 585 | "crossbeam-utils", 586 | ] 587 | 588 | [[package]] 589 | name = "crossbeam-utils" 590 | version = "0.8.8" 591 | source = "registry+https://github.com/rust-lang/crates.io-index" 592 | checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" 593 | dependencies = [ 594 | "cfg-if", 595 | "lazy_static", 596 | ] 597 | 598 | [[package]] 599 | name = "crunchy" 600 | version = "0.2.2" 601 | source = "registry+https://github.com/rust-lang/crates.io-index" 602 | checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" 603 | 604 | [[package]] 605 | name = "ct-logs" 606 | version = "0.8.0" 607 | source = "registry+https://github.com/rust-lang/crates.io-index" 608 | checksum = "c1a816186fa68d9e426e3cb4ae4dff1fcd8e4a2c34b781bf7a822574a0d0aac8" 609 | dependencies = [ 610 | "sct", 611 | ] 612 | 613 | [[package]] 614 | name = "ctor" 615 | version = "0.1.21" 616 | source = "registry+https://github.com/rust-lang/crates.io-index" 617 | checksum = "ccc0a48a9b826acdf4028595adc9db92caea352f7af011a3034acd172a52a0aa" 618 | dependencies = [ 619 | "quote", 620 | "syn", 621 | ] 622 | 623 | [[package]] 624 | name = "darling" 625 | version = "0.13.1" 626 | source = "registry+https://github.com/rust-lang/crates.io-index" 627 | checksum = "d0d720b8683f8dd83c65155f0530560cba68cd2bf395f6513a483caee57ff7f4" 628 | dependencies = [ 629 | "darling_core", 630 | "darling_macro", 631 | ] 632 | 633 | [[package]] 634 | name = "darling_core" 635 | version = "0.13.1" 636 | source = "registry+https://github.com/rust-lang/crates.io-index" 637 | checksum = "7a340f241d2ceed1deb47ae36c4144b2707ec7dd0b649f894cb39bb595986324" 638 | dependencies = [ 639 | "fnv", 640 | "ident_case", 641 | "proc-macro2", 642 | "quote", 643 | "strsim 0.10.0", 644 | "syn", 645 | ] 646 | 647 | [[package]] 648 | name = "darling_macro" 649 | version = "0.13.1" 650 | source = "registry+https://github.com/rust-lang/crates.io-index" 651 | checksum = "72c41b3b7352feb3211a0d743dc5700a4e3b60f51bd2b368892d1e0f9a95f44b" 652 | dependencies = [ 653 | "darling_core", 654 | "quote", 655 | "syn", 656 | ] 657 | 658 | [[package]] 659 | name = "diff" 660 | version = "0.1.12" 661 | source = "registry+https://github.com/rust-lang/crates.io-index" 662 | checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499" 663 | 664 | [[package]] 665 | name = "downcast-rs" 666 | version = "1.2.0" 667 | source = "registry+https://github.com/rust-lang/crates.io-index" 668 | checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" 669 | 670 | [[package]] 671 | name = "dummy" 672 | version = "0.4.1" 673 | source = "registry+https://github.com/rust-lang/crates.io-index" 674 | checksum = "5bf5ff6f74150b0bbb6e6057718a903b3d8f3fc7096c9190fc162ca99d3b2273" 675 | dependencies = [ 676 | "darling", 677 | "quote", 678 | "syn", 679 | ] 680 | 681 | [[package]] 682 | name = "dynamodb_email_indexer" 683 | version = "0.1.0" 684 | dependencies = [ 685 | "anyhow", 686 | "aws-config", 687 | "aws-sdk-dynamodb", 688 | "aws-sigv4", 689 | "aws_lambda_events", 690 | "env_logger", 691 | "fake", 692 | "futures", 693 | "http", 694 | "lambda_runtime", 695 | "log", 696 | "rand", 697 | "reqwest", 698 | "serde", 699 | "serde_json", 700 | "structopt", 701 | "tantivy", 702 | "tokio", 703 | "ulid", 704 | "xshell", 705 | ] 706 | 707 | [[package]] 708 | name = "either" 709 | version = "1.6.1" 710 | source = "registry+https://github.com/rust-lang/crates.io-index" 711 | checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" 712 | 713 | [[package]] 714 | name = "encoding_rs" 715 | version = "0.8.31" 716 | source = "registry+https://github.com/rust-lang/crates.io-index" 717 | checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" 718 | dependencies = [ 719 | "cfg-if", 720 | ] 721 | 722 | [[package]] 723 | name = "env_logger" 724 | version = "0.9.0" 725 | source = "registry+https://github.com/rust-lang/crates.io-index" 726 | checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" 727 | dependencies = [ 728 | "atty", 729 | "humantime", 730 | "log", 731 | "regex", 732 | "termcolor", 733 | ] 734 | 735 | [[package]] 736 | name = "fail" 737 | version = "0.5.0" 738 | source = "registry+https://github.com/rust-lang/crates.io-index" 739 | checksum = "ec3245a0ca564e7f3c797d20d833a6870f57a728ac967d5225b3ffdef4465011" 740 | dependencies = [ 741 | "lazy_static", 742 | "log", 743 | "rand", 744 | ] 745 | 746 | [[package]] 747 | name = "fake" 748 | version = "2.4.3" 749 | source = "registry+https://github.com/rust-lang/crates.io-index" 750 | checksum = "21a8531dd3a64fd1cfbe92fad4160bc2060489c6195fe847e045e5788f710bae" 751 | dependencies = [ 752 | "dummy", 753 | "rand", 754 | ] 755 | 756 | [[package]] 757 | name = "fastdivide" 758 | version = "0.4.0" 759 | source = "registry+https://github.com/rust-lang/crates.io-index" 760 | checksum = "25c7df09945d65ea8d70b3321547ed414bbc540aad5bac6883d021b970f35b04" 761 | 762 | [[package]] 763 | name = "fastfield_codecs" 764 | version = "0.1.0" 765 | source = "registry+https://github.com/rust-lang/crates.io-index" 766 | checksum = "bb0e8bfa31546b4ace05092c9db8d251d7bbc298a384875a08c945a473de4f1f" 767 | dependencies = [ 768 | "tantivy-bitpacker", 769 | "tantivy-common 0.1.0", 770 | ] 771 | 772 | [[package]] 773 | name = "fastrand" 774 | version = "1.7.0" 775 | source = "registry+https://github.com/rust-lang/crates.io-index" 776 | checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" 777 | dependencies = [ 778 | "instant", 779 | ] 780 | 781 | [[package]] 782 | name = "flate2" 783 | version = "1.0.22" 784 | source = "registry+https://github.com/rust-lang/crates.io-index" 785 | checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" 786 | dependencies = [ 787 | "cfg-if", 788 | "crc32fast", 789 | "libc", 790 | "miniz_oxide", 791 | ] 792 | 793 | [[package]] 794 | name = "fnv" 795 | version = "1.0.7" 796 | source = "registry+https://github.com/rust-lang/crates.io-index" 797 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 798 | 799 | [[package]] 800 | name = "foreign-types" 801 | version = "0.3.2" 802 | source = "registry+https://github.com/rust-lang/crates.io-index" 803 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 804 | dependencies = [ 805 | "foreign-types-shared", 806 | ] 807 | 808 | [[package]] 809 | name = "foreign-types-shared" 810 | version = "0.1.1" 811 | source = "registry+https://github.com/rust-lang/crates.io-index" 812 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 813 | 814 | [[package]] 815 | name = "form_urlencoded" 816 | version = "1.0.1" 817 | source = "registry+https://github.com/rust-lang/crates.io-index" 818 | checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" 819 | dependencies = [ 820 | "matches", 821 | "percent-encoding", 822 | ] 823 | 824 | [[package]] 825 | name = "fs2" 826 | version = "0.4.3" 827 | source = "registry+https://github.com/rust-lang/crates.io-index" 828 | checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" 829 | dependencies = [ 830 | "libc", 831 | "winapi", 832 | ] 833 | 834 | [[package]] 835 | name = "futures" 836 | version = "0.3.21" 837 | source = "registry+https://github.com/rust-lang/crates.io-index" 838 | checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e" 839 | dependencies = [ 840 | "futures-channel", 841 | "futures-core", 842 | "futures-executor", 843 | "futures-io", 844 | "futures-sink", 845 | "futures-task", 846 | "futures-util", 847 | ] 848 | 849 | [[package]] 850 | name = "futures-channel" 851 | version = "0.3.21" 852 | source = "registry+https://github.com/rust-lang/crates.io-index" 853 | checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" 854 | dependencies = [ 855 | "futures-core", 856 | "futures-sink", 857 | ] 858 | 859 | [[package]] 860 | name = "futures-core" 861 | version = "0.3.21" 862 | source = "registry+https://github.com/rust-lang/crates.io-index" 863 | checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" 864 | 865 | [[package]] 866 | name = "futures-executor" 867 | version = "0.3.21" 868 | source = "registry+https://github.com/rust-lang/crates.io-index" 869 | checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" 870 | dependencies = [ 871 | "futures-core", 872 | "futures-task", 873 | "futures-util", 874 | "num_cpus", 875 | ] 876 | 877 | [[package]] 878 | name = "futures-io" 879 | version = "0.3.21" 880 | source = "registry+https://github.com/rust-lang/crates.io-index" 881 | checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" 882 | 883 | [[package]] 884 | name = "futures-macro" 885 | version = "0.3.21" 886 | source = "registry+https://github.com/rust-lang/crates.io-index" 887 | checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" 888 | dependencies = [ 889 | "proc-macro2", 890 | "quote", 891 | "syn", 892 | ] 893 | 894 | [[package]] 895 | name = "futures-sink" 896 | version = "0.3.21" 897 | source = "registry+https://github.com/rust-lang/crates.io-index" 898 | checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" 899 | 900 | [[package]] 901 | name = "futures-task" 902 | version = "0.3.21" 903 | source = "registry+https://github.com/rust-lang/crates.io-index" 904 | checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" 905 | 906 | [[package]] 907 | name = "futures-util" 908 | version = "0.3.21" 909 | source = "registry+https://github.com/rust-lang/crates.io-index" 910 | checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" 911 | dependencies = [ 912 | "futures-channel", 913 | "futures-core", 914 | "futures-io", 915 | "futures-macro", 916 | "futures-sink", 917 | "futures-task", 918 | "memchr", 919 | "pin-project-lite", 920 | "pin-utils", 921 | "slab", 922 | ] 923 | 924 | [[package]] 925 | name = "getrandom" 926 | version = "0.2.5" 927 | source = "registry+https://github.com/rust-lang/crates.io-index" 928 | checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77" 929 | dependencies = [ 930 | "cfg-if", 931 | "libc", 932 | "wasi 0.10.0+wasi-snapshot-preview1", 933 | ] 934 | 935 | [[package]] 936 | name = "h2" 937 | version = "0.3.12" 938 | source = "registry+https://github.com/rust-lang/crates.io-index" 939 | checksum = "62eeb471aa3e3c9197aa4bfeabfe02982f6dc96f750486c0bb0009ac58b26d2b" 940 | dependencies = [ 941 | "bytes", 942 | "fnv", 943 | "futures-core", 944 | "futures-sink", 945 | "futures-util", 946 | "http", 947 | "indexmap", 948 | "slab", 949 | "tokio", 950 | "tokio-util 0.6.9", 951 | "tracing", 952 | ] 953 | 954 | [[package]] 955 | name = "hashbrown" 956 | version = "0.11.2" 957 | source = "registry+https://github.com/rust-lang/crates.io-index" 958 | checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" 959 | dependencies = [ 960 | "ahash", 961 | ] 962 | 963 | [[package]] 964 | name = "hdrhistogram" 965 | version = "7.5.0" 966 | source = "registry+https://github.com/rust-lang/crates.io-index" 967 | checksum = "31672b7011be2c4f7456c4ddbcb40e7e9a4a9fad8efe49a6ebaf5f307d0109c0" 968 | dependencies = [ 969 | "base64", 970 | "byteorder", 971 | "crossbeam-channel", 972 | "flate2", 973 | "nom", 974 | "num-traits", 975 | ] 976 | 977 | [[package]] 978 | name = "heck" 979 | version = "0.3.3" 980 | source = "registry+https://github.com/rust-lang/crates.io-index" 981 | checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" 982 | dependencies = [ 983 | "unicode-segmentation", 984 | ] 985 | 986 | [[package]] 987 | name = "hermit-abi" 988 | version = "0.1.19" 989 | source = "registry+https://github.com/rust-lang/crates.io-index" 990 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 991 | dependencies = [ 992 | "libc", 993 | ] 994 | 995 | [[package]] 996 | name = "hex" 997 | version = "0.4.3" 998 | source = "registry+https://github.com/rust-lang/crates.io-index" 999 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 1000 | 1001 | [[package]] 1002 | name = "htmlescape" 1003 | version = "0.3.1" 1004 | source = "registry+https://github.com/rust-lang/crates.io-index" 1005 | checksum = "e9025058dae765dee5070ec375f591e2ba14638c63feff74f13805a72e523163" 1006 | 1007 | [[package]] 1008 | name = "http" 1009 | version = "0.2.6" 1010 | source = "registry+https://github.com/rust-lang/crates.io-index" 1011 | checksum = "31f4c6746584866f0feabcc69893c5b51beef3831656a968ed7ae254cdc4fd03" 1012 | dependencies = [ 1013 | "bytes", 1014 | "fnv", 1015 | "itoa", 1016 | ] 1017 | 1018 | [[package]] 1019 | name = "http-body" 1020 | version = "0.4.4" 1021 | source = "registry+https://github.com/rust-lang/crates.io-index" 1022 | checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" 1023 | dependencies = [ 1024 | "bytes", 1025 | "http", 1026 | "pin-project-lite", 1027 | ] 1028 | 1029 | [[package]] 1030 | name = "http-serde" 1031 | version = "1.1.0" 1032 | source = "registry+https://github.com/rust-lang/crates.io-index" 1033 | checksum = "6d98b3d9662de70952b14c4840ee0f37e23973542a363e2275f4b9d024ff6cca" 1034 | dependencies = [ 1035 | "http", 1036 | "serde", 1037 | ] 1038 | 1039 | [[package]] 1040 | name = "httparse" 1041 | version = "1.6.0" 1042 | source = "registry+https://github.com/rust-lang/crates.io-index" 1043 | checksum = "9100414882e15fb7feccb4897e5f0ff0ff1ca7d1a86a23208ada4d7a18e6c6c4" 1044 | 1045 | [[package]] 1046 | name = "httpdate" 1047 | version = "1.0.2" 1048 | source = "registry+https://github.com/rust-lang/crates.io-index" 1049 | checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" 1050 | 1051 | [[package]] 1052 | name = "humantime" 1053 | version = "2.1.0" 1054 | source = "registry+https://github.com/rust-lang/crates.io-index" 1055 | checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" 1056 | 1057 | [[package]] 1058 | name = "hyper" 1059 | version = "0.14.17" 1060 | source = "registry+https://github.com/rust-lang/crates.io-index" 1061 | checksum = "043f0e083e9901b6cc658a77d1eb86f4fc650bbb977a4337dd63192826aa85dd" 1062 | dependencies = [ 1063 | "bytes", 1064 | "futures-channel", 1065 | "futures-core", 1066 | "futures-util", 1067 | "h2", 1068 | "http", 1069 | "http-body", 1070 | "httparse", 1071 | "httpdate", 1072 | "itoa", 1073 | "pin-project-lite", 1074 | "socket2", 1075 | "tokio", 1076 | "tower-service", 1077 | "tracing", 1078 | "want", 1079 | ] 1080 | 1081 | [[package]] 1082 | name = "hyper-rustls" 1083 | version = "0.22.1" 1084 | source = "registry+https://github.com/rust-lang/crates.io-index" 1085 | checksum = "5f9f7a97316d44c0af9b0301e65010573a853a9fc97046d7331d7f6bc0fd5a64" 1086 | dependencies = [ 1087 | "ct-logs", 1088 | "futures-util", 1089 | "hyper", 1090 | "log", 1091 | "rustls", 1092 | "rustls-native-certs", 1093 | "tokio", 1094 | "tokio-rustls", 1095 | "webpki", 1096 | ] 1097 | 1098 | [[package]] 1099 | name = "hyper-tls" 1100 | version = "0.5.0" 1101 | source = "registry+https://github.com/rust-lang/crates.io-index" 1102 | checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" 1103 | dependencies = [ 1104 | "bytes", 1105 | "hyper", 1106 | "native-tls", 1107 | "tokio", 1108 | "tokio-native-tls", 1109 | ] 1110 | 1111 | [[package]] 1112 | name = "ident_case" 1113 | version = "1.0.1" 1114 | source = "registry+https://github.com/rust-lang/crates.io-index" 1115 | checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 1116 | 1117 | [[package]] 1118 | name = "idna" 1119 | version = "0.2.3" 1120 | source = "registry+https://github.com/rust-lang/crates.io-index" 1121 | checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" 1122 | dependencies = [ 1123 | "matches", 1124 | "unicode-bidi", 1125 | "unicode-normalization", 1126 | ] 1127 | 1128 | [[package]] 1129 | name = "indexmap" 1130 | version = "1.8.0" 1131 | source = "registry+https://github.com/rust-lang/crates.io-index" 1132 | checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" 1133 | dependencies = [ 1134 | "autocfg", 1135 | "hashbrown", 1136 | ] 1137 | 1138 | [[package]] 1139 | name = "instant" 1140 | version = "0.1.12" 1141 | source = "registry+https://github.com/rust-lang/crates.io-index" 1142 | checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" 1143 | dependencies = [ 1144 | "cfg-if", 1145 | ] 1146 | 1147 | [[package]] 1148 | name = "ipnet" 1149 | version = "2.4.0" 1150 | source = "registry+https://github.com/rust-lang/crates.io-index" 1151 | checksum = "35e70ee094dc02fd9c13fdad4940090f22dbd6ac7c9e7094a46cf0232a50bc7c" 1152 | 1153 | [[package]] 1154 | name = "itertools" 1155 | version = "0.10.3" 1156 | source = "registry+https://github.com/rust-lang/crates.io-index" 1157 | checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" 1158 | dependencies = [ 1159 | "either", 1160 | ] 1161 | 1162 | [[package]] 1163 | name = "itoa" 1164 | version = "1.0.1" 1165 | source = "registry+https://github.com/rust-lang/crates.io-index" 1166 | checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" 1167 | 1168 | [[package]] 1169 | name = "js-sys" 1170 | version = "0.3.56" 1171 | source = "registry+https://github.com/rust-lang/crates.io-index" 1172 | checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04" 1173 | dependencies = [ 1174 | "wasm-bindgen", 1175 | ] 1176 | 1177 | [[package]] 1178 | name = "lambda_runtime" 1179 | version = "0.5.0" 1180 | source = "registry+https://github.com/rust-lang/crates.io-index" 1181 | checksum = "23cbc35fd60573f46fa6c6292a4539198bc094efe4b7691a7ff8c173a56e0382" 1182 | dependencies = [ 1183 | "async-stream", 1184 | "bytes", 1185 | "http", 1186 | "hyper", 1187 | "lambda_runtime_api_client", 1188 | "serde", 1189 | "serde_json", 1190 | "tokio", 1191 | "tokio-stream", 1192 | "tower", 1193 | "tracing", 1194 | ] 1195 | 1196 | [[package]] 1197 | name = "lambda_runtime_api_client" 1198 | version = "0.5.0" 1199 | source = "registry+https://github.com/rust-lang/crates.io-index" 1200 | checksum = "e921024b5eb4e2f0800a5d6e25c7ed554562aa62f02cf5f60a48c26c8a678974" 1201 | dependencies = [ 1202 | "http", 1203 | "hyper", 1204 | "tokio", 1205 | "tower-service", 1206 | ] 1207 | 1208 | [[package]] 1209 | name = "lazy_static" 1210 | version = "1.4.0" 1211 | source = "registry+https://github.com/rust-lang/crates.io-index" 1212 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 1213 | 1214 | [[package]] 1215 | name = "levenshtein_automata" 1216 | version = "0.2.1" 1217 | source = "registry+https://github.com/rust-lang/crates.io-index" 1218 | checksum = "0c2cdeb66e45e9f36bfad5bbdb4d2384e70936afbee843c6f6543f0c551ebb25" 1219 | 1220 | [[package]] 1221 | name = "libc" 1222 | version = "0.2.120" 1223 | source = "registry+https://github.com/rust-lang/crates.io-index" 1224 | checksum = "ad5c14e80759d0939d013e6ca49930e59fc53dd8e5009132f76240c179380c09" 1225 | 1226 | [[package]] 1227 | name = "lock_api" 1228 | version = "0.4.6" 1229 | source = "registry+https://github.com/rust-lang/crates.io-index" 1230 | checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" 1231 | dependencies = [ 1232 | "scopeguard", 1233 | ] 1234 | 1235 | [[package]] 1236 | name = "log" 1237 | version = "0.4.14" 1238 | source = "registry+https://github.com/rust-lang/crates.io-index" 1239 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" 1240 | dependencies = [ 1241 | "cfg-if", 1242 | ] 1243 | 1244 | [[package]] 1245 | name = "lru" 1246 | version = "0.7.3" 1247 | source = "registry+https://github.com/rust-lang/crates.io-index" 1248 | checksum = "fcb87f3080f6d1d69e8c564c0fcfde1d7aa8cc451ce40cae89479111f03bc0eb" 1249 | dependencies = [ 1250 | "hashbrown", 1251 | ] 1252 | 1253 | [[package]] 1254 | name = "lz4_flex" 1255 | version = "0.9.2" 1256 | source = "registry+https://github.com/rust-lang/crates.io-index" 1257 | checksum = "42c51df9d8d4842336c835df1d85ed447c4813baa237d033d95128bf5552ad8a" 1258 | dependencies = [ 1259 | "twox-hash", 1260 | ] 1261 | 1262 | [[package]] 1263 | name = "matches" 1264 | version = "0.1.9" 1265 | source = "registry+https://github.com/rust-lang/crates.io-index" 1266 | checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" 1267 | 1268 | [[package]] 1269 | name = "measure_time" 1270 | version = "0.8.0" 1271 | source = "registry+https://github.com/rust-lang/crates.io-index" 1272 | checksum = "5f07966480d8562b3622f51df0b4e3fe6ea7ddb3b48b19b0f44ef863c455bdf9" 1273 | dependencies = [ 1274 | "log", 1275 | ] 1276 | 1277 | [[package]] 1278 | name = "memchr" 1279 | version = "2.4.1" 1280 | source = "registry+https://github.com/rust-lang/crates.io-index" 1281 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" 1282 | 1283 | [[package]] 1284 | name = "memmap2" 1285 | version = "0.5.3" 1286 | source = "registry+https://github.com/rust-lang/crates.io-index" 1287 | checksum = "057a3db23999c867821a7a59feb06a578fcb03685e983dff90daf9e7d24ac08f" 1288 | dependencies = [ 1289 | "libc", 1290 | ] 1291 | 1292 | [[package]] 1293 | name = "memoffset" 1294 | version = "0.6.5" 1295 | source = "registry+https://github.com/rust-lang/crates.io-index" 1296 | checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" 1297 | dependencies = [ 1298 | "autocfg", 1299 | ] 1300 | 1301 | [[package]] 1302 | name = "mime" 1303 | version = "0.3.16" 1304 | source = "registry+https://github.com/rust-lang/crates.io-index" 1305 | checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" 1306 | 1307 | [[package]] 1308 | name = "minimal-lexical" 1309 | version = "0.2.1" 1310 | source = "registry+https://github.com/rust-lang/crates.io-index" 1311 | checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 1312 | 1313 | [[package]] 1314 | name = "miniz_oxide" 1315 | version = "0.4.4" 1316 | source = "registry+https://github.com/rust-lang/crates.io-index" 1317 | checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" 1318 | dependencies = [ 1319 | "adler", 1320 | "autocfg", 1321 | ] 1322 | 1323 | [[package]] 1324 | name = "mio" 1325 | version = "0.8.1" 1326 | source = "registry+https://github.com/rust-lang/crates.io-index" 1327 | checksum = "7ba42135c6a5917b9db9cd7b293e5409e1c6b041e6f9825e92e55a894c63b6f8" 1328 | dependencies = [ 1329 | "libc", 1330 | "log", 1331 | "miow", 1332 | "ntapi", 1333 | "wasi 0.11.0+wasi-snapshot-preview1", 1334 | "winapi", 1335 | ] 1336 | 1337 | [[package]] 1338 | name = "miow" 1339 | version = "0.3.7" 1340 | source = "registry+https://github.com/rust-lang/crates.io-index" 1341 | checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" 1342 | dependencies = [ 1343 | "winapi", 1344 | ] 1345 | 1346 | [[package]] 1347 | name = "murmurhash32" 1348 | version = "0.2.0" 1349 | source = "registry+https://github.com/rust-lang/crates.io-index" 1350 | checksum = "d736ff882f0e85fe9689fb23db229616c4c00aee2b3ac282f666d8f20eb25d4a" 1351 | dependencies = [ 1352 | "byteorder", 1353 | ] 1354 | 1355 | [[package]] 1356 | name = "native-tls" 1357 | version = "0.2.10" 1358 | source = "registry+https://github.com/rust-lang/crates.io-index" 1359 | checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9" 1360 | dependencies = [ 1361 | "lazy_static", 1362 | "libc", 1363 | "log", 1364 | "openssl", 1365 | "openssl-probe", 1366 | "openssl-sys", 1367 | "schannel", 1368 | "security-framework", 1369 | "security-framework-sys", 1370 | "tempfile", 1371 | ] 1372 | 1373 | [[package]] 1374 | name = "nom" 1375 | version = "7.1.1" 1376 | source = "registry+https://github.com/rust-lang/crates.io-index" 1377 | checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" 1378 | dependencies = [ 1379 | "memchr", 1380 | "minimal-lexical", 1381 | ] 1382 | 1383 | [[package]] 1384 | name = "ntapi" 1385 | version = "0.3.7" 1386 | source = "registry+https://github.com/rust-lang/crates.io-index" 1387 | checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" 1388 | dependencies = [ 1389 | "winapi", 1390 | ] 1391 | 1392 | [[package]] 1393 | name = "num-integer" 1394 | version = "0.1.44" 1395 | source = "registry+https://github.com/rust-lang/crates.io-index" 1396 | checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" 1397 | dependencies = [ 1398 | "autocfg", 1399 | "num-traits", 1400 | ] 1401 | 1402 | [[package]] 1403 | name = "num-traits" 1404 | version = "0.2.14" 1405 | source = "registry+https://github.com/rust-lang/crates.io-index" 1406 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" 1407 | dependencies = [ 1408 | "autocfg", 1409 | ] 1410 | 1411 | [[package]] 1412 | name = "num_cpus" 1413 | version = "1.13.1" 1414 | source = "registry+https://github.com/rust-lang/crates.io-index" 1415 | checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" 1416 | dependencies = [ 1417 | "hermit-abi", 1418 | "libc", 1419 | ] 1420 | 1421 | [[package]] 1422 | name = "num_threads" 1423 | version = "0.1.5" 1424 | source = "registry+https://github.com/rust-lang/crates.io-index" 1425 | checksum = "aba1801fb138d8e85e11d0fc70baf4fe1cdfffda7c6cd34a854905df588e5ed0" 1426 | dependencies = [ 1427 | "libc", 1428 | ] 1429 | 1430 | [[package]] 1431 | name = "once_cell" 1432 | version = "1.10.0" 1433 | source = "registry+https://github.com/rust-lang/crates.io-index" 1434 | checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" 1435 | 1436 | [[package]] 1437 | name = "openssl" 1438 | version = "0.10.38" 1439 | source = "registry+https://github.com/rust-lang/crates.io-index" 1440 | checksum = "0c7ae222234c30df141154f159066c5093ff73b63204dcda7121eb082fc56a95" 1441 | dependencies = [ 1442 | "bitflags", 1443 | "cfg-if", 1444 | "foreign-types", 1445 | "libc", 1446 | "once_cell", 1447 | "openssl-sys", 1448 | ] 1449 | 1450 | [[package]] 1451 | name = "openssl-probe" 1452 | version = "0.1.5" 1453 | source = "registry+https://github.com/rust-lang/crates.io-index" 1454 | checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" 1455 | 1456 | [[package]] 1457 | name = "openssl-sys" 1458 | version = "0.9.72" 1459 | source = "registry+https://github.com/rust-lang/crates.io-index" 1460 | checksum = "7e46109c383602735fa0a2e48dd2b7c892b048e1bf69e5c3b1d804b7d9c203cb" 1461 | dependencies = [ 1462 | "autocfg", 1463 | "cc", 1464 | "libc", 1465 | "pkg-config", 1466 | "vcpkg", 1467 | ] 1468 | 1469 | [[package]] 1470 | name = "output_vt100" 1471 | version = "0.1.3" 1472 | source = "registry+https://github.com/rust-lang/crates.io-index" 1473 | checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66" 1474 | dependencies = [ 1475 | "winapi", 1476 | ] 1477 | 1478 | [[package]] 1479 | name = "ownedbytes" 1480 | version = "0.2.0" 1481 | source = "registry+https://github.com/rust-lang/crates.io-index" 1482 | checksum = "0bfa208b217a39411d78b85427792e4c1bc40508acbcefd2836e765f44a5c99e" 1483 | dependencies = [ 1484 | "stable_deref_trait", 1485 | ] 1486 | 1487 | [[package]] 1488 | name = "parking_lot" 1489 | version = "0.12.0" 1490 | source = "registry+https://github.com/rust-lang/crates.io-index" 1491 | checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" 1492 | dependencies = [ 1493 | "lock_api", 1494 | "parking_lot_core", 1495 | ] 1496 | 1497 | [[package]] 1498 | name = "parking_lot_core" 1499 | version = "0.9.1" 1500 | source = "registry+https://github.com/rust-lang/crates.io-index" 1501 | checksum = "28141e0cc4143da2443301914478dc976a61ffdb3f043058310c70df2fed8954" 1502 | dependencies = [ 1503 | "cfg-if", 1504 | "libc", 1505 | "redox_syscall", 1506 | "smallvec", 1507 | "windows-sys", 1508 | ] 1509 | 1510 | [[package]] 1511 | name = "percent-encoding" 1512 | version = "2.1.0" 1513 | source = "registry+https://github.com/rust-lang/crates.io-index" 1514 | checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" 1515 | 1516 | [[package]] 1517 | name = "pin-project" 1518 | version = "1.0.10" 1519 | source = "registry+https://github.com/rust-lang/crates.io-index" 1520 | checksum = "58ad3879ad3baf4e44784bc6a718a8698867bb991f8ce24d1bcbe2cfb4c3a75e" 1521 | dependencies = [ 1522 | "pin-project-internal", 1523 | ] 1524 | 1525 | [[package]] 1526 | name = "pin-project-internal" 1527 | version = "1.0.10" 1528 | source = "registry+https://github.com/rust-lang/crates.io-index" 1529 | checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb" 1530 | dependencies = [ 1531 | "proc-macro2", 1532 | "quote", 1533 | "syn", 1534 | ] 1535 | 1536 | [[package]] 1537 | name = "pin-project-lite" 1538 | version = "0.2.8" 1539 | source = "registry+https://github.com/rust-lang/crates.io-index" 1540 | checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" 1541 | 1542 | [[package]] 1543 | name = "pin-utils" 1544 | version = "0.1.0" 1545 | source = "registry+https://github.com/rust-lang/crates.io-index" 1546 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1547 | 1548 | [[package]] 1549 | name = "pkg-config" 1550 | version = "0.3.25" 1551 | source = "registry+https://github.com/rust-lang/crates.io-index" 1552 | checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" 1553 | 1554 | [[package]] 1555 | name = "ppv-lite86" 1556 | version = "0.2.16" 1557 | source = "registry+https://github.com/rust-lang/crates.io-index" 1558 | checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" 1559 | 1560 | [[package]] 1561 | name = "pretty_assertions" 1562 | version = "1.2.0" 1563 | source = "registry+https://github.com/rust-lang/crates.io-index" 1564 | checksum = "57c038cb5319b9c704bf9c227c261d275bfec0ad438118a2787ce47944fb228b" 1565 | dependencies = [ 1566 | "ansi_term", 1567 | "ctor", 1568 | "diff", 1569 | "output_vt100", 1570 | ] 1571 | 1572 | [[package]] 1573 | name = "proc-macro-error" 1574 | version = "1.0.4" 1575 | source = "registry+https://github.com/rust-lang/crates.io-index" 1576 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 1577 | dependencies = [ 1578 | "proc-macro-error-attr", 1579 | "proc-macro2", 1580 | "quote", 1581 | "syn", 1582 | "version_check", 1583 | ] 1584 | 1585 | [[package]] 1586 | name = "proc-macro-error-attr" 1587 | version = "1.0.4" 1588 | source = "registry+https://github.com/rust-lang/crates.io-index" 1589 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 1590 | dependencies = [ 1591 | "proc-macro2", 1592 | "quote", 1593 | "version_check", 1594 | ] 1595 | 1596 | [[package]] 1597 | name = "proc-macro2" 1598 | version = "1.0.36" 1599 | source = "registry+https://github.com/rust-lang/crates.io-index" 1600 | checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" 1601 | dependencies = [ 1602 | "unicode-xid", 1603 | ] 1604 | 1605 | [[package]] 1606 | name = "query_map" 1607 | version = "0.4.0" 1608 | source = "registry+https://github.com/rust-lang/crates.io-index" 1609 | checksum = "af9b3184a42d995e58fefd23c04d277a2b4d51efb5df7ad4efbe0a43d77b5923" 1610 | dependencies = [ 1611 | "serde", 1612 | "serde_derive", 1613 | ] 1614 | 1615 | [[package]] 1616 | name = "quote" 1617 | version = "1.0.15" 1618 | source = "registry+https://github.com/rust-lang/crates.io-index" 1619 | checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" 1620 | dependencies = [ 1621 | "proc-macro2", 1622 | ] 1623 | 1624 | [[package]] 1625 | name = "rand" 1626 | version = "0.8.5" 1627 | source = "registry+https://github.com/rust-lang/crates.io-index" 1628 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 1629 | dependencies = [ 1630 | "libc", 1631 | "rand_chacha", 1632 | "rand_core", 1633 | ] 1634 | 1635 | [[package]] 1636 | name = "rand_chacha" 1637 | version = "0.3.1" 1638 | source = "registry+https://github.com/rust-lang/crates.io-index" 1639 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 1640 | dependencies = [ 1641 | "ppv-lite86", 1642 | "rand_core", 1643 | ] 1644 | 1645 | [[package]] 1646 | name = "rand_core" 1647 | version = "0.6.3" 1648 | source = "registry+https://github.com/rust-lang/crates.io-index" 1649 | checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" 1650 | dependencies = [ 1651 | "getrandom", 1652 | ] 1653 | 1654 | [[package]] 1655 | name = "rayon" 1656 | version = "1.5.1" 1657 | source = "registry+https://github.com/rust-lang/crates.io-index" 1658 | checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" 1659 | dependencies = [ 1660 | "autocfg", 1661 | "crossbeam-deque", 1662 | "either", 1663 | "rayon-core", 1664 | ] 1665 | 1666 | [[package]] 1667 | name = "rayon-core" 1668 | version = "1.9.1" 1669 | source = "registry+https://github.com/rust-lang/crates.io-index" 1670 | checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" 1671 | dependencies = [ 1672 | "crossbeam-channel", 1673 | "crossbeam-deque", 1674 | "crossbeam-utils", 1675 | "lazy_static", 1676 | "num_cpus", 1677 | ] 1678 | 1679 | [[package]] 1680 | name = "redox_syscall" 1681 | version = "0.2.11" 1682 | source = "registry+https://github.com/rust-lang/crates.io-index" 1683 | checksum = "8380fe0152551244f0747b1bf41737e0f8a74f97a14ccefd1148187271634f3c" 1684 | dependencies = [ 1685 | "bitflags", 1686 | ] 1687 | 1688 | [[package]] 1689 | name = "regex" 1690 | version = "1.5.5" 1691 | source = "registry+https://github.com/rust-lang/crates.io-index" 1692 | checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" 1693 | dependencies = [ 1694 | "aho-corasick", 1695 | "memchr", 1696 | "regex-syntax 0.6.25", 1697 | ] 1698 | 1699 | [[package]] 1700 | name = "regex-syntax" 1701 | version = "0.4.2" 1702 | source = "registry+https://github.com/rust-lang/crates.io-index" 1703 | checksum = "8e931c58b93d86f080c734bfd2bce7dd0079ae2331235818133c8be7f422e20e" 1704 | 1705 | [[package]] 1706 | name = "regex-syntax" 1707 | version = "0.6.25" 1708 | source = "registry+https://github.com/rust-lang/crates.io-index" 1709 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" 1710 | 1711 | [[package]] 1712 | name = "remove_dir_all" 1713 | version = "0.5.3" 1714 | source = "registry+https://github.com/rust-lang/crates.io-index" 1715 | checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" 1716 | dependencies = [ 1717 | "winapi", 1718 | ] 1719 | 1720 | [[package]] 1721 | name = "reqwest" 1722 | version = "0.11.10" 1723 | source = "registry+https://github.com/rust-lang/crates.io-index" 1724 | checksum = "46a1f7aa4f35e5e8b4160449f51afc758f0ce6454315a9fa7d0d113e958c41eb" 1725 | dependencies = [ 1726 | "base64", 1727 | "bytes", 1728 | "encoding_rs", 1729 | "futures-core", 1730 | "futures-util", 1731 | "h2", 1732 | "http", 1733 | "http-body", 1734 | "hyper", 1735 | "hyper-tls", 1736 | "ipnet", 1737 | "js-sys", 1738 | "lazy_static", 1739 | "log", 1740 | "mime", 1741 | "native-tls", 1742 | "percent-encoding", 1743 | "pin-project-lite", 1744 | "serde", 1745 | "serde_json", 1746 | "serde_urlencoded", 1747 | "tokio", 1748 | "tokio-native-tls", 1749 | "url", 1750 | "wasm-bindgen", 1751 | "wasm-bindgen-futures", 1752 | "web-sys", 1753 | "winreg", 1754 | ] 1755 | 1756 | [[package]] 1757 | name = "ring" 1758 | version = "0.16.20" 1759 | source = "registry+https://github.com/rust-lang/crates.io-index" 1760 | checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" 1761 | dependencies = [ 1762 | "cc", 1763 | "libc", 1764 | "once_cell", 1765 | "spin", 1766 | "untrusted", 1767 | "web-sys", 1768 | "winapi", 1769 | ] 1770 | 1771 | [[package]] 1772 | name = "rust-stemmers" 1773 | version = "1.2.0" 1774 | source = "registry+https://github.com/rust-lang/crates.io-index" 1775 | checksum = "e46a2036019fdb888131db7a4c847a1063a7493f971ed94ea82c67eada63ca54" 1776 | dependencies = [ 1777 | "serde", 1778 | "serde_derive", 1779 | ] 1780 | 1781 | [[package]] 1782 | name = "rustc_version" 1783 | version = "0.4.0" 1784 | source = "registry+https://github.com/rust-lang/crates.io-index" 1785 | checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" 1786 | dependencies = [ 1787 | "semver", 1788 | ] 1789 | 1790 | [[package]] 1791 | name = "rustls" 1792 | version = "0.19.1" 1793 | source = "registry+https://github.com/rust-lang/crates.io-index" 1794 | checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" 1795 | dependencies = [ 1796 | "base64", 1797 | "log", 1798 | "ring", 1799 | "sct", 1800 | "webpki", 1801 | ] 1802 | 1803 | [[package]] 1804 | name = "rustls-native-certs" 1805 | version = "0.5.0" 1806 | source = "registry+https://github.com/rust-lang/crates.io-index" 1807 | checksum = "5a07b7c1885bd8ed3831c289b7870b13ef46fe0e856d288c30d9cc17d75a2092" 1808 | dependencies = [ 1809 | "openssl-probe", 1810 | "rustls", 1811 | "schannel", 1812 | "security-framework", 1813 | ] 1814 | 1815 | [[package]] 1816 | name = "ryu" 1817 | version = "1.0.9" 1818 | source = "registry+https://github.com/rust-lang/crates.io-index" 1819 | checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" 1820 | 1821 | [[package]] 1822 | name = "schannel" 1823 | version = "0.1.19" 1824 | source = "registry+https://github.com/rust-lang/crates.io-index" 1825 | checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" 1826 | dependencies = [ 1827 | "lazy_static", 1828 | "winapi", 1829 | ] 1830 | 1831 | [[package]] 1832 | name = "scopeguard" 1833 | version = "1.1.0" 1834 | source = "registry+https://github.com/rust-lang/crates.io-index" 1835 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 1836 | 1837 | [[package]] 1838 | name = "sct" 1839 | version = "0.6.1" 1840 | source = "registry+https://github.com/rust-lang/crates.io-index" 1841 | checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" 1842 | dependencies = [ 1843 | "ring", 1844 | "untrusted", 1845 | ] 1846 | 1847 | [[package]] 1848 | name = "security-framework" 1849 | version = "2.6.1" 1850 | source = "registry+https://github.com/rust-lang/crates.io-index" 1851 | checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc" 1852 | dependencies = [ 1853 | "bitflags", 1854 | "core-foundation", 1855 | "core-foundation-sys", 1856 | "libc", 1857 | "security-framework-sys", 1858 | ] 1859 | 1860 | [[package]] 1861 | name = "security-framework-sys" 1862 | version = "2.6.1" 1863 | source = "registry+https://github.com/rust-lang/crates.io-index" 1864 | checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" 1865 | dependencies = [ 1866 | "core-foundation-sys", 1867 | "libc", 1868 | ] 1869 | 1870 | [[package]] 1871 | name = "semver" 1872 | version = "1.0.6" 1873 | source = "registry+https://github.com/rust-lang/crates.io-index" 1874 | checksum = "a4a3381e03edd24287172047536f20cabde766e2cd3e65e6b00fb3af51c4f38d" 1875 | 1876 | [[package]] 1877 | name = "serde" 1878 | version = "1.0.136" 1879 | source = "registry+https://github.com/rust-lang/crates.io-index" 1880 | checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" 1881 | dependencies = [ 1882 | "serde_derive", 1883 | ] 1884 | 1885 | [[package]] 1886 | name = "serde_derive" 1887 | version = "1.0.136" 1888 | source = "registry+https://github.com/rust-lang/crates.io-index" 1889 | checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" 1890 | dependencies = [ 1891 | "proc-macro2", 1892 | "quote", 1893 | "syn", 1894 | ] 1895 | 1896 | [[package]] 1897 | name = "serde_json" 1898 | version = "1.0.79" 1899 | source = "registry+https://github.com/rust-lang/crates.io-index" 1900 | checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" 1901 | dependencies = [ 1902 | "itoa", 1903 | "ryu", 1904 | "serde", 1905 | ] 1906 | 1907 | [[package]] 1908 | name = "serde_urlencoded" 1909 | version = "0.7.1" 1910 | source = "registry+https://github.com/rust-lang/crates.io-index" 1911 | checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 1912 | dependencies = [ 1913 | "form_urlencoded", 1914 | "itoa", 1915 | "ryu", 1916 | "serde", 1917 | ] 1918 | 1919 | [[package]] 1920 | name = "signal-hook-registry" 1921 | version = "1.4.0" 1922 | source = "registry+https://github.com/rust-lang/crates.io-index" 1923 | checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" 1924 | dependencies = [ 1925 | "libc", 1926 | ] 1927 | 1928 | [[package]] 1929 | name = "slab" 1930 | version = "0.4.5" 1931 | source = "registry+https://github.com/rust-lang/crates.io-index" 1932 | checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" 1933 | 1934 | [[package]] 1935 | name = "smallvec" 1936 | version = "1.8.0" 1937 | source = "registry+https://github.com/rust-lang/crates.io-index" 1938 | checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" 1939 | 1940 | [[package]] 1941 | name = "socket2" 1942 | version = "0.4.4" 1943 | source = "registry+https://github.com/rust-lang/crates.io-index" 1944 | checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" 1945 | dependencies = [ 1946 | "libc", 1947 | "winapi", 1948 | ] 1949 | 1950 | [[package]] 1951 | name = "spin" 1952 | version = "0.5.2" 1953 | source = "registry+https://github.com/rust-lang/crates.io-index" 1954 | checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" 1955 | 1956 | [[package]] 1957 | name = "stable_deref_trait" 1958 | version = "1.2.0" 1959 | source = "registry+https://github.com/rust-lang/crates.io-index" 1960 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 1961 | 1962 | [[package]] 1963 | name = "static_assertions" 1964 | version = "1.1.0" 1965 | source = "registry+https://github.com/rust-lang/crates.io-index" 1966 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 1967 | 1968 | [[package]] 1969 | name = "strsim" 1970 | version = "0.8.0" 1971 | source = "registry+https://github.com/rust-lang/crates.io-index" 1972 | checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 1973 | 1974 | [[package]] 1975 | name = "strsim" 1976 | version = "0.10.0" 1977 | source = "registry+https://github.com/rust-lang/crates.io-index" 1978 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 1979 | 1980 | [[package]] 1981 | name = "structopt" 1982 | version = "0.3.26" 1983 | source = "registry+https://github.com/rust-lang/crates.io-index" 1984 | checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" 1985 | dependencies = [ 1986 | "clap", 1987 | "lazy_static", 1988 | "structopt-derive", 1989 | ] 1990 | 1991 | [[package]] 1992 | name = "structopt-derive" 1993 | version = "0.4.18" 1994 | source = "registry+https://github.com/rust-lang/crates.io-index" 1995 | checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" 1996 | dependencies = [ 1997 | "heck", 1998 | "proc-macro-error", 1999 | "proc-macro2", 2000 | "quote", 2001 | "syn", 2002 | ] 2003 | 2004 | [[package]] 2005 | name = "syn" 2006 | version = "1.0.88" 2007 | source = "registry+https://github.com/rust-lang/crates.io-index" 2008 | checksum = "ebd69e719f31e88618baa1eaa6ee2de5c9a1c004f1e9ecdb58e8352a13f20a01" 2009 | dependencies = [ 2010 | "proc-macro2", 2011 | "quote", 2012 | "unicode-xid", 2013 | ] 2014 | 2015 | [[package]] 2016 | name = "tantivy" 2017 | version = "0.17.0" 2018 | source = "registry+https://github.com/rust-lang/crates.io-index" 2019 | checksum = "264c2549892aa83975386a924ef8d0b8e909674c837d37ea58b4bd8739495c6e" 2020 | dependencies = [ 2021 | "async-trait", 2022 | "base64", 2023 | "bitpacking", 2024 | "byteorder", 2025 | "census", 2026 | "chrono", 2027 | "crc32fast", 2028 | "crossbeam", 2029 | "downcast-rs", 2030 | "fail", 2031 | "fastdivide", 2032 | "fastfield_codecs", 2033 | "fnv", 2034 | "fs2", 2035 | "futures", 2036 | "htmlescape", 2037 | "itertools", 2038 | "levenshtein_automata", 2039 | "log", 2040 | "lru", 2041 | "lz4_flex", 2042 | "measure_time", 2043 | "memmap2", 2044 | "murmurhash32", 2045 | "num_cpus", 2046 | "once_cell", 2047 | "ownedbytes", 2048 | "pretty_assertions", 2049 | "rayon", 2050 | "regex", 2051 | "rust-stemmers", 2052 | "serde", 2053 | "serde_json", 2054 | "smallvec", 2055 | "stable_deref_trait", 2056 | "tantivy-bitpacker", 2057 | "tantivy-common 0.2.0", 2058 | "tantivy-fst", 2059 | "tantivy-query-grammar", 2060 | "tempfile", 2061 | "thiserror", 2062 | "uuid", 2063 | "winapi", 2064 | ] 2065 | 2066 | [[package]] 2067 | name = "tantivy-bitpacker" 2068 | version = "0.1.1" 2069 | source = "registry+https://github.com/rust-lang/crates.io-index" 2070 | checksum = "66d10a5ed75437a4f6bbbba67601cd5adab8d71f5188b677055381f0f36064f2" 2071 | 2072 | [[package]] 2073 | name = "tantivy-common" 2074 | version = "0.1.0" 2075 | source = "registry+https://github.com/rust-lang/crates.io-index" 2076 | checksum = "760e44073e328f4ea3f38660da9ba2598a19ad5ad4149cfb89ad89b4d5ee88d9" 2077 | dependencies = [ 2078 | "byteorder", 2079 | ] 2080 | 2081 | [[package]] 2082 | name = "tantivy-common" 2083 | version = "0.2.0" 2084 | source = "registry+https://github.com/rust-lang/crates.io-index" 2085 | checksum = "2078cd12c7e46eb2cd66ec813eac8472e0f9dfe816f26159effceffd2dbe4793" 2086 | dependencies = [ 2087 | "byteorder", 2088 | "ownedbytes", 2089 | ] 2090 | 2091 | [[package]] 2092 | name = "tantivy-fst" 2093 | version = "0.3.0" 2094 | source = "registry+https://github.com/rust-lang/crates.io-index" 2095 | checksum = "cb20cdc0d83e9184560bdde9cd60142dbb4af2e0f770e88fce45770495224205" 2096 | dependencies = [ 2097 | "byteorder", 2098 | "regex-syntax 0.4.2", 2099 | "utf8-ranges", 2100 | ] 2101 | 2102 | [[package]] 2103 | name = "tantivy-query-grammar" 2104 | version = "0.15.0" 2105 | source = "registry+https://github.com/rust-lang/crates.io-index" 2106 | checksum = "466e0218472a9b276a73e38b2571ac02f9a1b270b4481c9cd8cc23a63d1307e9" 2107 | dependencies = [ 2108 | "combine", 2109 | ] 2110 | 2111 | [[package]] 2112 | name = "tempfile" 2113 | version = "3.3.0" 2114 | source = "registry+https://github.com/rust-lang/crates.io-index" 2115 | checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" 2116 | dependencies = [ 2117 | "cfg-if", 2118 | "fastrand", 2119 | "libc", 2120 | "redox_syscall", 2121 | "remove_dir_all", 2122 | "winapi", 2123 | ] 2124 | 2125 | [[package]] 2126 | name = "termcolor" 2127 | version = "1.1.3" 2128 | source = "registry+https://github.com/rust-lang/crates.io-index" 2129 | checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" 2130 | dependencies = [ 2131 | "winapi-util", 2132 | ] 2133 | 2134 | [[package]] 2135 | name = "textwrap" 2136 | version = "0.11.0" 2137 | source = "registry+https://github.com/rust-lang/crates.io-index" 2138 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 2139 | dependencies = [ 2140 | "unicode-width", 2141 | ] 2142 | 2143 | [[package]] 2144 | name = "thiserror" 2145 | version = "1.0.30" 2146 | source = "registry+https://github.com/rust-lang/crates.io-index" 2147 | checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" 2148 | dependencies = [ 2149 | "thiserror-impl", 2150 | ] 2151 | 2152 | [[package]] 2153 | name = "thiserror-impl" 2154 | version = "1.0.30" 2155 | source = "registry+https://github.com/rust-lang/crates.io-index" 2156 | checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" 2157 | dependencies = [ 2158 | "proc-macro2", 2159 | "quote", 2160 | "syn", 2161 | ] 2162 | 2163 | [[package]] 2164 | name = "time" 2165 | version = "0.1.44" 2166 | source = "registry+https://github.com/rust-lang/crates.io-index" 2167 | checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" 2168 | dependencies = [ 2169 | "libc", 2170 | "wasi 0.10.0+wasi-snapshot-preview1", 2171 | "winapi", 2172 | ] 2173 | 2174 | [[package]] 2175 | name = "time" 2176 | version = "0.3.7" 2177 | source = "registry+https://github.com/rust-lang/crates.io-index" 2178 | checksum = "004cbc98f30fa233c61a38bc77e96a9106e65c88f2d3bef182ae952027e5753d" 2179 | dependencies = [ 2180 | "libc", 2181 | "num_threads", 2182 | ] 2183 | 2184 | [[package]] 2185 | name = "tinyvec" 2186 | version = "1.5.1" 2187 | source = "registry+https://github.com/rust-lang/crates.io-index" 2188 | checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" 2189 | dependencies = [ 2190 | "tinyvec_macros", 2191 | ] 2192 | 2193 | [[package]] 2194 | name = "tinyvec_macros" 2195 | version = "0.1.0" 2196 | source = "registry+https://github.com/rust-lang/crates.io-index" 2197 | checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" 2198 | 2199 | [[package]] 2200 | name = "tokio" 2201 | version = "1.17.0" 2202 | source = "registry+https://github.com/rust-lang/crates.io-index" 2203 | checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee" 2204 | dependencies = [ 2205 | "bytes", 2206 | "libc", 2207 | "memchr", 2208 | "mio", 2209 | "num_cpus", 2210 | "once_cell", 2211 | "parking_lot", 2212 | "pin-project-lite", 2213 | "signal-hook-registry", 2214 | "socket2", 2215 | "tokio-macros", 2216 | "winapi", 2217 | ] 2218 | 2219 | [[package]] 2220 | name = "tokio-macros" 2221 | version = "1.7.0" 2222 | source = "registry+https://github.com/rust-lang/crates.io-index" 2223 | checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" 2224 | dependencies = [ 2225 | "proc-macro2", 2226 | "quote", 2227 | "syn", 2228 | ] 2229 | 2230 | [[package]] 2231 | name = "tokio-native-tls" 2232 | version = "0.3.0" 2233 | source = "registry+https://github.com/rust-lang/crates.io-index" 2234 | checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" 2235 | dependencies = [ 2236 | "native-tls", 2237 | "tokio", 2238 | ] 2239 | 2240 | [[package]] 2241 | name = "tokio-rustls" 2242 | version = "0.22.0" 2243 | source = "registry+https://github.com/rust-lang/crates.io-index" 2244 | checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" 2245 | dependencies = [ 2246 | "rustls", 2247 | "tokio", 2248 | "webpki", 2249 | ] 2250 | 2251 | [[package]] 2252 | name = "tokio-stream" 2253 | version = "0.1.8" 2254 | source = "registry+https://github.com/rust-lang/crates.io-index" 2255 | checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3" 2256 | dependencies = [ 2257 | "futures-core", 2258 | "pin-project-lite", 2259 | "tokio", 2260 | ] 2261 | 2262 | [[package]] 2263 | name = "tokio-util" 2264 | version = "0.6.9" 2265 | source = "registry+https://github.com/rust-lang/crates.io-index" 2266 | checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" 2267 | dependencies = [ 2268 | "bytes", 2269 | "futures-core", 2270 | "futures-sink", 2271 | "log", 2272 | "pin-project-lite", 2273 | "tokio", 2274 | ] 2275 | 2276 | [[package]] 2277 | name = "tokio-util" 2278 | version = "0.7.0" 2279 | source = "registry+https://github.com/rust-lang/crates.io-index" 2280 | checksum = "64910e1b9c1901aaf5375561e35b9c057d95ff41a44ede043a03e09279eabaf1" 2281 | dependencies = [ 2282 | "bytes", 2283 | "futures-core", 2284 | "futures-sink", 2285 | "log", 2286 | "pin-project-lite", 2287 | "tokio", 2288 | ] 2289 | 2290 | [[package]] 2291 | name = "tower" 2292 | version = "0.4.12" 2293 | source = "registry+https://github.com/rust-lang/crates.io-index" 2294 | checksum = "9a89fd63ad6adf737582df5db40d286574513c69a11dac5214dc3b5603d6713e" 2295 | dependencies = [ 2296 | "futures-core", 2297 | "futures-util", 2298 | "hdrhistogram", 2299 | "indexmap", 2300 | "pin-project", 2301 | "pin-project-lite", 2302 | "rand", 2303 | "slab", 2304 | "tokio", 2305 | "tokio-util 0.7.0", 2306 | "tower-layer", 2307 | "tower-service", 2308 | "tracing", 2309 | ] 2310 | 2311 | [[package]] 2312 | name = "tower-layer" 2313 | version = "0.3.1" 2314 | source = "registry+https://github.com/rust-lang/crates.io-index" 2315 | checksum = "343bc9466d3fe6b0f960ef45960509f84480bf4fd96f92901afe7ff3df9d3a62" 2316 | 2317 | [[package]] 2318 | name = "tower-service" 2319 | version = "0.3.1" 2320 | source = "registry+https://github.com/rust-lang/crates.io-index" 2321 | checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" 2322 | 2323 | [[package]] 2324 | name = "tracing" 2325 | version = "0.1.32" 2326 | source = "registry+https://github.com/rust-lang/crates.io-index" 2327 | checksum = "4a1bdf54a7c28a2bbf701e1d2233f6c77f473486b94bee4f9678da5a148dca7f" 2328 | dependencies = [ 2329 | "cfg-if", 2330 | "log", 2331 | "pin-project-lite", 2332 | "tracing-attributes", 2333 | "tracing-core", 2334 | ] 2335 | 2336 | [[package]] 2337 | name = "tracing-attributes" 2338 | version = "0.1.20" 2339 | source = "registry+https://github.com/rust-lang/crates.io-index" 2340 | checksum = "2e65ce065b4b5c53e73bb28912318cb8c9e9ad3921f1d669eb0e68b4c8143a2b" 2341 | dependencies = [ 2342 | "proc-macro2", 2343 | "quote", 2344 | "syn", 2345 | ] 2346 | 2347 | [[package]] 2348 | name = "tracing-core" 2349 | version = "0.1.23" 2350 | source = "registry+https://github.com/rust-lang/crates.io-index" 2351 | checksum = "aa31669fa42c09c34d94d8165dd2012e8ff3c66aca50f3bb226b68f216f2706c" 2352 | dependencies = [ 2353 | "lazy_static", 2354 | ] 2355 | 2356 | [[package]] 2357 | name = "try-lock" 2358 | version = "0.2.3" 2359 | source = "registry+https://github.com/rust-lang/crates.io-index" 2360 | checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" 2361 | 2362 | [[package]] 2363 | name = "twox-hash" 2364 | version = "1.6.2" 2365 | source = "registry+https://github.com/rust-lang/crates.io-index" 2366 | checksum = "4ee73e6e4924fe940354b8d4d98cad5231175d615cd855b758adc658c0aac6a0" 2367 | dependencies = [ 2368 | "cfg-if", 2369 | "static_assertions", 2370 | ] 2371 | 2372 | [[package]] 2373 | name = "ulid" 2374 | version = "0.5.0" 2375 | source = "registry+https://github.com/rust-lang/crates.io-index" 2376 | checksum = "220b18413e1fe5e85a5580b22f44241f82404a66c792c9f3c9eda74c52d9a22e" 2377 | dependencies = [ 2378 | "chrono", 2379 | "rand", 2380 | ] 2381 | 2382 | [[package]] 2383 | name = "unicode-bidi" 2384 | version = "0.3.7" 2385 | source = "registry+https://github.com/rust-lang/crates.io-index" 2386 | checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" 2387 | 2388 | [[package]] 2389 | name = "unicode-normalization" 2390 | version = "0.1.19" 2391 | source = "registry+https://github.com/rust-lang/crates.io-index" 2392 | checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" 2393 | dependencies = [ 2394 | "tinyvec", 2395 | ] 2396 | 2397 | [[package]] 2398 | name = "unicode-segmentation" 2399 | version = "1.9.0" 2400 | source = "registry+https://github.com/rust-lang/crates.io-index" 2401 | checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" 2402 | 2403 | [[package]] 2404 | name = "unicode-width" 2405 | version = "0.1.9" 2406 | source = "registry+https://github.com/rust-lang/crates.io-index" 2407 | checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" 2408 | 2409 | [[package]] 2410 | name = "unicode-xid" 2411 | version = "0.2.2" 2412 | source = "registry+https://github.com/rust-lang/crates.io-index" 2413 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 2414 | 2415 | [[package]] 2416 | name = "untrusted" 2417 | version = "0.7.1" 2418 | source = "registry+https://github.com/rust-lang/crates.io-index" 2419 | checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" 2420 | 2421 | [[package]] 2422 | name = "url" 2423 | version = "2.2.2" 2424 | source = "registry+https://github.com/rust-lang/crates.io-index" 2425 | checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" 2426 | dependencies = [ 2427 | "form_urlencoded", 2428 | "idna", 2429 | "matches", 2430 | "percent-encoding", 2431 | ] 2432 | 2433 | [[package]] 2434 | name = "urlencoding" 2435 | version = "1.3.3" 2436 | source = "registry+https://github.com/rust-lang/crates.io-index" 2437 | checksum = "5a1f0175e03a0973cf4afd476bef05c26e228520400eb1fd473ad417b1c00ffb" 2438 | 2439 | [[package]] 2440 | name = "utf8-ranges" 2441 | version = "1.0.4" 2442 | source = "registry+https://github.com/rust-lang/crates.io-index" 2443 | checksum = "b4ae116fef2b7fea257ed6440d3cfcff7f190865f170cdad00bb6465bf18ecba" 2444 | 2445 | [[package]] 2446 | name = "uuid" 2447 | version = "0.8.2" 2448 | source = "registry+https://github.com/rust-lang/crates.io-index" 2449 | checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" 2450 | dependencies = [ 2451 | "getrandom", 2452 | "serde", 2453 | ] 2454 | 2455 | [[package]] 2456 | name = "vcpkg" 2457 | version = "0.2.15" 2458 | source = "registry+https://github.com/rust-lang/crates.io-index" 2459 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 2460 | 2461 | [[package]] 2462 | name = "vec_map" 2463 | version = "0.8.2" 2464 | source = "registry+https://github.com/rust-lang/crates.io-index" 2465 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 2466 | 2467 | [[package]] 2468 | name = "version_check" 2469 | version = "0.9.4" 2470 | source = "registry+https://github.com/rust-lang/crates.io-index" 2471 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 2472 | 2473 | [[package]] 2474 | name = "want" 2475 | version = "0.3.0" 2476 | source = "registry+https://github.com/rust-lang/crates.io-index" 2477 | checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" 2478 | dependencies = [ 2479 | "log", 2480 | "try-lock", 2481 | ] 2482 | 2483 | [[package]] 2484 | name = "wasi" 2485 | version = "0.10.0+wasi-snapshot-preview1" 2486 | source = "registry+https://github.com/rust-lang/crates.io-index" 2487 | checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" 2488 | 2489 | [[package]] 2490 | name = "wasi" 2491 | version = "0.11.0+wasi-snapshot-preview1" 2492 | source = "registry+https://github.com/rust-lang/crates.io-index" 2493 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 2494 | 2495 | [[package]] 2496 | name = "wasm-bindgen" 2497 | version = "0.2.79" 2498 | source = "registry+https://github.com/rust-lang/crates.io-index" 2499 | checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06" 2500 | dependencies = [ 2501 | "cfg-if", 2502 | "wasm-bindgen-macro", 2503 | ] 2504 | 2505 | [[package]] 2506 | name = "wasm-bindgen-backend" 2507 | version = "0.2.79" 2508 | source = "registry+https://github.com/rust-lang/crates.io-index" 2509 | checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca" 2510 | dependencies = [ 2511 | "bumpalo", 2512 | "lazy_static", 2513 | "log", 2514 | "proc-macro2", 2515 | "quote", 2516 | "syn", 2517 | "wasm-bindgen-shared", 2518 | ] 2519 | 2520 | [[package]] 2521 | name = "wasm-bindgen-futures" 2522 | version = "0.4.29" 2523 | source = "registry+https://github.com/rust-lang/crates.io-index" 2524 | checksum = "2eb6ec270a31b1d3c7e266b999739109abce8b6c87e4b31fcfcd788b65267395" 2525 | dependencies = [ 2526 | "cfg-if", 2527 | "js-sys", 2528 | "wasm-bindgen", 2529 | "web-sys", 2530 | ] 2531 | 2532 | [[package]] 2533 | name = "wasm-bindgen-macro" 2534 | version = "0.2.79" 2535 | source = "registry+https://github.com/rust-lang/crates.io-index" 2536 | checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01" 2537 | dependencies = [ 2538 | "quote", 2539 | "wasm-bindgen-macro-support", 2540 | ] 2541 | 2542 | [[package]] 2543 | name = "wasm-bindgen-macro-support" 2544 | version = "0.2.79" 2545 | source = "registry+https://github.com/rust-lang/crates.io-index" 2546 | checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc" 2547 | dependencies = [ 2548 | "proc-macro2", 2549 | "quote", 2550 | "syn", 2551 | "wasm-bindgen-backend", 2552 | "wasm-bindgen-shared", 2553 | ] 2554 | 2555 | [[package]] 2556 | name = "wasm-bindgen-shared" 2557 | version = "0.2.79" 2558 | source = "registry+https://github.com/rust-lang/crates.io-index" 2559 | checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2" 2560 | 2561 | [[package]] 2562 | name = "web-sys" 2563 | version = "0.3.56" 2564 | source = "registry+https://github.com/rust-lang/crates.io-index" 2565 | checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb" 2566 | dependencies = [ 2567 | "js-sys", 2568 | "wasm-bindgen", 2569 | ] 2570 | 2571 | [[package]] 2572 | name = "webpki" 2573 | version = "0.21.4" 2574 | source = "registry+https://github.com/rust-lang/crates.io-index" 2575 | checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" 2576 | dependencies = [ 2577 | "ring", 2578 | "untrusted", 2579 | ] 2580 | 2581 | [[package]] 2582 | name = "winapi" 2583 | version = "0.3.9" 2584 | source = "registry+https://github.com/rust-lang/crates.io-index" 2585 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 2586 | dependencies = [ 2587 | "winapi-i686-pc-windows-gnu", 2588 | "winapi-x86_64-pc-windows-gnu", 2589 | ] 2590 | 2591 | [[package]] 2592 | name = "winapi-i686-pc-windows-gnu" 2593 | version = "0.4.0" 2594 | source = "registry+https://github.com/rust-lang/crates.io-index" 2595 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 2596 | 2597 | [[package]] 2598 | name = "winapi-util" 2599 | version = "0.1.5" 2600 | source = "registry+https://github.com/rust-lang/crates.io-index" 2601 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 2602 | dependencies = [ 2603 | "winapi", 2604 | ] 2605 | 2606 | [[package]] 2607 | name = "winapi-x86_64-pc-windows-gnu" 2608 | version = "0.4.0" 2609 | source = "registry+https://github.com/rust-lang/crates.io-index" 2610 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 2611 | 2612 | [[package]] 2613 | name = "windows-sys" 2614 | version = "0.32.0" 2615 | source = "registry+https://github.com/rust-lang/crates.io-index" 2616 | checksum = "3df6e476185f92a12c072be4a189a0210dcdcf512a1891d6dff9edb874deadc6" 2617 | dependencies = [ 2618 | "windows_aarch64_msvc", 2619 | "windows_i686_gnu", 2620 | "windows_i686_msvc", 2621 | "windows_x86_64_gnu", 2622 | "windows_x86_64_msvc", 2623 | ] 2624 | 2625 | [[package]] 2626 | name = "windows_aarch64_msvc" 2627 | version = "0.32.0" 2628 | source = "registry+https://github.com/rust-lang/crates.io-index" 2629 | checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5" 2630 | 2631 | [[package]] 2632 | name = "windows_i686_gnu" 2633 | version = "0.32.0" 2634 | source = "registry+https://github.com/rust-lang/crates.io-index" 2635 | checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615" 2636 | 2637 | [[package]] 2638 | name = "windows_i686_msvc" 2639 | version = "0.32.0" 2640 | source = "registry+https://github.com/rust-lang/crates.io-index" 2641 | checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172" 2642 | 2643 | [[package]] 2644 | name = "windows_x86_64_gnu" 2645 | version = "0.32.0" 2646 | source = "registry+https://github.com/rust-lang/crates.io-index" 2647 | checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc" 2648 | 2649 | [[package]] 2650 | name = "windows_x86_64_msvc" 2651 | version = "0.32.0" 2652 | source = "registry+https://github.com/rust-lang/crates.io-index" 2653 | checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316" 2654 | 2655 | [[package]] 2656 | name = "winreg" 2657 | version = "0.10.1" 2658 | source = "registry+https://github.com/rust-lang/crates.io-index" 2659 | checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" 2660 | dependencies = [ 2661 | "winapi", 2662 | ] 2663 | 2664 | [[package]] 2665 | name = "xmlparser" 2666 | version = "0.13.3" 2667 | source = "registry+https://github.com/rust-lang/crates.io-index" 2668 | checksum = "114ba2b24d2167ef6d67d7d04c8cc86522b87f490025f39f0303b7db5bf5e3d8" 2669 | 2670 | [[package]] 2671 | name = "xshell" 2672 | version = "0.2.0" 2673 | source = "registry+https://github.com/rust-lang/crates.io-index" 2674 | checksum = "3332cab90be2998a2aacb6494db45344bd16dfcc43ff36c42255018c6bcc96be" 2675 | dependencies = [ 2676 | "xshell-macros", 2677 | ] 2678 | 2679 | [[package]] 2680 | name = "xshell-macros" 2681 | version = "0.2.0" 2682 | source = "registry+https://github.com/rust-lang/crates.io-index" 2683 | checksum = "f47e54cffa76000b7641328ab3bb1e146f93a1690ab86c5909c656f49d91019c" 2684 | 2685 | [[package]] 2686 | name = "zeroize" 2687 | version = "1.5.4" 2688 | source = "registry+https://github.com/rust-lang/crates.io-index" 2689 | checksum = "7eb5728b8afd3f280a869ce1d4c554ffaed35f45c231fc41bfbd0381bef50317" 2690 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dynamodb_email_indexer" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | log = "0.4.14" 8 | env_logger = "0.9.0" 9 | tantivy = "0.17.0" 10 | anyhow = "1.0.56" 11 | tokio = { version = "1.17.0", features = ["full"] } 12 | futures = { version = "0.3.21" } 13 | lambda_runtime = "0.5.0" 14 | serde = { version = "1.0.136", features = ["derive"] } 15 | serde_json = "1.0.79" 16 | aws_lambda_events = "0.6.1" 17 | aws-config = "0.9.0" 18 | aws-sdk-dynamodb = "0.9.0" 19 | 20 | [dev-dependencies] 21 | xshell = "0.2.0" 22 | ulid = "0.5.0" 23 | rand = "0.8" 24 | fake = { version = "2.4.3", features=['derive']} 25 | structopt = { version = "0.3.26" } 26 | aws-sigv4 = "0.9.0" 27 | reqwest = { version = "0.11", features = ["json"] } 28 | http = "0.2.6" 29 | 30 | [profile.release] 31 | strip = "debuginfo" -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Full-text search for DynamoDB using Lambda, EFS, Tantivy and Rust. 2 | 3 | Read more on my blog https://jakejscott.com/full-text-search-for-dynamodb-using-lambda-efs-tantivy-and-rust 4 | -------------------------------------------------------------------------------- /bin/dynamodb-email-indexer.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import "source-map-support/register"; 3 | import * as cdk from "aws-cdk-lib"; 4 | import * as dotenv from "dotenv"; 5 | import { get } from "env-var"; 6 | import { DynamodbEmailIndexerStack } from "../lib/dynamodb-email-indexer-stack"; 7 | 8 | dotenv.config(); 9 | 10 | const CDK_DEFAULT_ACCOUNT = get("CDK_DEFAULT_ACCOUNT").required().asString(); 11 | const CDK_DEFAULT_REGION = get("CDK_DEFAULT_REGION").required().asString(); 12 | 13 | const app = new cdk.App(); 14 | const stack = new DynamodbEmailIndexerStack(app, "dynamodb-email-indexer", { 15 | env: { 16 | account: CDK_DEFAULT_ACCOUNT, 17 | region: CDK_DEFAULT_REGION, 18 | }, 19 | }); 20 | -------------------------------------------------------------------------------- /cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "node -r esbuild-runner/register bin/dynamodb-email-indexer.ts", 3 | "watch": { 4 | "include": ["**"], 5 | "exclude": [ 6 | "README.md", 7 | "cdk*.json", 8 | "**/*.d.ts", 9 | "**/*.js", 10 | "tsconfig.json", 11 | "package*.json", 12 | "yarn.lock", 13 | "node_modules", 14 | "test" 15 | ] 16 | }, 17 | "context": { 18 | "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true, 19 | "@aws-cdk/core:stackRelativeExports": true, 20 | "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, 21 | "@aws-cdk/aws-lambda:recognizeVersionProps": true, 22 | "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true, 23 | "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, 24 | "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, 25 | "@aws-cdk/core:target-partitions": ["aws", "aws-cn"] 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/benchmark.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{Error, Result}; 2 | use aws_config::profile::ProfileFileCredentialsProvider; 3 | use aws_sdk_dynamodb::{ 4 | model::{PutRequest, WriteRequest}, 5 | Region, 6 | }; 7 | use aws_sigv4::http_request::{ 8 | sign, PayloadChecksumKind, SignableRequest, SignatureLocation, SigningParams, SigningSettings, 9 | }; 10 | use dynamodb_email_indexer::{ 11 | email::Email, search_request::SearchRequest, search_response::SearchResponse, 12 | }; 13 | use fake::{ 14 | faker::{ 15 | internet::en::FreeEmailProvider, 16 | lorem::en::{Paragraph, Sentence}, 17 | name::en::{FirstName, LastName}, 18 | }, 19 | Fake, 20 | }; 21 | use http; 22 | use log::{debug, info}; 23 | use serde_json::{json, Value}; 24 | use std::time::SystemTime; 25 | use std::{ 26 | collections::HashMap, 27 | time::{Duration, Instant}, 28 | }; 29 | use structopt::StructOpt; 30 | use tantivy::chrono::Utc; 31 | use tokio::fs; 32 | use ulid::Ulid; 33 | use xshell::{cmd, Shell}; 34 | 35 | #[derive(StructOpt, Debug)] 36 | #[structopt(name = "basic")] 37 | struct Opt { 38 | /// How many emails to create 39 | #[structopt(short, long)] 40 | how_many: u64, 41 | 42 | /// AWS credentials profile name 43 | #[structopt(short, long)] 44 | profile: String, 45 | } 46 | 47 | #[tokio::main] 48 | async fn main() -> Result<(), Error> { 49 | std::env::set_var("RUST_LOG", "benchmark=info"); 50 | env_logger::init(); 51 | 52 | let options = Opt::from_args(); 53 | let profile = options.profile; 54 | 55 | // NOTE: read the aws access key and secret from the profile 56 | let sh = Shell::new()?; 57 | 58 | let access_key = cmd!( 59 | sh, 60 | "aws configure get aws_access_key_id --profile {profile}" 61 | ) 62 | .read()?; 63 | 64 | let secret_key = cmd!( 65 | sh, 66 | "aws configure get aws_secret_access_key --profile {profile}" 67 | ) 68 | .read()?; 69 | 70 | let region = cmd!(sh, "aws configure get region --profile {profile}").read()?; 71 | 72 | let start = Instant::now(); 73 | 74 | let json = fs::read_to_string("outputs.json").await?; 75 | let outputs = serde_json::from_str::(&json)?; 76 | 77 | let email_table_name = outputs 78 | .get(&profile) 79 | .unwrap() 80 | .get("EmailTableName") 81 | .unwrap() 82 | .as_str() 83 | .unwrap(); 84 | 85 | let email_index_reader_function_url = outputs 86 | .get(&profile) 87 | .unwrap() 88 | .get("EmailIndexReaderFunctionUrl") 89 | .unwrap() 90 | .as_str() 91 | .unwrap(); 92 | 93 | let credentials_provider = ProfileFileCredentialsProvider::builder() 94 | .profile_name(&profile) 95 | .build(); 96 | 97 | let config = aws_config::from_env() 98 | .region(Region::new(region)) 99 | .credentials_provider(credentials_provider) 100 | .load() 101 | .await; 102 | 103 | let ddb = aws_sdk_dynamodb::Client::new(&config); 104 | 105 | // NOTE: Get a count of all the docs in the index before starting... 106 | let search_response = search_docs( 107 | email_index_reader_function_url, 108 | &access_key, 109 | &secret_key, 110 | SearchRequest { 111 | limit: Some(1), 112 | query: Some("*".to_string()), 113 | }, 114 | ) 115 | .await?; 116 | 117 | info!( 118 | "index num docs before starting: {}", 119 | search_response.index_num_docs.unwrap_or(0) 120 | ); 121 | 122 | let index_count_start = search_response.index_num_docs.unwrap_or(0); 123 | 124 | let mut write_requests: Vec = vec![]; 125 | 126 | for _ in 1..=options.how_many { 127 | let mut to: Vec = vec![]; 128 | 129 | for _ in 0..1 { 130 | let first_name: String = FirstName().fake(); 131 | let last_name: String = LastName().fake(); 132 | let domain: String = FreeEmailProvider().fake(); 133 | let email = format!( 134 | "\"{} {}\" <{}.{}@{}>", 135 | first_name, 136 | last_name, 137 | first_name.to_lowercase(), 138 | last_name.to_lowercase(), 139 | domain 140 | ); 141 | to.push(email); 142 | } 143 | 144 | // NOTE: Expire emails after 90 days 145 | let ttl = Utc::now() 146 | .checked_add_signed(tantivy::chrono::Duration::days(90)) 147 | .unwrap() 148 | .timestamp(); 149 | 150 | let email = Email { 151 | id: Ulid::new().to_string(), 152 | timestamp: Utc::now().timestamp(), 153 | subject: Sentence(1..5).fake(), 154 | body: Paragraph(1..3).fake::(), 155 | to: to, 156 | ttl: ttl, 157 | }; 158 | 159 | debug!("email {:?}", email); 160 | 161 | let put_request = PutRequest::builder() 162 | .set_item(Some(email.attributes())) 163 | .build(); 164 | 165 | let write_request = WriteRequest::builder().put_request(put_request).build(); 166 | write_requests.push(write_request); 167 | } 168 | 169 | let total = write_requests.len(); 170 | let mut count = 0; 171 | for batch in write_requests.chunks(25) { 172 | let request_items = HashMap::from([(email_table_name.to_owned(), batch.to_vec())]); 173 | 174 | ddb.batch_write_item() 175 | .set_request_items(Some(request_items)) 176 | .send() 177 | .await?; 178 | 179 | count += batch.len(); 180 | info!("sent {count} of {total}"); 181 | } 182 | 183 | info!("checking total docs count"); 184 | 185 | loop { 186 | tokio::time::sleep(Duration::from_secs(3)).await; 187 | 188 | let search_response = search_docs( 189 | email_index_reader_function_url, 190 | &access_key, 191 | &secret_key, 192 | SearchRequest { 193 | limit: Some(1), 194 | query: Some("*".to_string()), 195 | }, 196 | ) 197 | .await?; 198 | 199 | let total_indexed = 200 | search_response.index_num_docs.unwrap_or(0) as i64 - (index_count_start as i64); 201 | 202 | info!( 203 | "index num docs: {} total indexed: {} elapsed: {:?}", 204 | search_response.index_num_docs.unwrap_or(0), 205 | total_indexed, 206 | start.elapsed() 207 | ); 208 | 209 | if total_indexed >= options.how_many as i64 { 210 | break; 211 | } 212 | } 213 | 214 | info!("done: {:?}", start.elapsed()); 215 | 216 | Ok(()) 217 | } 218 | 219 | async fn search_docs( 220 | url: &str, 221 | access_key: &String, 222 | secret_key: &String, 223 | search_request: SearchRequest, 224 | ) -> Result { 225 | let body = json!(&search_request).to_string(); 226 | 227 | // NOTE: The lambda function url is secured using IAM_AUTH, so we need to sign the request using aws v4 sig 228 | let mut request = http::Request::builder() 229 | .uri(url) 230 | .method("POST") 231 | .header("Content-Type", "application/json") 232 | .body(body.clone()) 233 | .unwrap(); 234 | 235 | let mut signing_settings = SigningSettings::default(); 236 | signing_settings.payload_checksum_kind = PayloadChecksumKind::XAmzSha256; 237 | signing_settings.signature_location = SignatureLocation::Headers; 238 | 239 | let signing_params = SigningParams::builder() 240 | .access_key(access_key) 241 | .secret_key(secret_key) 242 | .region("ap-southeast-2") 243 | .service_name("lambda") 244 | .time(SystemTime::now()) 245 | .settings(signing_settings) 246 | .build() 247 | .unwrap(); 248 | 249 | let signable_request = SignableRequest::from(&request); 250 | let (signing_instructions, _signature) = sign(signable_request, &signing_params) 251 | .unwrap() 252 | .into_parts(); 253 | 254 | signing_instructions.apply_to_request(&mut request); 255 | 256 | let client = reqwest::Client::new(); 257 | 258 | let mut builder = client.post(url); 259 | for (name, value) in request.headers() { 260 | builder = builder.header(name.as_str(), value.to_str().unwrap()); 261 | } 262 | 263 | let response = builder.body(body).send().await?; 264 | if response.status().is_success() { 265 | let search_response = response.json::().await?; 266 | return Ok(search_response); 267 | } 268 | 269 | let text = response.text().await?; 270 | println!("Error searching docs {}", text); 271 | return Err(anyhow::anyhow!(text)); 272 | } 273 | -------------------------------------------------------------------------------- /examples/package.rs: -------------------------------------------------------------------------------- 1 | use xshell::{cmd, Shell}; 2 | 3 | fn main() -> anyhow::Result<()> { 4 | let sh = Shell::new()?; 5 | 6 | cmd!( 7 | sh, 8 | "cargo build --release --target x86_64-unknown-linux-musl" 9 | ) 10 | .run()?; 11 | 12 | let _ = sh.remove_path("./build"); 13 | sh.create_dir("./build")?; 14 | 15 | let functions = vec!["email_index_writer", "email_index_reader"]; 16 | 17 | for function in &functions { 18 | let func = *function; 19 | sh.copy_file( 20 | format!("./target/x86_64-unknown-linux-musl/release/{func}"), 21 | format!("./build/{func}"), 22 | )?; 23 | } 24 | 25 | sh.change_dir("./build"); 26 | 27 | for function in &functions { 28 | let func = *function; 29 | sh.copy_file(func, "bootstrap")?; 30 | cmd!(sh, "zip {func}.zip bootstrap").run()?; 31 | sh.remove_path(format!("{func}"))?; 32 | sh.remove_path(format!("bootstrap"))?; 33 | } 34 | 35 | Ok(()) 36 | } 37 | -------------------------------------------------------------------------------- /examples/search.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{Error, Result}; 2 | use aws_sigv4::http_request::{ 3 | sign, PayloadChecksumKind, SignableRequest, SignatureLocation, SigningParams, SigningSettings, 4 | }; 5 | use dynamodb_email_indexer::{search_request::SearchRequest, search_response::SearchResponse}; 6 | use http; 7 | use log::info; 8 | use serde_json::{json, Value}; 9 | use std::time::Instant; 10 | use std::time::SystemTime; 11 | use structopt::StructOpt; 12 | use tokio::fs; 13 | use xshell::{cmd, Shell}; 14 | 15 | #[derive(StructOpt, Debug)] 16 | #[structopt(name = "basic")] 17 | struct Opt { 18 | /// Search query string 19 | #[structopt(short, long)] 20 | query: String, 21 | 22 | /// Search query string 23 | #[structopt(short, long)] 24 | limit: Option, 25 | 26 | /// AWS credentials profile name 27 | #[structopt(short, long)] 28 | profile: String, 29 | } 30 | 31 | #[tokio::main] 32 | async fn main() -> Result<(), Error> { 33 | std::env::set_var("RUST_LOG", "search=info"); 34 | env_logger::init(); 35 | 36 | let options = Opt::from_args(); 37 | let profile = options.profile; 38 | let query = options.query; 39 | let limit = options.limit.unwrap_or(100); 40 | 41 | // NOTE: read the aws access key and secret from the profile 42 | let sh = Shell::new()?; 43 | 44 | let access_key = cmd!( 45 | sh, 46 | "aws configure get aws_access_key_id --profile {profile}" 47 | ) 48 | .read()?; 49 | 50 | let secret_key = cmd!( 51 | sh, 52 | "aws configure get aws_secret_access_key --profile {profile}" 53 | ) 54 | .read()?; 55 | 56 | let start = Instant::now(); 57 | let json = fs::read_to_string("outputs.json").await?; 58 | let outputs = serde_json::from_str::(&json)?; 59 | 60 | let email_index_reader_function_url = outputs 61 | .get(&profile) 62 | .unwrap() 63 | .get("EmailIndexReaderFunctionUrl") 64 | .unwrap() 65 | .as_str() 66 | .unwrap(); 67 | 68 | let search_response = search_docs( 69 | email_index_reader_function_url, 70 | &access_key, 71 | &secret_key, 72 | SearchRequest { 73 | limit: Some(limit), 74 | query: Some(query.to_string()), 75 | }, 76 | ) 77 | .await?; 78 | 79 | let json = json!(&search_response); 80 | let pretty = serde_json::to_string_pretty(&json)?; 81 | 82 | info!("search response:\n{}", pretty); 83 | info!("done: {:?}", start.elapsed()); 84 | 85 | Ok(()) 86 | } 87 | 88 | async fn search_docs( 89 | url: &str, 90 | access_key: &String, 91 | secret_key: &String, 92 | search_request: SearchRequest, 93 | ) -> Result { 94 | let body = json!(&search_request).to_string(); 95 | 96 | // NOTE: The lambda function url is secured using IAM_AUTH, so we need to sign the request using aws v4 sig 97 | let mut request = http::Request::builder() 98 | .uri(url) 99 | .method("POST") 100 | .header("Content-Type", "application/json") 101 | .body(body.clone()) 102 | .unwrap(); 103 | 104 | let mut signing_settings = SigningSettings::default(); 105 | signing_settings.payload_checksum_kind = PayloadChecksumKind::XAmzSha256; 106 | signing_settings.signature_location = SignatureLocation::Headers; 107 | 108 | let signing_params = SigningParams::builder() 109 | .access_key(access_key) 110 | .secret_key(secret_key) 111 | .region("ap-southeast-2") 112 | .service_name("lambda") 113 | .time(SystemTime::now()) 114 | .settings(signing_settings) 115 | .build() 116 | .unwrap(); 117 | 118 | let signable_request = SignableRequest::from(&request); 119 | let (signing_instructions, _signature) = sign(signable_request, &signing_params) 120 | .unwrap() 121 | .into_parts(); 122 | 123 | signing_instructions.apply_to_request(&mut request); 124 | 125 | let client = reqwest::Client::new(); 126 | 127 | let mut builder = client.post(url); 128 | for (name, value) in request.headers() { 129 | builder = builder.header(name.as_str(), value.to_str().unwrap()); 130 | } 131 | 132 | let response = builder.body(body).send().await?; 133 | if response.status().is_success() { 134 | let search_response = response.json::().await?; 135 | return Ok(search_response); 136 | } 137 | 138 | let text = response.text().await?; 139 | println!("Error searching docs {}", text); 140 | return Err(anyhow::anyhow!(text)); 141 | } 142 | -------------------------------------------------------------------------------- /lib/dynamodb-email-indexer-stack.ts: -------------------------------------------------------------------------------- 1 | import * as cdk from "aws-cdk-lib"; 2 | import * as dynamodb from "aws-cdk-lib/aws-dynamodb"; 3 | import * as ec2 from "aws-cdk-lib/aws-ec2"; 4 | import * as efs from "aws-cdk-lib/aws-efs"; 5 | import * as lambda from "aws-cdk-lib/aws-lambda"; 6 | import * as event_sources from "aws-cdk-lib/aws-lambda-event-sources"; 7 | import * as sqs from "aws-cdk-lib/aws-sqs"; 8 | import * as constructs from "constructs"; 9 | 10 | export class DynamodbEmailIndexerStack extends cdk.Stack { 11 | constructor(scope: constructs.Construct, id: string, props?: cdk.StackProps) { 12 | super(scope, id, props); 13 | 14 | const vpc = new ec2.Vpc(this, "VPC", { 15 | natGateways: 0, 16 | maxAzs: 2, 17 | gatewayEndpoints: { 18 | dynamodb: { 19 | service: ec2.GatewayVpcEndpointAwsService.DYNAMODB, // NOTE: So we can talk to Dynamo from the index_reader lambda function 20 | }, 21 | }, 22 | }); 23 | 24 | const emailTable = new dynamodb.Table(this, "EmailTable", { 25 | partitionKey: { 26 | name: "id", 27 | type: dynamodb.AttributeType.STRING, 28 | }, 29 | billingMode: dynamodb.BillingMode.PAY_PER_REQUEST, 30 | timeToLiveAttribute: "ttl", 31 | stream: dynamodb.StreamViewType.NEW_AND_OLD_IMAGES, 32 | removalPolicy: cdk.RemovalPolicy.DESTROY, 33 | }); 34 | 35 | const fileSystem = new efs.FileSystem(this, "Efs", { 36 | vpc, 37 | encrypted: true, 38 | removalPolicy: cdk.RemovalPolicy.DESTROY, 39 | }); 40 | 41 | const accessPoint = fileSystem.addAccessPoint("AccessPoint", { 42 | path: "/export/lambda", 43 | createAcl: { 44 | ownerUid: "1001", 45 | ownerGid: "1001", 46 | permissions: "750", 47 | }, 48 | posixUser: { 49 | uid: "1001", 50 | gid: "1001", 51 | }, 52 | }); 53 | 54 | const lambdaFilesystem = lambda.FileSystem.fromEfsAccessPoint( 55 | accessPoint, 56 | "/mnt/msg" 57 | ); 58 | 59 | const emailIndexWriterFunction = new lambda.Function( 60 | this, 61 | "EmailIndexWriterFunction", 62 | { 63 | timeout: cdk.Duration.seconds(60), 64 | code: lambda.Code.fromAsset("./build/email_index_writer.zip"), 65 | handler: "rust-runtime", 66 | memorySize: 2048, 67 | runtime: lambda.Runtime.PROVIDED_AL2, 68 | filesystem: lambdaFilesystem, 69 | reservedConcurrentExecutions: 1, // NOTE: Tantivy can only have a single index writer, so we need to make this a singleton function 70 | vpc: vpc, 71 | environment: { 72 | EFS_MOUNT_PATH: lambdaFilesystem.config.localMountPath, 73 | RUST_LOG: "info", 74 | }, 75 | onFailure: new event_sources.SqsDlq( 76 | new sqs.Queue(this, "EmailIndexWriterFunctionDLQ", { 77 | removalPolicy: cdk.RemovalPolicy.DESTROY, 78 | }) 79 | ), 80 | } 81 | ); 82 | 83 | emailIndexWriterFunction.addEventSource( 84 | new event_sources.DynamoEventSource(emailTable, { 85 | enabled: true, 86 | startingPosition: lambda.StartingPosition.TRIM_HORIZON, 87 | batchSize: 1000, 88 | maxBatchingWindow: cdk.Duration.seconds(30), 89 | bisectBatchOnError: false, 90 | retryAttempts: 0, 91 | parallelizationFactor: 1, // NOTE: Tantivy can only have a single index writer, so we cannot index in parallel 92 | reportBatchItemFailures: true, 93 | tumblingWindow: undefined, 94 | maxRecordAge: undefined, 95 | onFailure: new event_sources.SqsDlq( 96 | new sqs.Queue(this, "EmailIndexWriterDynamoStreamDLQ", { 97 | removalPolicy: cdk.RemovalPolicy.DESTROY, 98 | }) 99 | ), 100 | }) 101 | ); 102 | 103 | const emailIndexReaderFunction = new lambda.Function( 104 | this, 105 | "EmailIndexReaderFunction", 106 | { 107 | timeout: cdk.Duration.seconds(30), 108 | code: lambda.Code.fromAsset("./build/email_index_reader.zip"), 109 | handler: "rust-runtime", 110 | memorySize: 2048, 111 | runtime: lambda.Runtime.PROVIDED_AL2, 112 | filesystem: lambdaFilesystem, 113 | vpc: vpc, 114 | environment: { 115 | EFS_MOUNT_PATH: lambdaFilesystem.config.localMountPath, 116 | RUST_LOG: "info", 117 | TABLE_NAME: emailTable.tableName, 118 | }, 119 | } 120 | ); 121 | 122 | emailTable.grantReadData(emailIndexReaderFunction); 123 | 124 | // TODO: Using escape hatch until CDK support for function urls 125 | // https://github.com/aws/aws-cdk/pull/19817 126 | // https://github.com/aws/aws-cdk/issues/19798 127 | const emailIndexReaderUrl = new cdk.CfnResource( 128 | this, 129 | "EmailIndexReaderUrl", 130 | { 131 | type: "AWS::Lambda::Url", 132 | properties: { 133 | AuthType: "AWS_IAM", 134 | TargetFunctionArn: emailIndexReaderFunction.functionArn, 135 | }, 136 | } 137 | ); 138 | 139 | new cdk.CfnOutput(this, "EmailTableName", { 140 | value: emailTable.tableName, 141 | }); 142 | 143 | new cdk.CfnOutput(this, "EmailIndexReaderFunctionUrl", { 144 | value: emailIndexReaderUrl.getAtt("FunctionUrl").toString(), 145 | }); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /loadtest/index.js: -------------------------------------------------------------------------------- 1 | import http from "k6/http"; 2 | import { check, sleep } from "k6"; 3 | 4 | export const options = { 5 | stages: [ 6 | { duration: "15s", target: 10 }, 7 | { duration: "30s", target: 25 }, 8 | { duration: "60s", target: 50 }, 9 | { duration: "20s", target: 0 }, 10 | ], 11 | thresholds: { 12 | // 95% of requests must finish within 100ms. 13 | http_req_duration: ["p(95) < 100"], 14 | }, 15 | ext: { 16 | loadimpact: { 17 | distribution: { 18 | "amazon:au:sydney": { loadZone: "amazon:au:sydney", percent: 100 }, 19 | }, 20 | }, 21 | }, 22 | }; 23 | 24 | export default function () { 25 | const url = __ENV.URL; 26 | const query = __ENV.QUERY; 27 | const limit = parseInt(__ENV.LIMIT); 28 | 29 | const payload = JSON.stringify({ 30 | query: query, 31 | limit: limit, 32 | }); 33 | 34 | const params = { 35 | headers: { 36 | "Content-Type": "application/json", 37 | }, 38 | }; 39 | 40 | const res = http.post(url, payload, params); 41 | check(res, { "status was 200": (r) => r.status == 200 }); 42 | 43 | const json = res.json(); 44 | check(json, { 45 | "no errors": (x) => x.error == null, 46 | }); 47 | 48 | sleep(1); 49 | } 50 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dynamodb-email-indexer", 3 | "version": "0.1.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@balena/dockerignore": { 8 | "version": "1.0.2", 9 | "resolved": "https://registry.npmjs.org/@balena/dockerignore/-/dockerignore-1.0.2.tgz", 10 | "integrity": "sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==" 11 | }, 12 | "@types/aws-lambda": { 13 | "version": "8.10.93", 14 | "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.93.tgz", 15 | "integrity": "sha512-Vsyi9ogDAY3REZDjYnXMRJJa62SDvxHXxJI5nGDQdZW058dDE+av/anynN2rLKbCKXDRNw3D/sQmqxVflZFi4A==", 16 | "dev": true 17 | }, 18 | "@types/node": { 19 | "version": "17.0.21", 20 | "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", 21 | "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==", 22 | "dev": true 23 | }, 24 | "at-least-node": { 25 | "version": "1.0.0", 26 | "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", 27 | "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" 28 | }, 29 | "aws-cdk": { 30 | "version": "2.16.0", 31 | "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.16.0.tgz", 32 | "integrity": "sha512-7pX1FNY2lhBwFJwrD4b0fTfbaYqLgxiGrd0KEu3oOk2yAR/GFaSo9L83jT7fdNzpZE1GzB4Ryd6svCCyPLE18A==", 33 | "dev": true, 34 | "requires": { 35 | "fsevents": "2.3.2" 36 | } 37 | }, 38 | "aws-cdk-lib": { 39 | "version": "2.16.0", 40 | "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.16.0.tgz", 41 | "integrity": "sha512-qQlSNat5/HPNFbExZ8LTVDLOZaI+8O2Uv6/vsp87gqfapTqu6MM9m7MouTnFd+f3JiAdTBDu7A0q802oZSH6JA==", 42 | "requires": { 43 | "@balena/dockerignore": "^1.0.2", 44 | "case": "1.6.3", 45 | "fs-extra": "^9.1.0", 46 | "ignore": "^5.2.0", 47 | "jsonschema": "^1.4.0", 48 | "minimatch": "^3.1.2", 49 | "punycode": "^2.1.1", 50 | "semver": "^7.3.5", 51 | "yaml": "1.10.2" 52 | } 53 | }, 54 | "balanced-match": { 55 | "version": "1.0.2", 56 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 57 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" 58 | }, 59 | "brace-expansion": { 60 | "version": "1.1.11", 61 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 62 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 63 | "requires": { 64 | "balanced-match": "^1.0.0", 65 | "concat-map": "0.0.1" 66 | } 67 | }, 68 | "buffer-from": { 69 | "version": "1.1.2", 70 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", 71 | "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" 72 | }, 73 | "case": { 74 | "version": "1.6.3", 75 | "resolved": "https://registry.npmjs.org/case/-/case-1.6.3.tgz", 76 | "integrity": "sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ==" 77 | }, 78 | "concat-map": { 79 | "version": "0.0.1", 80 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 81 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 82 | }, 83 | "constructs": { 84 | "version": "10.0.89", 85 | "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.0.89.tgz", 86 | "integrity": "sha512-Oz6A6lOgECJiysnLK7I2fZMkscfKmsIuAo4KX6AgKDOK5TO3OqSx0zHxajFXLuP6NZk/K2eBjry0aUpr32ZKkg==" 87 | }, 88 | "dotenv": { 89 | "version": "16.0.0", 90 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.0.tgz", 91 | "integrity": "sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q==", 92 | "dev": true 93 | }, 94 | "env-var": { 95 | "version": "7.1.1", 96 | "resolved": "https://registry.npmjs.org/env-var/-/env-var-7.1.1.tgz", 97 | "integrity": "sha512-4+vvlq+wwGQNwY/nI3/+Ojc1MKHCmITRJ6VWkQzDtMD6fAEb60ACRUCnlIAonMKW9YzqYmYxbyVu9vTb++yNRg==" 98 | }, 99 | "esbuild": { 100 | "version": "0.14.27", 101 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.27.tgz", 102 | "integrity": "sha512-MZQt5SywZS3hA9fXnMhR22dv0oPGh6QtjJRIYbgL1AeqAoQZE+Qn5ppGYQAoHv/vq827flj4tIJ79Mrdiwk46Q==", 103 | "dev": true, 104 | "requires": { 105 | "esbuild-android-64": "0.14.27", 106 | "esbuild-android-arm64": "0.14.27", 107 | "esbuild-darwin-64": "0.14.27", 108 | "esbuild-darwin-arm64": "0.14.27", 109 | "esbuild-freebsd-64": "0.14.27", 110 | "esbuild-freebsd-arm64": "0.14.27", 111 | "esbuild-linux-32": "0.14.27", 112 | "esbuild-linux-64": "0.14.27", 113 | "esbuild-linux-arm": "0.14.27", 114 | "esbuild-linux-arm64": "0.14.27", 115 | "esbuild-linux-mips64le": "0.14.27", 116 | "esbuild-linux-ppc64le": "0.14.27", 117 | "esbuild-linux-riscv64": "0.14.27", 118 | "esbuild-linux-s390x": "0.14.27", 119 | "esbuild-netbsd-64": "0.14.27", 120 | "esbuild-openbsd-64": "0.14.27", 121 | "esbuild-sunos-64": "0.14.27", 122 | "esbuild-windows-32": "0.14.27", 123 | "esbuild-windows-64": "0.14.27", 124 | "esbuild-windows-arm64": "0.14.27" 125 | } 126 | }, 127 | "esbuild-android-64": { 128 | "version": "0.14.27", 129 | "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.27.tgz", 130 | "integrity": "sha512-LuEd4uPuj/16Y8j6kqy3Z2E9vNY9logfq8Tq+oTE2PZVuNs3M1kj5Qd4O95ee66yDGb3isaOCV7sOLDwtMfGaQ==", 131 | "dev": true, 132 | "optional": true 133 | }, 134 | "esbuild-android-arm64": { 135 | "version": "0.14.27", 136 | "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.27.tgz", 137 | "integrity": "sha512-E8Ktwwa6vX8q7QeJmg8yepBYXaee50OdQS3BFtEHKrzbV45H4foMOeEE7uqdjGQZFBap5VAqo7pvjlyA92wznQ==", 138 | "dev": true, 139 | "optional": true 140 | }, 141 | "esbuild-darwin-64": { 142 | "version": "0.14.27", 143 | "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.27.tgz", 144 | "integrity": "sha512-czw/kXl/1ZdenPWfw9jDc5iuIYxqUxgQ/Q+hRd4/3udyGGVI31r29LCViN2bAJgGvQkqyLGVcG03PJPEXQ5i2g==", 145 | "dev": true, 146 | "optional": true 147 | }, 148 | "esbuild-darwin-arm64": { 149 | "version": "0.14.27", 150 | "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.27.tgz", 151 | "integrity": "sha512-BEsv2U2U4o672oV8+xpXNxN9bgqRCtddQC6WBh4YhXKDcSZcdNh7+6nS+DM2vu7qWIWNA4JbRG24LUUYXysimQ==", 152 | "dev": true, 153 | "optional": true 154 | }, 155 | "esbuild-freebsd-64": { 156 | "version": "0.14.27", 157 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.27.tgz", 158 | "integrity": "sha512-7FeiFPGBo+ga+kOkDxtPmdPZdayrSzsV9pmfHxcyLKxu+3oTcajeZlOO1y9HW+t5aFZPiv7czOHM4KNd0tNwCA==", 159 | "dev": true, 160 | "optional": true 161 | }, 162 | "esbuild-freebsd-arm64": { 163 | "version": "0.14.27", 164 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.27.tgz", 165 | "integrity": "sha512-8CK3++foRZJluOWXpllG5zwAVlxtv36NpHfsbWS7TYlD8S+QruXltKlXToc/5ZNzBK++l6rvRKELu/puCLc7jA==", 166 | "dev": true, 167 | "optional": true 168 | }, 169 | "esbuild-linux-32": { 170 | "version": "0.14.27", 171 | "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.27.tgz", 172 | "integrity": "sha512-qhNYIcT+EsYSBClZ5QhLzFzV5iVsP1YsITqblSaztr3+ZJUI+GoK8aXHyzKd7/CKKuK93cxEMJPpfi1dfsOfdw==", 173 | "dev": true, 174 | "optional": true 175 | }, 176 | "esbuild-linux-64": { 177 | "version": "0.14.27", 178 | "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.27.tgz", 179 | "integrity": "sha512-ESjck9+EsHoTaKWlFKJpPZRN26uiav5gkI16RuI8WBxUdLrrAlYuYSndxxKgEn1csd968BX/8yQZATYf/9+/qg==", 180 | "dev": true, 181 | "optional": true 182 | }, 183 | "esbuild-linux-arm": { 184 | "version": "0.14.27", 185 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.27.tgz", 186 | "integrity": "sha512-JnnmgUBdqLQO9hoNZQqNHFWlNpSX82vzB3rYuCJMhtkuaWQEmQz6Lec1UIxJdC38ifEghNTBsF9bbe8dFilnCw==", 187 | "dev": true, 188 | "optional": true 189 | }, 190 | "esbuild-linux-arm64": { 191 | "version": "0.14.27", 192 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.27.tgz", 193 | "integrity": "sha512-no6Mi17eV2tHlJnqBHRLekpZ2/VYx+NfGxKcBE/2xOMYwctsanCaXxw4zapvNrGE9X38vefVXLz6YCF8b1EHiQ==", 194 | "dev": true, 195 | "optional": true 196 | }, 197 | "esbuild-linux-mips64le": { 198 | "version": "0.14.27", 199 | "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.27.tgz", 200 | "integrity": "sha512-NolWP2uOvIJpbwpsDbwfeExZOY1bZNlWE/kVfkzLMsSgqeVcl5YMen/cedRe9mKnpfLli+i0uSp7N+fkKNU27A==", 201 | "dev": true, 202 | "optional": true 203 | }, 204 | "esbuild-linux-ppc64le": { 205 | "version": "0.14.27", 206 | "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.27.tgz", 207 | "integrity": "sha512-/7dTjDvXMdRKmsSxKXeWyonuGgblnYDn0MI1xDC7J1VQXny8k1qgNp6VmrlsawwnsymSUUiThhkJsI+rx0taNA==", 208 | "dev": true, 209 | "optional": true 210 | }, 211 | "esbuild-linux-riscv64": { 212 | "version": "0.14.27", 213 | "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.27.tgz", 214 | "integrity": "sha512-D+aFiUzOJG13RhrSmZgrcFaF4UUHpqj7XSKrIiCXIj1dkIkFqdrmqMSOtSs78dOtObWiOrFCDDzB24UyeEiNGg==", 215 | "dev": true, 216 | "optional": true 217 | }, 218 | "esbuild-linux-s390x": { 219 | "version": "0.14.27", 220 | "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.27.tgz", 221 | "integrity": "sha512-CD/D4tj0U4UQjELkdNlZhQ8nDHU5rBn6NGp47Hiz0Y7/akAY5i0oGadhEIg0WCY/HYVXFb3CsSPPwaKcTOW3bg==", 222 | "dev": true, 223 | "optional": true 224 | }, 225 | "esbuild-netbsd-64": { 226 | "version": "0.14.27", 227 | "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.27.tgz", 228 | "integrity": "sha512-h3mAld69SrO1VoaMpYl3a5FNdGRE/Nqc+E8VtHOag4tyBwhCQXxtvDDOAKOUQexBGca0IuR6UayQ4ntSX5ij1Q==", 229 | "dev": true, 230 | "optional": true 231 | }, 232 | "esbuild-openbsd-64": { 233 | "version": "0.14.27", 234 | "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.27.tgz", 235 | "integrity": "sha512-xwSje6qIZaDHXWoPpIgvL+7fC6WeubHHv18tusLYMwL+Z6bEa4Pbfs5IWDtQdHkArtfxEkIZz77944z8MgDxGw==", 236 | "dev": true, 237 | "optional": true 238 | }, 239 | "esbuild-runner": { 240 | "version": "2.2.1", 241 | "resolved": "https://registry.npmjs.org/esbuild-runner/-/esbuild-runner-2.2.1.tgz", 242 | "integrity": "sha512-VP0VfJJZiZ3cKzdOH59ZceDxx/GzBKra7tiGM8MfFMLv6CR1/cpsvtQ3IsJI3pz7HyeYxtbPyecj3fHwR+3XcQ==", 243 | "dev": true, 244 | "requires": { 245 | "source-map-support": "0.5.19", 246 | "tslib": "2.3.1" 247 | }, 248 | "dependencies": { 249 | "source-map-support": { 250 | "version": "0.5.19", 251 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", 252 | "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", 253 | "dev": true, 254 | "requires": { 255 | "buffer-from": "^1.0.0", 256 | "source-map": "^0.6.0" 257 | } 258 | } 259 | } 260 | }, 261 | "esbuild-sunos-64": { 262 | "version": "0.14.27", 263 | "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.27.tgz", 264 | "integrity": "sha512-/nBVpWIDjYiyMhuqIqbXXsxBc58cBVH9uztAOIfWShStxq9BNBik92oPQPJ57nzWXRNKQUEFWr4Q98utDWz7jg==", 265 | "dev": true, 266 | "optional": true 267 | }, 268 | "esbuild-windows-32": { 269 | "version": "0.14.27", 270 | "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.27.tgz", 271 | "integrity": "sha512-Q9/zEjhZJ4trtWhFWIZvS/7RUzzi8rvkoaS9oiizkHTTKd8UxFwn/Mm2OywsAfYymgUYm8+y2b+BKTNEFxUekw==", 272 | "dev": true, 273 | "optional": true 274 | }, 275 | "esbuild-windows-64": { 276 | "version": "0.14.27", 277 | "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.27.tgz", 278 | "integrity": "sha512-b3y3vTSl5aEhWHK66ngtiS/c6byLf6y/ZBvODH1YkBM+MGtVL6jN38FdHUsZasCz9gFwYs/lJMVY9u7GL6wfYg==", 279 | "dev": true, 280 | "optional": true 281 | }, 282 | "esbuild-windows-arm64": { 283 | "version": "0.14.27", 284 | "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.27.tgz", 285 | "integrity": "sha512-I/reTxr6TFMcR5qbIkwRGvldMIaiBu2+MP0LlD7sOlNXrfqIl9uNjsuxFPGEG4IRomjfQ5q8WT+xlF/ySVkqKg==", 286 | "dev": true, 287 | "optional": true 288 | }, 289 | "fs-extra": { 290 | "version": "9.1.0", 291 | "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", 292 | "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", 293 | "requires": { 294 | "at-least-node": "^1.0.0", 295 | "graceful-fs": "^4.2.0", 296 | "jsonfile": "^6.0.1", 297 | "universalify": "^2.0.0" 298 | } 299 | }, 300 | "fsevents": { 301 | "version": "2.3.2", 302 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 303 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 304 | "dev": true, 305 | "optional": true 306 | }, 307 | "graceful-fs": { 308 | "version": "4.2.10", 309 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", 310 | "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" 311 | }, 312 | "ignore": { 313 | "version": "5.2.0", 314 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", 315 | "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==" 316 | }, 317 | "jsonfile": { 318 | "version": "6.1.0", 319 | "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", 320 | "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", 321 | "requires": { 322 | "graceful-fs": "^4.1.6", 323 | "universalify": "^2.0.0" 324 | } 325 | }, 326 | "jsonschema": { 327 | "version": "1.4.0", 328 | "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.0.tgz", 329 | "integrity": "sha512-/YgW6pRMr6M7C+4o8kS+B/2myEpHCrxO4PEWnqJNBFMjn7EWXqlQ4tGwL6xTHeRplwuZmcAncdvfOad1nT2yMw==" 330 | }, 331 | "lru-cache": { 332 | "version": "6.0.0", 333 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", 334 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", 335 | "requires": { 336 | "yallist": "^4.0.0" 337 | } 338 | }, 339 | "minimatch": { 340 | "version": "3.1.2", 341 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 342 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 343 | "requires": { 344 | "brace-expansion": "^1.1.7" 345 | } 346 | }, 347 | "minimist": { 348 | "version": "1.2.6", 349 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", 350 | "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", 351 | "dev": true 352 | }, 353 | "punycode": { 354 | "version": "2.1.1", 355 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 356 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" 357 | }, 358 | "semver": { 359 | "version": "7.3.5", 360 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", 361 | "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", 362 | "requires": { 363 | "lru-cache": "^6.0.0" 364 | } 365 | }, 366 | "source-map": { 367 | "version": "0.6.1", 368 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 369 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" 370 | }, 371 | "source-map-support": { 372 | "version": "0.5.21", 373 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", 374 | "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", 375 | "requires": { 376 | "buffer-from": "^1.0.0", 377 | "source-map": "^0.6.0" 378 | } 379 | }, 380 | "tslib": { 381 | "version": "2.3.1", 382 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", 383 | "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", 384 | "dev": true 385 | }, 386 | "typescript": { 387 | "version": "4.6.2", 388 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.2.tgz", 389 | "integrity": "sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg==", 390 | "dev": true 391 | }, 392 | "universalify": { 393 | "version": "2.0.0", 394 | "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", 395 | "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" 396 | }, 397 | "yallist": { 398 | "version": "4.0.0", 399 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", 400 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" 401 | }, 402 | "yaml": { 403 | "version": "1.10.2", 404 | "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", 405 | "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" 406 | } 407 | } 408 | } 409 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dynamodb-email-indexer", 3 | "version": "0.1.0", 4 | "bin": { 5 | "dynamodb-email-indexer": "bin/dynamodb-email-indexer.js" 6 | }, 7 | "scripts": { 8 | "build": "tsc", 9 | "watch": "tsc -w", 10 | "cdk": "cdk", 11 | "bootstrap": "cdk bootstrap --profile dynamodb-email-indexer", 12 | "synth": "cdk synth dynamodb-email-indexer --profile dynamodb-email-indexer", 13 | "deploy": "cdk deploy dynamodb-email-indexer --profile dynamodb-email-indexer --outputs-file outputs.json", 14 | "destroy": "cdk destroy dynamodb-email-indexer --profile dynamodb-email-indexer", 15 | "benchmark": "cargo run --example benchmark -- --how-many 100 --profile dynamodb-email-indexer", 16 | "package": "cargo run --example package" 17 | }, 18 | "devDependencies": { 19 | "@types/aws-lambda": "^8.10.93", 20 | "@types/node": "17.0.21", 21 | "aws-cdk": "2.16.0", 22 | "dotenv": "^16.0.0", 23 | "esbuild": "0.14.27", 24 | "esbuild-runner": "^2.2.1", 25 | "minimist": ">=1.2.6", 26 | "typescript": "4.6.2" 27 | }, 28 | "dependencies": { 29 | "aws-cdk-lib": "2.16.0", 30 | "constructs": "10.0.89", 31 | "env-var": "^7.1.1", 32 | "source-map-support": "0.5.21" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/attribute_helper.rs: -------------------------------------------------------------------------------- 1 | use aws_sdk_dynamodb::model::AttributeValue; 2 | use std::collections::HashMap; 3 | 4 | pub struct AttributeHelper; 5 | 6 | impl AttributeHelper { 7 | pub fn parse_string( 8 | attributes: &HashMap, 9 | attribute_name: &str, 10 | ) -> anyhow::Result { 11 | if let Some(attr) = attributes.get(attribute_name) { 12 | if let AttributeValue::S(value) = attr { 13 | return Ok(value.clone()); 14 | } 15 | } 16 | 17 | Err(anyhow::anyhow!("{attribute_name} missing")) 18 | } 19 | 20 | pub fn parse_int_64( 21 | attributes: &HashMap, 22 | attribute_name: &str, 23 | ) -> anyhow::Result { 24 | if let Some(attr) = attributes.get(attribute_name) { 25 | if let AttributeValue::S(value) = attr { 26 | let result: i64 = value.parse()?; 27 | return Ok(result); 28 | } 29 | } 30 | 31 | Err(anyhow::anyhow!("{attribute_name} missing")) 32 | } 33 | 34 | pub fn parse_string_array( 35 | attributes: &HashMap, 36 | attribute_name: &str, 37 | ) -> anyhow::Result> { 38 | if let Some(attr) = attributes.get(attribute_name) { 39 | if let AttributeValue::Ss(values) = attr { 40 | return Ok(values.clone()); 41 | } 42 | }; 43 | 44 | Err(anyhow::anyhow!("{attribute_name} missing")) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/bin/email_index_reader.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Context; 2 | use aws_sdk_dynamodb::{ 3 | model::{AttributeValue, KeysAndAttributes}, 4 | Client, 5 | }; 6 | use dynamodb_email_indexer::email_index_schema::EmailIndexSchema; 7 | use dynamodb_email_indexer::search_response::SearchResponse; 8 | use dynamodb_email_indexer::{email::Email, search_request::SearchRequest}; 9 | use lambda_runtime::{service_fn, Error, LambdaEvent}; 10 | use log::info; 11 | use serde::{Deserialize, Serialize}; 12 | use serde_json::json; 13 | use std::{ 14 | collections::HashMap, 15 | sync::{Arc, Mutex}, 16 | time::{Duration, Instant}, 17 | }; 18 | use tantivy::{collector::Count, collector::TopDocs, query::QueryParser, IndexReader}; 19 | 20 | #[derive(Serialize, Deserialize)] 21 | struct LambdaFunctionUrlRequest { 22 | body: String, 23 | } 24 | 25 | struct Config { 26 | index_reader: IndexReader, 27 | email_index_schema: EmailIndexSchema, 28 | query_parser: QueryParser, 29 | ddb: Client, 30 | table_name: String, 31 | last_reload: Instant, 32 | } 33 | 34 | type SharedConfig = Arc>; 35 | 36 | #[tokio::main] 37 | async fn main() -> Result<(), Error> { 38 | env_logger::init(); 39 | 40 | let table_name = std::env::var("TABLE_NAME").context("TABLE_NAME env var missing")?; 41 | let config = aws_config::load_from_env().await; 42 | let ddb = aws_sdk_dynamodb::Client::new(&config); 43 | 44 | let email_index_schema = EmailIndexSchema::new(); 45 | let email_index = email_index_schema.ensure_index()?; 46 | 47 | let index_reader = email_index 48 | .reader_builder() 49 | .reload_policy(tantivy::ReloadPolicy::OnCommit) 50 | .try_into()?; 51 | 52 | let query_parser = QueryParser::for_index(&email_index, email_index_schema.default_fields()); 53 | 54 | let config = Config { 55 | index_reader, 56 | email_index_schema, 57 | query_parser, 58 | ddb, 59 | table_name, 60 | last_reload: Instant::now(), 61 | }; 62 | 63 | let shared_config = SharedConfig::new(Mutex::new(config)); 64 | 65 | lambda_runtime::run(service_fn( 66 | |event: LambdaEvent| async { 67 | let (event, _context) = event.into_parts(); 68 | info!("event: {}", json!(event)); 69 | 70 | let search_request: SearchRequest = serde_json::from_str(event.body.as_str())?; 71 | 72 | let start = Instant::now(); 73 | let config = &mut *shared_config.lock().unwrap(); 74 | 75 | if Instant::now() - config.last_reload > Duration::from_secs(3) { 76 | config.index_reader.reload()?; 77 | config.last_reload = Instant::now(); 78 | } 79 | 80 | let result = search(config, search_request).await?; 81 | 82 | println!("elapsed: {:?}", start.elapsed()); 83 | 84 | return Ok::(result); 85 | }, 86 | )) 87 | .await?; 88 | 89 | Ok(()) 90 | } 91 | 92 | async fn search(config: &Config, request: SearchRequest) -> Result { 93 | if request.query.is_none() { 94 | return Ok(SearchResponse::error("query is required")); 95 | } 96 | 97 | let query = request.query.unwrap(); 98 | let limit: usize = request.limit.unwrap_or(10); 99 | 100 | match config.query_parser.parse_query(query.as_str()) { 101 | Ok(query) => { 102 | let searcher = config.index_reader.searcher(); 103 | 104 | let total = searcher.num_docs(); 105 | let top_docs = searcher.search(&query, &TopDocs::with_limit(limit))?; 106 | let count = searcher.search(&query, &Count)?; 107 | 108 | let mut ids: Vec = vec![]; 109 | 110 | for (_, doc_address) in top_docs { 111 | let retrieved_doc = searcher.doc(doc_address)?; 112 | 113 | let id = retrieved_doc 114 | .get_first(config.email_index_schema.fields.id) 115 | .unwrap() 116 | .as_text() 117 | .unwrap(); 118 | 119 | ids.push(id.to_string()); 120 | } 121 | 122 | let emails: Vec = batch_get_items(config, &ids).await?; 123 | 124 | return Ok(SearchResponse::success(total, count, emails)); 125 | } 126 | Err(error) => { 127 | return Ok(SearchResponse::error(error.to_string().as_str())); 128 | } 129 | }; 130 | } 131 | 132 | async fn batch_get_items(config: &Config, ids: &Vec) -> anyhow::Result> { 133 | let mut emails: Vec = vec![]; 134 | 135 | for batch in ids.chunks(100) { 136 | let mut keys: Vec> = vec![]; 137 | 138 | for id in batch { 139 | let item = HashMap::from([("id".to_owned(), AttributeValue::S(id.to_owned()))]); 140 | keys.push(item); 141 | } 142 | 143 | let response = config 144 | .ddb 145 | .batch_get_item() 146 | .request_items( 147 | config.table_name.clone(), 148 | KeysAndAttributes::builder().set_keys(Some(keys)).build(), 149 | ) 150 | .send() 151 | .await?; 152 | 153 | for response in response.responses() { 154 | if let Some(rows) = response.get(config.table_name.as_str()) { 155 | for attributes in rows { 156 | let email = Email::from(attributes)?; 157 | emails.push(email); 158 | } 159 | } 160 | } 161 | } 162 | 163 | Ok(emails) 164 | } 165 | -------------------------------------------------------------------------------- /src/bin/email_index_writer.rs: -------------------------------------------------------------------------------- 1 | use aws_lambda_events::dynamodb::{attributes::AttributeValue, Event}; 2 | use dynamodb_email_indexer::email_index_schema::EmailIndexSchema; 3 | use lambda_runtime::{service_fn, Error, LambdaEvent}; 4 | use log::{debug, info}; 5 | use serde_json::json; 6 | use std::{ 7 | collections::HashMap, 8 | sync::{Arc, Mutex}, 9 | time::Instant, 10 | }; 11 | use tantivy::{doc, Document, IndexWriter, Term}; 12 | struct Config { 13 | email_index_schema: EmailIndexSchema, 14 | } 15 | 16 | type SharedConfig = Arc>; 17 | 18 | #[tokio::main] 19 | async fn main() -> Result<(), Error> { 20 | env_logger::init(); 21 | 22 | let email_index_schema = EmailIndexSchema::new(); 23 | let email_index = email_index_schema.ensure_index()?; 24 | let config = Config { email_index_schema }; 25 | let shared_config = SharedConfig::new(Mutex::new(config)); 26 | 27 | lambda_runtime::run(service_fn(|event: LambdaEvent| async { 28 | let (event, _context) = event.into_parts(); 29 | let start = Instant::now(); 30 | 31 | let config = &mut *shared_config.lock().unwrap(); 32 | let mut index_writer = email_index.writer(200_000_000)?; 33 | 34 | let result = index_write(config, &mut index_writer, event).await?; 35 | 36 | index_writer.wait_merging_threads()?; 37 | println!("elapsed: {:?}", start.elapsed()); 38 | 39 | return Ok::<(), Error>(result); 40 | })) 41 | .await?; 42 | 43 | Ok(()) 44 | } 45 | 46 | async fn index_write( 47 | config: &mut Config, 48 | index_writer: &mut IndexWriter, 49 | event: Event, 50 | ) -> Result<(), Error> { 51 | let total = event.records.len() as u32; 52 | 53 | let mut created = 0_u32; 54 | let mut updated = 0_u32; 55 | let mut deleted = 0_u32; 56 | 57 | for record in event.records { 58 | match record.event_name.as_str() { 59 | "INSERT" => { 60 | let doc = parse_document(config, record.change.new_image)?; 61 | debug!("creating document"); 62 | index_writer.add_document(doc)?; 63 | created += 1; 64 | } 65 | "MODIFY" => { 66 | let doc = parse_document(config, record.change.new_image)?; 67 | debug!("updating document"); 68 | let term = get_id_term(config, &doc); 69 | index_writer.delete_term(term); 70 | index_writer.add_document(doc)?; 71 | updated += 1; 72 | } 73 | "REMOVE" => { 74 | let doc = parse_document(config, record.change.old_image)?; 75 | debug!("deleting document"); 76 | let term = get_id_term(config, &doc); 77 | index_writer.delete_term(term); 78 | deleted += 1; 79 | } 80 | _ => {} 81 | } 82 | } 83 | 84 | info!("commiting index"); 85 | index_writer.commit()?; 86 | 87 | let result = json!({ 88 | "total": total, 89 | "created": created, 90 | "updated": updated, 91 | "deleted": deleted, 92 | "skipped": total - created - updated - deleted, 93 | }); 94 | 95 | info!("indexed {}", result); 96 | 97 | Ok(()) 98 | } 99 | 100 | fn get_id_term(config: &Config, doc: &Document) -> Term { 101 | let id = doc 102 | .get_first(config.email_index_schema.fields.id) 103 | .expect("Documents should have a id value") 104 | .as_text() 105 | .expect("id value should be text"); 106 | 107 | let term = Term::from_field_text(config.email_index_schema.fields.id, id); 108 | term 109 | } 110 | 111 | fn parse_document( 112 | config: &Config, 113 | attributes: HashMap, 114 | ) -> anyhow::Result { 115 | let id = parse_string(&attributes, "id")?; 116 | let timestamp: i64 = parse_string(&attributes, "timestamp")?.parse()?; 117 | let subject = parse_string(&attributes, "subject")?; 118 | let body = parse_string(&attributes, "body")?; 119 | let to = parse_string_array(&attributes, "to")?; 120 | 121 | let mut doc = doc!( 122 | config.email_index_schema.fields.id => id, 123 | config.email_index_schema.fields.timestamp => timestamp, 124 | config.email_index_schema.fields.subject => subject, 125 | config.email_index_schema.fields.body => body, 126 | ); 127 | 128 | for email in to { 129 | doc.add_text(config.email_index_schema.fields.to, email); 130 | } 131 | 132 | Ok(doc) 133 | } 134 | 135 | pub fn parse_string( 136 | attributes: &HashMap, 137 | attribute_name: &str, 138 | ) -> anyhow::Result { 139 | if let Some(attr) = attributes.get(attribute_name) { 140 | if let AttributeValue::String(value) = attr { 141 | return Ok(value.clone()); 142 | } 143 | } 144 | 145 | Err(anyhow::anyhow!("{attribute_name} missing")) 146 | } 147 | 148 | pub fn parse_string_array( 149 | attributes: &HashMap, 150 | attribute_name: &str, 151 | ) -> anyhow::Result> { 152 | if let Some(attr) = attributes.get(attribute_name) { 153 | if let AttributeValue::StringSet(values) = attr { 154 | return Ok(values.clone()); 155 | } 156 | }; 157 | 158 | Err(anyhow::anyhow!("{attribute_name} missing")) 159 | } 160 | -------------------------------------------------------------------------------- /src/email.rs: -------------------------------------------------------------------------------- 1 | use crate::attribute_helper::AttributeHelper; 2 | use aws_sdk_dynamodb::model::AttributeValue; 3 | use serde::{Deserialize, Serialize}; 4 | use std::collections::HashMap; 5 | 6 | #[derive(Serialize, Deserialize, Debug)] 7 | pub struct Email { 8 | pub id: String, 9 | pub timestamp: i64, 10 | pub subject: String, 11 | pub body: String, 12 | pub to: Vec, 13 | pub ttl: i64, 14 | } 15 | 16 | impl Email { 17 | pub fn attributes(self) -> HashMap { 18 | HashMap::from([ 19 | ("id".into(), AttributeValue::S(self.id)), 20 | ( 21 | "timestamp".into(), 22 | AttributeValue::S(self.timestamp.to_string()), 23 | ), 24 | ("subject".into(), AttributeValue::S(self.subject)), 25 | ("body".into(), AttributeValue::S(self.body)), 26 | ("to".into(), AttributeValue::Ss(self.to)), 27 | ("ttl".into(), AttributeValue::S(self.ttl.to_string())), 28 | ]) 29 | } 30 | 31 | pub fn from(attributes: &HashMap) -> anyhow::Result { 32 | let id = AttributeHelper::parse_string(attributes, "id")?; 33 | let timestamp = AttributeHelper::parse_int_64(attributes, "timestamp")?; 34 | let subject = AttributeHelper::parse_string(attributes, "subject")?; 35 | let body = AttributeHelper::parse_string(attributes, "body")?; 36 | let to = AttributeHelper::parse_string_array(attributes, "to")?; 37 | let ttl = AttributeHelper::parse_int_64(attributes, "ttl")?; 38 | 39 | let email = Email { 40 | id: id, 41 | timestamp: timestamp, 42 | body: body, 43 | subject: subject, 44 | to: to, 45 | ttl: ttl, 46 | }; 47 | 48 | Ok(email) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/email_index_schema.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{Context, Result}; 2 | use log::info; 3 | use std::{path::PathBuf, str::FromStr}; 4 | use tantivy::{ 5 | schema::{Field, Schema, INDEXED, STORED, STRING, TEXT}, 6 | Index, 7 | }; 8 | pub struct EmailIndexSchema { 9 | pub schema: Schema, 10 | pub fields: EmailIndexFields, 11 | } 12 | 13 | pub struct EmailIndexFields { 14 | pub id: Field, 15 | pub timestamp: Field, 16 | pub subject: Field, 17 | pub body: Field, 18 | pub to: Field, 19 | } 20 | 21 | impl EmailIndexSchema { 22 | pub fn new() -> Self { 23 | let mut builder = Schema::builder(); 24 | 25 | let id = builder.add_text_field("id", STRING | STORED); 26 | let timestamp = builder.add_i64_field("timestamp", INDEXED); // ADD FAST FIELD. Also test if indexed is needed 27 | let subject = builder.add_text_field("subject", TEXT); 28 | let body = builder.add_text_field("body", TEXT); 29 | let to = builder.add_text_field("to", TEXT); 30 | 31 | let schema = builder.build(); 32 | 33 | let fields = EmailIndexFields { 34 | id, 35 | timestamp, 36 | to, 37 | body, 38 | subject, 39 | }; 40 | 41 | EmailIndexSchema { schema, fields } 42 | } 43 | 44 | pub fn default_fields(&self) -> Vec { 45 | vec![ 46 | self.fields.id, 47 | self.fields.timestamp, 48 | self.fields.subject, 49 | self.fields.body, 50 | self.fields.to, 51 | ] 52 | } 53 | 54 | pub fn ensure_index(&self) -> Result { 55 | let index_path = self.get_index_path()?; 56 | 57 | let index: Index; 58 | if !index_path.exists() { 59 | info!("creating index"); 60 | std::fs::create_dir(&index_path).context("Error creating index dir")?; 61 | index = Index::create_in_dir(&index_path, self.schema.clone()) 62 | .context("Error creating index")?; 63 | } else { 64 | info!("opening index"); 65 | index = self.open().context("Error opening index")?; 66 | } 67 | 68 | Ok(index) 69 | } 70 | 71 | fn open(&self) -> Result { 72 | let index_path = self.get_index_path()?; 73 | let index = Index::open_in_dir(&index_path).context("Error opening index")?; 74 | 75 | Ok(index) 76 | } 77 | 78 | fn get_index_path(&self) -> Result { 79 | let mount_path = 80 | std::env::var("EFS_MOUNT_PATH").context("EFS_MOUNT_PATH env var missing")?; 81 | 82 | let path = PathBuf::from_str(mount_path.as_str()).context("EFS_MOUNT_PATH is not valid")?; 83 | let index_path = path.join(PathBuf::from("index")); 84 | Ok(index_path) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod attribute_helper; 2 | pub mod email; 3 | pub mod email_index_schema; 4 | pub mod search_request; 5 | pub mod search_response; 6 | -------------------------------------------------------------------------------- /src/search_request.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Deserialize, Serialize)] 4 | pub struct SearchRequest { 5 | pub query: Option, 6 | pub limit: Option, 7 | } 8 | -------------------------------------------------------------------------------- /src/search_response.rs: -------------------------------------------------------------------------------- 1 | use crate::email::Email; 2 | 3 | use serde::Deserialize; 4 | use serde::Serialize; 5 | 6 | #[derive(Serialize, Deserialize, Default, Debug)] 7 | pub struct SearchResponse { 8 | pub index_num_docs: Option, 9 | pub query_num_docs: Option, 10 | pub emails: Option>, 11 | pub error: Option, 12 | } 13 | 14 | impl SearchResponse { 15 | pub fn error(error: &str) -> Self { 16 | SearchResponse { 17 | error: Some(error.to_string()), 18 | ..Default::default() 19 | } 20 | } 21 | 22 | pub fn success(total: u64, count: usize, emails: Vec) -> Self { 23 | SearchResponse { 24 | index_num_docs: Some(total), 25 | query_num_docs: Some(count), 26 | emails: Some(emails), 27 | error: None, 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "module": "commonjs", 5 | "lib": ["es2018"], 6 | "declaration": true, 7 | "strict": true, 8 | "noImplicitAny": true, 9 | "strictNullChecks": true, 10 | "noImplicitThis": true, 11 | "alwaysStrict": true, 12 | "noUnusedLocals": false, 13 | "noUnusedParameters": false, 14 | "noImplicitReturns": true, 15 | "noFallthroughCasesInSwitch": false, 16 | "inlineSourceMap": true, 17 | "inlineSources": true, 18 | "experimentalDecorators": true, 19 | "strictPropertyInitialization": false, 20 | "noEmit": true, 21 | "typeRoots": ["./node_modules/@types"] 22 | }, 23 | "exclude": ["node_modules", "cdk.out"] 24 | } 25 | --------------------------------------------------------------------------------