├── .gitignore ├── .vscode └── settings.json ├── Cargo.lock ├── Cargo.toml ├── Readme.md ├── base ├── Cargo.toml ├── sql │ ├── create_tables.sql │ ├── insert_inline_link.sql │ ├── insert_reference_link.sql │ ├── insert_resource.sql │ ├── insert_tag.sql │ ├── migrate_tables.sql │ ├── select_links_by_referrer.sql │ ├── select_links_by_target.sql │ ├── select_resource_by_url.sql │ ├── select_tags_by_name.sql │ └── select_tags_by_target.sql └── src │ ├── data.rs │ ├── index.rs │ ├── lib.rs │ ├── schema.rs │ ├── server.rs │ ├── service.rs │ ├── state.rs │ └── store.rs ├── program ├── Cargo.toml └── src │ └── main.rs ├── scanner ├── Cargo.toml └── src │ ├── frontmatter.rs │ ├── lib.rs │ ├── markdown.rs │ ├── resource.rs │ └── scanner.rs └── syntax ├── Cargo.toml └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.procMacro.enabled": true 3 | } -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "aho-corasick" 5 | version = "0.7.10" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada" 8 | dependencies = [ 9 | "memchr", 10 | ] 11 | 12 | [[package]] 13 | name = "anyhow" 14 | version = "1.0.28" 15 | source = "registry+https://github.com/rust-lang/crates.io-index" 16 | checksum = "d9a60d744a80c30fcb657dfe2c1b22bcb3e814c1a1e3674f32bf5820b570fbff" 17 | 18 | [[package]] 19 | name = "arrayref" 20 | version = "0.3.6" 21 | source = "registry+https://github.com/rust-lang/crates.io-index" 22 | checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" 23 | 24 | [[package]] 25 | name = "arrayvec" 26 | version = "0.5.1" 27 | source = "registry+https://github.com/rust-lang/crates.io-index" 28 | checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" 29 | 30 | [[package]] 31 | name = "async-attributes" 32 | version = "1.1.1" 33 | source = "registry+https://github.com/rust-lang/crates.io-index" 34 | checksum = "efd3d156917d94862e779f356c5acae312b08fd3121e792c857d7928c8088423" 35 | dependencies = [ 36 | "quote", 37 | "syn", 38 | ] 39 | 40 | [[package]] 41 | name = "async-std" 42 | version = "1.5.0" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "538ecb01eb64eecd772087e5b6f7540cbc917f047727339a472dafed2185b267" 45 | dependencies = [ 46 | "async-attributes", 47 | "async-task", 48 | "broadcaster", 49 | "crossbeam-channel 0.4.2", 50 | "crossbeam-deque", 51 | "crossbeam-utils 0.7.2", 52 | "futures-core", 53 | "futures-io", 54 | "futures-timer", 55 | "kv-log-macro", 56 | "log", 57 | "memchr", 58 | "mio", 59 | "mio-uds", 60 | "num_cpus", 61 | "once_cell", 62 | "pin-project-lite", 63 | "pin-utils", 64 | "slab", 65 | ] 66 | 67 | [[package]] 68 | name = "async-task" 69 | version = "1.3.1" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | checksum = "0ac2c016b079e771204030951c366db398864f5026f84a44dafb0ff20f02085d" 72 | dependencies = [ 73 | "libc", 74 | "winapi 0.3.8", 75 | ] 76 | 77 | [[package]] 78 | name = "async-trait" 79 | version = "0.1.29" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | checksum = "bab5c215748dc1ad11a145359b1067107ae0f8ca5e99844fa64067ed5bf198e3" 82 | dependencies = [ 83 | "proc-macro2", 84 | "quote", 85 | "syn", 86 | ] 87 | 88 | [[package]] 89 | name = "atomicwrites" 90 | version = "0.2.5" 91 | source = "registry+https://github.com/rust-lang/crates.io-index" 92 | checksum = "6a2baf2feb820299c53c7ad1cc4f5914a220a1cb76d7ce321d2522a94b54651f" 93 | dependencies = [ 94 | "nix", 95 | "tempdir", 96 | "winapi 0.3.8", 97 | ] 98 | 99 | [[package]] 100 | name = "atty" 101 | version = "0.2.14" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 104 | dependencies = [ 105 | "hermit-abi", 106 | "libc", 107 | "winapi 0.3.8", 108 | ] 109 | 110 | [[package]] 111 | name = "autocfg" 112 | version = "0.1.7" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" 115 | 116 | [[package]] 117 | name = "autocfg" 118 | version = "1.0.0" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" 121 | 122 | [[package]] 123 | name = "backtrace" 124 | version = "0.3.46" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "b1e692897359247cc6bb902933361652380af0f1b7651ae5c5013407f30e109e" 127 | dependencies = [ 128 | "backtrace-sys", 129 | "cfg-if", 130 | "libc", 131 | "rustc-demangle", 132 | ] 133 | 134 | [[package]] 135 | name = "backtrace-sys" 136 | version = "0.1.35" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "7de8aba10a69c8e8d7622c5710229485ec32e9d55fdad160ea559c086fdcd118" 139 | dependencies = [ 140 | "cc", 141 | "libc", 142 | ] 143 | 144 | [[package]] 145 | name = "base64" 146 | version = "0.11.0" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" 149 | 150 | [[package]] 151 | name = "bitflags" 152 | version = "1.2.1" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 155 | 156 | [[package]] 157 | name = "bitpacking" 158 | version = "0.8.2" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | checksum = "3744aff20a3437a99ebc0bb7733e9e60c7bf590478c9b897e95b38d57e5acb68" 161 | dependencies = [ 162 | "crunchy", 163 | ] 164 | 165 | [[package]] 166 | name = "blake2b_simd" 167 | version = "0.5.10" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" 170 | dependencies = [ 171 | "arrayref", 172 | "arrayvec", 173 | "constant_time_eq", 174 | ] 175 | 176 | [[package]] 177 | name = "broadcaster" 178 | version = "1.0.0" 179 | source = "registry+https://github.com/rust-lang/crates.io-index" 180 | checksum = "d9c972e21e0d055a36cf73e4daae870941fe7a8abcd5ac3396aab9e4c126bd87" 181 | dependencies = [ 182 | "futures-channel", 183 | "futures-core", 184 | "futures-sink", 185 | "futures-util", 186 | "parking_lot 0.10.0", 187 | "slab", 188 | ] 189 | 190 | [[package]] 191 | name = "bstr" 192 | version = "0.2.12" 193 | source = "registry+https://github.com/rust-lang/crates.io-index" 194 | checksum = "2889e6d50f394968c8bf4240dc3f2a7eb4680844d27308f798229ac9d4725f41" 195 | dependencies = [ 196 | "memchr", 197 | ] 198 | 199 | [[package]] 200 | name = "bumpalo" 201 | version = "3.2.1" 202 | source = "registry+https://github.com/rust-lang/crates.io-index" 203 | checksum = "12ae9db68ad7fac5fe51304d20f016c911539251075a214f8e663babefa35187" 204 | 205 | [[package]] 206 | name = "byteorder" 207 | version = "1.3.4" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" 210 | 211 | [[package]] 212 | name = "bytes" 213 | version = "0.4.12" 214 | source = "registry+https://github.com/rust-lang/crates.io-index" 215 | checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" 216 | dependencies = [ 217 | "byteorder", 218 | "either", 219 | "iovec", 220 | ] 221 | 222 | [[package]] 223 | name = "bytes" 224 | version = "0.5.4" 225 | source = "registry+https://github.com/rust-lang/crates.io-index" 226 | checksum = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1" 227 | 228 | [[package]] 229 | name = "cc" 230 | version = "1.0.50" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" 233 | 234 | [[package]] 235 | name = "census" 236 | version = "0.4.0" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | checksum = "5927edd8345aef08578bcbb4aea7314f340d80c7f4931f99fbeb40b99d8f5060" 239 | 240 | [[package]] 241 | name = "cfg-if" 242 | version = "0.1.10" 243 | source = "registry+https://github.com/rust-lang/crates.io-index" 244 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 245 | 246 | [[package]] 247 | name = "chrono" 248 | version = "0.4.11" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | checksum = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2" 251 | dependencies = [ 252 | "num-integer", 253 | "num-traits", 254 | "time", 255 | ] 256 | 257 | [[package]] 258 | name = "cloudabi" 259 | version = "0.0.3" 260 | source = "registry+https://github.com/rust-lang/crates.io-index" 261 | checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" 262 | dependencies = [ 263 | "bitflags", 264 | ] 265 | 266 | [[package]] 267 | name = "combine" 268 | version = "4.0.1" 269 | source = "registry+https://github.com/rust-lang/crates.io-index" 270 | checksum = "c875843236b5e2eb535fd0b696387bfb623f896479b10ed626cf442b836e8032" 271 | dependencies = [ 272 | "bytes 0.5.4", 273 | "memchr", 274 | ] 275 | 276 | [[package]] 277 | name = "commander-core" 278 | version = "1.2.0" 279 | source = "git+https://github.com/gozala/commander-rust#bb8a6627986494680638cd4a2621fd9a771bff0d" 280 | dependencies = [ 281 | "lazy_static", 282 | ] 283 | 284 | [[package]] 285 | name = "commander-macros" 286 | version = "1.2.0" 287 | source = "git+https://github.com/gozala/commander-rust#bb8a6627986494680638cd4a2621fd9a771bff0d" 288 | dependencies = [ 289 | "lazy_static", 290 | "proc-macro2", 291 | "quote", 292 | "syn", 293 | ] 294 | 295 | [[package]] 296 | name = "commander-rust" 297 | version = "1.2.1" 298 | source = "git+https://github.com/gozala/commander-rust#bb8a6627986494680638cd4a2621fd9a771bff0d" 299 | dependencies = [ 300 | "commander-core", 301 | "commander-macros", 302 | "lazy_static", 303 | ] 304 | 305 | [[package]] 306 | name = "constant_time_eq" 307 | version = "0.1.5" 308 | source = "registry+https://github.com/rust-lang/crates.io-index" 309 | checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" 310 | 311 | [[package]] 312 | name = "cookie" 313 | version = "0.12.0" 314 | source = "registry+https://github.com/rust-lang/crates.io-index" 315 | checksum = "888604f00b3db336d2af898ec3c1d5d0ddf5e6d462220f2ededc33a87ac4bbd5" 316 | dependencies = [ 317 | "time", 318 | "url 1.7.2", 319 | ] 320 | 321 | [[package]] 322 | name = "crc32fast" 323 | version = "1.2.0" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" 326 | dependencies = [ 327 | "cfg-if", 328 | ] 329 | 330 | [[package]] 331 | name = "crossbeam" 332 | version = "0.7.3" 333 | source = "registry+https://github.com/rust-lang/crates.io-index" 334 | checksum = "69323bff1fb41c635347b8ead484a5ca6c3f11914d784170b158d8449ab07f8e" 335 | dependencies = [ 336 | "cfg-if", 337 | "crossbeam-channel 0.4.2", 338 | "crossbeam-deque", 339 | "crossbeam-epoch", 340 | "crossbeam-queue", 341 | "crossbeam-utils 0.7.2", 342 | ] 343 | 344 | [[package]] 345 | name = "crossbeam-channel" 346 | version = "0.3.9" 347 | source = "registry+https://github.com/rust-lang/crates.io-index" 348 | checksum = "c8ec7fcd21571dc78f96cc96243cab8d8f035247c3efd16c687be154c3fa9efa" 349 | dependencies = [ 350 | "crossbeam-utils 0.6.6", 351 | ] 352 | 353 | [[package]] 354 | name = "crossbeam-channel" 355 | version = "0.4.2" 356 | source = "registry+https://github.com/rust-lang/crates.io-index" 357 | checksum = "cced8691919c02aac3cb0a1bc2e9b73d89e832bf9a06fc579d4e71b68a2da061" 358 | dependencies = [ 359 | "crossbeam-utils 0.7.2", 360 | "maybe-uninit", 361 | ] 362 | 363 | [[package]] 364 | name = "crossbeam-deque" 365 | version = "0.7.3" 366 | source = "registry+https://github.com/rust-lang/crates.io-index" 367 | checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285" 368 | dependencies = [ 369 | "crossbeam-epoch", 370 | "crossbeam-utils 0.7.2", 371 | "maybe-uninit", 372 | ] 373 | 374 | [[package]] 375 | name = "crossbeam-epoch" 376 | version = "0.8.2" 377 | source = "registry+https://github.com/rust-lang/crates.io-index" 378 | checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" 379 | dependencies = [ 380 | "autocfg 1.0.0", 381 | "cfg-if", 382 | "crossbeam-utils 0.7.2", 383 | "lazy_static", 384 | "maybe-uninit", 385 | "memoffset", 386 | "scopeguard", 387 | ] 388 | 389 | [[package]] 390 | name = "crossbeam-queue" 391 | version = "0.2.1" 392 | source = "registry+https://github.com/rust-lang/crates.io-index" 393 | checksum = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db" 394 | dependencies = [ 395 | "cfg-if", 396 | "crossbeam-utils 0.7.2", 397 | ] 398 | 399 | [[package]] 400 | name = "crossbeam-utils" 401 | version = "0.6.6" 402 | source = "registry+https://github.com/rust-lang/crates.io-index" 403 | checksum = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" 404 | dependencies = [ 405 | "cfg-if", 406 | "lazy_static", 407 | ] 408 | 409 | [[package]] 410 | name = "crossbeam-utils" 411 | version = "0.7.2" 412 | source = "registry+https://github.com/rust-lang/crates.io-index" 413 | checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" 414 | dependencies = [ 415 | "autocfg 1.0.0", 416 | "cfg-if", 417 | "lazy_static", 418 | ] 419 | 420 | [[package]] 421 | name = "crunchy" 422 | version = "0.2.2" 423 | source = "registry+https://github.com/rust-lang/crates.io-index" 424 | checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" 425 | 426 | [[package]] 427 | name = "curl" 428 | version = "0.4.28" 429 | source = "registry+https://github.com/rust-lang/crates.io-index" 430 | checksum = "eda1c0c03cacf3365d84818a40293f0e3f3953db8759c9c565a3b434edf0b52e" 431 | dependencies = [ 432 | "curl-sys", 433 | "libc", 434 | "openssl-probe", 435 | "openssl-sys", 436 | "schannel", 437 | "socket2", 438 | "winapi 0.3.8", 439 | ] 440 | 441 | [[package]] 442 | name = "curl-sys" 443 | version = "0.4.30+curl-7.69.1" 444 | source = "registry+https://github.com/rust-lang/crates.io-index" 445 | checksum = "923b38e423a8f47a4058e96f2a1fa2865a6231097ee860debd678d244277d50c" 446 | dependencies = [ 447 | "cc", 448 | "libc", 449 | "libnghttp2-sys", 450 | "libz-sys", 451 | "openssl-sys", 452 | "pkg-config", 453 | "vcpkg", 454 | "winapi 0.3.8", 455 | ] 456 | 457 | [[package]] 458 | name = "data-encoding" 459 | version = "2.2.0" 460 | source = "registry+https://github.com/rust-lang/crates.io-index" 461 | checksum = "11c0346158a19b3627234e15596f5e465c360fcdb97d817bcb255e0510f5a788" 462 | 463 | [[package]] 464 | name = "dataloader" 465 | version = "0.11.0" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | checksum = "69042f5521ffb051d02f25058c99a0d534f4be572ba4e6149407589a193888dd" 468 | dependencies = [ 469 | "async-std", 470 | "async-trait", 471 | ] 472 | 473 | [[package]] 474 | name = "dirs" 475 | version = "2.0.2" 476 | source = "registry+https://github.com/rust-lang/crates.io-index" 477 | checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" 478 | dependencies = [ 479 | "cfg-if", 480 | "dirs-sys", 481 | ] 482 | 483 | [[package]] 484 | name = "dirs-sys" 485 | version = "0.3.4" 486 | source = "registry+https://github.com/rust-lang/crates.io-index" 487 | checksum = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b" 488 | dependencies = [ 489 | "cfg-if", 490 | "libc", 491 | "redox_users", 492 | "winapi 0.3.8", 493 | ] 494 | 495 | [[package]] 496 | name = "downcast-rs" 497 | version = "1.1.1" 498 | source = "registry+https://github.com/rust-lang/crates.io-index" 499 | checksum = "52ba6eb47c2131e784a38b726eb54c1e1484904f013e576a25354d0124161af6" 500 | 501 | [[package]] 502 | name = "dtoa" 503 | version = "0.4.5" 504 | source = "registry+https://github.com/rust-lang/crates.io-index" 505 | checksum = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3" 506 | 507 | [[package]] 508 | name = "either" 509 | version = "1.5.3" 510 | source = "registry+https://github.com/rust-lang/crates.io-index" 511 | checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" 512 | 513 | [[package]] 514 | name = "env_logger" 515 | version = "0.7.1" 516 | source = "registry+https://github.com/rust-lang/crates.io-index" 517 | checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" 518 | dependencies = [ 519 | "atty", 520 | "humantime", 521 | "log", 522 | "regex", 523 | "termcolor", 524 | ] 525 | 526 | [[package]] 527 | name = "error-chain" 528 | version = "0.12.2" 529 | source = "registry+https://github.com/rust-lang/crates.io-index" 530 | checksum = "d371106cc88ffdfb1eabd7111e432da544f16f3e2d7bf1dfe8bf575f1df045cd" 531 | dependencies = [ 532 | "backtrace", 533 | "version_check", 534 | ] 535 | 536 | [[package]] 537 | name = "fail" 538 | version = "0.3.0" 539 | source = "registry+https://github.com/rust-lang/crates.io-index" 540 | checksum = "f63eec71a3013ee912a0ecb339ff0c5fa5ed9660df04bfefa10c250b885d018c" 541 | dependencies = [ 542 | "lazy_static", 543 | "log", 544 | "rand 0.6.5", 545 | ] 546 | 547 | [[package]] 548 | name = "failure" 549 | version = "0.1.7" 550 | source = "registry+https://github.com/rust-lang/crates.io-index" 551 | checksum = "b8529c2421efa3066a5cbd8063d2244603824daccb6936b079010bb2aa89464b" 552 | dependencies = [ 553 | "backtrace", 554 | "failure_derive", 555 | ] 556 | 557 | [[package]] 558 | name = "failure_derive" 559 | version = "0.1.7" 560 | source = "registry+https://github.com/rust-lang/crates.io-index" 561 | checksum = "030a733c8287d6213886dd487564ff5c8f6aae10278b3588ed177f9d18f8d231" 562 | dependencies = [ 563 | "proc-macro2", 564 | "quote", 565 | "syn", 566 | "synstructure", 567 | ] 568 | 569 | [[package]] 570 | name = "fallible-iterator" 571 | version = "0.2.0" 572 | source = "registry+https://github.com/rust-lang/crates.io-index" 573 | checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" 574 | 575 | [[package]] 576 | name = "fallible-streaming-iterator" 577 | version = "0.1.9" 578 | source = "registry+https://github.com/rust-lang/crates.io-index" 579 | checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" 580 | 581 | [[package]] 582 | name = "filetime" 583 | version = "0.2.9" 584 | source = "registry+https://github.com/rust-lang/crates.io-index" 585 | checksum = "f59efc38004c988e4201d11d263b8171f49a2e7ec0bdbb71773433f271504a5e" 586 | dependencies = [ 587 | "cfg-if", 588 | "libc", 589 | "redox_syscall", 590 | "winapi 0.3.8", 591 | ] 592 | 593 | [[package]] 594 | name = "fnv" 595 | version = "1.0.6" 596 | source = "registry+https://github.com/rust-lang/crates.io-index" 597 | checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" 598 | 599 | [[package]] 600 | name = "frontmatter" 601 | version = "0.3.0" 602 | source = "registry+https://github.com/rust-lang/crates.io-index" 603 | checksum = "255b3ae116eff4634420b8ed2f404e38648401245f8ed47e97e5ee243734b251" 604 | dependencies = [ 605 | "yaml-rust", 606 | ] 607 | 608 | [[package]] 609 | name = "fs2" 610 | version = "0.4.3" 611 | source = "registry+https://github.com/rust-lang/crates.io-index" 612 | checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" 613 | dependencies = [ 614 | "libc", 615 | "winapi 0.3.8", 616 | ] 617 | 618 | [[package]] 619 | name = "fsevent" 620 | version = "0.4.0" 621 | source = "registry+https://github.com/rust-lang/crates.io-index" 622 | checksum = "5ab7d1bd1bd33cc98b0889831b72da23c0aa4df9cec7e0702f46ecea04b35db6" 623 | dependencies = [ 624 | "bitflags", 625 | "fsevent-sys", 626 | ] 627 | 628 | [[package]] 629 | name = "fsevent-sys" 630 | version = "2.0.1" 631 | source = "registry+https://github.com/rust-lang/crates.io-index" 632 | checksum = "f41b048a94555da0f42f1d632e2e19510084fb8e303b0daa2816e733fb3644a0" 633 | dependencies = [ 634 | "libc", 635 | ] 636 | 637 | [[package]] 638 | name = "fst" 639 | version = "0.3.5" 640 | source = "registry+https://github.com/rust-lang/crates.io-index" 641 | checksum = "927fb434ff9f0115b215dc0efd2e4fbdd7448522a92a1aa37c77d6a2f8f1ebd6" 642 | dependencies = [ 643 | "byteorder", 644 | ] 645 | 646 | [[package]] 647 | name = "fuchsia-cprng" 648 | version = "0.1.1" 649 | source = "registry+https://github.com/rust-lang/crates.io-index" 650 | checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" 651 | 652 | [[package]] 653 | name = "fuchsia-zircon" 654 | version = "0.3.3" 655 | source = "registry+https://github.com/rust-lang/crates.io-index" 656 | checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" 657 | dependencies = [ 658 | "bitflags", 659 | "fuchsia-zircon-sys", 660 | ] 661 | 662 | [[package]] 663 | name = "fuchsia-zircon-sys" 664 | version = "0.3.3" 665 | source = "registry+https://github.com/rust-lang/crates.io-index" 666 | checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" 667 | 668 | [[package]] 669 | name = "futures" 670 | version = "0.1.29" 671 | source = "registry+https://github.com/rust-lang/crates.io-index" 672 | checksum = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef" 673 | 674 | [[package]] 675 | name = "futures" 676 | version = "0.3.4" 677 | source = "registry+https://github.com/rust-lang/crates.io-index" 678 | checksum = "5c329ae8753502fb44ae4fc2b622fa2a94652c41e795143765ba0927f92ab780" 679 | dependencies = [ 680 | "futures-channel", 681 | "futures-core", 682 | "futures-executor", 683 | "futures-io", 684 | "futures-sink", 685 | "futures-task", 686 | "futures-util", 687 | ] 688 | 689 | [[package]] 690 | name = "futures-channel" 691 | version = "0.3.4" 692 | source = "registry+https://github.com/rust-lang/crates.io-index" 693 | checksum = "f0c77d04ce8edd9cb903932b608268b3fffec4163dc053b3b402bf47eac1f1a8" 694 | dependencies = [ 695 | "futures-core", 696 | "futures-sink", 697 | ] 698 | 699 | [[package]] 700 | name = "futures-channel-preview" 701 | version = "0.3.0-alpha.19" 702 | source = "registry+https://github.com/rust-lang/crates.io-index" 703 | checksum = "d5e5f4df964fa9c1c2f8bddeb5c3611631cacd93baf810fc8bb2fb4b495c263a" 704 | dependencies = [ 705 | "futures-core-preview", 706 | "futures-sink-preview", 707 | ] 708 | 709 | [[package]] 710 | name = "futures-core" 711 | version = "0.3.4" 712 | source = "registry+https://github.com/rust-lang/crates.io-index" 713 | checksum = "f25592f769825e89b92358db00d26f965761e094951ac44d3663ef25b7ac464a" 714 | 715 | [[package]] 716 | name = "futures-core-preview" 717 | version = "0.3.0-alpha.19" 718 | source = "registry+https://github.com/rust-lang/crates.io-index" 719 | checksum = "b35b6263fb1ef523c3056565fa67b1d16f0a8604ff12b11b08c25f28a734c60a" 720 | 721 | [[package]] 722 | name = "futures-cpupool" 723 | version = "0.1.8" 724 | source = "registry+https://github.com/rust-lang/crates.io-index" 725 | checksum = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" 726 | dependencies = [ 727 | "futures 0.1.29", 728 | "num_cpus", 729 | ] 730 | 731 | [[package]] 732 | name = "futures-executor" 733 | version = "0.3.4" 734 | source = "registry+https://github.com/rust-lang/crates.io-index" 735 | checksum = "f674f3e1bcb15b37284a90cedf55afdba482ab061c407a9c0ebbd0f3109741ba" 736 | dependencies = [ 737 | "futures-core", 738 | "futures-task", 739 | "futures-util", 740 | "num_cpus", 741 | ] 742 | 743 | [[package]] 744 | name = "futures-executor-preview" 745 | version = "0.3.0-alpha.19" 746 | source = "registry+https://github.com/rust-lang/crates.io-index" 747 | checksum = "75236e88bd9fe88e5e8bfcd175b665d0528fe03ca4c5207fabc028c8f9d93e98" 748 | dependencies = [ 749 | "futures-core-preview", 750 | "futures-util-preview", 751 | "num_cpus", 752 | ] 753 | 754 | [[package]] 755 | name = "futures-io" 756 | version = "0.3.4" 757 | source = "registry+https://github.com/rust-lang/crates.io-index" 758 | checksum = "a638959aa96152c7a4cddf50fcb1e3fede0583b27157c26e67d6f99904090dc6" 759 | 760 | [[package]] 761 | name = "futures-io-preview" 762 | version = "0.3.0-alpha.19" 763 | source = "registry+https://github.com/rust-lang/crates.io-index" 764 | checksum = "f4914ae450db1921a56c91bde97a27846287d062087d4a652efc09bb3a01ebda" 765 | 766 | [[package]] 767 | name = "futures-macro" 768 | version = "0.3.4" 769 | source = "registry+https://github.com/rust-lang/crates.io-index" 770 | checksum = "9a5081aa3de1f7542a794a397cde100ed903b0630152d0973479018fd85423a7" 771 | dependencies = [ 772 | "proc-macro-hack", 773 | "proc-macro2", 774 | "quote", 775 | "syn", 776 | ] 777 | 778 | [[package]] 779 | name = "futures-preview" 780 | version = "0.3.0-alpha.19" 781 | source = "registry+https://github.com/rust-lang/crates.io-index" 782 | checksum = "3b1dce2a0267ada5c6ff75a8ba864b4e679a9e2aa44262af7a3b5516d530d76e" 783 | dependencies = [ 784 | "futures-channel-preview", 785 | "futures-core-preview", 786 | "futures-executor-preview", 787 | "futures-io-preview", 788 | "futures-sink-preview", 789 | "futures-util-preview", 790 | ] 791 | 792 | [[package]] 793 | name = "futures-sink" 794 | version = "0.3.4" 795 | source = "registry+https://github.com/rust-lang/crates.io-index" 796 | checksum = "3466821b4bc114d95b087b850a724c6f83115e929bc88f1fa98a3304a944c8a6" 797 | 798 | [[package]] 799 | name = "futures-sink-preview" 800 | version = "0.3.0-alpha.19" 801 | source = "registry+https://github.com/rust-lang/crates.io-index" 802 | checksum = "86f148ef6b69f75bb610d4f9a2336d4fc88c4b5b67129d1a340dd0fd362efeec" 803 | 804 | [[package]] 805 | name = "futures-task" 806 | version = "0.3.4" 807 | source = "registry+https://github.com/rust-lang/crates.io-index" 808 | checksum = "7b0a34e53cf6cdcd0178aa573aed466b646eb3db769570841fda0c7ede375a27" 809 | 810 | [[package]] 811 | name = "futures-timer" 812 | version = "2.0.2" 813 | source = "registry+https://github.com/rust-lang/crates.io-index" 814 | checksum = "a1de7508b218029b0f01662ed8f61b1c964b3ae99d6f25462d0f55a595109df6" 815 | 816 | [[package]] 817 | name = "futures-util" 818 | version = "0.3.4" 819 | source = "registry+https://github.com/rust-lang/crates.io-index" 820 | checksum = "22766cf25d64306bedf0384da004d05c9974ab104fcc4528f1236181c18004c5" 821 | dependencies = [ 822 | "futures 0.1.29", 823 | "futures-channel", 824 | "futures-core", 825 | "futures-io", 826 | "futures-macro", 827 | "futures-sink", 828 | "futures-task", 829 | "memchr", 830 | "pin-utils", 831 | "proc-macro-hack", 832 | "proc-macro-nested", 833 | "slab", 834 | "tokio-io", 835 | ] 836 | 837 | [[package]] 838 | name = "futures-util-preview" 839 | version = "0.3.0-alpha.19" 840 | source = "registry+https://github.com/rust-lang/crates.io-index" 841 | checksum = "5ce968633c17e5f97936bd2797b6e38fb56cf16a7422319f7ec2e30d3c470e8d" 842 | dependencies = [ 843 | "futures 0.1.29", 844 | "futures-channel-preview", 845 | "futures-core-preview", 846 | "futures-io-preview", 847 | "futures-sink-preview", 848 | "memchr", 849 | "pin-utils", 850 | "slab", 851 | "tokio-io", 852 | ] 853 | 854 | [[package]] 855 | name = "getopts" 856 | version = "0.2.21" 857 | source = "registry+https://github.com/rust-lang/crates.io-index" 858 | checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" 859 | dependencies = [ 860 | "unicode-width", 861 | ] 862 | 863 | [[package]] 864 | name = "getrandom" 865 | version = "0.1.14" 866 | source = "registry+https://github.com/rust-lang/crates.io-index" 867 | checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" 868 | dependencies = [ 869 | "cfg-if", 870 | "libc", 871 | "wasi", 872 | ] 873 | 874 | [[package]] 875 | name = "globset" 876 | version = "0.4.5" 877 | source = "registry+https://github.com/rust-lang/crates.io-index" 878 | checksum = "7ad1da430bd7281dde2576f44c84cc3f0f7b475e7202cd503042dff01a8c8120" 879 | dependencies = [ 880 | "aho-corasick", 881 | "bstr", 882 | "fnv", 883 | "log", 884 | "regex", 885 | ] 886 | 887 | [[package]] 888 | name = "h2" 889 | version = "0.1.26" 890 | source = "registry+https://github.com/rust-lang/crates.io-index" 891 | checksum = "a5b34c246847f938a410a03c5458c7fee2274436675e76d8b903c08efc29c462" 892 | dependencies = [ 893 | "byteorder", 894 | "bytes 0.4.12", 895 | "fnv", 896 | "futures 0.1.29", 897 | "http", 898 | "indexmap", 899 | "log", 900 | "slab", 901 | "string", 902 | "tokio-io", 903 | ] 904 | 905 | [[package]] 906 | name = "hermit-abi" 907 | version = "0.1.10" 908 | source = "registry+https://github.com/rust-lang/crates.io-index" 909 | checksum = "725cf19794cf90aa94e65050cb4191ff5d8fa87a498383774c47b332e3af952e" 910 | dependencies = [ 911 | "libc", 912 | ] 913 | 914 | [[package]] 915 | name = "htmlescape" 916 | version = "0.3.1" 917 | source = "registry+https://github.com/rust-lang/crates.io-index" 918 | checksum = "e9025058dae765dee5070ec375f591e2ba14638c63feff74f13805a72e523163" 919 | 920 | [[package]] 921 | name = "http" 922 | version = "0.1.21" 923 | source = "registry+https://github.com/rust-lang/crates.io-index" 924 | checksum = "d6ccf5ede3a895d8856620237b2f02972c1bbc78d2965ad7fe8838d4a0ed41f0" 925 | dependencies = [ 926 | "bytes 0.4.12", 927 | "fnv", 928 | "itoa", 929 | ] 930 | 931 | [[package]] 932 | name = "http-body" 933 | version = "0.1.0" 934 | source = "registry+https://github.com/rust-lang/crates.io-index" 935 | checksum = "6741c859c1b2463a423a1dbce98d418e6c3c3fc720fb0d45528657320920292d" 936 | dependencies = [ 937 | "bytes 0.4.12", 938 | "futures 0.1.29", 939 | "http", 940 | "tokio-buf", 941 | ] 942 | 943 | [[package]] 944 | name = "http-service" 945 | version = "0.4.0" 946 | source = "registry+https://github.com/rust-lang/crates.io-index" 947 | checksum = "9625f605ddfaf894bf78a544a7b8e31f562dc843654723a49892d9c7e75ac708" 948 | dependencies = [ 949 | "async-std", 950 | "bytes 0.4.12", 951 | "futures 0.3.4", 952 | "http", 953 | "pin-project-lite", 954 | ] 955 | 956 | [[package]] 957 | name = "http-service-hyper" 958 | version = "0.4.1" 959 | source = "registry+https://github.com/rust-lang/crates.io-index" 960 | checksum = "e33d5dae94e0fdb82f9524ea2f2b98458b3d8448526d8cc8beccb3d3fded8aff" 961 | dependencies = [ 962 | "futures 0.3.4", 963 | "http", 964 | "http-service", 965 | "hyper", 966 | ] 967 | 968 | [[package]] 969 | name = "httparse" 970 | version = "1.3.4" 971 | source = "registry+https://github.com/rust-lang/crates.io-index" 972 | checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" 973 | 974 | [[package]] 975 | name = "humantime" 976 | version = "1.3.0" 977 | source = "registry+https://github.com/rust-lang/crates.io-index" 978 | checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" 979 | dependencies = [ 980 | "quick-error", 981 | ] 982 | 983 | [[package]] 984 | name = "hyper" 985 | version = "0.12.35" 986 | source = "registry+https://github.com/rust-lang/crates.io-index" 987 | checksum = "9dbe6ed1438e1f8ad955a4701e9a944938e9519f6888d12d8558b645e247d5f6" 988 | dependencies = [ 989 | "bytes 0.4.12", 990 | "futures 0.1.29", 991 | "futures-cpupool", 992 | "h2", 993 | "http", 994 | "http-body", 995 | "httparse", 996 | "iovec", 997 | "itoa", 998 | "log", 999 | "net2", 1000 | "rustc_version", 1001 | "time", 1002 | "tokio", 1003 | "tokio-buf", 1004 | "tokio-executor", 1005 | "tokio-io", 1006 | "tokio-reactor", 1007 | "tokio-tcp", 1008 | "tokio-threadpool", 1009 | "tokio-timer", 1010 | "want", 1011 | ] 1012 | 1013 | [[package]] 1014 | name = "idna" 1015 | version = "0.1.5" 1016 | source = "registry+https://github.com/rust-lang/crates.io-index" 1017 | checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" 1018 | dependencies = [ 1019 | "matches", 1020 | "unicode-bidi", 1021 | "unicode-normalization", 1022 | ] 1023 | 1024 | [[package]] 1025 | name = "idna" 1026 | version = "0.2.0" 1027 | source = "registry+https://github.com/rust-lang/crates.io-index" 1028 | checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" 1029 | dependencies = [ 1030 | "matches", 1031 | "unicode-bidi", 1032 | "unicode-normalization", 1033 | ] 1034 | 1035 | [[package]] 1036 | name = "ignore" 1037 | version = "0.4.14" 1038 | source = "registry+https://github.com/rust-lang/crates.io-index" 1039 | checksum = "ddf60d063dbe6b75388eec66cfc07781167ae3d34a09e0c433e6c5de0511f7fb" 1040 | dependencies = [ 1041 | "crossbeam-channel 0.4.2", 1042 | "crossbeam-utils 0.7.2", 1043 | "globset", 1044 | "lazy_static", 1045 | "log", 1046 | "memchr", 1047 | "regex", 1048 | "same-file", 1049 | "thread_local", 1050 | "walkdir", 1051 | "winapi-util", 1052 | ] 1053 | 1054 | [[package]] 1055 | name = "indexmap" 1056 | version = "1.3.2" 1057 | source = "registry+https://github.com/rust-lang/crates.io-index" 1058 | checksum = "076f042c5b7b98f31d205f1249267e12a6518c1481e9dae9764af19b707d2292" 1059 | dependencies = [ 1060 | "autocfg 1.0.0", 1061 | "serde", 1062 | ] 1063 | 1064 | [[package]] 1065 | name = "inotify" 1066 | version = "0.7.0" 1067 | source = "registry+https://github.com/rust-lang/crates.io-index" 1068 | checksum = "24e40d6fd5d64e2082e0c796495c8ef5ad667a96d03e5aaa0becfd9d47bcbfb8" 1069 | dependencies = [ 1070 | "bitflags", 1071 | "inotify-sys", 1072 | "libc", 1073 | ] 1074 | 1075 | [[package]] 1076 | name = "inotify-sys" 1077 | version = "0.1.3" 1078 | source = "registry+https://github.com/rust-lang/crates.io-index" 1079 | checksum = "e74a1aa87c59aeff6ef2cc2fa62d41bc43f54952f55652656b18a02fd5e356c0" 1080 | dependencies = [ 1081 | "libc", 1082 | ] 1083 | 1084 | [[package]] 1085 | name = "iovec" 1086 | version = "0.1.4" 1087 | source = "registry+https://github.com/rust-lang/crates.io-index" 1088 | checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" 1089 | dependencies = [ 1090 | "libc", 1091 | ] 1092 | 1093 | [[package]] 1094 | name = "isahc" 1095 | version = "0.7.6" 1096 | source = "registry+https://github.com/rust-lang/crates.io-index" 1097 | checksum = "17b77027f12e53ae59a379f7074259d32eb10867e6183388020e922832d9c3fb" 1098 | dependencies = [ 1099 | "bytes 0.4.12", 1100 | "crossbeam-channel 0.3.9", 1101 | "crossbeam-utils 0.6.6", 1102 | "curl", 1103 | "curl-sys", 1104 | "futures-io-preview", 1105 | "futures-util-preview", 1106 | "http", 1107 | "lazy_static", 1108 | "log", 1109 | "slab", 1110 | "sluice", 1111 | ] 1112 | 1113 | [[package]] 1114 | name = "itertools" 1115 | version = "0.8.2" 1116 | source = "registry+https://github.com/rust-lang/crates.io-index" 1117 | checksum = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484" 1118 | dependencies = [ 1119 | "either", 1120 | ] 1121 | 1122 | [[package]] 1123 | name = "itoa" 1124 | version = "0.4.5" 1125 | source = "registry+https://github.com/rust-lang/crates.io-index" 1126 | checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" 1127 | 1128 | [[package]] 1129 | name = "js-sys" 1130 | version = "0.3.37" 1131 | source = "registry+https://github.com/rust-lang/crates.io-index" 1132 | checksum = "6a27d435371a2fa5b6d2b028a74bbdb1234f308da363226a2854ca3ff8ba7055" 1133 | dependencies = [ 1134 | "wasm-bindgen", 1135 | ] 1136 | 1137 | [[package]] 1138 | name = "juniper" 1139 | version = "0.14.2" 1140 | source = "git+https://github.com/gozala/juniper?branch=async-await#c8d641ecf25b945279b165eec0b5fe53f85d1ed3" 1141 | dependencies = [ 1142 | "chrono", 1143 | "fnv", 1144 | "futures 0.3.4", 1145 | "indexmap", 1146 | "juniper_codegen", 1147 | "serde", 1148 | "serde_derive", 1149 | "url 2.1.1", 1150 | "uuid 0.7.4", 1151 | ] 1152 | 1153 | [[package]] 1154 | name = "juniper_codegen" 1155 | version = "0.14.2" 1156 | source = "git+https://github.com/gozala/juniper?branch=async-await#c8d641ecf25b945279b165eec0b5fe53f85d1ed3" 1157 | dependencies = [ 1158 | "proc-macro-error", 1159 | "proc-macro2", 1160 | "quote", 1161 | "syn", 1162 | ] 1163 | 1164 | [[package]] 1165 | name = "kernel32-sys" 1166 | version = "0.2.2" 1167 | source = "registry+https://github.com/rust-lang/crates.io-index" 1168 | checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" 1169 | dependencies = [ 1170 | "winapi 0.2.8", 1171 | "winapi-build", 1172 | ] 1173 | 1174 | [[package]] 1175 | name = "knowledge-server" 1176 | version = "0.1.0" 1177 | dependencies = [ 1178 | "async-std", 1179 | "commander-rust", 1180 | "dirs", 1181 | "env_logger", 1182 | "knowledge-server-base", 1183 | "knowledge-server-scanner", 1184 | "syntax", 1185 | ] 1186 | 1187 | [[package]] 1188 | name = "knowledge-server-base" 1189 | version = "0.1.0" 1190 | dependencies = [ 1191 | "anyhow", 1192 | "async-std", 1193 | "async-trait", 1194 | "dataloader", 1195 | "dirs", 1196 | "futures 0.3.4", 1197 | "juniper", 1198 | "log", 1199 | "open", 1200 | "r2d2", 1201 | "r2d2_sqlite", 1202 | "rusqlite", 1203 | "stopwords", 1204 | "tantivy", 1205 | "tide", 1206 | "tique", 1207 | ] 1208 | 1209 | [[package]] 1210 | name = "knowledge-server-scanner" 1211 | version = "0.1.0" 1212 | dependencies = [ 1213 | "async-std", 1214 | "async-trait", 1215 | "dirs", 1216 | "frontmatter", 1217 | "ignore", 1218 | "knowledge-server-base", 1219 | "pulldown-cmark", 1220 | "surf", 1221 | "url 2.1.1", 1222 | "yaml-rust", 1223 | ] 1224 | 1225 | [[package]] 1226 | name = "kv-log-macro" 1227 | version = "1.0.4" 1228 | source = "registry+https://github.com/rust-lang/crates.io-index" 1229 | checksum = "8c54d9f465d530a752e6ebdc217e081a7a614b48cb200f6f0aee21ba6bc9aabb" 1230 | dependencies = [ 1231 | "log", 1232 | ] 1233 | 1234 | [[package]] 1235 | name = "lazy_static" 1236 | version = "1.4.0" 1237 | source = "registry+https://github.com/rust-lang/crates.io-index" 1238 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 1239 | 1240 | [[package]] 1241 | name = "lazycell" 1242 | version = "1.2.1" 1243 | source = "registry+https://github.com/rust-lang/crates.io-index" 1244 | checksum = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" 1245 | 1246 | [[package]] 1247 | name = "levenshtein_automata" 1248 | version = "0.1.1" 1249 | source = "registry+https://github.com/rust-lang/crates.io-index" 1250 | checksum = "73a004f877f468548d8d0ac4977456a249d8fabbdb8416c36db163dfc8f2e8ca" 1251 | dependencies = [ 1252 | "fst", 1253 | ] 1254 | 1255 | [[package]] 1256 | name = "libc" 1257 | version = "0.2.68" 1258 | source = "registry+https://github.com/rust-lang/crates.io-index" 1259 | checksum = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0" 1260 | 1261 | [[package]] 1262 | name = "libnghttp2-sys" 1263 | version = "0.1.3" 1264 | source = "registry+https://github.com/rust-lang/crates.io-index" 1265 | checksum = "b359f5ec8106bc297694c9a562ace312be2cfd17a5fc68dc12249845aa144b11" 1266 | dependencies = [ 1267 | "cc", 1268 | "libc", 1269 | ] 1270 | 1271 | [[package]] 1272 | name = "libsqlite3-sys" 1273 | version = "0.17.1" 1274 | source = "registry+https://github.com/rust-lang/crates.io-index" 1275 | checksum = "266eb8c361198e8d1f682bc974e5d9e2ae90049fb1943890904d11dad7d4a77d" 1276 | dependencies = [ 1277 | "pkg-config", 1278 | "vcpkg", 1279 | ] 1280 | 1281 | [[package]] 1282 | name = "libz-sys" 1283 | version = "1.0.25" 1284 | source = "registry+https://github.com/rust-lang/crates.io-index" 1285 | checksum = "2eb5e43362e38e2bca2fd5f5134c4d4564a23a5c28e9b95411652021a8675ebe" 1286 | dependencies = [ 1287 | "cc", 1288 | "libc", 1289 | "pkg-config", 1290 | "vcpkg", 1291 | ] 1292 | 1293 | [[package]] 1294 | name = "linked-hash-map" 1295 | version = "0.5.2" 1296 | source = "registry+https://github.com/rust-lang/crates.io-index" 1297 | checksum = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" 1298 | 1299 | [[package]] 1300 | name = "lock_api" 1301 | version = "0.3.3" 1302 | source = "registry+https://github.com/rust-lang/crates.io-index" 1303 | checksum = "79b2de95ecb4691949fea4716ca53cdbcfccb2c612e19644a8bad05edcf9f47b" 1304 | dependencies = [ 1305 | "scopeguard", 1306 | ] 1307 | 1308 | [[package]] 1309 | name = "log" 1310 | version = "0.4.8" 1311 | source = "registry+https://github.com/rust-lang/crates.io-index" 1312 | checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" 1313 | dependencies = [ 1314 | "cfg-if", 1315 | ] 1316 | 1317 | [[package]] 1318 | name = "lru-cache" 1319 | version = "0.1.2" 1320 | source = "registry+https://github.com/rust-lang/crates.io-index" 1321 | checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" 1322 | dependencies = [ 1323 | "linked-hash-map", 1324 | ] 1325 | 1326 | [[package]] 1327 | name = "matches" 1328 | version = "0.1.8" 1329 | source = "registry+https://github.com/rust-lang/crates.io-index" 1330 | checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" 1331 | 1332 | [[package]] 1333 | name = "maybe-uninit" 1334 | version = "2.0.0" 1335 | source = "registry+https://github.com/rust-lang/crates.io-index" 1336 | checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" 1337 | 1338 | [[package]] 1339 | name = "memchr" 1340 | version = "2.3.3" 1341 | source = "registry+https://github.com/rust-lang/crates.io-index" 1342 | checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" 1343 | 1344 | [[package]] 1345 | name = "memmap" 1346 | version = "0.7.0" 1347 | source = "registry+https://github.com/rust-lang/crates.io-index" 1348 | checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" 1349 | dependencies = [ 1350 | "libc", 1351 | "winapi 0.3.8", 1352 | ] 1353 | 1354 | [[package]] 1355 | name = "memoffset" 1356 | version = "0.5.4" 1357 | source = "registry+https://github.com/rust-lang/crates.io-index" 1358 | checksum = "b4fc2c02a7e374099d4ee95a193111f72d2110197fe200272371758f6c3643d8" 1359 | dependencies = [ 1360 | "autocfg 1.0.0", 1361 | ] 1362 | 1363 | [[package]] 1364 | name = "mime" 1365 | version = "0.3.16" 1366 | source = "registry+https://github.com/rust-lang/crates.io-index" 1367 | checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" 1368 | 1369 | [[package]] 1370 | name = "mime_guess" 1371 | version = "2.0.3" 1372 | source = "registry+https://github.com/rust-lang/crates.io-index" 1373 | checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212" 1374 | dependencies = [ 1375 | "mime", 1376 | "unicase", 1377 | ] 1378 | 1379 | [[package]] 1380 | name = "mio" 1381 | version = "0.6.21" 1382 | source = "registry+https://github.com/rust-lang/crates.io-index" 1383 | checksum = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f" 1384 | dependencies = [ 1385 | "cfg-if", 1386 | "fuchsia-zircon", 1387 | "fuchsia-zircon-sys", 1388 | "iovec", 1389 | "kernel32-sys", 1390 | "libc", 1391 | "log", 1392 | "miow", 1393 | "net2", 1394 | "slab", 1395 | "winapi 0.2.8", 1396 | ] 1397 | 1398 | [[package]] 1399 | name = "mio-extras" 1400 | version = "2.0.6" 1401 | source = "registry+https://github.com/rust-lang/crates.io-index" 1402 | checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19" 1403 | dependencies = [ 1404 | "lazycell", 1405 | "log", 1406 | "mio", 1407 | "slab", 1408 | ] 1409 | 1410 | [[package]] 1411 | name = "mio-uds" 1412 | version = "0.6.7" 1413 | source = "registry+https://github.com/rust-lang/crates.io-index" 1414 | checksum = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" 1415 | dependencies = [ 1416 | "iovec", 1417 | "libc", 1418 | "mio", 1419 | ] 1420 | 1421 | [[package]] 1422 | name = "miow" 1423 | version = "0.2.1" 1424 | source = "registry+https://github.com/rust-lang/crates.io-index" 1425 | checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" 1426 | dependencies = [ 1427 | "kernel32-sys", 1428 | "net2", 1429 | "winapi 0.2.8", 1430 | "ws2_32-sys", 1431 | ] 1432 | 1433 | [[package]] 1434 | name = "murmurhash32" 1435 | version = "0.2.0" 1436 | source = "registry+https://github.com/rust-lang/crates.io-index" 1437 | checksum = "d736ff882f0e85fe9689fb23db229616c4c00aee2b3ac282f666d8f20eb25d4a" 1438 | dependencies = [ 1439 | "byteorder", 1440 | ] 1441 | 1442 | [[package]] 1443 | name = "net2" 1444 | version = "0.2.33" 1445 | source = "registry+https://github.com/rust-lang/crates.io-index" 1446 | checksum = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" 1447 | dependencies = [ 1448 | "cfg-if", 1449 | "libc", 1450 | "winapi 0.3.8", 1451 | ] 1452 | 1453 | [[package]] 1454 | name = "nix" 1455 | version = "0.14.1" 1456 | source = "registry+https://github.com/rust-lang/crates.io-index" 1457 | checksum = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce" 1458 | dependencies = [ 1459 | "bitflags", 1460 | "cc", 1461 | "cfg-if", 1462 | "libc", 1463 | "void", 1464 | ] 1465 | 1466 | [[package]] 1467 | name = "notify" 1468 | version = "4.0.15" 1469 | source = "registry+https://github.com/rust-lang/crates.io-index" 1470 | checksum = "80ae4a7688d1fab81c5bf19c64fc8db920be8d519ce6336ed4e7efe024724dbd" 1471 | dependencies = [ 1472 | "bitflags", 1473 | "filetime", 1474 | "fsevent", 1475 | "fsevent-sys", 1476 | "inotify", 1477 | "libc", 1478 | "mio", 1479 | "mio-extras", 1480 | "walkdir", 1481 | "winapi 0.3.8", 1482 | ] 1483 | 1484 | [[package]] 1485 | name = "num-integer" 1486 | version = "0.1.42" 1487 | source = "registry+https://github.com/rust-lang/crates.io-index" 1488 | checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" 1489 | dependencies = [ 1490 | "autocfg 1.0.0", 1491 | "num-traits", 1492 | ] 1493 | 1494 | [[package]] 1495 | name = "num-traits" 1496 | version = "0.2.11" 1497 | source = "registry+https://github.com/rust-lang/crates.io-index" 1498 | checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" 1499 | dependencies = [ 1500 | "autocfg 1.0.0", 1501 | ] 1502 | 1503 | [[package]] 1504 | name = "num_cpus" 1505 | version = "1.12.0" 1506 | source = "registry+https://github.com/rust-lang/crates.io-index" 1507 | checksum = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6" 1508 | dependencies = [ 1509 | "hermit-abi", 1510 | "libc", 1511 | ] 1512 | 1513 | [[package]] 1514 | name = "once_cell" 1515 | version = "1.3.1" 1516 | source = "registry+https://github.com/rust-lang/crates.io-index" 1517 | checksum = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b" 1518 | 1519 | [[package]] 1520 | name = "open" 1521 | version = "1.4.0" 1522 | source = "registry+https://github.com/rust-lang/crates.io-index" 1523 | checksum = "7c283bf0114efea9e42f1a60edea9859e8c47528eae09d01df4b29c1e489cc48" 1524 | dependencies = [ 1525 | "winapi 0.3.8", 1526 | ] 1527 | 1528 | [[package]] 1529 | name = "openssl-probe" 1530 | version = "0.1.2" 1531 | source = "registry+https://github.com/rust-lang/crates.io-index" 1532 | checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" 1533 | 1534 | [[package]] 1535 | name = "openssl-sys" 1536 | version = "0.9.54" 1537 | source = "registry+https://github.com/rust-lang/crates.io-index" 1538 | checksum = "1024c0a59774200a555087a6da3f253a9095a5f344e353b212ac4c8b8e450986" 1539 | dependencies = [ 1540 | "autocfg 1.0.0", 1541 | "cc", 1542 | "libc", 1543 | "pkg-config", 1544 | "vcpkg", 1545 | ] 1546 | 1547 | [[package]] 1548 | name = "owned-read" 1549 | version = "0.4.1" 1550 | source = "registry+https://github.com/rust-lang/crates.io-index" 1551 | checksum = "b66d1e235abcebc845cf93550b89b74f468c051496fafb433ede4104b9f71ba1" 1552 | dependencies = [ 1553 | "stable_deref_trait", 1554 | ] 1555 | 1556 | [[package]] 1557 | name = "owning_ref" 1558 | version = "0.4.1" 1559 | source = "registry+https://github.com/rust-lang/crates.io-index" 1560 | checksum = "6ff55baddef9e4ad00f88b6c743a2a8062d4c6ade126c2a528644b8e444d52ce" 1561 | dependencies = [ 1562 | "stable_deref_trait", 1563 | ] 1564 | 1565 | [[package]] 1566 | name = "parking_lot" 1567 | version = "0.9.0" 1568 | source = "registry+https://github.com/rust-lang/crates.io-index" 1569 | checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" 1570 | dependencies = [ 1571 | "lock_api", 1572 | "parking_lot_core 0.6.2", 1573 | "rustc_version", 1574 | ] 1575 | 1576 | [[package]] 1577 | name = "parking_lot" 1578 | version = "0.10.0" 1579 | source = "registry+https://github.com/rust-lang/crates.io-index" 1580 | checksum = "92e98c49ab0b7ce5b222f2cc9193fc4efe11c6d0bd4f648e374684a6857b1cfc" 1581 | dependencies = [ 1582 | "lock_api", 1583 | "parking_lot_core 0.7.0", 1584 | ] 1585 | 1586 | [[package]] 1587 | name = "parking_lot_core" 1588 | version = "0.6.2" 1589 | source = "registry+https://github.com/rust-lang/crates.io-index" 1590 | checksum = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" 1591 | dependencies = [ 1592 | "cfg-if", 1593 | "cloudabi", 1594 | "libc", 1595 | "redox_syscall", 1596 | "rustc_version", 1597 | "smallvec 0.6.13", 1598 | "winapi 0.3.8", 1599 | ] 1600 | 1601 | [[package]] 1602 | name = "parking_lot_core" 1603 | version = "0.7.0" 1604 | source = "registry+https://github.com/rust-lang/crates.io-index" 1605 | checksum = "7582838484df45743c8434fbff785e8edf260c28748353d44bc0da32e0ceabf1" 1606 | dependencies = [ 1607 | "cfg-if", 1608 | "cloudabi", 1609 | "libc", 1610 | "redox_syscall", 1611 | "smallvec 1.2.0", 1612 | "winapi 0.3.8", 1613 | ] 1614 | 1615 | [[package]] 1616 | name = "percent-encoding" 1617 | version = "1.0.1" 1618 | source = "registry+https://github.com/rust-lang/crates.io-index" 1619 | checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" 1620 | 1621 | [[package]] 1622 | name = "percent-encoding" 1623 | version = "2.1.0" 1624 | source = "registry+https://github.com/rust-lang/crates.io-index" 1625 | checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" 1626 | 1627 | [[package]] 1628 | name = "pin-project-lite" 1629 | version = "0.1.4" 1630 | source = "registry+https://github.com/rust-lang/crates.io-index" 1631 | checksum = "237844750cfbb86f67afe27eee600dfbbcb6188d734139b534cbfbf4f96792ae" 1632 | 1633 | [[package]] 1634 | name = "pin-utils" 1635 | version = "0.1.0-alpha.4" 1636 | source = "registry+https://github.com/rust-lang/crates.io-index" 1637 | checksum = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" 1638 | 1639 | [[package]] 1640 | name = "pkg-config" 1641 | version = "0.3.17" 1642 | source = "registry+https://github.com/rust-lang/crates.io-index" 1643 | checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" 1644 | 1645 | [[package]] 1646 | name = "ppv-lite86" 1647 | version = "0.2.6" 1648 | source = "registry+https://github.com/rust-lang/crates.io-index" 1649 | checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" 1650 | 1651 | [[package]] 1652 | name = "proc-macro-error" 1653 | version = "0.3.4" 1654 | source = "registry+https://github.com/rust-lang/crates.io-index" 1655 | checksum = "03e202b6302b80433b759eb9314be63521361a6622f6c125fe0dc3f6196e2722" 1656 | dependencies = [ 1657 | "proc-macro-error-attr", 1658 | "proc-macro2", 1659 | "quote", 1660 | "syn", 1661 | ] 1662 | 1663 | [[package]] 1664 | name = "proc-macro-error-attr" 1665 | version = "0.3.4" 1666 | source = "registry+https://github.com/rust-lang/crates.io-index" 1667 | checksum = "d140e22ca819b3aa3719d1aafbdea40544799e3f901886a4ee72c3ff710de7c9" 1668 | dependencies = [ 1669 | "proc-macro2", 1670 | "quote", 1671 | "rustversion", 1672 | "syn", 1673 | ] 1674 | 1675 | [[package]] 1676 | name = "proc-macro-hack" 1677 | version = "0.5.15" 1678 | source = "registry+https://github.com/rust-lang/crates.io-index" 1679 | checksum = "0d659fe7c6d27f25e9d80a1a094c223f5246f6a6596453e09d7229bf42750b63" 1680 | 1681 | [[package]] 1682 | name = "proc-macro-nested" 1683 | version = "0.1.4" 1684 | source = "registry+https://github.com/rust-lang/crates.io-index" 1685 | checksum = "8e946095f9d3ed29ec38de908c22f95d9ac008e424c7bcae54c75a79c527c694" 1686 | 1687 | [[package]] 1688 | name = "proc-macro2" 1689 | version = "1.0.10" 1690 | source = "registry+https://github.com/rust-lang/crates.io-index" 1691 | checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" 1692 | dependencies = [ 1693 | "unicode-xid", 1694 | ] 1695 | 1696 | [[package]] 1697 | name = "pulldown-cmark" 1698 | version = "0.7.0" 1699 | source = "git+https://github.com/gozala/pulldown-cmark#1ac14071268ca11489afad4acf45ed0ef5333c61" 1700 | dependencies = [ 1701 | "bitflags", 1702 | "getopts", 1703 | "memchr", 1704 | "unicase", 1705 | ] 1706 | 1707 | [[package]] 1708 | name = "quick-error" 1709 | version = "1.2.3" 1710 | source = "registry+https://github.com/rust-lang/crates.io-index" 1711 | checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" 1712 | 1713 | [[package]] 1714 | name = "quote" 1715 | version = "1.0.3" 1716 | source = "registry+https://github.com/rust-lang/crates.io-index" 1717 | checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" 1718 | dependencies = [ 1719 | "proc-macro2", 1720 | ] 1721 | 1722 | [[package]] 1723 | name = "r2d2" 1724 | version = "0.8.8" 1725 | source = "registry+https://github.com/rust-lang/crates.io-index" 1726 | checksum = "1497e40855348e4a8a40767d8e55174bce1e445a3ac9254ad44ad468ee0485af" 1727 | dependencies = [ 1728 | "log", 1729 | "parking_lot 0.10.0", 1730 | "scheduled-thread-pool", 1731 | ] 1732 | 1733 | [[package]] 1734 | name = "r2d2_sqlite" 1735 | version = "0.14.0" 1736 | source = "registry+https://github.com/rust-lang/crates.io-index" 1737 | checksum = "7c4689dcbe44341a3388a696a056cbb96124f7e70a1fbc9cd6a7bb6bab543e3b" 1738 | dependencies = [ 1739 | "r2d2", 1740 | "rusqlite", 1741 | ] 1742 | 1743 | [[package]] 1744 | name = "rand" 1745 | version = "0.4.6" 1746 | source = "registry+https://github.com/rust-lang/crates.io-index" 1747 | checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" 1748 | dependencies = [ 1749 | "fuchsia-cprng", 1750 | "libc", 1751 | "rand_core 0.3.1", 1752 | "rdrand", 1753 | "winapi 0.3.8", 1754 | ] 1755 | 1756 | [[package]] 1757 | name = "rand" 1758 | version = "0.6.5" 1759 | source = "registry+https://github.com/rust-lang/crates.io-index" 1760 | checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" 1761 | dependencies = [ 1762 | "autocfg 0.1.7", 1763 | "libc", 1764 | "rand_chacha 0.1.1", 1765 | "rand_core 0.4.2", 1766 | "rand_hc 0.1.0", 1767 | "rand_isaac", 1768 | "rand_jitter", 1769 | "rand_os", 1770 | "rand_pcg", 1771 | "rand_xorshift", 1772 | "winapi 0.3.8", 1773 | ] 1774 | 1775 | [[package]] 1776 | name = "rand" 1777 | version = "0.7.3" 1778 | source = "registry+https://github.com/rust-lang/crates.io-index" 1779 | checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" 1780 | dependencies = [ 1781 | "getrandom", 1782 | "libc", 1783 | "rand_chacha 0.2.2", 1784 | "rand_core 0.5.1", 1785 | "rand_hc 0.2.0", 1786 | ] 1787 | 1788 | [[package]] 1789 | name = "rand_chacha" 1790 | version = "0.1.1" 1791 | source = "registry+https://github.com/rust-lang/crates.io-index" 1792 | checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" 1793 | dependencies = [ 1794 | "autocfg 0.1.7", 1795 | "rand_core 0.3.1", 1796 | ] 1797 | 1798 | [[package]] 1799 | name = "rand_chacha" 1800 | version = "0.2.2" 1801 | source = "registry+https://github.com/rust-lang/crates.io-index" 1802 | checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" 1803 | dependencies = [ 1804 | "ppv-lite86", 1805 | "rand_core 0.5.1", 1806 | ] 1807 | 1808 | [[package]] 1809 | name = "rand_core" 1810 | version = "0.3.1" 1811 | source = "registry+https://github.com/rust-lang/crates.io-index" 1812 | checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" 1813 | dependencies = [ 1814 | "rand_core 0.4.2", 1815 | ] 1816 | 1817 | [[package]] 1818 | name = "rand_core" 1819 | version = "0.4.2" 1820 | source = "registry+https://github.com/rust-lang/crates.io-index" 1821 | checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" 1822 | 1823 | [[package]] 1824 | name = "rand_core" 1825 | version = "0.5.1" 1826 | source = "registry+https://github.com/rust-lang/crates.io-index" 1827 | checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 1828 | dependencies = [ 1829 | "getrandom", 1830 | ] 1831 | 1832 | [[package]] 1833 | name = "rand_hc" 1834 | version = "0.1.0" 1835 | source = "registry+https://github.com/rust-lang/crates.io-index" 1836 | checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" 1837 | dependencies = [ 1838 | "rand_core 0.3.1", 1839 | ] 1840 | 1841 | [[package]] 1842 | name = "rand_hc" 1843 | version = "0.2.0" 1844 | source = "registry+https://github.com/rust-lang/crates.io-index" 1845 | checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" 1846 | dependencies = [ 1847 | "rand_core 0.5.1", 1848 | ] 1849 | 1850 | [[package]] 1851 | name = "rand_isaac" 1852 | version = "0.1.1" 1853 | source = "registry+https://github.com/rust-lang/crates.io-index" 1854 | checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" 1855 | dependencies = [ 1856 | "rand_core 0.3.1", 1857 | ] 1858 | 1859 | [[package]] 1860 | name = "rand_jitter" 1861 | version = "0.1.4" 1862 | source = "registry+https://github.com/rust-lang/crates.io-index" 1863 | checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" 1864 | dependencies = [ 1865 | "libc", 1866 | "rand_core 0.4.2", 1867 | "winapi 0.3.8", 1868 | ] 1869 | 1870 | [[package]] 1871 | name = "rand_os" 1872 | version = "0.1.3" 1873 | source = "registry+https://github.com/rust-lang/crates.io-index" 1874 | checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" 1875 | dependencies = [ 1876 | "cloudabi", 1877 | "fuchsia-cprng", 1878 | "libc", 1879 | "rand_core 0.4.2", 1880 | "rdrand", 1881 | "winapi 0.3.8", 1882 | ] 1883 | 1884 | [[package]] 1885 | name = "rand_pcg" 1886 | version = "0.1.2" 1887 | source = "registry+https://github.com/rust-lang/crates.io-index" 1888 | checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" 1889 | dependencies = [ 1890 | "autocfg 0.1.7", 1891 | "rand_core 0.4.2", 1892 | ] 1893 | 1894 | [[package]] 1895 | name = "rand_xorshift" 1896 | version = "0.1.1" 1897 | source = "registry+https://github.com/rust-lang/crates.io-index" 1898 | checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" 1899 | dependencies = [ 1900 | "rand_core 0.3.1", 1901 | ] 1902 | 1903 | [[package]] 1904 | name = "rayon" 1905 | version = "1.3.0" 1906 | source = "registry+https://github.com/rust-lang/crates.io-index" 1907 | checksum = "db6ce3297f9c85e16621bb8cca38a06779ffc31bb8184e1be4bed2be4678a098" 1908 | dependencies = [ 1909 | "crossbeam-deque", 1910 | "either", 1911 | "rayon-core", 1912 | ] 1913 | 1914 | [[package]] 1915 | name = "rayon-core" 1916 | version = "1.7.0" 1917 | source = "registry+https://github.com/rust-lang/crates.io-index" 1918 | checksum = "08a89b46efaf957e52b18062fb2f4660f8b8a4dde1807ca002690868ef2c85a9" 1919 | dependencies = [ 1920 | "crossbeam-deque", 1921 | "crossbeam-queue", 1922 | "crossbeam-utils 0.7.2", 1923 | "lazy_static", 1924 | "num_cpus", 1925 | ] 1926 | 1927 | [[package]] 1928 | name = "rdrand" 1929 | version = "0.4.0" 1930 | source = "registry+https://github.com/rust-lang/crates.io-index" 1931 | checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" 1932 | dependencies = [ 1933 | "rand_core 0.3.1", 1934 | ] 1935 | 1936 | [[package]] 1937 | name = "redox_syscall" 1938 | version = "0.1.56" 1939 | source = "registry+https://github.com/rust-lang/crates.io-index" 1940 | checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" 1941 | 1942 | [[package]] 1943 | name = "redox_users" 1944 | version = "0.3.4" 1945 | source = "registry+https://github.com/rust-lang/crates.io-index" 1946 | checksum = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431" 1947 | dependencies = [ 1948 | "getrandom", 1949 | "redox_syscall", 1950 | "rust-argon2", 1951 | ] 1952 | 1953 | [[package]] 1954 | name = "regex" 1955 | version = "1.3.6" 1956 | source = "registry+https://github.com/rust-lang/crates.io-index" 1957 | checksum = "7f6946991529684867e47d86474e3a6d0c0ab9b82d5821e314b1ede31fa3a4b3" 1958 | dependencies = [ 1959 | "aho-corasick", 1960 | "memchr", 1961 | "regex-syntax 0.6.17", 1962 | "thread_local", 1963 | ] 1964 | 1965 | [[package]] 1966 | name = "regex-syntax" 1967 | version = "0.4.2" 1968 | source = "registry+https://github.com/rust-lang/crates.io-index" 1969 | checksum = "8e931c58b93d86f080c734bfd2bce7dd0079ae2331235818133c8be7f422e20e" 1970 | 1971 | [[package]] 1972 | name = "regex-syntax" 1973 | version = "0.6.17" 1974 | source = "registry+https://github.com/rust-lang/crates.io-index" 1975 | checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" 1976 | 1977 | [[package]] 1978 | name = "remove_dir_all" 1979 | version = "0.5.2" 1980 | source = "registry+https://github.com/rust-lang/crates.io-index" 1981 | checksum = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" 1982 | dependencies = [ 1983 | "winapi 0.3.8", 1984 | ] 1985 | 1986 | [[package]] 1987 | name = "route-recognizer" 1988 | version = "0.1.13" 1989 | source = "registry+https://github.com/rust-lang/crates.io-index" 1990 | checksum = "ea509065eb0b3c446acdd0102f0d46567dc30902dc0be91d6552035d92b0f4f8" 1991 | 1992 | [[package]] 1993 | name = "rusqlite" 1994 | version = "0.21.0" 1995 | source = "registry+https://github.com/rust-lang/crates.io-index" 1996 | checksum = "64a656821bb6317a84b257737b7934f79c0dbb7eb694710475908280ebad3e64" 1997 | dependencies = [ 1998 | "bitflags", 1999 | "fallible-iterator", 2000 | "fallible-streaming-iterator", 2001 | "libsqlite3-sys", 2002 | "lru-cache", 2003 | "memchr", 2004 | "time", 2005 | ] 2006 | 2007 | [[package]] 2008 | name = "rust-argon2" 2009 | version = "0.7.0" 2010 | source = "registry+https://github.com/rust-lang/crates.io-index" 2011 | checksum = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017" 2012 | dependencies = [ 2013 | "base64", 2014 | "blake2b_simd", 2015 | "constant_time_eq", 2016 | "crossbeam-utils 0.7.2", 2017 | ] 2018 | 2019 | [[package]] 2020 | name = "rust-stemmers" 2021 | version = "1.2.0" 2022 | source = "registry+https://github.com/rust-lang/crates.io-index" 2023 | checksum = "e46a2036019fdb888131db7a4c847a1063a7493f971ed94ea82c67eada63ca54" 2024 | dependencies = [ 2025 | "serde", 2026 | "serde_derive", 2027 | ] 2028 | 2029 | [[package]] 2030 | name = "rustc-demangle" 2031 | version = "0.1.16" 2032 | source = "registry+https://github.com/rust-lang/crates.io-index" 2033 | checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" 2034 | 2035 | [[package]] 2036 | name = "rustc_version" 2037 | version = "0.2.3" 2038 | source = "registry+https://github.com/rust-lang/crates.io-index" 2039 | checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 2040 | dependencies = [ 2041 | "semver", 2042 | ] 2043 | 2044 | [[package]] 2045 | name = "rustversion" 2046 | version = "1.0.2" 2047 | source = "registry+https://github.com/rust-lang/crates.io-index" 2048 | checksum = "b3bba175698996010c4f6dce5e7f173b6eb781fce25d2cfc45e27091ce0b79f6" 2049 | dependencies = [ 2050 | "proc-macro2", 2051 | "quote", 2052 | "syn", 2053 | ] 2054 | 2055 | [[package]] 2056 | name = "ryu" 2057 | version = "1.0.3" 2058 | source = "registry+https://github.com/rust-lang/crates.io-index" 2059 | checksum = "535622e6be132bccd223f4bb2b8ac8d53cda3c7a6394944d3b2b33fb974f9d76" 2060 | 2061 | [[package]] 2062 | name = "same-file" 2063 | version = "1.0.6" 2064 | source = "registry+https://github.com/rust-lang/crates.io-index" 2065 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 2066 | dependencies = [ 2067 | "winapi-util", 2068 | ] 2069 | 2070 | [[package]] 2071 | name = "schannel" 2072 | version = "0.1.18" 2073 | source = "registry+https://github.com/rust-lang/crates.io-index" 2074 | checksum = "039c25b130bd8c1321ee2d7de7fde2659fa9c2744e4bb29711cfc852ea53cd19" 2075 | dependencies = [ 2076 | "lazy_static", 2077 | "winapi 0.3.8", 2078 | ] 2079 | 2080 | [[package]] 2081 | name = "scheduled-thread-pool" 2082 | version = "0.2.4" 2083 | source = "registry+https://github.com/rust-lang/crates.io-index" 2084 | checksum = "0988d7fdf88d5e5fcf5923a0f1e8ab345f3e98ab4bc6bc45a2d5ff7f7458fbf6" 2085 | dependencies = [ 2086 | "parking_lot 0.10.0", 2087 | ] 2088 | 2089 | [[package]] 2090 | name = "scopeguard" 2091 | version = "1.1.0" 2092 | source = "registry+https://github.com/rust-lang/crates.io-index" 2093 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 2094 | 2095 | [[package]] 2096 | name = "semver" 2097 | version = "0.9.0" 2098 | source = "registry+https://github.com/rust-lang/crates.io-index" 2099 | checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 2100 | dependencies = [ 2101 | "semver-parser", 2102 | ] 2103 | 2104 | [[package]] 2105 | name = "semver-parser" 2106 | version = "0.7.0" 2107 | source = "registry+https://github.com/rust-lang/crates.io-index" 2108 | checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 2109 | 2110 | [[package]] 2111 | name = "serde" 2112 | version = "1.0.105" 2113 | source = "registry+https://github.com/rust-lang/crates.io-index" 2114 | checksum = "e707fbbf255b8fc8c3b99abb91e7257a622caeb20a9818cbadbeeede4e0932ff" 2115 | dependencies = [ 2116 | "serde_derive", 2117 | ] 2118 | 2119 | [[package]] 2120 | name = "serde_derive" 2121 | version = "1.0.105" 2122 | source = "registry+https://github.com/rust-lang/crates.io-index" 2123 | checksum = "ac5d00fc561ba2724df6758a17de23df5914f20e41cb00f94d5b7ae42fffaff8" 2124 | dependencies = [ 2125 | "proc-macro2", 2126 | "quote", 2127 | "syn", 2128 | ] 2129 | 2130 | [[package]] 2131 | name = "serde_json" 2132 | version = "1.0.50" 2133 | source = "registry+https://github.com/rust-lang/crates.io-index" 2134 | checksum = "78a7a12c167809363ec3bd7329fc0a3369056996de43c4b37ef3cd54a6ce4867" 2135 | dependencies = [ 2136 | "itoa", 2137 | "ryu", 2138 | "serde", 2139 | ] 2140 | 2141 | [[package]] 2142 | name = "serde_qs" 2143 | version = "0.5.2" 2144 | source = "registry+https://github.com/rust-lang/crates.io-index" 2145 | checksum = "d43eef44996bbe16e99ac720e1577eefa16f7b76b5172165c98ced20ae9903e1" 2146 | dependencies = [ 2147 | "data-encoding", 2148 | "error-chain", 2149 | "percent-encoding 1.0.1", 2150 | "serde", 2151 | ] 2152 | 2153 | [[package]] 2154 | name = "serde_urlencoded" 2155 | version = "0.6.1" 2156 | source = "registry+https://github.com/rust-lang/crates.io-index" 2157 | checksum = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97" 2158 | dependencies = [ 2159 | "dtoa", 2160 | "itoa", 2161 | "serde", 2162 | "url 2.1.1", 2163 | ] 2164 | 2165 | [[package]] 2166 | name = "slab" 2167 | version = "0.4.2" 2168 | source = "registry+https://github.com/rust-lang/crates.io-index" 2169 | checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" 2170 | 2171 | [[package]] 2172 | name = "sluice" 2173 | version = "0.4.2" 2174 | source = "registry+https://github.com/rust-lang/crates.io-index" 2175 | checksum = "0a7d06dfb3e8743bc19e6de8a302277471d08077d68946b307280496dc5a3531" 2176 | dependencies = [ 2177 | "futures-channel-preview", 2178 | "futures-core-preview", 2179 | "futures-io-preview", 2180 | ] 2181 | 2182 | [[package]] 2183 | name = "smallvec" 2184 | version = "0.6.13" 2185 | source = "registry+https://github.com/rust-lang/crates.io-index" 2186 | checksum = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" 2187 | dependencies = [ 2188 | "maybe-uninit", 2189 | ] 2190 | 2191 | [[package]] 2192 | name = "smallvec" 2193 | version = "1.2.0" 2194 | source = "registry+https://github.com/rust-lang/crates.io-index" 2195 | checksum = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc" 2196 | 2197 | [[package]] 2198 | name = "snap" 2199 | version = "1.0.0" 2200 | source = "registry+https://github.com/rust-lang/crates.io-index" 2201 | checksum = "f7fb9b0bb877b35a1cc1474a3b43d9c226a2625311760cdda2cbccbc0c7a8376" 2202 | 2203 | [[package]] 2204 | name = "socket2" 2205 | version = "0.3.12" 2206 | source = "registry+https://github.com/rust-lang/crates.io-index" 2207 | checksum = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918" 2208 | dependencies = [ 2209 | "cfg-if", 2210 | "libc", 2211 | "redox_syscall", 2212 | "winapi 0.3.8", 2213 | ] 2214 | 2215 | [[package]] 2216 | name = "stable_deref_trait" 2217 | version = "1.1.1" 2218 | source = "registry+https://github.com/rust-lang/crates.io-index" 2219 | checksum = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" 2220 | 2221 | [[package]] 2222 | name = "stopwords" 2223 | version = "0.1.0" 2224 | source = "registry+https://github.com/rust-lang/crates.io-index" 2225 | checksum = "96384d500e133dee46c0db6d9b94db5606a2ae6fade3978ece39a28180b7947e" 2226 | dependencies = [ 2227 | "failure", 2228 | "lazy_static", 2229 | ] 2230 | 2231 | [[package]] 2232 | name = "string" 2233 | version = "0.2.1" 2234 | source = "registry+https://github.com/rust-lang/crates.io-index" 2235 | checksum = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d" 2236 | dependencies = [ 2237 | "bytes 0.4.12", 2238 | ] 2239 | 2240 | [[package]] 2241 | name = "surf" 2242 | version = "1.0.3" 2243 | source = "registry+https://github.com/rust-lang/crates.io-index" 2244 | checksum = "741a8008f8a833ef16f47df94a30754478fb2c2bf822b9c2e6f7f09203b97ace" 2245 | dependencies = [ 2246 | "futures-preview", 2247 | "http", 2248 | "isahc", 2249 | "js-sys", 2250 | "log", 2251 | "mime", 2252 | "mime_guess", 2253 | "serde", 2254 | "serde_json", 2255 | "serde_urlencoded", 2256 | "url 2.1.1", 2257 | "wasm-bindgen", 2258 | "wasm-bindgen-futures", 2259 | "web-sys", 2260 | ] 2261 | 2262 | [[package]] 2263 | name = "syn" 2264 | version = "1.0.17" 2265 | source = "registry+https://github.com/rust-lang/crates.io-index" 2266 | checksum = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" 2267 | dependencies = [ 2268 | "proc-macro2", 2269 | "quote", 2270 | "unicode-xid", 2271 | ] 2272 | 2273 | [[package]] 2274 | name = "synstructure" 2275 | version = "0.12.3" 2276 | source = "registry+https://github.com/rust-lang/crates.io-index" 2277 | checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" 2278 | dependencies = [ 2279 | "proc-macro2", 2280 | "quote", 2281 | "syn", 2282 | "unicode-xid", 2283 | ] 2284 | 2285 | [[package]] 2286 | name = "syntax" 2287 | version = "0.1.0" 2288 | dependencies = [ 2289 | "quote", 2290 | "syn", 2291 | ] 2292 | 2293 | [[package]] 2294 | name = "tantivy" 2295 | version = "0.12.0" 2296 | source = "registry+https://github.com/rust-lang/crates.io-index" 2297 | checksum = "02e1d2fbfa82ab829208e5f03f4d2c177b8a126252ab4f80ed232e1064770efb" 2298 | dependencies = [ 2299 | "atomicwrites", 2300 | "base64", 2301 | "bitpacking", 2302 | "byteorder", 2303 | "census", 2304 | "chrono", 2305 | "crc32fast", 2306 | "crossbeam", 2307 | "downcast-rs", 2308 | "fail", 2309 | "failure", 2310 | "fnv", 2311 | "fs2", 2312 | "futures 0.3.4", 2313 | "htmlescape", 2314 | "itertools", 2315 | "levenshtein_automata", 2316 | "log", 2317 | "memmap", 2318 | "murmurhash32", 2319 | "notify", 2320 | "num_cpus", 2321 | "once_cell", 2322 | "owned-read", 2323 | "owning_ref", 2324 | "rayon", 2325 | "regex", 2326 | "rust-stemmers", 2327 | "serde", 2328 | "serde_derive", 2329 | "serde_json", 2330 | "smallvec 1.2.0", 2331 | "snap", 2332 | "stable_deref_trait", 2333 | "tantivy-fst", 2334 | "tantivy-query-grammar", 2335 | "tempfile", 2336 | "uuid 0.8.1", 2337 | "winapi 0.3.8", 2338 | ] 2339 | 2340 | [[package]] 2341 | name = "tantivy-fst" 2342 | version = "0.2.1" 2343 | source = "registry+https://github.com/rust-lang/crates.io-index" 2344 | checksum = "38878efb477cf2efb7d9112b12b230c27d32abdfec4bea5e66095733f2928610" 2345 | dependencies = [ 2346 | "byteorder", 2347 | "levenshtein_automata", 2348 | "regex-syntax 0.4.2", 2349 | "utf8-ranges", 2350 | ] 2351 | 2352 | [[package]] 2353 | name = "tantivy-query-grammar" 2354 | version = "0.12.0" 2355 | source = "registry+https://github.com/rust-lang/crates.io-index" 2356 | checksum = "900f098da37d350b0e8f116791b9ee43e600704cb6b5cc83b7f826d1b119f21c" 2357 | dependencies = [ 2358 | "combine", 2359 | ] 2360 | 2361 | [[package]] 2362 | name = "tempdir" 2363 | version = "0.3.7" 2364 | source = "registry+https://github.com/rust-lang/crates.io-index" 2365 | checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" 2366 | dependencies = [ 2367 | "rand 0.4.6", 2368 | "remove_dir_all", 2369 | ] 2370 | 2371 | [[package]] 2372 | name = "tempfile" 2373 | version = "3.1.0" 2374 | source = "registry+https://github.com/rust-lang/crates.io-index" 2375 | checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" 2376 | dependencies = [ 2377 | "cfg-if", 2378 | "libc", 2379 | "rand 0.7.3", 2380 | "redox_syscall", 2381 | "remove_dir_all", 2382 | "winapi 0.3.8", 2383 | ] 2384 | 2385 | [[package]] 2386 | name = "termcolor" 2387 | version = "1.1.0" 2388 | source = "registry+https://github.com/rust-lang/crates.io-index" 2389 | checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" 2390 | dependencies = [ 2391 | "winapi-util", 2392 | ] 2393 | 2394 | [[package]] 2395 | name = "thread_local" 2396 | version = "1.0.1" 2397 | source = "registry+https://github.com/rust-lang/crates.io-index" 2398 | checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" 2399 | dependencies = [ 2400 | "lazy_static", 2401 | ] 2402 | 2403 | [[package]] 2404 | name = "tide" 2405 | version = "0.6.0" 2406 | source = "registry+https://github.com/rust-lang/crates.io-index" 2407 | checksum = "e619c99048ae107912703d0efeec4ff4fbff704f064e51d3eee614b28ea7b739" 2408 | dependencies = [ 2409 | "async-std", 2410 | "cookie", 2411 | "futures 0.3.4", 2412 | "http", 2413 | "http-service", 2414 | "http-service-hyper", 2415 | "log", 2416 | "mime", 2417 | "pin-project-lite", 2418 | "route-recognizer", 2419 | "serde", 2420 | "serde_json", 2421 | "serde_qs", 2422 | ] 2423 | 2424 | [[package]] 2425 | name = "time" 2426 | version = "0.1.42" 2427 | source = "registry+https://github.com/rust-lang/crates.io-index" 2428 | checksum = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" 2429 | dependencies = [ 2430 | "libc", 2431 | "redox_syscall", 2432 | "winapi 0.3.8", 2433 | ] 2434 | 2435 | [[package]] 2436 | name = "tique" 2437 | version = "0.4.0" 2438 | source = "registry+https://github.com/rust-lang/crates.io-index" 2439 | checksum = "876f304960d9f681faeef903f9cbe5aeda6b89de52afd338bf9cdb7fcd56fe85" 2440 | dependencies = [ 2441 | "tantivy", 2442 | ] 2443 | 2444 | [[package]] 2445 | name = "tokio" 2446 | version = "0.1.22" 2447 | source = "registry+https://github.com/rust-lang/crates.io-index" 2448 | checksum = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" 2449 | dependencies = [ 2450 | "bytes 0.4.12", 2451 | "futures 0.1.29", 2452 | "mio", 2453 | "num_cpus", 2454 | "tokio-current-thread", 2455 | "tokio-executor", 2456 | "tokio-io", 2457 | "tokio-reactor", 2458 | "tokio-threadpool", 2459 | "tokio-timer", 2460 | ] 2461 | 2462 | [[package]] 2463 | name = "tokio-buf" 2464 | version = "0.1.1" 2465 | source = "registry+https://github.com/rust-lang/crates.io-index" 2466 | checksum = "8fb220f46c53859a4b7ec083e41dec9778ff0b1851c0942b211edb89e0ccdc46" 2467 | dependencies = [ 2468 | "bytes 0.4.12", 2469 | "either", 2470 | "futures 0.1.29", 2471 | ] 2472 | 2473 | [[package]] 2474 | name = "tokio-current-thread" 2475 | version = "0.1.7" 2476 | source = "registry+https://github.com/rust-lang/crates.io-index" 2477 | checksum = "b1de0e32a83f131e002238d7ccde18211c0a5397f60cbfffcb112868c2e0e20e" 2478 | dependencies = [ 2479 | "futures 0.1.29", 2480 | "tokio-executor", 2481 | ] 2482 | 2483 | [[package]] 2484 | name = "tokio-executor" 2485 | version = "0.1.10" 2486 | source = "registry+https://github.com/rust-lang/crates.io-index" 2487 | checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671" 2488 | dependencies = [ 2489 | "crossbeam-utils 0.7.2", 2490 | "futures 0.1.29", 2491 | ] 2492 | 2493 | [[package]] 2494 | name = "tokio-io" 2495 | version = "0.1.13" 2496 | source = "registry+https://github.com/rust-lang/crates.io-index" 2497 | checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" 2498 | dependencies = [ 2499 | "bytes 0.4.12", 2500 | "futures 0.1.29", 2501 | "log", 2502 | ] 2503 | 2504 | [[package]] 2505 | name = "tokio-reactor" 2506 | version = "0.1.12" 2507 | source = "registry+https://github.com/rust-lang/crates.io-index" 2508 | checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351" 2509 | dependencies = [ 2510 | "crossbeam-utils 0.7.2", 2511 | "futures 0.1.29", 2512 | "lazy_static", 2513 | "log", 2514 | "mio", 2515 | "num_cpus", 2516 | "parking_lot 0.9.0", 2517 | "slab", 2518 | "tokio-executor", 2519 | "tokio-io", 2520 | "tokio-sync", 2521 | ] 2522 | 2523 | [[package]] 2524 | name = "tokio-sync" 2525 | version = "0.1.8" 2526 | source = "registry+https://github.com/rust-lang/crates.io-index" 2527 | checksum = "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee" 2528 | dependencies = [ 2529 | "fnv", 2530 | "futures 0.1.29", 2531 | ] 2532 | 2533 | [[package]] 2534 | name = "tokio-tcp" 2535 | version = "0.1.4" 2536 | source = "registry+https://github.com/rust-lang/crates.io-index" 2537 | checksum = "98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72" 2538 | dependencies = [ 2539 | "bytes 0.4.12", 2540 | "futures 0.1.29", 2541 | "iovec", 2542 | "mio", 2543 | "tokio-io", 2544 | "tokio-reactor", 2545 | ] 2546 | 2547 | [[package]] 2548 | name = "tokio-threadpool" 2549 | version = "0.1.18" 2550 | source = "registry+https://github.com/rust-lang/crates.io-index" 2551 | checksum = "df720b6581784c118f0eb4310796b12b1d242a7eb95f716a8367855325c25f89" 2552 | dependencies = [ 2553 | "crossbeam-deque", 2554 | "crossbeam-queue", 2555 | "crossbeam-utils 0.7.2", 2556 | "futures 0.1.29", 2557 | "lazy_static", 2558 | "log", 2559 | "num_cpus", 2560 | "slab", 2561 | "tokio-executor", 2562 | ] 2563 | 2564 | [[package]] 2565 | name = "tokio-timer" 2566 | version = "0.2.13" 2567 | source = "registry+https://github.com/rust-lang/crates.io-index" 2568 | checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296" 2569 | dependencies = [ 2570 | "crossbeam-utils 0.7.2", 2571 | "futures 0.1.29", 2572 | "slab", 2573 | "tokio-executor", 2574 | ] 2575 | 2576 | [[package]] 2577 | name = "try-lock" 2578 | version = "0.2.2" 2579 | source = "registry+https://github.com/rust-lang/crates.io-index" 2580 | checksum = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" 2581 | 2582 | [[package]] 2583 | name = "unicase" 2584 | version = "2.6.0" 2585 | source = "registry+https://github.com/rust-lang/crates.io-index" 2586 | checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" 2587 | dependencies = [ 2588 | "version_check", 2589 | ] 2590 | 2591 | [[package]] 2592 | name = "unicode-bidi" 2593 | version = "0.3.4" 2594 | source = "registry+https://github.com/rust-lang/crates.io-index" 2595 | checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" 2596 | dependencies = [ 2597 | "matches", 2598 | ] 2599 | 2600 | [[package]] 2601 | name = "unicode-normalization" 2602 | version = "0.1.12" 2603 | source = "registry+https://github.com/rust-lang/crates.io-index" 2604 | checksum = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" 2605 | dependencies = [ 2606 | "smallvec 1.2.0", 2607 | ] 2608 | 2609 | [[package]] 2610 | name = "unicode-width" 2611 | version = "0.1.7" 2612 | source = "registry+https://github.com/rust-lang/crates.io-index" 2613 | checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" 2614 | 2615 | [[package]] 2616 | name = "unicode-xid" 2617 | version = "0.2.0" 2618 | source = "registry+https://github.com/rust-lang/crates.io-index" 2619 | checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" 2620 | 2621 | [[package]] 2622 | name = "url" 2623 | version = "1.7.2" 2624 | source = "registry+https://github.com/rust-lang/crates.io-index" 2625 | checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" 2626 | dependencies = [ 2627 | "idna 0.1.5", 2628 | "matches", 2629 | "percent-encoding 1.0.1", 2630 | ] 2631 | 2632 | [[package]] 2633 | name = "url" 2634 | version = "2.1.1" 2635 | source = "registry+https://github.com/rust-lang/crates.io-index" 2636 | checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" 2637 | dependencies = [ 2638 | "idna 0.2.0", 2639 | "matches", 2640 | "percent-encoding 2.1.0", 2641 | ] 2642 | 2643 | [[package]] 2644 | name = "utf8-ranges" 2645 | version = "1.0.4" 2646 | source = "registry+https://github.com/rust-lang/crates.io-index" 2647 | checksum = "b4ae116fef2b7fea257ed6440d3cfcff7f190865f170cdad00bb6465bf18ecba" 2648 | 2649 | [[package]] 2650 | name = "uuid" 2651 | version = "0.7.4" 2652 | source = "registry+https://github.com/rust-lang/crates.io-index" 2653 | checksum = "90dbc611eb48397705a6b0f6e917da23ae517e4d127123d2cf7674206627d32a" 2654 | 2655 | [[package]] 2656 | name = "uuid" 2657 | version = "0.8.1" 2658 | source = "registry+https://github.com/rust-lang/crates.io-index" 2659 | checksum = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11" 2660 | dependencies = [ 2661 | "rand 0.7.3", 2662 | "serde", 2663 | ] 2664 | 2665 | [[package]] 2666 | name = "vcpkg" 2667 | version = "0.2.8" 2668 | source = "registry+https://github.com/rust-lang/crates.io-index" 2669 | checksum = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168" 2670 | 2671 | [[package]] 2672 | name = "version_check" 2673 | version = "0.9.1" 2674 | source = "registry+https://github.com/rust-lang/crates.io-index" 2675 | checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" 2676 | 2677 | [[package]] 2678 | name = "void" 2679 | version = "1.0.2" 2680 | source = "registry+https://github.com/rust-lang/crates.io-index" 2681 | checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 2682 | 2683 | [[package]] 2684 | name = "walkdir" 2685 | version = "2.3.1" 2686 | source = "registry+https://github.com/rust-lang/crates.io-index" 2687 | checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" 2688 | dependencies = [ 2689 | "same-file", 2690 | "winapi 0.3.8", 2691 | "winapi-util", 2692 | ] 2693 | 2694 | [[package]] 2695 | name = "want" 2696 | version = "0.2.0" 2697 | source = "registry+https://github.com/rust-lang/crates.io-index" 2698 | checksum = "b6395efa4784b027708f7451087e647ec73cc74f5d9bc2e418404248d679a230" 2699 | dependencies = [ 2700 | "futures 0.1.29", 2701 | "log", 2702 | "try-lock", 2703 | ] 2704 | 2705 | [[package]] 2706 | name = "wasi" 2707 | version = "0.9.0+wasi-snapshot-preview1" 2708 | source = "registry+https://github.com/rust-lang/crates.io-index" 2709 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 2710 | 2711 | [[package]] 2712 | name = "wasm-bindgen" 2713 | version = "0.2.60" 2714 | source = "registry+https://github.com/rust-lang/crates.io-index" 2715 | checksum = "2cc57ce05287f8376e998cbddfb4c8cb43b84a7ec55cf4551d7c00eef317a47f" 2716 | dependencies = [ 2717 | "cfg-if", 2718 | "wasm-bindgen-macro", 2719 | ] 2720 | 2721 | [[package]] 2722 | name = "wasm-bindgen-backend" 2723 | version = "0.2.60" 2724 | source = "registry+https://github.com/rust-lang/crates.io-index" 2725 | checksum = "d967d37bf6c16cca2973ca3af071d0a2523392e4a594548155d89a678f4237cd" 2726 | dependencies = [ 2727 | "bumpalo", 2728 | "lazy_static", 2729 | "log", 2730 | "proc-macro2", 2731 | "quote", 2732 | "syn", 2733 | "wasm-bindgen-shared", 2734 | ] 2735 | 2736 | [[package]] 2737 | name = "wasm-bindgen-futures" 2738 | version = "0.3.27" 2739 | source = "registry+https://github.com/rust-lang/crates.io-index" 2740 | checksum = "83420b37346c311b9ed822af41ec2e82839bfe99867ec6c54e2da43b7538771c" 2741 | dependencies = [ 2742 | "cfg-if", 2743 | "futures 0.1.29", 2744 | "futures-channel-preview", 2745 | "futures-util-preview", 2746 | "js-sys", 2747 | "lazy_static", 2748 | "wasm-bindgen", 2749 | "web-sys", 2750 | ] 2751 | 2752 | [[package]] 2753 | name = "wasm-bindgen-macro" 2754 | version = "0.2.60" 2755 | source = "registry+https://github.com/rust-lang/crates.io-index" 2756 | checksum = "8bd151b63e1ea881bb742cd20e1d6127cef28399558f3b5d415289bc41eee3a4" 2757 | dependencies = [ 2758 | "quote", 2759 | "wasm-bindgen-macro-support", 2760 | ] 2761 | 2762 | [[package]] 2763 | name = "wasm-bindgen-macro-support" 2764 | version = "0.2.60" 2765 | source = "registry+https://github.com/rust-lang/crates.io-index" 2766 | checksum = "d68a5b36eef1be7868f668632863292e37739656a80fc4b9acec7b0bd35a4931" 2767 | dependencies = [ 2768 | "proc-macro2", 2769 | "quote", 2770 | "syn", 2771 | "wasm-bindgen-backend", 2772 | "wasm-bindgen-shared", 2773 | ] 2774 | 2775 | [[package]] 2776 | name = "wasm-bindgen-shared" 2777 | version = "0.2.60" 2778 | source = "registry+https://github.com/rust-lang/crates.io-index" 2779 | checksum = "daf76fe7d25ac79748a37538b7daeed1c7a6867c92d3245c12c6222e4a20d639" 2780 | 2781 | [[package]] 2782 | name = "web-sys" 2783 | version = "0.3.37" 2784 | source = "registry+https://github.com/rust-lang/crates.io-index" 2785 | checksum = "2d6f51648d8c56c366144378a33290049eafdd784071077f6fe37dae64c1c4cb" 2786 | dependencies = [ 2787 | "js-sys", 2788 | "wasm-bindgen", 2789 | ] 2790 | 2791 | [[package]] 2792 | name = "winapi" 2793 | version = "0.2.8" 2794 | source = "registry+https://github.com/rust-lang/crates.io-index" 2795 | checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" 2796 | 2797 | [[package]] 2798 | name = "winapi" 2799 | version = "0.3.8" 2800 | source = "registry+https://github.com/rust-lang/crates.io-index" 2801 | checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" 2802 | dependencies = [ 2803 | "winapi-i686-pc-windows-gnu", 2804 | "winapi-x86_64-pc-windows-gnu", 2805 | ] 2806 | 2807 | [[package]] 2808 | name = "winapi-build" 2809 | version = "0.1.1" 2810 | source = "registry+https://github.com/rust-lang/crates.io-index" 2811 | checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" 2812 | 2813 | [[package]] 2814 | name = "winapi-i686-pc-windows-gnu" 2815 | version = "0.4.0" 2816 | source = "registry+https://github.com/rust-lang/crates.io-index" 2817 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 2818 | 2819 | [[package]] 2820 | name = "winapi-util" 2821 | version = "0.1.4" 2822 | source = "registry+https://github.com/rust-lang/crates.io-index" 2823 | checksum = "fa515c5163a99cc82bab70fd3bfdd36d827be85de63737b40fcef2ce084a436e" 2824 | dependencies = [ 2825 | "winapi 0.3.8", 2826 | ] 2827 | 2828 | [[package]] 2829 | name = "winapi-x86_64-pc-windows-gnu" 2830 | version = "0.4.0" 2831 | source = "registry+https://github.com/rust-lang/crates.io-index" 2832 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 2833 | 2834 | [[package]] 2835 | name = "ws2_32-sys" 2836 | version = "0.2.1" 2837 | source = "registry+https://github.com/rust-lang/crates.io-index" 2838 | checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" 2839 | dependencies = [ 2840 | "winapi 0.2.8", 2841 | "winapi-build", 2842 | ] 2843 | 2844 | [[package]] 2845 | name = "yaml-rust" 2846 | version = "0.3.5" 2847 | source = "registry+https://github.com/rust-lang/crates.io-index" 2848 | checksum = "e66366e18dc58b46801afbf2ca7661a9f59cc8c5962c29892b6039b4f86fa992" 2849 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = [ 4 | "base", 5 | "scanner", 6 | "program", 7 | "syntax" 8 | ] 9 | 10 | default-members = ["program"] 11 | 12 | [patch.crates-io] 13 | pulldown-cmark = { git = 'https://github.com/gozala/pulldown-cmark' } 14 | juniper = { git = "https://github.com/gozala/juniper", branch = "async-await", features = ["async"] } 15 | commander-rust = { git = 'https://github.com/gozala/commander-rust' } -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # The Knowledge Server 2 | 3 | We want to discover & surface connections between ideas, documents and concepts. This ability is something other tools have specialized in but each within their own silos. By creating an index of that knowledge for a user, we can give them the superpower to discover connections among their own work where ever they go. 4 | 5 | *Warning: This is an experimental piece of research software presented for transparency, and is incomplete, unsupported, and likely to stop working.* 6 | 7 | ### Implementation 8 | 9 | This repository is reference implementation in Rust. It exposes [GraphQL][] API 10 | so that other tools can interface to submit / discover connections between 11 | resources identified by URLs (Local resources are represented via file:/// URLs). 12 | 13 | ### Usage 14 | 15 | At the moment no binaries are distributed, however you can build / run using 16 | [cargo][]. To create a (debug) build run: 17 | 18 | ```sh 19 | cargo +nightly build 20 | ``` 21 | 22 | By default, the build will produce a binary at `./target/debug/knowledge-server`. 23 | 24 | #### Server 25 | 26 | You can start a knowledge-server by running: 27 | 28 | ```sh 29 | ./target/debug/knowledge-server serve 30 | ``` 31 | 32 | Once it's running you can explore protocol schema, execute queries / mutations 33 | using GraphQL IDE at http://localhost:8080/graphiql. 34 | 35 | (This won't be very interesting at first, as you won't have submitted any data to it.) 36 | 37 | #### Daemon 38 | 39 | You can spawn a knowledge-server as a daemon by runing: 40 | 41 | ```sh 42 | ./target/debug/knowledge-server daemon 43 | ``` 44 | 45 | #### Scan / Ingest content 46 | 47 | You can ingest local markdown files into your knowledge base 48 | by running: 49 | 50 | ```sh 51 | ./target/debug/knowledge-server scan $YOUR_PATH_HERE 52 | ``` 53 | 54 | ### Local Storage 55 | 56 | Browsing history can be sensitive. That's one of the reasons KSP keeps all its data on your local machine. We haven't built any tools to remove data from KSP, but it stores it data in `~/.knowledge-service/`. Most of the data is stored in a `.sqlite` file, which you can inspect and modify at your own peril, but the full-text TF-IDF data lives in a special format used by the [Tantivy](https://github.com/tantivy-search/tantivy/) library. 57 | 58 | ### Hacking Notes 59 | 60 | We ran into [issue][rust-lang/rls-vscode#755] with [rls-vscode][] extension. If 61 | you use vscode you may want to consider [rust analyzer][] instead. 62 | 63 | [rust-lang/rls-vscode#755]: https://github.com/rust-lang/rls-vscode/issues/755 64 | [cargo]: https://doc.rust-lang.org/cargo/ 'Rust package manager' 65 | [graphql]: https://graphql.org/ 'A query language for your API' 66 | [rls-vscode]: https://github.com/rust-lang/rls-vscode 'Rust support for Visual Studio Code' 67 | [rust analyzer]: https://rust-analyzer.github.io/ 68 | -------------------------------------------------------------------------------- /base/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "knowledge-server-base" 3 | version = "0.1.0" 4 | authors = ["Irakli Gozalishvili "] 5 | edition = "2018" 6 | 7 | 8 | [dependencies] 9 | async-std = { version = "1.5.0", features = ["attributes"] } 10 | futures = { version = "0.3.4", features = ["compat"] } 11 | juniper = { version = "0.14.2", features = ["async"] } 12 | tide = "0.6.0" 13 | rusqlite = "0.21.0" 14 | r2d2_sqlite = "0.14.0" 15 | r2d2 = "0.8.8" 16 | dirs = "2.0.2" 17 | async-trait = "0.1.27" 18 | open = "1.4.0" 19 | log = "0.4.8" 20 | dataloader = "0.11.0" 21 | tique = "0.4.0" 22 | tantivy = "0.12.0" 23 | anyhow = "1.0.28" 24 | stopwords = "0.1.0" 25 | -------------------------------------------------------------------------------- /base/sql/create_tables.sql: -------------------------------------------------------------------------------- 1 | PRAGMA foreign_keys = ON; 2 | 3 | CREATE TABLE IF NOT EXISTS resources ( 4 | url NOT NULL, 5 | title Text, 6 | description Text, 7 | cid Text, 8 | icon Text, 9 | image Text, 10 | 11 | PRIMARY KEY (url) 12 | ) 13 | WITHOUT ROWID; 14 | 15 | 16 | CREATE TABLE IF NOT EXISTS inline_links( 17 | referrer_url Text, 18 | referrer_fragment Text, 19 | referrer_location Text, 20 | 21 | target_url Text NOT NULL, 22 | name Text NOT NULL, 23 | title Text, 24 | 25 | FOREIGN KEY(referrer_url) REFERENCES resources(url) 26 | ); 27 | 28 | CREATE INDEX IF NOT EXISTS inline_links_idx_target_url ON 29 | inline_links (target_url); 30 | CREATE INDEX IF NOT EXISTS inline_links_idx_referrer_url ON 31 | inline_links (referrer_url); 32 | 33 | CREATE TABLE IF NOT EXISTS reference_links ( 34 | referrer_url Text, 35 | referrer_fragment Text, 36 | referrer_location Text, 37 | 38 | target_url Text NOT NULL, 39 | identifier Text NOT NULL, 40 | name Text NOT NULL, 41 | title Text, 42 | 43 | FOREIGN KEY(referrer_url) REFERENCES resources(url) 44 | ); 45 | CREATE INDEX IF NOT EXISTS reference_links_idx_target_url ON 46 | reference_links (target_url); 47 | CREATE INDEX IF NOT EXISTS reference_links_idx_referrer_url ON 48 | reference_links (referrer_url); 49 | CREATE INDEX IF NOT EXISTS reference_links_idx_identifier ON 50 | reference_links (identifier); 51 | 52 | 53 | CREATE TABLE IF NOT EXISTS tags ( 54 | target_url Text NOT NULL, 55 | name Text NOT NULL, 56 | target_fragment Text NOT NULL, 57 | target_location Text, 58 | 59 | FOREIGN KEY (target_url) REFERENCES resources(url), 60 | PRIMARY KEY (target_url, name, target_fragment) 61 | ) 62 | WITHOUT ROWID; 63 | CREATE INDEX IF NOT EXISTS tags_idx_target_url on tags (target_url); 64 | CREATE INDEX IF NOT EXISTS tags_idx_name on tags (name); 65 | 66 | 67 | CREATE VIEW IF NOT EXISTS 68 | view_links 69 | AS 70 | SELECT 71 | referrer_url, 72 | resources.title as referrer_title, 73 | resources.description as referrer_description, 74 | resources.cid as referrer_cid, 75 | resources.icon as referrer_icon, 76 | resources.image as referrer_image, 77 | referrer_fragment, 78 | referrer_location, 79 | 80 | target_url, 81 | NULL as identifier, 82 | name, 83 | inline_links.title as title, 84 | 0 as kind 85 | FROM 86 | inline_links 87 | INNER JOIN 88 | resources 89 | ON 90 | inline_links.referrer_url = resources.url 91 | 92 | UNION 93 | 94 | SELECT 95 | referrer_url, 96 | resources.title as referrer_title, 97 | resources.description as referrer_description, 98 | resources.cid as referrer_cid, 99 | resources.icon as referrer_icon, 100 | resources.image as referrer_image, 101 | referrer_fragment, 102 | referrer_location, 103 | 104 | target_url, 105 | identifier, 106 | name, 107 | reference_links.title AS title, 108 | 1 as kind 109 | FROM 110 | reference_links 111 | INNER JOIN 112 | resources 113 | ON 114 | reference_links.referrer_url = resources.url; 115 | 116 | 117 | PRAGMA user_version = 1; -------------------------------------------------------------------------------- /base/sql/insert_inline_link.sql: -------------------------------------------------------------------------------- 1 | INSERT OR REPLACE INTO 2 | inline_links 3 | ( 4 | referrer_url, 5 | referrer_fragment, 6 | referrer_location, 7 | target_url, 8 | name, 9 | title 10 | ) 11 | VALUES 12 | ( 13 | :referrer_url, 14 | :referrer_fragment, 15 | :referrer_location, 16 | :target_url, 17 | :name, 18 | :title 19 | ); -------------------------------------------------------------------------------- /base/sql/insert_reference_link.sql: -------------------------------------------------------------------------------- 1 | INSERT OR REPLACE INTO 2 | reference_links 3 | ( 4 | referrer_url, 5 | referrer_fragment, 6 | referrer_location, 7 | target_url, 8 | identifier, 9 | name, 10 | title 11 | ) 12 | VALUES 13 | ( 14 | :referrer_url, 15 | :referrer_fragment, 16 | :referrer_location, 17 | :target_url, 18 | :identifier, 19 | :name, 20 | :title 21 | ); -------------------------------------------------------------------------------- /base/sql/insert_resource.sql: -------------------------------------------------------------------------------- 1 | INSERT OR REPLACE INTO resources 2 | (url, title, description, cid, icon, image) 3 | VALUES 4 | (:url, :title, :description, :cid, :icon, :image); 5 | -------------------------------------------------------------------------------- /base/sql/insert_tag.sql: -------------------------------------------------------------------------------- 1 | INSERT OR REPLACE INTO 2 | tags 3 | (target_url, target_fragment, target_location, name) 4 | VALUES 5 | (:target_url, :target_fragment, :target_location, :name); -------------------------------------------------------------------------------- /base/sql/migrate_tables.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE resources ADD COLUMN icon Text; 2 | ALTER TABLE resources ADD COLUMN image Text; 3 | DROP VIEW view_links; 4 | CREATE VIEW IF NOT EXISTS 5 | view_links 6 | AS 7 | SELECT 8 | referrer_url, 9 | resources.title as referrer_title, 10 | resources.description as referrer_description, 11 | resources.cid as referrer_cid, 12 | resources.icon as referrer_icon, 13 | resources.image as referrer_image, 14 | referrer_fragment, 15 | referrer_location, 16 | 17 | target_url, 18 | NULL as identifier, 19 | name, 20 | inline_links.title as title, 21 | 0 as kind 22 | FROM 23 | inline_links 24 | INNER JOIN 25 | resources 26 | ON 27 | inline_links.referrer_url = resources.url 28 | 29 | UNION 30 | 31 | SELECT 32 | referrer_url, 33 | resources.title as referrer_title, 34 | resources.description as referrer_description, 35 | resources.cid as referrer_cid, 36 | resources.icon as referrer_icon, 37 | resources.image as referrer_image, 38 | referrer_fragment, 39 | referrer_location, 40 | 41 | target_url, 42 | identifier, 43 | name, 44 | reference_links.title AS title, 45 | 1 as kind 46 | FROM 47 | reference_links 48 | INNER JOIN 49 | resources 50 | ON 51 | reference_links.referrer_url = resources.url; 52 | 53 | PRAGMA user_version = 1; -------------------------------------------------------------------------------- /base/sql/select_links_by_referrer.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | kind, 3 | referrer_url, 4 | referrer_cid, 5 | referrer_title, 6 | referrer_description, 7 | referrer_icon, 8 | referrer_image, 9 | referrer_fragment, 10 | referrer_location, 11 | 12 | target_url, 13 | identifier, 14 | name, 15 | title 16 | FROM 17 | view_links 18 | WHERE 19 | referrer_url = :referrer_url; -------------------------------------------------------------------------------- /base/sql/select_links_by_target.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | kind, 3 | referrer_url, 4 | referrer_cid, 5 | referrer_title, 6 | referrer_description, 7 | referrer_icon, 8 | referrer_image, 9 | referrer_fragment, 10 | referrer_location, 11 | 12 | target_url, 13 | identifier, 14 | name, 15 | title 16 | FROM 17 | view_links 18 | WHERE 19 | target_url = :target_url; 20 | -------------------------------------------------------------------------------- /base/sql/select_resource_by_url.sql: -------------------------------------------------------------------------------- 1 | SELECT cid, title, description, icon, image 2 | FROM resources 3 | WHERE url = :url 4 | LIMIT 1; 5 | -------------------------------------------------------------------------------- /base/sql/select_tags_by_name.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | target_url, 3 | name, 4 | target_fragment, 5 | target_location 6 | FROM 7 | tags 8 | WHERE 9 | name = :name; -------------------------------------------------------------------------------- /base/sql/select_tags_by_target.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | target_url, 3 | name, 4 | target_fragment, 5 | target_location 6 | FROM 7 | tags 8 | WHERE 9 | target_url = :target_url; -------------------------------------------------------------------------------- /base/src/data.rs: -------------------------------------------------------------------------------- 1 | use juniper; 2 | use std::convert::From; 3 | use tique::topterms::Keywords; 4 | 5 | #[derive(juniper::GraphQLEnum, Clone, Copy, Debug)] 6 | pub enum LinkKind { 7 | Inline = 0, 8 | Reference = 1, 9 | } 10 | 11 | #[derive(Clone, Debug)] 12 | pub struct Link { 13 | pub kind: LinkKind, 14 | pub referrer_url: String, 15 | pub referrer_cid: Option, 16 | pub referrer_title: String, 17 | pub referrer_description: String, 18 | pub referrer_icon: Option, 19 | pub referrer_image: Option, 20 | 21 | pub referrer_fragment: Option, 22 | pub referrer_location: Option, 23 | 24 | pub target_url: String, 25 | pub name: String, 26 | pub title: String, 27 | pub identifier: Option, 28 | } 29 | 30 | #[derive(Clone, Debug)] 31 | pub struct Tag { 32 | pub name: String, 33 | pub target_url: String, 34 | pub target_fragment: Option, 35 | pub target_location: Option, 36 | } 37 | 38 | #[derive(Debug, Clone)] 39 | pub struct Resource { 40 | pub url: String, 41 | pub info: Option, 42 | } 43 | 44 | #[derive(juniper::GraphQLObject, Debug, Clone)] 45 | pub struct ResourceInfo { 46 | pub title: String, 47 | pub description: String, 48 | pub cid: Option, 49 | pub icon: Option, 50 | pub image: Option, 51 | } 52 | 53 | #[derive(Clone, Debug)] 54 | pub struct SimilarResource { 55 | pub similarity_score: f32, 56 | // URL of the similar resource 57 | pub target_url: String, 58 | } 59 | 60 | // TODO: Implement Debug 61 | #[derive(Clone)] 62 | pub struct SimilarResources { 63 | pub keywords: Keywords, 64 | pub source_url: String, 65 | } 66 | 67 | #[derive(juniper::GraphQLInputObject, Clone, Debug)] 68 | pub struct InputLink { 69 | #[graphql(name = "targetURL")] 70 | pub target_url: String, 71 | 72 | pub referrer_fragment: Option, 73 | pub referrer_location: Option, 74 | 75 | pub kind: LinkKind, 76 | pub name: String, 77 | pub title: String, 78 | pub identifier: Option, 79 | } 80 | 81 | #[derive(juniper::GraphQLInputObject, Clone, Debug)] 82 | pub struct InputTag { 83 | pub name: String, 84 | pub target_fragment: Option, 85 | pub target_location: Option, 86 | } 87 | 88 | #[derive(juniper::GraphQLInputObject, Clone, Debug)] 89 | pub struct InputResource { 90 | pub url: String, 91 | pub cid: Option, 92 | pub title: String, 93 | pub description: String, 94 | pub links: Option>, 95 | pub tags: Option>, 96 | pub icon: Option, 97 | pub image: Option, 98 | 99 | pub content: Option, 100 | } 101 | 102 | #[derive(juniper::GraphQLObject, Clone, Debug)] 103 | pub struct Open { 104 | pub open_ok: bool, 105 | pub exit_ok: bool, 106 | pub code: Option, 107 | } 108 | 109 | #[derive(juniper::GraphQLInputObject, Clone, Debug)] 110 | pub struct InputSimilar { 111 | pub url: Option, 112 | pub content: String, 113 | } 114 | 115 | impl From for Resource { 116 | fn from(url: String) -> Self { 117 | Resource { 118 | url: url, 119 | info: None, 120 | } 121 | } 122 | } 123 | impl From<&str> for Resource { 124 | fn from(url: &str) -> Self { 125 | Resource { 126 | url: url.to_string(), 127 | info: None, 128 | } 129 | } 130 | } 131 | 132 | impl From<&String> for Resource { 133 | fn from(url: &String) -> Self { 134 | Resource { 135 | url: url.to_string(), 136 | info: None, 137 | } 138 | } 139 | } 140 | 141 | impl From for Resource { 142 | fn from(input: InputResource) -> Self { 143 | Resource { 144 | url: input.url, 145 | info: Some(ResourceInfo { 146 | cid: input.cid, 147 | title: input.title, 148 | description: input.description, 149 | icon: input.icon, 150 | image: input.image, 151 | }), 152 | } 153 | } 154 | } 155 | 156 | impl From<&InputResource> for Resource { 157 | fn from(input: &InputResource) -> Self { 158 | Resource { 159 | url: input.url.clone(), 160 | info: Some(ResourceInfo { 161 | cid: input.cid.clone(), 162 | title: input.title.clone(), 163 | description: input.description.clone(), 164 | icon: input.icon.clone(), 165 | image: input.image.clone(), 166 | }), 167 | } 168 | } 169 | } 170 | 171 | impl From for InputTag { 172 | fn from(name: String) -> Self { 173 | InputTag { 174 | name: name, 175 | target_fragment: None, 176 | target_location: None, 177 | } 178 | } 179 | } 180 | 181 | #[derive(Debug, Clone)] 182 | pub struct Query; 183 | #[derive(Debug, Clone)] 184 | pub struct Mutations; 185 | -------------------------------------------------------------------------------- /base/src/index.rs: -------------------------------------------------------------------------------- 1 | use crate::data::SimilarResource; 2 | use dirs; 3 | use log; 4 | use std::convert::From; 5 | use std::fmt; 6 | use std::path::Path; 7 | use std::string::FromUtf8Error; 8 | use std::sync::{Arc, PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard}; 9 | use stopwords::{Stopwords, NLTK}; 10 | use tantivy::collector::TopDocs; 11 | use tantivy::directory; 12 | use tantivy::query::{BooleanQuery, Occur, Query, TermQuery}; 13 | use tantivy::schema; 14 | use tantivy::schema::{IndexRecordOption, TextFieldIndexing, TextOptions}; 15 | use tantivy::tokenizer; 16 | use tantivy::{Index, IndexReader, IndexWriter, Opstamp, Term}; 17 | use tique::topterms::{Keywords, TopTerms}; 18 | 19 | #[derive(Clone)] 20 | struct Schema { 21 | url: schema::Field, 22 | title: schema::Field, 23 | body: schema::Field, 24 | schema: schema::Schema, 25 | } 26 | impl fmt::Debug for Schema { 27 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 28 | f.debug_struct("Schema").finish() 29 | } 30 | } 31 | 32 | impl Schema { 33 | pub fn tokenizer() -> tokenizer::TextAnalyzer { 34 | let words = NLTK::stopwords(stopwords::Language::English) 35 | .unwrap() 36 | .iter() 37 | .map(|word| word.to_string()) 38 | .collect(); 39 | 40 | tokenizer::TextAnalyzer::from(tokenizer::SimpleTokenizer) 41 | .filter(tokenizer::RemoveLongFilter::limit(40)) 42 | .filter(tokenizer::LowerCaser) 43 | .filter(tokenizer::StopWordFilter::remove(words)) 44 | // Disable stemmer as keywords appear to be stems instead of 45 | // actual terms. 46 | // .filter(tokenizer::Stemmer::new(tokenizer::Language::English)) 47 | } 48 | pub fn indexer() -> TextFieldIndexing { 49 | TextFieldIndexing::default() 50 | .set_tokenizer("en_with_stopwords") 51 | .set_index_option(IndexRecordOption::WithFreqsAndPositions) 52 | } 53 | pub fn text_options() -> TextOptions { 54 | schema::TextOptions::default().set_indexing_options(Schema::indexer()) 55 | } 56 | pub fn new() -> Self { 57 | let mut schema = schema::SchemaBuilder::default(); 58 | // Field corresponds to the resource URL. Because we want to be able 59 | // to see the `url` in returned documents we mark it stored. 60 | let url = schema.add_text_field("url", schema::STRING | schema::STORED); 61 | 62 | // Field corresponds to resource.info.title. Field has a `Text` flag which 63 | // implies that it will be tokenized and indexed, along with its term 64 | // frequency and term positions. 65 | // Field also has `STORED` flag which means that the field will also be 66 | // saved in a compressed row-oriented key-value store. 67 | let title = schema.add_text_field("title", Schema::text_options()); 68 | 69 | // Field `body` corresponds to content of the resource. It will have 70 | // full-text search, but not an ability to reconstruct it. 71 | let body = schema.add_text_field("body", Schema::text_options().set_stored()); 72 | 73 | Schema { 74 | url, 75 | title, 76 | body, 77 | schema: schema.build(), 78 | } 79 | } 80 | 81 | fn index(&self, path: &Path) -> Result { 82 | let directory = tantivy::directory::MmapDirectory::open(path)?; 83 | let index = tantivy::Index::open_or_create(directory, self.schema.clone())?; 84 | 85 | index 86 | .tokenizers() 87 | .register("en_with_stopwords", Schema::tokenizer()); 88 | 89 | Ok(index) 90 | } 91 | 92 | fn document(&self, url: &str, title: &str, body: &str) -> Result { 93 | let mut document = schema::Document::new(); 94 | document.add_text(self.url, url); 95 | document.add_text(self.title, title); 96 | document.add_text(self.body, body); 97 | Ok(document) 98 | } 99 | 100 | fn document_url(&self, doc: schema::Document) -> Result { 101 | let value = doc.get_first(self.url).ok_or(Error::URLReadError)?; 102 | value.text().map(String::from).ok_or(Error::URLReadError) 103 | } 104 | } 105 | 106 | pub struct IndexService { 107 | schema: Schema, 108 | reader: IndexReader, 109 | writer: Arc>, 110 | topterms: TopTerms, 111 | } 112 | 113 | impl IndexService { 114 | pub fn open() -> Result { 115 | let mut path = dirs::home_dir().expect("Unable to locate user home directory"); 116 | path.push(".knowledge-service"); 117 | path.push("tantivy"); 118 | std::fs::create_dir_all(&path)?; 119 | IndexService::activate(&path) 120 | } 121 | pub fn activate(path: &Path) -> Result { 122 | let schema = Schema::new(); 123 | let index = schema.index(path)?; 124 | 125 | let topterms = TopTerms::new(&index, vec![schema.body])?; 126 | let reader = index.reader()?; 127 | let writer = Arc::new(RwLock::new(index.writer(50_000_000)?)); 128 | Ok(IndexService { 129 | schema, 130 | reader, 131 | writer, 132 | topterms, 133 | }) 134 | } 135 | pub async fn ingest(&self, url: &str, title: &str, body: &str) -> Result { 136 | let writer = self.writer.read()?; 137 | // Delete all documents with the matching url before adding a new document. 138 | writer.delete_term(Term::from_field_text(self.schema.url, &url)); 139 | let doc = self.schema.document(url, title, body)?; 140 | Ok(writer.add_document(doc)) 141 | } 142 | pub async fn commit(&self) -> Result { 143 | let mut writer = self.writer.write()?; 144 | Ok(writer.commit()?) 145 | } 146 | pub fn extract_keywords(&self, content: &str, limit: usize) -> Keywords { 147 | self.topterms.extract(limit, content) 148 | } 149 | pub fn search_with_keywords( 150 | &self, 151 | source_url: &str, 152 | keywords: &Keywords, 153 | limit: usize, 154 | ) -> Result, Error> { 155 | let searcher = self.reader.searcher(); 156 | let keyword_query: Box = Box::new(keywords.clone().into_query()); 157 | let source_query = Box::new(TermQuery::new( 158 | Term::from_field_text(self.schema.url, source_url), 159 | IndexRecordOption::Basic, 160 | )); 161 | let query = BooleanQuery::from(vec![ 162 | (Occur::Must, keyword_query), 163 | (Occur::MustNot, source_query), 164 | ]); 165 | 166 | let top_docs = searcher.search(&query, &TopDocs::with_limit(limit))?; 167 | let mut similar = Vec::new(); 168 | for (score, address) in top_docs { 169 | log::info!("Found match {:?} {:?}", &address, &score); 170 | let doc = searcher.doc(address)?; 171 | let target_url = self.schema.document_url(doc)?; 172 | log::info!("Doc maps to {:?}", target_url); 173 | similar.push(SimilarResource { 174 | target_url, 175 | similarity_score: score, 176 | }); 177 | } 178 | Ok(similar) 179 | } 180 | } 181 | 182 | impl fmt::Debug for IndexService { 183 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 184 | f.debug_struct("Schema") 185 | .field("schema", &self.schema) 186 | .finish() 187 | } 188 | } 189 | 190 | #[derive(Debug)] 191 | pub enum Error { 192 | IndexError(tantivy::TantivyError), 193 | OpenIndexError(directory::error::OpenDirectoryError), 194 | ReadLockError, 195 | WriteLockError, 196 | URLDecodeError(FromUtf8Error), 197 | URLReadError, 198 | MissingField(String), 199 | IOError(std::io::Error), 200 | } 201 | 202 | impl std::error::Error for Error {} 203 | 204 | impl fmt::Display for Error { 205 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 206 | match self { 207 | Error::IndexError(error) => error.fmt(f), 208 | Error::OpenIndexError(error) => error.fmt(f), 209 | Error::ReadLockError => write!(f, "Fiiled to create read lock on index"), 210 | Error::WriteLockError => write!(f, "Failed to create write lock on index"), 211 | Error::URLDecodeError(error) => error.fmt(f), 212 | Error::URLReadError => write!(f, "Was unable to read url for the indexed document"), 213 | Error::MissingField(name) => write!( 214 | f, 215 | "Was unable to read {} field of the indexed document", 216 | name 217 | ), 218 | Error::IOError(error) => error.fmt(f), 219 | } 220 | } 221 | } 222 | 223 | impl From for Error { 224 | fn from(error: directory::error::OpenDirectoryError) -> Self { 225 | Error::OpenIndexError(error) 226 | } 227 | } 228 | 229 | impl<'a> From>> for Error { 230 | fn from(_: PoisonError>) -> Error { 231 | Error::ReadLockError 232 | } 233 | } 234 | 235 | impl<'a> From>> for Error { 236 | fn from(_: PoisonError>) -> Self { 237 | Error::WriteLockError 238 | } 239 | } 240 | 241 | impl From for Error { 242 | fn from(error: tantivy::TantivyError) -> Self { 243 | Error::IndexError(error) 244 | } 245 | } 246 | 247 | impl From for Error { 248 | fn from(error: FromUtf8Error) -> Self { 249 | Error::URLDecodeError(error) 250 | } 251 | } 252 | 253 | impl From for Error { 254 | fn from(error: std::io::Error) -> Self { 255 | Error::IOError(error) 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /base/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod data; 2 | mod index; 3 | pub mod schema; 4 | pub mod server; 5 | pub mod service; 6 | mod store; 7 | -------------------------------------------------------------------------------- /base/src/schema.rs: -------------------------------------------------------------------------------- 1 | pub use crate::data::Mutations; 2 | use crate::data::{ 3 | InputResource, InputSimilar, Link, LinkKind, Open, Query, Resource, ResourceInfo, 4 | SimilarResource, SimilarResources, Tag, 5 | }; 6 | use crate::index::IndexService; 7 | use crate::store::DataStore; 8 | pub use juniper::FieldError; 9 | use juniper::{FieldResult, RootNode}; 10 | use log; 11 | use open; 12 | use std::io; 13 | use std::sync::Arc; 14 | #[derive(Debug)] 15 | pub struct State { 16 | pub store: DataStore, 17 | pub index: Arc, 18 | } 19 | impl State { 20 | pub fn new() -> io::Result { 21 | let store = DataStore::open()?; 22 | let index = Arc::new(IndexService::open().unwrap()); 23 | 24 | Ok(State { store, index }) 25 | } 26 | 27 | // pub async fn execute<'a>(&'a self, request: &'a GraphQLRequest) -> GraphQLResponse<'a> { 28 | // let root = &self.schema.root; 29 | // let response: GraphQLResponse<'a> = request.execute_async(root, self).await; 30 | // response 31 | // } 32 | } 33 | impl juniper::Context for State {} 34 | 35 | /// Resource tag. For files resources on MacOS/iOS that roughly translates to file / directory tag. 36 | // For web resources that roughly translates to bookmark tags. 37 | #[juniper::graphql_object(Context = State)] 38 | impl Tag { 39 | /// tag name 40 | fn name(&self) -> &str { 41 | &self.name 42 | } 43 | 44 | fn fragment(&self) -> Option { 45 | self.target_fragment.clone() 46 | } 47 | 48 | fn location(&self) -> Option { 49 | self.target_location.clone() 50 | } 51 | 52 | fn target(&self) -> Resource { 53 | Resource::from(&self.target_url) 54 | } 55 | } 56 | 57 | /// Represents an inline link in markdown file. 58 | #[juniper::graphql_object(Context = State)] 59 | impl Link { 60 | /// link kind in markdown terms which is either reference link or an 61 | /// iniline link. 62 | fn kind(&self) -> LinkKind { 63 | self.kind 64 | } 65 | /// Name that link was encountered by 66 | fn name(&self) -> &str { 67 | &self.name 68 | } 69 | /// Titile that link was encountered by 70 | fn title(&self) -> &str { 71 | &self.title 72 | } 73 | 74 | /// Label it is referred by 75 | /// e.g. In [Local-first software][local-first] it is "local-first" 76 | fn identifier(&self) -> Option<&String> { 77 | self.identifier.as_ref() 78 | } 79 | 80 | /// Target resource of the link 81 | async fn target(&self) -> Resource { 82 | Resource::from(&self.target_url) 83 | } 84 | /// Referrer resource 85 | async fn referrer(&self) -> Resource { 86 | Resource { 87 | url: self.referrer_url.clone(), 88 | info: Some(ResourceInfo { 89 | cid: self.referrer_cid.clone(), 90 | title: self.referrer_title.clone(), 91 | description: self.referrer_description.clone(), 92 | icon: self.referrer_icon.clone(), 93 | image: self.referrer_image.clone(), 94 | }), 95 | } 96 | } 97 | /// Fragment of the resource content where link was discovered. 98 | fn fragment(&self) -> Option<&String> { 99 | self.referrer_fragment.as_ref() 100 | } 101 | /// Location in the resource document where link was discovered. 102 | fn location(&self) -> Option<&String> { 103 | self.referrer_location.as_ref() 104 | } 105 | } 106 | 107 | #[juniper::graphql_object(Context = State)] 108 | impl Resource { 109 | /// URL of the resource 110 | fn url(&self) -> &str { 111 | &self.url 112 | } 113 | /// information containing general information about the resource, kind of 114 | /// a web-card for this resource. 115 | async fn info(&self, state: &State) -> ResourceInfo { 116 | if let Some(info) = &self.info { 117 | info.clone() 118 | } else { 119 | if let Ok(info) = state.store.find_resource_by_url(&self.url).await { 120 | info 121 | } else { 122 | ResourceInfo { 123 | title: self.url.split("/").last().unwrap_or("").to_string(), 124 | description: format!(""), 125 | cid: None, 126 | icon: None, 127 | image: None, 128 | } 129 | } 130 | } 131 | } 132 | 133 | /// Resources this document links to. 134 | async fn links(&self, state: &State) -> FieldResult> { 135 | state.store.find_links_by_referrer(&self.url).await 136 | } 137 | 138 | /// Resources that link to this document. 139 | async fn backLinks(&self, state: &State) -> FieldResult> { 140 | state.store.find_links_by_target(&self.url).await 141 | } 142 | 143 | /// Tag associated to this document. 144 | async fn tags(&self, state: &State) -> FieldResult> { 145 | state.store.find_tags_by_target(&self.url).await 146 | } 147 | 148 | // Resources similar to this one. 149 | #[graphql(arguments(first(default = 5)))] 150 | async fn similar(&self, _first: i32, _state: &State) -> Vec { 151 | // let index = &state.index; 152 | // index.search_similar(input, 10); 153 | 154 | vec![] 155 | } 156 | } 157 | 158 | #[juniper::graphql_object(Context = State)] 159 | impl SimilarResources { 160 | /// keywords by which similar resources were identified. 161 | fn keywords(&self) -> Vec { 162 | self.keywords 163 | .terms() 164 | .map(|t| t.text()) 165 | .map(String::from) 166 | .collect() 167 | } 168 | /// Similar resources. 169 | #[graphql(arguments(first(default = 5)))] 170 | fn similar(&self, first: i32, state: &State) -> FieldResult> { 171 | Ok(state 172 | .index 173 | .search_with_keywords(&self.source_url, &self.keywords, first as usize)?) 174 | } 175 | } 176 | 177 | #[juniper::graphql_object(Context = State)] 178 | impl SimilarResource { 179 | /// Similar resource. 180 | fn resource(&self) -> Resource { 181 | Resource::from(&self.target_url) 182 | } 183 | /// Score of similarity. 184 | fn score(&self) -> f64 { 185 | self.similarity_score as f64 186 | } 187 | } 188 | 189 | #[juniper::graphql_object(Context = State)] 190 | impl Query { 191 | /// gives a resource for the given url. 192 | async fn resource(_state: &State, url: String) -> Resource { 193 | Resource::from(url) 194 | } 195 | /// finds tags for the given name. 196 | async fn tags(state: &State, name: String) -> FieldResult> { 197 | state.store.find_tags_by_name(&name).await 198 | } 199 | 200 | #[graphql(arguments(first(default = 5)))] 201 | async fn similar(state: &State, input: InputSimilar, first: i32) -> SimilarResources { 202 | let keywords = state.index.extract_keywords(&input.content, first as usize); 203 | SimilarResources { 204 | keywords, 205 | source_url: input.url.unwrap_or(String::from("")), 206 | } 207 | } 208 | } 209 | 210 | impl Mutations { 211 | /// Injests resource into knowledge base. 212 | pub async fn ingest(state: &State, input: InputResource) -> FieldResult { 213 | log::info!("Ingesting resource {:}", input.url); 214 | let resource = state.store.insert_resource(&input)?; 215 | 216 | if let Some(tags) = input.tags { 217 | state.store.insert_tags(&input.url, &tags)?; 218 | } 219 | if let Some(links) = input.links { 220 | state.store.insert_links(&input.url, &links)?; 221 | } 222 | 223 | if let Some(content) = input.content { 224 | let index = &state.index; 225 | index.ingest(&input.url, &input.title, &content).await?; 226 | } 227 | log::info!("Resource was ingested {:}", input.url); 228 | 229 | Ok(resource) 230 | } 231 | } 232 | 233 | #[juniper::graphql_object(Context = State)] 234 | impl Mutations { 235 | async fn ingest(state: &State, resource: InputResource) -> FieldResult { 236 | Mutations::ingest(state, resource).await 237 | } 238 | async fn open(_state: &State, url: String) -> Open { 239 | log::info!("Opening a resource {:}", url); 240 | if let Ok(status) = open::that(url) { 241 | Open { 242 | open_ok: true, 243 | exit_ok: status.success(), 244 | code: status.code(), 245 | } 246 | } else { 247 | Open { 248 | open_ok: false, 249 | exit_ok: false, 250 | code: None, 251 | } 252 | } 253 | } 254 | } 255 | 256 | #[derive(Debug)] 257 | pub struct Schema { 258 | pub root: RootNode<'static, Query, Mutations>, 259 | } 260 | impl Schema { 261 | pub fn new() -> Schema { 262 | Schema { 263 | root: RootNode::new(Query, Mutations), 264 | } 265 | } 266 | } 267 | -------------------------------------------------------------------------------- /base/src/server.rs: -------------------------------------------------------------------------------- 1 | use crate::service::Service; 2 | use async_trait::async_trait; 3 | use futures::future::BoxFuture; 4 | use juniper::http::GraphQLRequest; 5 | use log; 6 | use std::sync::RwLock; 7 | use tide::{Middleware, Next, Request, Response, Server}; 8 | 9 | // #[derive(Debug)] 10 | // struct State { 11 | // pub schema: Schema, 12 | // pub state: SchemaState, 13 | // } 14 | type State = Service; 15 | 16 | async fn handle_graphiql(_: Request) -> Response { 17 | Response::new(200) 18 | .body_string(juniper::http::graphiql::graphiql_source("/graphql")) 19 | .set_header("content-type", "text/html;charset=utf-8") 20 | } 21 | 22 | async fn handle_graphql(mut request: Request) -> Response { 23 | log::info!("Received graphql query"); 24 | let json = request.body_json().await; 25 | 26 | let query: GraphQLRequest = json.expect("be able to deserialize the graphql request"); 27 | 28 | // TODO: We had to clone here because otherwise data loader would be shared 29 | // accross multiple requests which is undesired as: 30 | // 1. It would use more and more memory over time. 31 | // 2. DB changes would not be picked up. 32 | // let state = &request.state().state; 33 | // let schema = &request.state().schema; 34 | // let root = &schema.root; 35 | 36 | // // probably worth making the schema a singleton using lazy_static library 37 | // let response = query.execute_async(root, state).await; 38 | let state = request.state(); 39 | let response = state 40 | .execute(query, |response| { 41 | Response::new(if response.is_ok() { 200 } else { 400 }) 42 | .body_json(&response) 43 | .expect("be able to serialize the graphql response") 44 | }) 45 | .await; 46 | // let status = if response.is_ok() { 200 } else { 400 }; 47 | log::info!("Responding to the graphl query"); 48 | 49 | // Response::new(status) 50 | // .body_json(&response) 51 | // .expect("be able to serialize the graphql response") 52 | response 53 | } 54 | 55 | async fn handle_root_head(_request: Request) -> Response { 56 | Response::new(200) 57 | } 58 | 59 | struct Header { 60 | key: &'static str, 61 | value: &'static str, 62 | } 63 | 64 | struct Headers { 65 | headers: RwLock>, 66 | } 67 | 68 | impl Headers { 69 | fn new() -> Self { 70 | let headers = vec![]; 71 | Headers { 72 | headers: RwLock::new(headers), 73 | } 74 | } 75 | fn set(mut self, key: &'static str, value: &'static str) -> Self { 76 | if let Ok(headers) = self.headers.get_mut() { 77 | headers.push(Header { key, value }) 78 | } 79 | self 80 | } 81 | } 82 | 83 | #[async_trait] 84 | impl Middleware for Headers { 85 | fn handle<'a>( 86 | &'a self, 87 | request: Request, 88 | next: Next<'a, State>, 89 | ) -> BoxFuture<'a, Response> { 90 | Box::pin(async move { 91 | let mut response = next.run(request).await; 92 | if let Ok(headers) = self.headers.read() { 93 | for header in headers.iter() { 94 | response = response.set_header(header.key, header.value); 95 | } 96 | response 97 | } else { 98 | response 99 | } 100 | }) 101 | } 102 | } 103 | 104 | pub async fn activate(address: &str) -> std::io::Result<()> { 105 | // let schema = Schema::new(); 106 | // let state = State { 107 | // schema, 108 | // state: SchemaState::new()?, 109 | // }; 110 | let state = Service::new()?; 111 | let headers = Headers::new().set("Server", "Knowledge-Server"); 112 | let mut server = Server::with_state(state); 113 | server.middleware(headers); 114 | server.at("/").get(tide::redirect("/graphiql")); 115 | server.at("/").head(handle_root_head); 116 | server.at("/graphql").post(handle_graphql); 117 | server.at("/graphiql").get(handle_graphiql); 118 | 119 | server.listen(address).await 120 | } 121 | -------------------------------------------------------------------------------- /base/src/service.rs: -------------------------------------------------------------------------------- 1 | use crate::data::InputResource; 2 | use crate::index::IndexService; 3 | use crate::schema::{Mutations, Schema, State}; 4 | use crate::store::DataStore; 5 | use juniper::http::{GraphQLRequest, GraphQLResponse}; 6 | use std::io; 7 | use std::sync::Arc; 8 | 9 | #[derive(Debug)] 10 | pub struct Service { 11 | pub schema: Schema, 12 | pub store: DataStore, 13 | pub index: Arc, 14 | } 15 | impl Service { 16 | pub fn new() -> io::Result { 17 | let store = DataStore::open()?; 18 | let index = Arc::new(IndexService::open().unwrap()); 19 | let schema = Schema::new(); 20 | 21 | Ok(Service { 22 | index, 23 | schema, 24 | store, 25 | }) 26 | } 27 | pub async fn execute(&self, request: GraphQLRequest, f: F) -> B 28 | where 29 | F: Fn(GraphQLResponse<'_>) -> B, 30 | { 31 | let state = State { 32 | store: self.store.clone(), 33 | index: self.index.clone(), 34 | }; 35 | let root = &self.schema.root; 36 | let response: GraphQLResponse<'_> = request.execute_async(root, &state).await; 37 | // TODO: Fix no unwrap belongs here. 38 | state.index.commit().await.unwrap(); 39 | f(response) 40 | } 41 | pub async fn ingest(&self, input: InputResource) -> io::Result<()> { 42 | let state = State { 43 | store: self.store.clone(), 44 | index: self.index.clone(), 45 | }; 46 | 47 | Mutations::ingest(&state, input) 48 | .await 49 | .map_err(|e| io::Error::new(io::ErrorKind::Other, e.message()))?; 50 | 51 | Ok(()) 52 | } 53 | pub async fn commit(&self) -> io::Result<()> { 54 | self.index 55 | .commit() 56 | .await 57 | .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("{}", e)))?; 58 | Ok(()) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /base/src/state.rs: -------------------------------------------------------------------------------- 1 | use crate::loader::DataLoader; 2 | use crate::store::DataStore; 3 | pub fn init() -> io::Result { 4 | let store = DataStore::open()?; 5 | let loader = DataLoader::new(store); 6 | 7 | Ok(State { loader }) 8 | } 9 | -------------------------------------------------------------------------------- /base/src/store.rs: -------------------------------------------------------------------------------- 1 | use crate::data::{ 2 | InputLink, InputResource, InputTag, Link, LinkKind, Resource, ResourceInfo, Tag, 3 | }; 4 | use async_trait::async_trait; 5 | use dataloader::cached::Loader; 6 | use dataloader::BatchFn; 7 | use dirs; 8 | pub use juniper::{FieldError, FieldResult}; 9 | use log; 10 | use r2d2_sqlite::SqliteConnectionManager; 11 | use rusqlite::{named_params, Connection, Row}; 12 | use std::collections::HashMap; 13 | use std::fmt; 14 | use std::{include_str, io}; 15 | 16 | pub type DecodeResult = Result; 17 | 18 | trait RowDecoder 19 | where 20 | Self: std::marker::Sized, 21 | { 22 | fn decode_row(row: &Row<'_>) -> Result; 23 | fn decode_rows(rows: &mut rusqlite::Rows) -> Result, FieldError> { 24 | let mut records = Vec::new(); 25 | while let Some(row) = rows.next()? { 26 | records.push(Self::decode_row(&row)?); 27 | } 28 | Ok(records) 29 | } 30 | } 31 | 32 | type Pool = r2d2::Pool; 33 | 34 | pub struct DataStore { 35 | pool: r2d2::Pool, 36 | 37 | links_by_referrer: Loader, Error, LinksByReferrer>, 38 | links_by_target: Loader, Error, LinksByTarget>, 39 | resource_info_by_url: Loader, 40 | tags_by_target: Loader, Error, TagsByTarget>, 41 | tags_by_name: Loader, Error, TagsByName>, 42 | } 43 | 44 | impl DataStore { 45 | pub fn new(pool: Pool) -> Self { 46 | DataStore { 47 | links_by_target: Loader::new(LinksByTarget::new(&pool)), 48 | links_by_referrer: Loader::new(LinksByReferrer::new(&pool)), 49 | tags_by_target: Loader::new(TagsByTarget::new(&pool)), 50 | tags_by_name: Loader::new(TagsByName::new(&pool)), 51 | resource_info_by_url: Loader::new(ResourceInfoByURL::new(&pool)), 52 | pool: pool, 53 | } 54 | } 55 | pub fn open() -> io::Result { 56 | let mut path = dirs::home_dir().expect("Unable to locate user home directory"); 57 | path.push(".knowledge-service"); 58 | // Ensure that there is such directory 59 | std::fs::create_dir_all(&path)?; 60 | path.push("knowledge.sqlite"); 61 | let manager = SqliteConnectionManager::file(&path).with_init(DataStore::create_tables); 62 | let pool = r2d2::Pool::new(manager).expect("Failed to initialize connection manager"); 63 | let store = DataStore::new(pool); 64 | println!("Data base was initialized at {:?}", path.to_str()); 65 | Ok(store) 66 | } 67 | 68 | pub(crate) fn create_tables(connection: &mut Connection) -> Result<(), rusqlite::Error> { 69 | #[allow(unused_must_use)] 70 | { 71 | // It's ok if this fails. This is temporary workaround due to not using 72 | // versions before. 73 | connection.execute_batch(include_str!("../sql/migrate_tables.sql")); 74 | } 75 | Ok(connection.execute_batch(include_str!("../sql/create_tables.sql"))?) 76 | } 77 | pub(crate) fn insert_resource(&self, input: &InputResource) -> DecodeResult { 78 | let connection = self.pool.get()?; 79 | let mut insert = connection.prepare_cached(include_str!("../sql/insert_resource.sql"))?; 80 | insert.execute_named(named_params! { 81 | ":url": input.url, 82 | ":title": input.title, 83 | ":description": input.description, 84 | ":cid": input.cid, 85 | ":icon": input.icon, 86 | ":image": input.image 87 | })?; 88 | 89 | Ok(Resource::from(input)) 90 | } 91 | pub(crate) fn insert_links( 92 | &self, 93 | referrer_url: &str, 94 | links: &Vec, 95 | ) -> FieldResult<()> { 96 | log::info!("Inserting {:} resource links into db", links.len()); 97 | let connection = self.pool.get()?; 98 | let mut insert_inline = 99 | connection.prepare_cached(include_str!("../sql/insert_inline_link.sql"))?; 100 | let mut insert_reference = 101 | connection.prepare_cached(include_str!("../sql/insert_reference_link.sql"))?; 102 | 103 | for link in links { 104 | match link.kind { 105 | LinkKind::Inline => { 106 | insert_inline.execute_named(named_params! { 107 | ":referrer_url": referrer_url, 108 | ":referrer_fragment": link.referrer_fragment, 109 | ":referrer_location": link.referrer_location, 110 | ":target_url": link.target_url, 111 | ":name": link.name, 112 | ":title": link.title 113 | })?; 114 | log::info!("Link {:} -> {:}resource", referrer_url, link.target_url); 115 | } 116 | LinkKind::Reference => { 117 | insert_reference.execute_named(named_params! { 118 | ":referrer_url": referrer_url, 119 | ":referrer_fragment": link.referrer_fragment, 120 | ":referrer_location": link.referrer_location, 121 | ":target_url": link.target_url, 122 | ":identifier": match &link.identifier { 123 | Some(name) => name, 124 | None => "", 125 | }, 126 | ":name": link.name, 127 | ":title": link.title 128 | })?; 129 | log::info!("Link {:} -> {:} resource", referrer_url, link.target_url); 130 | } 131 | } 132 | } 133 | 134 | Ok(()) 135 | } 136 | pub(crate) fn insert_tags(&self, target_url: &str, tags: &Vec) -> DecodeResult<()> { 137 | log::info!("Inserting {:} resource tags into db", tags.len()); 138 | let connection = self.pool.get()?; 139 | let mut insert = connection.prepare_cached(include_str!("../sql/insert_tag.sql"))?; 140 | let no_fragment = String::new(); 141 | for tag in tags { 142 | insert.execute_named(named_params! { 143 | ":name": tag.name, 144 | ":target_url": target_url, 145 | ":target_fragment": tag.target_fragment.as_ref().unwrap_or(&no_fragment), 146 | ":target_location": tag.target_location, 147 | })?; 148 | log::info!("Add #{:} tag to {:}", tag.name, target_url); 149 | } 150 | Ok(()) 151 | } 152 | 153 | pub(crate) async fn find_resource_by_url(&self, url: &str) -> DecodeResult { 154 | self.resource_info_by_url 155 | .load(url.to_string()) 156 | .await 157 | .map_err(FieldError::from) 158 | } 159 | 160 | pub(crate) async fn find_links_by_target(&self, url: &str) -> FieldResult> { 161 | self.links_by_target 162 | .load(url.to_string()) 163 | .await 164 | .map_err(FieldError::from) 165 | } 166 | 167 | pub(crate) async fn find_links_by_referrer(&self, url: &str) -> FieldResult> { 168 | self.links_by_referrer 169 | .load(url.to_string()) 170 | .await 171 | .map_err(FieldError::from) 172 | } 173 | 174 | pub(crate) async fn find_tags_by_target(&self, target_url: &str) -> DecodeResult> { 175 | self.tags_by_target 176 | .load(target_url.to_string()) 177 | .await 178 | .map_err(FieldError::from) 179 | } 180 | pub(crate) async fn find_tags_by_name(&self, name: &str) -> DecodeResult> { 181 | self.tags_by_name 182 | .load(name.to_string()) 183 | .await 184 | .map_err(FieldError::from) 185 | } 186 | } 187 | 188 | impl Clone for DataStore { 189 | fn clone(&self) -> Self { 190 | DataStore::new(self.pool.clone()) 191 | } 192 | } 193 | 194 | impl fmt::Debug for DataStore { 195 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 196 | f.debug_struct("DataLoader") 197 | .field("pool", &self.pool) 198 | .finish() 199 | } 200 | } 201 | 202 | impl RowDecoder for Link { 203 | fn decode_row(row: &rusqlite::Row<'_>) -> Result { 204 | Ok(Link { 205 | kind: match row.get(0)? { 206 | 0 => LinkKind::Inline, 207 | 1 => LinkKind::Reference, 208 | _ => LinkKind::Inline, 209 | }, 210 | referrer_url: row.get(1)?, 211 | referrer_cid: row.get(2)?, 212 | referrer_title: row.get(3)?, 213 | referrer_description: row.get(4)?, 214 | referrer_icon: row.get(5)?, 215 | referrer_image: row.get(6)?, 216 | referrer_fragment: row.get(7)?, 217 | referrer_location: row.get(8)?, 218 | 219 | target_url: row.get(9)?, 220 | identifier: row.get(10)?, 221 | name: row.get(11)?, 222 | title: row.get(12)?, 223 | }) 224 | } 225 | } 226 | 227 | impl RowDecoder for Tag { 228 | fn decode_row(row: &Row<'_>) -> Result { 229 | Ok(Tag { 230 | target_url: row.get(0)?, 231 | name: row.get(1)?, 232 | target_fragment: row.get(2)?, 233 | target_location: row.get(3)?, 234 | }) 235 | } 236 | } 237 | 238 | impl RowDecoder for ResourceInfo { 239 | fn decode_row(row: &Row) -> Result { 240 | Ok(ResourceInfo { 241 | cid: row.get(0)?, 242 | title: row.get(1)?, 243 | description: row.get(2)?, 244 | icon: row.get(3)?, 245 | image: row.get(4)?, 246 | }) 247 | } 248 | } 249 | 250 | pub struct LinksByTarget { 251 | pool: Pool, 252 | } 253 | impl LinksByTarget { 254 | pub fn new(pool: &Pool) -> Self { 255 | LinksByTarget { pool: pool.clone() } 256 | } 257 | pub fn select(&self, target_url: &str) -> Result, FieldError> { 258 | log::info!("selecting links by target {:} in db", target_url); 259 | let connection = self.pool.get()?; 260 | 261 | let mut select = 262 | connection.prepare_cached(include_str!("../sql/select_links_by_target.sql"))?; 263 | let mut rows = select.query_named(named_params! {":target_url": target_url})?; 264 | Link::decode_rows(&mut rows) 265 | } 266 | } 267 | 268 | #[async_trait] 269 | impl BatchFn> for LinksByTarget { 270 | type Error = Error; 271 | async fn load(&self, urls: &[String]) -> HashMap, Self::Error>> { 272 | urls.iter() 273 | .map(|url| (url.clone(), self.select(url).map_err(Error::from))) 274 | .collect() 275 | } 276 | } 277 | 278 | pub struct LinksByReferrer { 279 | pool: Pool, 280 | } 281 | impl LinksByReferrer { 282 | pub fn new(pool: &Pool) -> Self { 283 | LinksByReferrer { pool: pool.clone() } 284 | } 285 | pub fn select(&self, referrer_url: &str) -> Result, FieldError> { 286 | log::info!("selecting links by referrer {:} in db", referrer_url); 287 | let connection = self.pool.get()?; 288 | let mut select = 289 | connection.prepare_cached(include_str!("../sql/select_links_by_referrer.sql"))?; 290 | let mut rows = select.query_named(named_params! {":referrer_url": referrer_url})?; 291 | Link::decode_rows(&mut rows) 292 | } 293 | } 294 | #[async_trait] 295 | impl BatchFn> for LinksByReferrer { 296 | type Error = Error; 297 | async fn load(&self, urls: &[String]) -> HashMap, Self::Error>> { 298 | urls.iter() 299 | .map(|url| (url.to_string(), self.select(&url).map_err(Error::from))) 300 | .collect() 301 | } 302 | } 303 | 304 | pub struct TagsByTarget { 305 | pool: Pool, 306 | } 307 | 308 | impl TagsByTarget { 309 | pub fn new(pool: &Pool) -> Self { 310 | TagsByTarget { pool: pool.clone() } 311 | } 312 | pub fn select(&self, target_url: &str) -> Result, FieldError> { 313 | log::info!("selecting tags by target {:} in db", target_url); 314 | 315 | let connection = self.pool.get()?; 316 | 317 | let mut select = 318 | connection.prepare_cached(include_str!("../sql/select_tags_by_target.sql"))?; 319 | let mut rows = select.query_named(named_params! {":target_url": target_url})?; 320 | Tag::decode_rows(&mut rows) 321 | } 322 | } 323 | #[async_trait] 324 | impl BatchFn> for TagsByTarget { 325 | type Error = Error; 326 | async fn load(&self, urls: &[String]) -> HashMap, Self::Error>> { 327 | urls.iter() 328 | .map(|url| (url.clone(), self.select(&url).map_err(Error::from))) 329 | .collect() 330 | } 331 | } 332 | 333 | pub struct TagsByName { 334 | pool: Pool, 335 | } 336 | 337 | impl TagsByName { 338 | pub fn new(pool: &Pool) -> Self { 339 | TagsByName { pool: pool.clone() } 340 | } 341 | pub fn select(&self, name: &str) -> Result, FieldError> { 342 | log::info!("selecting tags by name #{:} in db", name); 343 | 344 | let connection = self.pool.get()?; 345 | 346 | let mut select = 347 | connection.prepare_cached(include_str!("../sql/select_tags_by_name.sql"))?; 348 | let mut rows = select.query_named(named_params! {":name": name})?; 349 | Tag::decode_rows(&mut rows) 350 | } 351 | } 352 | #[async_trait] 353 | impl BatchFn> for TagsByName { 354 | type Error = Error; 355 | async fn load(&self, names: &[String]) -> HashMap, Self::Error>> { 356 | names 357 | .iter() 358 | .map(|name| (name.clone(), self.select(&name).map_err(Error::from))) 359 | .collect() 360 | } 361 | } 362 | pub struct ResourceInfoByURL { 363 | pool: Pool, 364 | } 365 | impl ResourceInfoByURL { 366 | pub fn new(pool: &Pool) -> Self { 367 | ResourceInfoByURL { pool: pool.clone() } 368 | } 369 | pub fn select(&self, url: &str) -> Result { 370 | log::info!("selecting a resource in db{:}", url); 371 | let connection = self.pool.get()?; 372 | let mut select = 373 | connection.prepare_cached(include_str!("../sql/select_resource_by_url.sql"))?; 374 | let info = select.query_row_named(named_params! {":url": url}, ResourceInfo::decode_row)?; 375 | 376 | Ok(info) 377 | } 378 | } 379 | 380 | #[async_trait] 381 | impl BatchFn for ResourceInfoByURL { 382 | type Error = Error; 383 | async fn load(&self, urls: &[String]) -> HashMap> { 384 | urls.iter() 385 | .map(|url| (url.to_string(), self.select(url).map_err(Error::from))) 386 | .collect() 387 | } 388 | } 389 | 390 | #[derive(Debug, Clone)] 391 | pub enum Error { 392 | SQLError(String), 393 | } 394 | impl From for Error { 395 | fn from(error: FieldError) -> Self { 396 | Error::SQLError(format!("{:}", error.message())) 397 | } 398 | } 399 | impl From for Error { 400 | fn from(error: rusqlite::Error) -> Self { 401 | Error::SQLError(format!("{:}", error)) 402 | } 403 | } 404 | impl fmt::Display for Error { 405 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 406 | match self { 407 | Error::SQLError(error) => error.fmt(f), 408 | } 409 | } 410 | } 411 | -------------------------------------------------------------------------------- /program/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "knowledge-server" 3 | version = "0.1.0" 4 | description = "Knowledge Server" 5 | authors = ["Irakli Gozalishvili "] 6 | edition = "2018" 7 | 8 | [dependencies] 9 | async-std = { version = "1.5.0", features = ["attributes"] } 10 | knowledge-server-scanner={path="../scanner"} 11 | knowledge-server-base={path="../base"} 12 | dirs = "2.0.2" 13 | commander-rust = "1.2.1" 14 | syntax={path="../syntax"} 15 | env_logger = "0.7.1" 16 | -------------------------------------------------------------------------------- /program/src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(proc_macro_hygiene)] 2 | 3 | use commander_rust::{command, entry, option, run, Cli}; 4 | use dirs; 5 | use env_logger; 6 | use knowledge_server_base::server; 7 | use knowledge_server_scanner::scanner; 8 | use std::env; 9 | use std::io::Result; 10 | use std::process::{Command, Stdio}; 11 | use syntax::wait; 12 | 13 | #[option(-p, --port , "Port to be used by the knowledge-server (Default 8080)")] 14 | #[option(-o, --out , "Path where service log is written")] 15 | #[command(daemon, "Run server in the background")] 16 | fn daemon(cli: Cli) -> Result<()> { 17 | let path = if cli.has("out") { 18 | let mut path = env::current_dir()?; 19 | path.push(cli.get_or("out", format!(""))); 20 | path 21 | } else { 22 | let mut path = dirs::home_dir().expect("Unable to locate user home directory"); 23 | path.push(".knowledge-service"); 24 | std::fs::create_dir_all(&path)?; 25 | path.push("service.log"); 26 | path 27 | }; 28 | 29 | let port = cli.get_or("port", format!("8080")); 30 | let log = std::fs::File::create(&path)?; 31 | let args: Vec = env::args().collect(); 32 | 33 | Command::new(&args[0]) 34 | .arg("serve") 35 | .arg("--port") 36 | .arg(port) 37 | // So that if anything is read from standard input, it will crash does to 38 | // EOF immediately. 39 | .stdin(Stdio::null()) 40 | // If there is a write to stderror crash the process. 41 | .stderr(Stdio::null()) 42 | .stdout(log) 43 | .spawn()?; 44 | 45 | println!("knowledge-server is running in the background"); 46 | Ok(()) 47 | } 48 | 49 | #[wait] 50 | #[option(-p, --port , "Port to be used by the knowledge-server (Default 8080)")] 51 | #[command(serve, "Run server in the foreground")] 52 | async fn serve(cli: Cli) -> Result<()> { 53 | let port = cli.get_or("port", format!("8080")); 54 | let address = format!("127.0.0.1:{:}", port); 55 | server::activate(&address).await?; 56 | println!("Starting server http://{}", address); 57 | Ok(()) 58 | } 59 | 60 | #[wait] 61 | #[option(-n, --dry-run, "Don't actually add the file(s), just show.")] 62 | #[option(-t, --tag , "Comma delimited list of tags applied to all findings.")] 63 | #[command(scan , "Scans directory and submits all findings to knowledge-server")] 64 | async fn scan(path: String, cli: Cli) -> Result<()> { 65 | // Resolve the given path. 66 | let mut base = env::current_dir()?; 67 | base.push(path); 68 | println!("Scanning resources {:?}", base); 69 | 70 | let tags_param = cli.get_or("tag", format!("")); 71 | let tags = tags_param 72 | .split(",") 73 | .map(|tag| tag.trim()) 74 | .filter(|tag| !tag.is_empty()) 75 | .collect(); 76 | 77 | let dry_run = cli.has("dry-run"); 78 | let n = scanner::scan(&base, &tags, dry_run).await?; 79 | println!("Ingested {:} files", n); 80 | 81 | Ok(()) 82 | } 83 | 84 | #[wait] 85 | #[entry] 86 | async fn main() -> Result<()> { 87 | env_logger::init(); 88 | let app = run!(); 89 | if let Some(out) = app.out { 90 | out 91 | } else { 92 | // If no commands were matched just print out help 93 | println!("{:}", app); 94 | Ok(()) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /scanner/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "knowledge-server-scanner" 3 | version = "0.1.0" 4 | authors = ["Irakli Gozalishvili "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | async-std = { version = "1.5.0", features = ["attributes"] } 11 | dirs = "2.0.2" 12 | ignore = "0.4.13" 13 | pulldown-cmark = "0.7.0" 14 | knowledge-server-base={path = "../base"} 15 | url = "2.1.1" 16 | async-trait = "0.1.27" 17 | frontmatter = "0.3.0" 18 | yaml-rust = "0.3" 19 | surf = "1.0.3" 20 | -------------------------------------------------------------------------------- /scanner/src/frontmatter.rs: -------------------------------------------------------------------------------- 1 | use frontmatter; 2 | use yaml_rust::yaml::{Hash, Yaml}; 3 | 4 | pub struct Metadata { 5 | pub title: Option, 6 | pub description: Option, 7 | pub tags: Option>, 8 | } 9 | 10 | pub async fn read_metadata(source: &str) -> Metadata { 11 | let metadata = frontmatter::parse(&source) 12 | .unwrap_or(None) 13 | .and_then(|data| data.into_hash()); 14 | 15 | if let Some(data) = metadata.as_ref() { 16 | let tags = read_tags(data); 17 | let title = read_str_field("title", data); 18 | let description = read_str_field("description", data); 19 | 20 | Metadata { 21 | tags, 22 | title, 23 | description, 24 | } 25 | } else { 26 | Metadata { 27 | title: None, 28 | description: None, 29 | tags: None, 30 | } 31 | } 32 | } 33 | 34 | pub fn read_str_field(key: &str, map: &Hash) -> Option { 35 | map.get(&Yaml::String(key.to_string())) 36 | .and_then(|v| v.as_str()) 37 | .map(|v| v.trim().to_string()) 38 | } 39 | 40 | pub fn read_tags(map: &Hash) -> Option> { 41 | get_tags(map).and_then(decode_tags) 42 | } 43 | 44 | fn get_tags(map: &Hash) -> Option<&Yaml> { 45 | map.get(&Yaml::String(format!("tags"))) 46 | .or_else(|| map.get(&Yaml::String(format!("Tags")))) 47 | } 48 | 49 | fn decode_tags(value: &Yaml) -> Option> { 50 | decode_tags_array(value).or_else(|| decode_tags_string(value)) 51 | } 52 | 53 | fn decode_tags_array(value: &Yaml) -> Option> { 54 | value.as_vec().as_ref().map(|vec| { 55 | vec.into_iter() 56 | .filter_map(decode_tag) 57 | .map(|s| s.trim().to_string()) 58 | .filter(|s| !s.is_empty()) 59 | .collect() 60 | }) 61 | } 62 | 63 | fn decode_tags_string(value: &Yaml) -> Option> { 64 | value.as_str().as_ref().map(|s| { 65 | s.rsplit(",") 66 | .map(|s| s.trim().to_string()) 67 | .filter(|s| !s.is_empty()) 68 | .collect() 69 | }) 70 | } 71 | 72 | fn decode_tag(value: &Yaml) -> Option<&str> { 73 | if let Yaml::String(tag) = value { 74 | Some(tag) 75 | } else { 76 | None 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /scanner/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod frontmatter; 2 | pub mod markdown; 3 | pub mod resource; 4 | pub mod scanner; 5 | -------------------------------------------------------------------------------- /scanner/src/markdown.rs: -------------------------------------------------------------------------------- 1 | use crate::frontmatter::read_metadata; 2 | use crate::resource::Resource; 3 | use core::ops::Range; 4 | use knowledge_server_base::data::{InputLink, InputResource, InputTag, LinkKind}; 5 | use pulldown_cmark::{Event as Token, LinkType, Parser, Tag as Span}; 6 | use std::io::Result; 7 | 8 | pub async fn read(resource: &Resource) -> Result { 9 | let mut content = String::new(); 10 | resource.read_to_string(&mut content).await?; 11 | let metadata = read_metadata(&content).await; 12 | let data = parse(&content, &resource).await; 13 | let tags = metadata 14 | .tags 15 | .map(|tags| tags.into_iter().map(InputTag::from).collect()); 16 | 17 | let resource = InputResource { 18 | url: resource.url().to_string(), 19 | cid: None, 20 | links: Some(data.links), 21 | tags, 22 | title: metadata.title.or(data.title).unwrap_or(format!("")), 23 | description: metadata 24 | .description 25 | .or(data.description) 26 | .unwrap_or(format!("")), 27 | icon: None, 28 | image: None, 29 | content: Some(content), 30 | }; 31 | 32 | Ok(resource) 33 | } 34 | 35 | trait LinkKindExt { 36 | /// Translates link type from [pulldown-cmark][] representation to 37 | /// representation used by knowledge-server. 38 | /// [pulldown-cmark]:https://crates.io/crates/pulldown-cmark 39 | fn from_type(link_type: LinkType) -> Self; 40 | } 41 | 42 | impl LinkKindExt for LinkKind { 43 | fn from_type(link_type: LinkType) -> Self { 44 | match link_type { 45 | LinkType::Inline => LinkKind::Inline, 46 | LinkType::Reference => LinkKind::Reference, 47 | LinkType::ReferenceUnknown => LinkKind::Reference, 48 | LinkType::Collapsed => LinkKind::Reference, 49 | LinkType::CollapsedUnknown => LinkKind::Reference, 50 | LinkType::Shortcut => LinkKind::Reference, 51 | LinkType::ShortcutUnknown => LinkKind::Reference, 52 | LinkType::Autolink => LinkKind::Inline, 53 | LinkType::Email => LinkKind::Inline, 54 | } 55 | } 56 | } 57 | 58 | #[derive(Debug, Clone)] 59 | pub struct ParseData { 60 | title: Option, 61 | description: Option, 62 | links: Vec, 63 | } 64 | 65 | enum LinkContext { 66 | Heading, 67 | Paragraph, 68 | BlockQuote, 69 | ListItem, 70 | TableCell, 71 | } 72 | 73 | pub async fn parse(source: &str, resource: &Resource) -> ParseData { 74 | let mut links = vec![]; 75 | let mut title = None; 76 | let mut description = None; 77 | let tokens = Parser::new(source).into_offset_iter(); 78 | let mut context: Option<(LinkContext, Range)> = None; 79 | 80 | for (token, range) in tokens { 81 | match token { 82 | // If we encounter header and we have not encountered title yet, 83 | // this is it. Header acts as link context so we save range in 84 | // case link is discovered with-in it. 85 | Token::Start(Span::Heading(level)) => { 86 | if title.is_none() && level == 1 { 87 | // start + 2 to exclude `# ` & `end - 1` to exclude `\n`. 88 | title = Some(String::from(&source[range.start + 2..range.end - 1])); 89 | } 90 | context = Some((LinkContext::Heading, range)); 91 | } 92 | Token::End(Span::Heading(_level)) => { 93 | context = None; 94 | } 95 | 96 | // Paragraph act as a link context. If it is encountered and we do not 97 | // have a description this is it. 98 | Token::Start(Span::Paragraph) => { 99 | if description.is_none() { 100 | description = Some(String::from(&source[range.start..range.end - 1])); 101 | } 102 | 103 | // If it is BlockQuote we just ignore. 104 | if let None = context { 105 | context = Some((LinkContext::Paragraph, range)); 106 | } 107 | } 108 | Token::End(Span::Paragraph) => { 109 | // If it is not Paragraph we just ignore. 110 | if let Some((LinkContext::Paragraph, _)) = context { 111 | context = None; 112 | } 113 | } 114 | 115 | Token::Start(Span::BlockQuote) => { 116 | context = Some((LinkContext::BlockQuote, range)); 117 | } 118 | Token::End(Span::BlockQuote) => { 119 | context = None; 120 | } 121 | 122 | // List item 123 | Token::Start(Span::Item) => { 124 | // Drop `- ` / `* `. 125 | context = Some((LinkContext::ListItem, range)); 126 | } 127 | Token::End(Span::Item) => { 128 | context = None; 129 | } 130 | 131 | // Links 132 | Token::Start(Span::Link(_type, _url, _title, _id)) => {} 133 | Token::End(Span::Link(link_type, url, title, id)) => { 134 | let context_text = context.as_ref().map(|context| match context { 135 | (LinkContext::Heading, range) => source[range.start + 2..range.end].into(), 136 | (LinkContext::Paragraph, range) => source[range.start..range.end].into(), 137 | (LinkContext::ListItem, range) => source[range.start + 2..range.end].into(), 138 | (LinkContext::BlockQuote, range) => { 139 | format!("> {:}", &source[range.start..range.end]) 140 | } 141 | (LinkContext::TableCell, range) => source[range.start..range.end].into(), 142 | }); 143 | 144 | let url = resource 145 | .url() 146 | .join(&url) 147 | .map(|a| a.to_string()) 148 | .unwrap_or_else(|_| url.into_string()); 149 | 150 | let link = InputLink { 151 | kind: LinkKind::from_type(link_type), 152 | target_url: url, 153 | name: String::from(&source[range.start..range.end]), 154 | title: title.into_string(), 155 | identifier: { 156 | if id.is_empty() { 157 | None 158 | } else { 159 | Some(id.into_string()) 160 | } 161 | }, 162 | referrer_fragment: context_text, 163 | referrer_location: None, 164 | }; 165 | links.push(link); 166 | } 167 | 168 | Token::Start(Span::TableCell) => { 169 | context = Some((LinkContext::TableCell, range)); 170 | } 171 | Token::End(Span::TableCell) => { 172 | context = None; 173 | } 174 | _ => {} 175 | } 176 | } 177 | 178 | // let title_range = md_title(tokens); 179 | // let title = title_range.map(|range| String::from(&source[range])); 180 | 181 | ParseData { 182 | title, 183 | description, 184 | links, 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /scanner/src/resource.rs: -------------------------------------------------------------------------------- 1 | use async_std::fs::File; 2 | use async_std::io::{Error, ErrorKind, Read, ReadExt, Result, Seek}; 3 | pub use std::convert::TryFrom; 4 | use std::fmt; 5 | use std::path::Path; 6 | use url::Url; 7 | 8 | #[derive(Debug, Clone)] 9 | pub enum Resource { 10 | File(Url), 11 | } 12 | 13 | impl Resource { 14 | pub fn url(&self) -> &Url { 15 | match self { 16 | Resource::File(url) => &url, 17 | } 18 | } 19 | 20 | pub fn from_file_path(path: &Path) -> Result { 21 | Resource::try_from(path) 22 | } 23 | 24 | pub async fn reader(&self) -> Result { 25 | match self { 26 | // .unwrap is fine as we know it's file:// url 27 | Resource::File(url) => File::open(url.to_file_path().unwrap()).await, 28 | } 29 | } 30 | 31 | pub async fn read_to_string<'a>(&self, buf: &'a mut String) -> Result { 32 | let mut reader = self.reader().await?; 33 | reader.read_to_string(buf).await 34 | } 35 | } 36 | 37 | impl TryFrom<&Path> for Resource { 38 | type Error = Error; 39 | 40 | fn try_from(path: &Path) -> Result { 41 | let url = Url::from_file_path(&path) 42 | .map_err(|_| Error::new(ErrorKind::Other, "Unable to create file URL for path"))?; 43 | 44 | Ok(Resource::File(url)) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /scanner/src/scanner.rs: -------------------------------------------------------------------------------- 1 | use crate::markdown; 2 | use crate::resource::Resource; 3 | use async_std::io; 4 | use ignore::DirEntry; 5 | use knowledge_server_base::data::InputTag; 6 | // use knowledge_server_base::schema::{FieldError, Mutations, State}; 7 | use knowledge_server_base::service::Service; 8 | use std::path::Path; 9 | 10 | fn markdown_type() -> Result { 11 | let mut types = ignore::types::TypesBuilder::new(); 12 | types.add("markdown", "*.md")?; 13 | types.select("markdown"); 14 | types.build() 15 | } 16 | 17 | pub fn walk(path: &Path) -> impl Iterator { 18 | let markdown = markdown_type().unwrap(); 19 | let overrides = ignore::overrides::OverrideBuilder::new("") 20 | .add("!node_modules") 21 | .unwrap() 22 | .build() 23 | .unwrap(); 24 | let walker = ignore::WalkBuilder::new(path) 25 | .overrides(overrides) 26 | .standard_filters(true) 27 | .add_custom_ignore_filename(".ksignore") 28 | .types(markdown) 29 | .build(); 30 | 31 | walker 32 | .filter_map(Result::ok) 33 | .filter(|e| e.file_type().map(|t| t.is_file()).unwrap_or(false)) 34 | } 35 | 36 | pub struct ScanReport { 37 | pub links: usize, 38 | pub tags: usize, 39 | } 40 | 41 | pub async fn scan<'a>(path: &Path, tags: &Vec<&str>, dry_run: bool) -> io::Result { 42 | let entries = walk(path); 43 | let mut n = 0; 44 | let service = Service::new()?; 45 | 46 | for entry in entries { 47 | let path = entry.path(); 48 | let resource = Resource::from_file_path(path)?; 49 | let mut data = markdown::read(&resource).await?; 50 | let mut resource_tags = data.tags.unwrap_or(vec![]); 51 | 52 | for tag in tags { 53 | resource_tags.push(InputTag { 54 | name: format!("{:}", tag), 55 | target_fragment: None, 56 | target_location: None, 57 | }) 58 | } 59 | data.tags = Some(resource_tags); 60 | 61 | if dry_run { 62 | println!("{:#?}", data); 63 | } else { 64 | service.ingest(data).await?; 65 | n += 1; 66 | } 67 | } 68 | service.commit().await?; 69 | 70 | Ok(n) 71 | } 72 | -------------------------------------------------------------------------------- /syntax/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "syntax" 3 | version = "0.1.0" 4 | authors = ["Irakli Gozalishvili "] 5 | edition = "2018" 6 | 7 | [lib] 8 | proc-macro = true 9 | 10 | 11 | [dependencies] 12 | syn = { version = "1.0", features = ["full"] } 13 | quote = "1.0" 14 | -------------------------------------------------------------------------------- /syntax/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![forbid(unsafe_code, future_incompatible, rust_2018_idioms)] 2 | #![deny(missing_debug_implementations, nonstandard_style)] 3 | #![recursion_limit = "512"] 4 | 5 | // Had to add disable unused_imports warnings due to rust-analyzer bug 6 | // See: https://github.com/rust-analyzer/rust-analyzer/issues/3860 7 | #[allow(unused_imports)] 8 | use proc_macro::TokenStream; 9 | #[allow(unused_imports)] 10 | use quote::{quote, quote_spanned}; 11 | #[allow(unused_imports)] 12 | use syn::spanned::Spanned; 13 | 14 | #[cfg(not(test))] // NOTE: exporting main breaks tests, we should file an issue. 15 | #[proc_macro_attribute] 16 | pub fn wait(_attr: TokenStream, item: TokenStream) -> TokenStream { 17 | let input = syn::parse_macro_input!(item as syn::ItemFn); 18 | 19 | let ret = &input.sig.output; 20 | let inputs = &input.sig.inputs; 21 | let name = &input.sig.ident; 22 | let body = &input.block; 23 | let attrs = &input.attrs; 24 | 25 | if input.sig.asyncness.is_none() { 26 | return TokenStream::from(quote_spanned! { input.span() => 27 | compile_error!("the async keyword is missing from the function declaration"), 28 | }); 29 | } 30 | 31 | let result = quote! { 32 | #(#attrs)* 33 | fn #name(#inputs) #ret { 34 | async_std::task::block_on(async { 35 | #body 36 | }) 37 | } 38 | }; 39 | 40 | result.into() 41 | } 42 | --------------------------------------------------------------------------------