├── .editorconfig ├── .github └── workflows │ ├── chr-ci.yml │ └── chr-release.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── build.rs ├── logo.png ├── src ├── category.rs └── main.rs └── tests └── cli_integration_tests.rs /.editorconfig: -------------------------------------------------------------------------------- 1 | # Copyright © 2021 Peter M. Stahl pemistahl@gmail.com 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either expressed or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Editor configuration, see http://editorconfig.org 16 | root = true 17 | 18 | [*.rs] 19 | charset = utf-8 20 | indent_style = space 21 | indent_size = 4 22 | insert_final_newline = true 23 | trim_trailing_whitespace = true 24 | max_line_length = 100 25 | 26 | [*.md] 27 | max_line_length = off 28 | trim_trailing_whitespace = false 29 | -------------------------------------------------------------------------------- /.github/workflows/chr-ci.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright © 2021 Peter M. Stahl pemistahl@gmail.com 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either expressed or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | name: chr CI 17 | 18 | on: [push, pull_request] 19 | 20 | jobs: 21 | build-and-test: 22 | name: ${{ matrix.name }} 23 | 24 | runs-on: ${{ matrix.os }} 25 | 26 | env: 27 | RUST_BACKTRACE: full 28 | 29 | strategy: 30 | fail-fast: false 31 | matrix: 32 | os: [ubuntu-latest, macos-latest, windows-latest] 33 | include: 34 | - os: ubuntu-latest 35 | name: Linux Binary 64-Bit 36 | target: x86_64-unknown-linux-musl 37 | 38 | - os: macos-latest 39 | name: MacOS Binary 64-Bit 40 | target: x86_64-apple-darwin 41 | env: 42 | MACOSX_DEPLOYMENT_TARGET: 10.7 43 | 44 | - os: windows-latest 45 | name: Windows Binary 64-Bit 46 | target: x86_64-pc-windows-msvc 47 | 48 | steps: 49 | - name: Check out repository 50 | uses: actions/checkout@v2 51 | 52 | - name: Add rustup target 53 | run: rustup target add ${{ matrix.target }} 54 | 55 | - name: Install apt packages 56 | if: ${{ matrix.os == 'ubuntu-latest' }} 57 | run: sudo apt-get install musl-tools libssl-dev 58 | 59 | # needed to fix file corruption of cache 60 | # https://github.com/actions/cache/issues/403 61 | - name: Install GNU tar 62 | if: ${{ matrix.os == 'macos-latest' }} 63 | run: | 64 | brew install gnu-tar 65 | echo "/usr/local/opt/gnu-tar/libexec/gnubin" >> $GITHUB_PATH 66 | 67 | - name: Store or retrieve cargo caches 68 | uses: actions/cache@v2 69 | with: 70 | path: | 71 | ~/.cargo/registry 72 | ~/.cargo/git 73 | target 74 | key: ${{ runner.os }}-rust-cargo-${{ hashFiles('**/Cargo.lock') }} 75 | 76 | - name: Build in debug mode 77 | run: cargo build --target ${{ matrix.target }} --locked 78 | 79 | # ########################################################## 80 | # TESTS ARE CURRENTLY NOT WORKING RELIABLY ON GITHUB 81 | # BECAUSE THE SQLITE DB CANNOT BE ACCESSED WITHOUT PROBLEMS. 82 | # THIS IS A GITHUB ISSUE AND MUST BE INVESTIGATED. 83 | # 84 | # error: 85 | # https://github.com/pemistahl/chr/runs/1646184911?check_suite_focus=true#step:10:106 86 | # ########################################################## 87 | 88 | #- name: Test in debug mode 89 | # run: cargo test --target ${{ matrix.target }} 90 | 91 | #- name: Create code coverage report 92 | # if: ${{ matrix.os == 'ubuntu-latest' && github.event_name == 'push' }} 93 | # uses: actions-rs/tarpaulin@v0.1 94 | 95 | #- name: Upload code coverage report to Codecov 96 | # if: ${{ matrix.os == 'ubuntu-latest' && github.event_name == 'push' }} 97 | # uses: codecov/codecov-action@v1 -------------------------------------------------------------------------------- /.github/workflows/chr-release.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright © 2021 Peter M. Stahl pemistahl@gmail.com 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either expressed or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | name: chr Release 17 | 18 | on: 19 | push: 20 | tags: 21 | - v1.* 22 | 23 | jobs: 24 | create-release: 25 | name: ${{ matrix.name }} 26 | 27 | runs-on: ${{ matrix.os }} 28 | 29 | strategy: 30 | matrix: 31 | os: [ubuntu-latest, macos-latest, windows-latest] 32 | include: 33 | - os: ubuntu-latest 34 | name: Linux Binary 64-Bit 35 | target: x86_64-unknown-linux-musl 36 | 37 | - os: macos-latest 38 | name: MacOS Binary 64-Bit 39 | target: x86_64-apple-darwin 40 | env: 41 | MACOSX_DEPLOYMENT_TARGET: 10.7 42 | 43 | - os: windows-latest 44 | name: Windows Binary 64-Bit 45 | target: x86_64-pc-windows-msvc 46 | 47 | steps: 48 | - name: Check out repository 49 | uses: actions/checkout@v2 50 | 51 | - name: Add rustup target 52 | run: rustup target add ${{ matrix.target }} 53 | 54 | - name: Install apt packages 55 | if: ${{ matrix.os == 'ubuntu-latest' }} 56 | run: | 57 | sudo apt-get install musl-tools libssl-dev 58 | sudo ln -s /usr/bin/g++ /usr/bin/musl-g++ 59 | 60 | - name: Build in release mode 61 | run: cargo build --release --target ${{ matrix.target }} --locked 62 | 63 | - name: Get latest release version number 64 | id: get_version 65 | uses: battila7/get-version-action@v2 66 | 67 | - name: Create zip file on Windows 68 | if: ${{ matrix.os == 'windows-latest' }} 69 | run: | 70 | choco install zip 71 | cd target/${{ matrix.target }}/release 72 | zip chr-${{ steps.get_version.outputs.version }}-${{ matrix.target }}.zip chr.exe 73 | cd ../../.. 74 | 75 | - name: Create tar.gz file on macOS and Linux 76 | if: ${{ matrix.os != 'windows-latest' }} 77 | run: | 78 | chmod +x target/${{ matrix.target }}/release/chr 79 | tar -zcf target/${{ matrix.target }}/release/chr-${{ steps.get_version.outputs.version }}-${{ matrix.target }}.tar.gz -C target/${{ matrix.target }}/release chr 80 | 81 | - name: Upload release and assets to GitHub 82 | uses: svenstaro/upload-release-action@v2 83 | with: 84 | repo_token: ${{ secrets.GITHUB_TOKEN }} 85 | tag: ${{ github.ref }} 86 | release_name: chr ${{ steps.get_version.outputs.version-without-v }} 87 | file_glob: true 88 | file: target/${{ matrix.target }}/release/chr-${{ steps.get_version.outputs.version }}-${{ matrix.target }}.{zip,tar.gz} 89 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright © 2021 Peter M. Stahl pemistahl@gmail.com 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either expressed or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | /target/ 17 | **/*.rs.bk 18 | 19 | .idea 20 | .project 21 | .c9/ 22 | *.launch 23 | .settings/ 24 | .metadata/ 25 | *.sublime-workspace 26 | bin/ 27 | tmp/ 28 | out/ 29 | *.iml 30 | *.ipr 31 | *.iws 32 | *.bak 33 | *.tmp 34 | *.class 35 | .buildpath 36 | .classpath 37 | .vscode/* 38 | !.vscode/settings.json 39 | !.vscode/tasks.json 40 | !.vscode/launch.json 41 | !.vscode/extensions.json 42 | 43 | .DS_Store 44 | Thumbs.db 45 | $RECYCLE.BIN/ 46 | ._* 47 | .AppleDouble 48 | .LSOverride 49 | *.lnk 50 | Desktop.ini 51 | ehthumbs.db -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "adler32" 5 | version = "1.2.0" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" 8 | 9 | [[package]] 10 | name = "ahash" 11 | version = "0.4.7" 12 | source = "registry+https://github.com/rust-lang/crates.io-index" 13 | checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e" 14 | 15 | [[package]] 16 | name = "aho-corasick" 17 | version = "0.7.15" 18 | source = "registry+https://github.com/rust-lang/crates.io-index" 19 | checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" 20 | dependencies = [ 21 | "memchr", 22 | ] 23 | 24 | [[package]] 25 | name = "ansi_term" 26 | version = "0.11.0" 27 | source = "registry+https://github.com/rust-lang/crates.io-index" 28 | checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" 29 | dependencies = [ 30 | "winapi 0.3.9", 31 | ] 32 | 33 | [[package]] 34 | name = "arrayref" 35 | version = "0.3.6" 36 | source = "registry+https://github.com/rust-lang/crates.io-index" 37 | checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" 38 | 39 | [[package]] 40 | name = "arrayvec" 41 | version = "0.5.2" 42 | source = "registry+https://github.com/rust-lang/crates.io-index" 43 | checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" 44 | 45 | [[package]] 46 | name = "assert_cmd" 47 | version = "1.0.2" 48 | source = "registry+https://github.com/rust-lang/crates.io-index" 49 | checksum = "3dc1679af9a1ab4bea16f228b05d18f8363f8327b1fa8db00d2760cfafc6b61e" 50 | dependencies = [ 51 | "doc-comment", 52 | "predicates", 53 | "predicates-core", 54 | "predicates-tree", 55 | "wait-timeout", 56 | ] 57 | 58 | [[package]] 59 | name = "atty" 60 | version = "0.2.14" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 63 | dependencies = [ 64 | "hermit-abi", 65 | "libc", 66 | "winapi 0.3.9", 67 | ] 68 | 69 | [[package]] 70 | name = "autocfg" 71 | version = "1.0.1" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 74 | 75 | [[package]] 76 | name = "base64" 77 | version = "0.13.0" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" 80 | 81 | [[package]] 82 | name = "bitflags" 83 | version = "1.2.1" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 86 | 87 | [[package]] 88 | name = "blake2b_simd" 89 | version = "0.5.11" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" 92 | dependencies = [ 93 | "arrayref", 94 | "arrayvec", 95 | "constant_time_eq", 96 | ] 97 | 98 | [[package]] 99 | name = "bstr" 100 | version = "0.2.14" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "473fc6b38233f9af7baa94fb5852dca389e3d95b8e21c8e3719301462c5d9faf" 103 | dependencies = [ 104 | "lazy_static", 105 | "memchr", 106 | "regex-automata", 107 | "serde", 108 | ] 109 | 110 | [[package]] 111 | name = "bumpalo" 112 | version = "3.4.0" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" 115 | 116 | [[package]] 117 | name = "byteorder" 118 | version = "1.3.4" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" 121 | 122 | [[package]] 123 | name = "bytes" 124 | version = "0.5.6" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" 127 | 128 | [[package]] 129 | name = "bzip2" 130 | version = "0.3.3" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | checksum = "42b7c3cbf0fa9c1b82308d57191728ca0256cb821220f4e2fd410a72ade26e3b" 133 | dependencies = [ 134 | "bzip2-sys", 135 | "libc", 136 | ] 137 | 138 | [[package]] 139 | name = "bzip2-sys" 140 | version = "0.1.9+1.0.8" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | checksum = "ad3b39a260062fca31f7b0b12f207e8f2590a67d32ec7d59c20484b07ea7285e" 143 | dependencies = [ 144 | "cc", 145 | "libc", 146 | "pkg-config", 147 | ] 148 | 149 | [[package]] 150 | name = "cc" 151 | version = "1.0.66" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" 154 | 155 | [[package]] 156 | name = "cfg-if" 157 | version = "0.1.10" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 160 | 161 | [[package]] 162 | name = "cfg-if" 163 | version = "1.0.0" 164 | source = "registry+https://github.com/rust-lang/crates.io-index" 165 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 166 | 167 | [[package]] 168 | name = "chr" 169 | version = "1.0.0" 170 | dependencies = [ 171 | "assert_cmd", 172 | "colored", 173 | "csv", 174 | "dirs", 175 | "indoc", 176 | "minus", 177 | "predicates", 178 | "reqwest", 179 | "rusqlite", 180 | "serde", 181 | "serde_json", 182 | "structopt", 183 | "strum", 184 | "strum_macros", 185 | "zip", 186 | ] 187 | 188 | [[package]] 189 | name = "clap" 190 | version = "2.33.3" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" 193 | dependencies = [ 194 | "ansi_term", 195 | "atty", 196 | "bitflags", 197 | "strsim", 198 | "textwrap", 199 | "unicode-width", 200 | "vec_map", 201 | ] 202 | 203 | [[package]] 204 | name = "colored" 205 | version = "2.0.0" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" 208 | dependencies = [ 209 | "atty", 210 | "lazy_static", 211 | "winapi 0.3.9", 212 | ] 213 | 214 | [[package]] 215 | name = "constant_time_eq" 216 | version = "0.1.5" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" 219 | 220 | [[package]] 221 | name = "core-foundation" 222 | version = "0.9.1" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" 225 | dependencies = [ 226 | "core-foundation-sys", 227 | "libc", 228 | ] 229 | 230 | [[package]] 231 | name = "core-foundation-sys" 232 | version = "0.8.2" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" 235 | 236 | [[package]] 237 | name = "crc32fast" 238 | version = "1.2.1" 239 | source = "registry+https://github.com/rust-lang/crates.io-index" 240 | checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" 241 | dependencies = [ 242 | "cfg-if 1.0.0", 243 | ] 244 | 245 | [[package]] 246 | name = "crossbeam-utils" 247 | version = "0.8.1" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d" 250 | dependencies = [ 251 | "autocfg", 252 | "cfg-if 1.0.0", 253 | "lazy_static", 254 | ] 255 | 256 | [[package]] 257 | name = "crossterm" 258 | version = "0.18.2" 259 | source = "registry+https://github.com/rust-lang/crates.io-index" 260 | checksum = "4e86d73f2a0b407b5768d10a8c720cf5d2df49a9efc10ca09176d201ead4b7fb" 261 | dependencies = [ 262 | "bitflags", 263 | "crossterm_winapi", 264 | "lazy_static", 265 | "libc", 266 | "mio 0.7.7", 267 | "parking_lot", 268 | "signal-hook", 269 | "winapi 0.3.9", 270 | ] 271 | 272 | [[package]] 273 | name = "crossterm_winapi" 274 | version = "0.6.2" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | checksum = "c2265c3f8e080075d9b6417aa72293fc71662f34b4af2612d8d1b074d29510db" 277 | dependencies = [ 278 | "winapi 0.3.9", 279 | ] 280 | 281 | [[package]] 282 | name = "csv" 283 | version = "1.1.5" 284 | source = "registry+https://github.com/rust-lang/crates.io-index" 285 | checksum = "f9d58633299b24b515ac72a3f869f8b91306a3cec616a602843a383acd6f9e97" 286 | dependencies = [ 287 | "bstr", 288 | "csv-core", 289 | "itoa", 290 | "ryu", 291 | "serde", 292 | ] 293 | 294 | [[package]] 295 | name = "csv-core" 296 | version = "0.1.10" 297 | source = "registry+https://github.com/rust-lang/crates.io-index" 298 | checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" 299 | dependencies = [ 300 | "memchr", 301 | ] 302 | 303 | [[package]] 304 | name = "difference" 305 | version = "2.0.0" 306 | source = "registry+https://github.com/rust-lang/crates.io-index" 307 | checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" 308 | 309 | [[package]] 310 | name = "dirs" 311 | version = "3.0.1" 312 | source = "registry+https://github.com/rust-lang/crates.io-index" 313 | checksum = "142995ed02755914747cc6ca76fc7e4583cd18578746716d0508ea6ed558b9ff" 314 | dependencies = [ 315 | "dirs-sys", 316 | ] 317 | 318 | [[package]] 319 | name = "dirs-sys" 320 | version = "0.3.5" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a" 323 | dependencies = [ 324 | "libc", 325 | "redox_users", 326 | "winapi 0.3.9", 327 | ] 328 | 329 | [[package]] 330 | name = "doc-comment" 331 | version = "0.3.3" 332 | source = "registry+https://github.com/rust-lang/crates.io-index" 333 | checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" 334 | 335 | [[package]] 336 | name = "encoding_rs" 337 | version = "0.8.26" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | checksum = "801bbab217d7f79c0062f4f7205b5d4427c6d1a7bd7aafdd1475f7c59d62b283" 340 | dependencies = [ 341 | "cfg-if 1.0.0", 342 | ] 343 | 344 | [[package]] 345 | name = "fallible-iterator" 346 | version = "0.2.0" 347 | source = "registry+https://github.com/rust-lang/crates.io-index" 348 | checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" 349 | 350 | [[package]] 351 | name = "fallible-streaming-iterator" 352 | version = "0.1.9" 353 | source = "registry+https://github.com/rust-lang/crates.io-index" 354 | checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" 355 | 356 | [[package]] 357 | name = "flate2" 358 | version = "1.0.14" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "2cfff41391129e0a856d6d822600b8d71179d46879e310417eb9c762eb178b42" 361 | dependencies = [ 362 | "cfg-if 0.1.10", 363 | "crc32fast", 364 | "libc", 365 | "miniz_oxide", 366 | ] 367 | 368 | [[package]] 369 | name = "float-cmp" 370 | version = "0.8.0" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "e1267f4ac4f343772758f7b1bdcbe767c218bbab93bb432acbf5162bbf85a6c4" 373 | dependencies = [ 374 | "num-traits", 375 | ] 376 | 377 | [[package]] 378 | name = "fnv" 379 | version = "1.0.7" 380 | source = "registry+https://github.com/rust-lang/crates.io-index" 381 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 382 | 383 | [[package]] 384 | name = "foreign-types" 385 | version = "0.3.2" 386 | source = "registry+https://github.com/rust-lang/crates.io-index" 387 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 388 | dependencies = [ 389 | "foreign-types-shared", 390 | ] 391 | 392 | [[package]] 393 | name = "foreign-types-shared" 394 | version = "0.1.1" 395 | source = "registry+https://github.com/rust-lang/crates.io-index" 396 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 397 | 398 | [[package]] 399 | name = "form_urlencoded" 400 | version = "1.0.0" 401 | source = "registry+https://github.com/rust-lang/crates.io-index" 402 | checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00" 403 | dependencies = [ 404 | "matches", 405 | "percent-encoding", 406 | ] 407 | 408 | [[package]] 409 | name = "fuchsia-zircon" 410 | version = "0.3.3" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" 413 | dependencies = [ 414 | "bitflags", 415 | "fuchsia-zircon-sys", 416 | ] 417 | 418 | [[package]] 419 | name = "fuchsia-zircon-sys" 420 | version = "0.3.3" 421 | source = "registry+https://github.com/rust-lang/crates.io-index" 422 | checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" 423 | 424 | [[package]] 425 | name = "futures-channel" 426 | version = "0.3.8" 427 | source = "registry+https://github.com/rust-lang/crates.io-index" 428 | checksum = "4b7109687aa4e177ef6fe84553af6280ef2778bdb7783ba44c9dc3399110fe64" 429 | dependencies = [ 430 | "futures-core", 431 | ] 432 | 433 | [[package]] 434 | name = "futures-core" 435 | version = "0.3.8" 436 | source = "registry+https://github.com/rust-lang/crates.io-index" 437 | checksum = "847ce131b72ffb13b6109a221da9ad97a64cbe48feb1028356b836b47b8f1748" 438 | 439 | [[package]] 440 | name = "futures-io" 441 | version = "0.3.8" 442 | source = "registry+https://github.com/rust-lang/crates.io-index" 443 | checksum = "611834ce18aaa1bd13c4b374f5d653e1027cf99b6b502584ff8c9a64413b30bb" 444 | 445 | [[package]] 446 | name = "futures-sink" 447 | version = "0.3.8" 448 | source = "registry+https://github.com/rust-lang/crates.io-index" 449 | checksum = "f878195a49cee50e006b02b93cf7e0a95a38ac7b776b4c4d9cc1207cd20fcb3d" 450 | 451 | [[package]] 452 | name = "futures-task" 453 | version = "0.3.8" 454 | source = "registry+https://github.com/rust-lang/crates.io-index" 455 | checksum = "7c554eb5bf48b2426c4771ab68c6b14468b6e76cc90996f528c3338d761a4d0d" 456 | dependencies = [ 457 | "once_cell", 458 | ] 459 | 460 | [[package]] 461 | name = "futures-util" 462 | version = "0.3.8" 463 | source = "registry+https://github.com/rust-lang/crates.io-index" 464 | checksum = "d304cff4a7b99cfb7986f7d43fbe93d175e72e704a8860787cc95e9ffd85cbd2" 465 | dependencies = [ 466 | "futures-core", 467 | "futures-io", 468 | "futures-task", 469 | "memchr", 470 | "pin-project 1.0.2", 471 | "pin-utils", 472 | "slab", 473 | ] 474 | 475 | [[package]] 476 | name = "getrandom" 477 | version = "0.1.16" 478 | source = "registry+https://github.com/rust-lang/crates.io-index" 479 | checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" 480 | dependencies = [ 481 | "cfg-if 1.0.0", 482 | "libc", 483 | "wasi 0.9.0+wasi-snapshot-preview1", 484 | ] 485 | 486 | [[package]] 487 | name = "h2" 488 | version = "0.2.7" 489 | source = "registry+https://github.com/rust-lang/crates.io-index" 490 | checksum = "5e4728fd124914ad25e99e3d15a9361a879f6620f63cb56bbb08f95abb97a535" 491 | dependencies = [ 492 | "bytes", 493 | "fnv", 494 | "futures-core", 495 | "futures-sink", 496 | "futures-util", 497 | "http", 498 | "indexmap", 499 | "slab", 500 | "tokio", 501 | "tokio-util", 502 | "tracing", 503 | "tracing-futures", 504 | ] 505 | 506 | [[package]] 507 | name = "hashbrown" 508 | version = "0.9.1" 509 | source = "registry+https://github.com/rust-lang/crates.io-index" 510 | checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" 511 | dependencies = [ 512 | "ahash", 513 | ] 514 | 515 | [[package]] 516 | name = "hashlink" 517 | version = "0.6.0" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | checksum = "d99cf782f0dc4372d26846bec3de7804ceb5df083c2d4462c0b8d2330e894fa8" 520 | dependencies = [ 521 | "hashbrown", 522 | ] 523 | 524 | [[package]] 525 | name = "heck" 526 | version = "0.3.2" 527 | source = "registry+https://github.com/rust-lang/crates.io-index" 528 | checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac" 529 | dependencies = [ 530 | "unicode-segmentation", 531 | ] 532 | 533 | [[package]] 534 | name = "hermit-abi" 535 | version = "0.1.17" 536 | source = "registry+https://github.com/rust-lang/crates.io-index" 537 | checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" 538 | dependencies = [ 539 | "libc", 540 | ] 541 | 542 | [[package]] 543 | name = "http" 544 | version = "0.2.2" 545 | source = "registry+https://github.com/rust-lang/crates.io-index" 546 | checksum = "84129d298a6d57d246960ff8eb831ca4af3f96d29e2e28848dae275408658e26" 547 | dependencies = [ 548 | "bytes", 549 | "fnv", 550 | "itoa", 551 | ] 552 | 553 | [[package]] 554 | name = "http-body" 555 | version = "0.3.1" 556 | source = "registry+https://github.com/rust-lang/crates.io-index" 557 | checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" 558 | dependencies = [ 559 | "bytes", 560 | "http", 561 | ] 562 | 563 | [[package]] 564 | name = "httparse" 565 | version = "1.3.4" 566 | source = "registry+https://github.com/rust-lang/crates.io-index" 567 | checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" 568 | 569 | [[package]] 570 | name = "httpdate" 571 | version = "0.3.2" 572 | source = "registry+https://github.com/rust-lang/crates.io-index" 573 | checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47" 574 | 575 | [[package]] 576 | name = "hyper" 577 | version = "0.13.9" 578 | source = "registry+https://github.com/rust-lang/crates.io-index" 579 | checksum = "f6ad767baac13b44d4529fcf58ba2cd0995e36e7b435bc5b039de6f47e880dbf" 580 | dependencies = [ 581 | "bytes", 582 | "futures-channel", 583 | "futures-core", 584 | "futures-util", 585 | "h2", 586 | "http", 587 | "http-body", 588 | "httparse", 589 | "httpdate", 590 | "itoa", 591 | "pin-project 1.0.2", 592 | "socket2", 593 | "tokio", 594 | "tower-service", 595 | "tracing", 596 | "want", 597 | ] 598 | 599 | [[package]] 600 | name = "hyper-tls" 601 | version = "0.4.3" 602 | source = "registry+https://github.com/rust-lang/crates.io-index" 603 | checksum = "d979acc56dcb5b8dddba3917601745e877576475aa046df3226eabdecef78eed" 604 | dependencies = [ 605 | "bytes", 606 | "hyper", 607 | "native-tls", 608 | "tokio", 609 | "tokio-tls", 610 | ] 611 | 612 | [[package]] 613 | name = "idna" 614 | version = "0.2.0" 615 | source = "registry+https://github.com/rust-lang/crates.io-index" 616 | checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" 617 | dependencies = [ 618 | "matches", 619 | "unicode-bidi", 620 | "unicode-normalization", 621 | ] 622 | 623 | [[package]] 624 | name = "indexmap" 625 | version = "1.6.1" 626 | source = "registry+https://github.com/rust-lang/crates.io-index" 627 | checksum = "4fb1fa934250de4de8aef298d81c729a7d33d8c239daa3a7575e6b92bfc7313b" 628 | dependencies = [ 629 | "autocfg", 630 | "hashbrown", 631 | ] 632 | 633 | [[package]] 634 | name = "indoc" 635 | version = "1.0.3" 636 | source = "registry+https://github.com/rust-lang/crates.io-index" 637 | checksum = "e5a75aeaaef0ce18b58056d306c27b07436fbb34b8816c53094b76dd81803136" 638 | dependencies = [ 639 | "unindent", 640 | ] 641 | 642 | [[package]] 643 | name = "instant" 644 | version = "0.1.9" 645 | source = "registry+https://github.com/rust-lang/crates.io-index" 646 | checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" 647 | dependencies = [ 648 | "cfg-if 1.0.0", 649 | ] 650 | 651 | [[package]] 652 | name = "iovec" 653 | version = "0.1.4" 654 | source = "registry+https://github.com/rust-lang/crates.io-index" 655 | checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" 656 | dependencies = [ 657 | "libc", 658 | ] 659 | 660 | [[package]] 661 | name = "ipnet" 662 | version = "2.3.0" 663 | source = "registry+https://github.com/rust-lang/crates.io-index" 664 | checksum = "47be2f14c678be2fdcab04ab1171db51b2762ce6f0a8ee87c8dd4a04ed216135" 665 | 666 | [[package]] 667 | name = "itoa" 668 | version = "0.4.7" 669 | source = "registry+https://github.com/rust-lang/crates.io-index" 670 | checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" 671 | 672 | [[package]] 673 | name = "js-sys" 674 | version = "0.3.46" 675 | source = "registry+https://github.com/rust-lang/crates.io-index" 676 | checksum = "cf3d7383929f7c9c7c2d0fa596f325832df98c3704f2c60553080f7127a58175" 677 | dependencies = [ 678 | "wasm-bindgen", 679 | ] 680 | 681 | [[package]] 682 | name = "kernel32-sys" 683 | version = "0.2.2" 684 | source = "registry+https://github.com/rust-lang/crates.io-index" 685 | checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" 686 | dependencies = [ 687 | "winapi 0.2.8", 688 | "winapi-build", 689 | ] 690 | 691 | [[package]] 692 | name = "lazy_static" 693 | version = "1.4.0" 694 | source = "registry+https://github.com/rust-lang/crates.io-index" 695 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 696 | 697 | [[package]] 698 | name = "libc" 699 | version = "0.2.81" 700 | source = "registry+https://github.com/rust-lang/crates.io-index" 701 | checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb" 702 | 703 | [[package]] 704 | name = "libsqlite3-sys" 705 | version = "0.20.1" 706 | source = "registry+https://github.com/rust-lang/crates.io-index" 707 | checksum = "64d31059f22935e6c31830db5249ba2b7ecd54fd73a9909286f0a67aa55c2fbd" 708 | dependencies = [ 709 | "cc", 710 | "pkg-config", 711 | "vcpkg", 712 | ] 713 | 714 | [[package]] 715 | name = "lock_api" 716 | version = "0.4.2" 717 | source = "registry+https://github.com/rust-lang/crates.io-index" 718 | checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312" 719 | dependencies = [ 720 | "scopeguard", 721 | ] 722 | 723 | [[package]] 724 | name = "log" 725 | version = "0.4.11" 726 | source = "registry+https://github.com/rust-lang/crates.io-index" 727 | checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" 728 | dependencies = [ 729 | "cfg-if 0.1.10", 730 | ] 731 | 732 | [[package]] 733 | name = "matches" 734 | version = "0.1.8" 735 | source = "registry+https://github.com/rust-lang/crates.io-index" 736 | checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" 737 | 738 | [[package]] 739 | name = "memchr" 740 | version = "2.3.4" 741 | source = "registry+https://github.com/rust-lang/crates.io-index" 742 | checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" 743 | 744 | [[package]] 745 | name = "mime" 746 | version = "0.3.16" 747 | source = "registry+https://github.com/rust-lang/crates.io-index" 748 | checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" 749 | 750 | [[package]] 751 | name = "mime_guess" 752 | version = "2.0.3" 753 | source = "registry+https://github.com/rust-lang/crates.io-index" 754 | checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212" 755 | dependencies = [ 756 | "mime", 757 | "unicase", 758 | ] 759 | 760 | [[package]] 761 | name = "miniz_oxide" 762 | version = "0.3.7" 763 | source = "registry+https://github.com/rust-lang/crates.io-index" 764 | checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" 765 | dependencies = [ 766 | "adler32", 767 | ] 768 | 769 | [[package]] 770 | name = "minus" 771 | version = "2.1.0" 772 | source = "registry+https://github.com/rust-lang/crates.io-index" 773 | checksum = "84a4fc2000e944e211bbaeab348aeb1734cb4bfdc8b10d07d5eab0bea8687bfc" 774 | dependencies = [ 775 | "crossterm", 776 | "thiserror", 777 | ] 778 | 779 | [[package]] 780 | name = "mio" 781 | version = "0.6.23" 782 | source = "registry+https://github.com/rust-lang/crates.io-index" 783 | checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" 784 | dependencies = [ 785 | "cfg-if 0.1.10", 786 | "fuchsia-zircon", 787 | "fuchsia-zircon-sys", 788 | "iovec", 789 | "kernel32-sys", 790 | "libc", 791 | "log", 792 | "miow 0.2.2", 793 | "net2", 794 | "slab", 795 | "winapi 0.2.8", 796 | ] 797 | 798 | [[package]] 799 | name = "mio" 800 | version = "0.7.7" 801 | source = "registry+https://github.com/rust-lang/crates.io-index" 802 | checksum = "e50ae3f04d169fcc9bde0b547d1c205219b7157e07ded9c5aff03e0637cb3ed7" 803 | dependencies = [ 804 | "libc", 805 | "log", 806 | "miow 0.3.6", 807 | "ntapi", 808 | "winapi 0.3.9", 809 | ] 810 | 811 | [[package]] 812 | name = "miow" 813 | version = "0.2.2" 814 | source = "registry+https://github.com/rust-lang/crates.io-index" 815 | checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" 816 | dependencies = [ 817 | "kernel32-sys", 818 | "net2", 819 | "winapi 0.2.8", 820 | "ws2_32-sys", 821 | ] 822 | 823 | [[package]] 824 | name = "miow" 825 | version = "0.3.6" 826 | source = "registry+https://github.com/rust-lang/crates.io-index" 827 | checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897" 828 | dependencies = [ 829 | "socket2", 830 | "winapi 0.3.9", 831 | ] 832 | 833 | [[package]] 834 | name = "native-tls" 835 | version = "0.2.7" 836 | source = "registry+https://github.com/rust-lang/crates.io-index" 837 | checksum = "b8d96b2e1c8da3957d58100b09f102c6d9cfdfced01b7ec5a8974044bb09dbd4" 838 | dependencies = [ 839 | "lazy_static", 840 | "libc", 841 | "log", 842 | "openssl", 843 | "openssl-probe", 844 | "openssl-sys", 845 | "schannel", 846 | "security-framework", 847 | "security-framework-sys", 848 | "tempfile", 849 | ] 850 | 851 | [[package]] 852 | name = "net2" 853 | version = "0.2.37" 854 | source = "registry+https://github.com/rust-lang/crates.io-index" 855 | checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" 856 | dependencies = [ 857 | "cfg-if 0.1.10", 858 | "libc", 859 | "winapi 0.3.9", 860 | ] 861 | 862 | [[package]] 863 | name = "normalize-line-endings" 864 | version = "0.3.0" 865 | source = "registry+https://github.com/rust-lang/crates.io-index" 866 | checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" 867 | 868 | [[package]] 869 | name = "ntapi" 870 | version = "0.3.6" 871 | source = "registry+https://github.com/rust-lang/crates.io-index" 872 | checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" 873 | dependencies = [ 874 | "winapi 0.3.9", 875 | ] 876 | 877 | [[package]] 878 | name = "num-traits" 879 | version = "0.2.14" 880 | source = "registry+https://github.com/rust-lang/crates.io-index" 881 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" 882 | dependencies = [ 883 | "autocfg", 884 | ] 885 | 886 | [[package]] 887 | name = "num_cpus" 888 | version = "1.13.0" 889 | source = "registry+https://github.com/rust-lang/crates.io-index" 890 | checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" 891 | dependencies = [ 892 | "hermit-abi", 893 | "libc", 894 | ] 895 | 896 | [[package]] 897 | name = "once_cell" 898 | version = "1.5.2" 899 | source = "registry+https://github.com/rust-lang/crates.io-index" 900 | checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" 901 | 902 | [[package]] 903 | name = "openssl" 904 | version = "0.10.32" 905 | source = "registry+https://github.com/rust-lang/crates.io-index" 906 | checksum = "038d43985d1ddca7a9900630d8cd031b56e4794eecc2e9ea39dd17aa04399a70" 907 | dependencies = [ 908 | "bitflags", 909 | "cfg-if 1.0.0", 910 | "foreign-types", 911 | "lazy_static", 912 | "libc", 913 | "openssl-sys", 914 | ] 915 | 916 | [[package]] 917 | name = "openssl-probe" 918 | version = "0.1.2" 919 | source = "registry+https://github.com/rust-lang/crates.io-index" 920 | checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" 921 | 922 | [[package]] 923 | name = "openssl-sys" 924 | version = "0.9.60" 925 | source = "registry+https://github.com/rust-lang/crates.io-index" 926 | checksum = "921fc71883267538946025deffb622905ecad223c28efbfdef9bb59a0175f3e6" 927 | dependencies = [ 928 | "autocfg", 929 | "cc", 930 | "libc", 931 | "pkg-config", 932 | "vcpkg", 933 | ] 934 | 935 | [[package]] 936 | name = "parking_lot" 937 | version = "0.11.1" 938 | source = "registry+https://github.com/rust-lang/crates.io-index" 939 | checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" 940 | dependencies = [ 941 | "instant", 942 | "lock_api", 943 | "parking_lot_core", 944 | ] 945 | 946 | [[package]] 947 | name = "parking_lot_core" 948 | version = "0.8.2" 949 | source = "registry+https://github.com/rust-lang/crates.io-index" 950 | checksum = "9ccb628cad4f84851442432c60ad8e1f607e29752d0bf072cbd0baf28aa34272" 951 | dependencies = [ 952 | "cfg-if 1.0.0", 953 | "instant", 954 | "libc", 955 | "redox_syscall", 956 | "smallvec", 957 | "winapi 0.3.9", 958 | ] 959 | 960 | [[package]] 961 | name = "percent-encoding" 962 | version = "2.1.0" 963 | source = "registry+https://github.com/rust-lang/crates.io-index" 964 | checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" 965 | 966 | [[package]] 967 | name = "pin-project" 968 | version = "0.4.27" 969 | source = "registry+https://github.com/rust-lang/crates.io-index" 970 | checksum = "2ffbc8e94b38ea3d2d8ba92aea2983b503cd75d0888d75b86bb37970b5698e15" 971 | dependencies = [ 972 | "pin-project-internal 0.4.27", 973 | ] 974 | 975 | [[package]] 976 | name = "pin-project" 977 | version = "1.0.2" 978 | source = "registry+https://github.com/rust-lang/crates.io-index" 979 | checksum = "9ccc2237c2c489783abd8c4c80e5450fc0e98644555b1364da68cc29aa151ca7" 980 | dependencies = [ 981 | "pin-project-internal 1.0.2", 982 | ] 983 | 984 | [[package]] 985 | name = "pin-project-internal" 986 | version = "0.4.27" 987 | source = "registry+https://github.com/rust-lang/crates.io-index" 988 | checksum = "65ad2ae56b6abe3a1ee25f15ee605bacadb9a764edaba9c2bf4103800d4a1895" 989 | dependencies = [ 990 | "proc-macro2", 991 | "quote", 992 | "syn", 993 | ] 994 | 995 | [[package]] 996 | name = "pin-project-internal" 997 | version = "1.0.2" 998 | source = "registry+https://github.com/rust-lang/crates.io-index" 999 | checksum = "f8e8d2bf0b23038a4424865103a4df472855692821aab4e4f5c3312d461d9e5f" 1000 | dependencies = [ 1001 | "proc-macro2", 1002 | "quote", 1003 | "syn", 1004 | ] 1005 | 1006 | [[package]] 1007 | name = "pin-project-lite" 1008 | version = "0.1.11" 1009 | source = "registry+https://github.com/rust-lang/crates.io-index" 1010 | checksum = "c917123afa01924fc84bb20c4c03f004d9c38e5127e3c039bbf7f4b9c76a2f6b" 1011 | 1012 | [[package]] 1013 | name = "pin-project-lite" 1014 | version = "0.2.0" 1015 | source = "registry+https://github.com/rust-lang/crates.io-index" 1016 | checksum = "6b063f57ec186e6140e2b8b6921e5f1bd89c7356dda5b33acc5401203ca6131c" 1017 | 1018 | [[package]] 1019 | name = "pin-utils" 1020 | version = "0.1.0" 1021 | source = "registry+https://github.com/rust-lang/crates.io-index" 1022 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1023 | 1024 | [[package]] 1025 | name = "pkg-config" 1026 | version = "0.3.19" 1027 | source = "registry+https://github.com/rust-lang/crates.io-index" 1028 | checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" 1029 | 1030 | [[package]] 1031 | name = "ppv-lite86" 1032 | version = "0.2.10" 1033 | source = "registry+https://github.com/rust-lang/crates.io-index" 1034 | checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" 1035 | 1036 | [[package]] 1037 | name = "predicates" 1038 | version = "1.0.6" 1039 | source = "registry+https://github.com/rust-lang/crates.io-index" 1040 | checksum = "73dd9b7b200044694dfede9edf907c1ca19630908443e9447e624993700c6932" 1041 | dependencies = [ 1042 | "difference", 1043 | "float-cmp", 1044 | "normalize-line-endings", 1045 | "predicates-core", 1046 | "regex", 1047 | ] 1048 | 1049 | [[package]] 1050 | name = "predicates-core" 1051 | version = "1.0.1" 1052 | source = "registry+https://github.com/rust-lang/crates.io-index" 1053 | checksum = "fb3dbeaaf793584e29c58c7e3a82bbb3c7c06b63cea68d13b0e3cddc124104dc" 1054 | 1055 | [[package]] 1056 | name = "predicates-tree" 1057 | version = "1.0.1" 1058 | source = "registry+https://github.com/rust-lang/crates.io-index" 1059 | checksum = "aee95d988ee893cb35c06b148c80ed2cd52c8eea927f50ba7a0be1a786aeab73" 1060 | dependencies = [ 1061 | "predicates-core", 1062 | "treeline", 1063 | ] 1064 | 1065 | [[package]] 1066 | name = "proc-macro-error" 1067 | version = "1.0.4" 1068 | source = "registry+https://github.com/rust-lang/crates.io-index" 1069 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 1070 | dependencies = [ 1071 | "proc-macro-error-attr", 1072 | "proc-macro2", 1073 | "quote", 1074 | "syn", 1075 | "version_check", 1076 | ] 1077 | 1078 | [[package]] 1079 | name = "proc-macro-error-attr" 1080 | version = "1.0.4" 1081 | source = "registry+https://github.com/rust-lang/crates.io-index" 1082 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 1083 | dependencies = [ 1084 | "proc-macro2", 1085 | "quote", 1086 | "version_check", 1087 | ] 1088 | 1089 | [[package]] 1090 | name = "proc-macro2" 1091 | version = "1.0.24" 1092 | source = "registry+https://github.com/rust-lang/crates.io-index" 1093 | checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" 1094 | dependencies = [ 1095 | "unicode-xid", 1096 | ] 1097 | 1098 | [[package]] 1099 | name = "quote" 1100 | version = "1.0.8" 1101 | source = "registry+https://github.com/rust-lang/crates.io-index" 1102 | checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" 1103 | dependencies = [ 1104 | "proc-macro2", 1105 | ] 1106 | 1107 | [[package]] 1108 | name = "rand" 1109 | version = "0.7.3" 1110 | source = "registry+https://github.com/rust-lang/crates.io-index" 1111 | checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" 1112 | dependencies = [ 1113 | "getrandom", 1114 | "libc", 1115 | "rand_chacha", 1116 | "rand_core", 1117 | "rand_hc", 1118 | ] 1119 | 1120 | [[package]] 1121 | name = "rand_chacha" 1122 | version = "0.2.2" 1123 | source = "registry+https://github.com/rust-lang/crates.io-index" 1124 | checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" 1125 | dependencies = [ 1126 | "ppv-lite86", 1127 | "rand_core", 1128 | ] 1129 | 1130 | [[package]] 1131 | name = "rand_core" 1132 | version = "0.5.1" 1133 | source = "registry+https://github.com/rust-lang/crates.io-index" 1134 | checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 1135 | dependencies = [ 1136 | "getrandom", 1137 | ] 1138 | 1139 | [[package]] 1140 | name = "rand_hc" 1141 | version = "0.2.0" 1142 | source = "registry+https://github.com/rust-lang/crates.io-index" 1143 | checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" 1144 | dependencies = [ 1145 | "rand_core", 1146 | ] 1147 | 1148 | [[package]] 1149 | name = "redox_syscall" 1150 | version = "0.1.57" 1151 | source = "registry+https://github.com/rust-lang/crates.io-index" 1152 | checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" 1153 | 1154 | [[package]] 1155 | name = "redox_users" 1156 | version = "0.3.5" 1157 | source = "registry+https://github.com/rust-lang/crates.io-index" 1158 | checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d" 1159 | dependencies = [ 1160 | "getrandom", 1161 | "redox_syscall", 1162 | "rust-argon2", 1163 | ] 1164 | 1165 | [[package]] 1166 | name = "regex" 1167 | version = "1.4.2" 1168 | source = "registry+https://github.com/rust-lang/crates.io-index" 1169 | checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c" 1170 | dependencies = [ 1171 | "aho-corasick", 1172 | "memchr", 1173 | "regex-syntax", 1174 | "thread_local", 1175 | ] 1176 | 1177 | [[package]] 1178 | name = "regex-automata" 1179 | version = "0.1.9" 1180 | source = "registry+https://github.com/rust-lang/crates.io-index" 1181 | checksum = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4" 1182 | dependencies = [ 1183 | "byteorder", 1184 | ] 1185 | 1186 | [[package]] 1187 | name = "regex-syntax" 1188 | version = "0.6.21" 1189 | source = "registry+https://github.com/rust-lang/crates.io-index" 1190 | checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" 1191 | 1192 | [[package]] 1193 | name = "remove_dir_all" 1194 | version = "0.5.3" 1195 | source = "registry+https://github.com/rust-lang/crates.io-index" 1196 | checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" 1197 | dependencies = [ 1198 | "winapi 0.3.9", 1199 | ] 1200 | 1201 | [[package]] 1202 | name = "reqwest" 1203 | version = "0.10.10" 1204 | source = "registry+https://github.com/rust-lang/crates.io-index" 1205 | checksum = "0718f81a8e14c4dbb3b34cf23dc6aaf9ab8a0dfec160c534b3dbca1aaa21f47c" 1206 | dependencies = [ 1207 | "base64", 1208 | "bytes", 1209 | "encoding_rs", 1210 | "futures-core", 1211 | "futures-util", 1212 | "http", 1213 | "http-body", 1214 | "hyper", 1215 | "hyper-tls", 1216 | "ipnet", 1217 | "js-sys", 1218 | "lazy_static", 1219 | "log", 1220 | "mime", 1221 | "mime_guess", 1222 | "native-tls", 1223 | "percent-encoding", 1224 | "pin-project-lite 0.2.0", 1225 | "serde", 1226 | "serde_urlencoded", 1227 | "tokio", 1228 | "tokio-tls", 1229 | "url", 1230 | "wasm-bindgen", 1231 | "wasm-bindgen-futures", 1232 | "web-sys", 1233 | "winreg", 1234 | ] 1235 | 1236 | [[package]] 1237 | name = "rusqlite" 1238 | version = "0.24.2" 1239 | source = "registry+https://github.com/rust-lang/crates.io-index" 1240 | checksum = "d5f38ee71cbab2c827ec0ac24e76f82eca723cee92c509a65f67dee393c25112" 1241 | dependencies = [ 1242 | "bitflags", 1243 | "fallible-iterator", 1244 | "fallible-streaming-iterator", 1245 | "hashlink", 1246 | "libsqlite3-sys", 1247 | "memchr", 1248 | "smallvec", 1249 | ] 1250 | 1251 | [[package]] 1252 | name = "rust-argon2" 1253 | version = "0.8.3" 1254 | source = "registry+https://github.com/rust-lang/crates.io-index" 1255 | checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb" 1256 | dependencies = [ 1257 | "base64", 1258 | "blake2b_simd", 1259 | "constant_time_eq", 1260 | "crossbeam-utils", 1261 | ] 1262 | 1263 | [[package]] 1264 | name = "ryu" 1265 | version = "1.0.5" 1266 | source = "registry+https://github.com/rust-lang/crates.io-index" 1267 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 1268 | 1269 | [[package]] 1270 | name = "schannel" 1271 | version = "0.1.19" 1272 | source = "registry+https://github.com/rust-lang/crates.io-index" 1273 | checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" 1274 | dependencies = [ 1275 | "lazy_static", 1276 | "winapi 0.3.9", 1277 | ] 1278 | 1279 | [[package]] 1280 | name = "scopeguard" 1281 | version = "1.1.0" 1282 | source = "registry+https://github.com/rust-lang/crates.io-index" 1283 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 1284 | 1285 | [[package]] 1286 | name = "security-framework" 1287 | version = "2.0.0" 1288 | source = "registry+https://github.com/rust-lang/crates.io-index" 1289 | checksum = "c1759c2e3c8580017a484a7ac56d3abc5a6c1feadf88db2f3633f12ae4268c69" 1290 | dependencies = [ 1291 | "bitflags", 1292 | "core-foundation", 1293 | "core-foundation-sys", 1294 | "libc", 1295 | "security-framework-sys", 1296 | ] 1297 | 1298 | [[package]] 1299 | name = "security-framework-sys" 1300 | version = "2.0.0" 1301 | source = "registry+https://github.com/rust-lang/crates.io-index" 1302 | checksum = "f99b9d5e26d2a71633cc4f2ebae7cc9f874044e0c351a27e17892d76dce5678b" 1303 | dependencies = [ 1304 | "core-foundation-sys", 1305 | "libc", 1306 | ] 1307 | 1308 | [[package]] 1309 | name = "serde" 1310 | version = "1.0.118" 1311 | source = "registry+https://github.com/rust-lang/crates.io-index" 1312 | checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800" 1313 | dependencies = [ 1314 | "serde_derive", 1315 | ] 1316 | 1317 | [[package]] 1318 | name = "serde_derive" 1319 | version = "1.0.118" 1320 | source = "registry+https://github.com/rust-lang/crates.io-index" 1321 | checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df" 1322 | dependencies = [ 1323 | "proc-macro2", 1324 | "quote", 1325 | "syn", 1326 | ] 1327 | 1328 | [[package]] 1329 | name = "serde_json" 1330 | version = "1.0.61" 1331 | source = "registry+https://github.com/rust-lang/crates.io-index" 1332 | checksum = "4fceb2595057b6891a4ee808f70054bd2d12f0e97f1cbb78689b59f676df325a" 1333 | dependencies = [ 1334 | "itoa", 1335 | "ryu", 1336 | "serde", 1337 | ] 1338 | 1339 | [[package]] 1340 | name = "serde_urlencoded" 1341 | version = "0.7.0" 1342 | source = "registry+https://github.com/rust-lang/crates.io-index" 1343 | checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" 1344 | dependencies = [ 1345 | "form_urlencoded", 1346 | "itoa", 1347 | "ryu", 1348 | "serde", 1349 | ] 1350 | 1351 | [[package]] 1352 | name = "signal-hook" 1353 | version = "0.1.17" 1354 | source = "registry+https://github.com/rust-lang/crates.io-index" 1355 | checksum = "7e31d442c16f047a671b5a71e2161d6e68814012b7f5379d269ebd915fac2729" 1356 | dependencies = [ 1357 | "libc", 1358 | "mio 0.7.7", 1359 | "signal-hook-registry", 1360 | ] 1361 | 1362 | [[package]] 1363 | name = "signal-hook-registry" 1364 | version = "1.3.0" 1365 | source = "registry+https://github.com/rust-lang/crates.io-index" 1366 | checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6" 1367 | dependencies = [ 1368 | "libc", 1369 | ] 1370 | 1371 | [[package]] 1372 | name = "slab" 1373 | version = "0.4.2" 1374 | source = "registry+https://github.com/rust-lang/crates.io-index" 1375 | checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" 1376 | 1377 | [[package]] 1378 | name = "smallvec" 1379 | version = "1.6.0" 1380 | source = "registry+https://github.com/rust-lang/crates.io-index" 1381 | checksum = "1a55ca5f3b68e41c979bf8c46a6f1da892ca4db8f94023ce0bd32407573b1ac0" 1382 | 1383 | [[package]] 1384 | name = "socket2" 1385 | version = "0.3.19" 1386 | source = "registry+https://github.com/rust-lang/crates.io-index" 1387 | checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" 1388 | dependencies = [ 1389 | "cfg-if 1.0.0", 1390 | "libc", 1391 | "winapi 0.3.9", 1392 | ] 1393 | 1394 | [[package]] 1395 | name = "strsim" 1396 | version = "0.8.0" 1397 | source = "registry+https://github.com/rust-lang/crates.io-index" 1398 | checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 1399 | 1400 | [[package]] 1401 | name = "structopt" 1402 | version = "0.3.21" 1403 | source = "registry+https://github.com/rust-lang/crates.io-index" 1404 | checksum = "5277acd7ee46e63e5168a80734c9f6ee81b1367a7d8772a2d765df2a3705d28c" 1405 | dependencies = [ 1406 | "clap", 1407 | "lazy_static", 1408 | "structopt-derive", 1409 | ] 1410 | 1411 | [[package]] 1412 | name = "structopt-derive" 1413 | version = "0.4.14" 1414 | source = "registry+https://github.com/rust-lang/crates.io-index" 1415 | checksum = "5ba9cdfda491b814720b6b06e0cac513d922fc407582032e8706e9f137976f90" 1416 | dependencies = [ 1417 | "heck", 1418 | "proc-macro-error", 1419 | "proc-macro2", 1420 | "quote", 1421 | "syn", 1422 | ] 1423 | 1424 | [[package]] 1425 | name = "strum" 1426 | version = "0.20.0" 1427 | source = "registry+https://github.com/rust-lang/crates.io-index" 1428 | checksum = "7318c509b5ba57f18533982607f24070a55d353e90d4cae30c467cdb2ad5ac5c" 1429 | 1430 | [[package]] 1431 | name = "strum_macros" 1432 | version = "0.20.1" 1433 | source = "registry+https://github.com/rust-lang/crates.io-index" 1434 | checksum = "ee8bc6b87a5112aeeab1f4a9f7ab634fe6cbefc4850006df31267f4cfb9e3149" 1435 | dependencies = [ 1436 | "heck", 1437 | "proc-macro2", 1438 | "quote", 1439 | "syn", 1440 | ] 1441 | 1442 | [[package]] 1443 | name = "syn" 1444 | version = "1.0.57" 1445 | source = "registry+https://github.com/rust-lang/crates.io-index" 1446 | checksum = "4211ce9909eb971f111059df92c45640aad50a619cf55cd76476be803c4c68e6" 1447 | dependencies = [ 1448 | "proc-macro2", 1449 | "quote", 1450 | "unicode-xid", 1451 | ] 1452 | 1453 | [[package]] 1454 | name = "tempfile" 1455 | version = "3.1.0" 1456 | source = "registry+https://github.com/rust-lang/crates.io-index" 1457 | checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" 1458 | dependencies = [ 1459 | "cfg-if 0.1.10", 1460 | "libc", 1461 | "rand", 1462 | "redox_syscall", 1463 | "remove_dir_all", 1464 | "winapi 0.3.9", 1465 | ] 1466 | 1467 | [[package]] 1468 | name = "textwrap" 1469 | version = "0.11.0" 1470 | source = "registry+https://github.com/rust-lang/crates.io-index" 1471 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 1472 | dependencies = [ 1473 | "unicode-width", 1474 | ] 1475 | 1476 | [[package]] 1477 | name = "thiserror" 1478 | version = "1.0.23" 1479 | source = "registry+https://github.com/rust-lang/crates.io-index" 1480 | checksum = "76cc616c6abf8c8928e2fdcc0dbfab37175edd8fb49a4641066ad1364fdab146" 1481 | dependencies = [ 1482 | "thiserror-impl", 1483 | ] 1484 | 1485 | [[package]] 1486 | name = "thiserror-impl" 1487 | version = "1.0.23" 1488 | source = "registry+https://github.com/rust-lang/crates.io-index" 1489 | checksum = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1" 1490 | dependencies = [ 1491 | "proc-macro2", 1492 | "quote", 1493 | "syn", 1494 | ] 1495 | 1496 | [[package]] 1497 | name = "thread_local" 1498 | version = "1.0.1" 1499 | source = "registry+https://github.com/rust-lang/crates.io-index" 1500 | checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" 1501 | dependencies = [ 1502 | "lazy_static", 1503 | ] 1504 | 1505 | [[package]] 1506 | name = "time" 1507 | version = "0.1.44" 1508 | source = "registry+https://github.com/rust-lang/crates.io-index" 1509 | checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" 1510 | dependencies = [ 1511 | "libc", 1512 | "wasi 0.10.0+wasi-snapshot-preview1", 1513 | "winapi 0.3.9", 1514 | ] 1515 | 1516 | [[package]] 1517 | name = "tinyvec" 1518 | version = "1.1.0" 1519 | source = "registry+https://github.com/rust-lang/crates.io-index" 1520 | checksum = "ccf8dbc19eb42fba10e8feaaec282fb50e2c14b2726d6301dbfeed0f73306a6f" 1521 | dependencies = [ 1522 | "tinyvec_macros", 1523 | ] 1524 | 1525 | [[package]] 1526 | name = "tinyvec_macros" 1527 | version = "0.1.0" 1528 | source = "registry+https://github.com/rust-lang/crates.io-index" 1529 | checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" 1530 | 1531 | [[package]] 1532 | name = "tokio" 1533 | version = "0.2.24" 1534 | source = "registry+https://github.com/rust-lang/crates.io-index" 1535 | checksum = "099837d3464c16a808060bb3f02263b412f6fafcb5d01c533d309985fbeebe48" 1536 | dependencies = [ 1537 | "bytes", 1538 | "fnv", 1539 | "futures-core", 1540 | "iovec", 1541 | "lazy_static", 1542 | "memchr", 1543 | "mio 0.6.23", 1544 | "num_cpus", 1545 | "pin-project-lite 0.1.11", 1546 | "slab", 1547 | ] 1548 | 1549 | [[package]] 1550 | name = "tokio-tls" 1551 | version = "0.3.1" 1552 | source = "registry+https://github.com/rust-lang/crates.io-index" 1553 | checksum = "9a70f4fcd7b3b24fb194f837560168208f669ca8cb70d0c4b862944452396343" 1554 | dependencies = [ 1555 | "native-tls", 1556 | "tokio", 1557 | ] 1558 | 1559 | [[package]] 1560 | name = "tokio-util" 1561 | version = "0.3.1" 1562 | source = "registry+https://github.com/rust-lang/crates.io-index" 1563 | checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" 1564 | dependencies = [ 1565 | "bytes", 1566 | "futures-core", 1567 | "futures-sink", 1568 | "log", 1569 | "pin-project-lite 0.1.11", 1570 | "tokio", 1571 | ] 1572 | 1573 | [[package]] 1574 | name = "tower-service" 1575 | version = "0.3.0" 1576 | source = "registry+https://github.com/rust-lang/crates.io-index" 1577 | checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860" 1578 | 1579 | [[package]] 1580 | name = "tracing" 1581 | version = "0.1.22" 1582 | source = "registry+https://github.com/rust-lang/crates.io-index" 1583 | checksum = "9f47026cdc4080c07e49b37087de021820269d996f581aac150ef9e5583eefe3" 1584 | dependencies = [ 1585 | "cfg-if 1.0.0", 1586 | "log", 1587 | "pin-project-lite 0.2.0", 1588 | "tracing-core", 1589 | ] 1590 | 1591 | [[package]] 1592 | name = "tracing-core" 1593 | version = "0.1.17" 1594 | source = "registry+https://github.com/rust-lang/crates.io-index" 1595 | checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f" 1596 | dependencies = [ 1597 | "lazy_static", 1598 | ] 1599 | 1600 | [[package]] 1601 | name = "tracing-futures" 1602 | version = "0.2.4" 1603 | source = "registry+https://github.com/rust-lang/crates.io-index" 1604 | checksum = "ab7bb6f14721aa00656086e9335d363c5c8747bae02ebe32ea2c7dece5689b4c" 1605 | dependencies = [ 1606 | "pin-project 0.4.27", 1607 | "tracing", 1608 | ] 1609 | 1610 | [[package]] 1611 | name = "treeline" 1612 | version = "0.1.0" 1613 | source = "registry+https://github.com/rust-lang/crates.io-index" 1614 | checksum = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41" 1615 | 1616 | [[package]] 1617 | name = "try-lock" 1618 | version = "0.2.3" 1619 | source = "registry+https://github.com/rust-lang/crates.io-index" 1620 | checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" 1621 | 1622 | [[package]] 1623 | name = "unicase" 1624 | version = "2.6.0" 1625 | source = "registry+https://github.com/rust-lang/crates.io-index" 1626 | checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" 1627 | dependencies = [ 1628 | "version_check", 1629 | ] 1630 | 1631 | [[package]] 1632 | name = "unicode-bidi" 1633 | version = "0.3.4" 1634 | source = "registry+https://github.com/rust-lang/crates.io-index" 1635 | checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" 1636 | dependencies = [ 1637 | "matches", 1638 | ] 1639 | 1640 | [[package]] 1641 | name = "unicode-normalization" 1642 | version = "0.1.16" 1643 | source = "registry+https://github.com/rust-lang/crates.io-index" 1644 | checksum = "a13e63ab62dbe32aeee58d1c5408d35c36c392bba5d9d3142287219721afe606" 1645 | dependencies = [ 1646 | "tinyvec", 1647 | ] 1648 | 1649 | [[package]] 1650 | name = "unicode-segmentation" 1651 | version = "1.7.1" 1652 | source = "registry+https://github.com/rust-lang/crates.io-index" 1653 | checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" 1654 | 1655 | [[package]] 1656 | name = "unicode-width" 1657 | version = "0.1.8" 1658 | source = "registry+https://github.com/rust-lang/crates.io-index" 1659 | checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" 1660 | 1661 | [[package]] 1662 | name = "unicode-xid" 1663 | version = "0.2.1" 1664 | source = "registry+https://github.com/rust-lang/crates.io-index" 1665 | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" 1666 | 1667 | [[package]] 1668 | name = "unindent" 1669 | version = "0.1.7" 1670 | source = "registry+https://github.com/rust-lang/crates.io-index" 1671 | checksum = "f14ee04d9415b52b3aeab06258a3f07093182b88ba0f9b8d203f211a7a7d41c7" 1672 | 1673 | [[package]] 1674 | name = "url" 1675 | version = "2.2.0" 1676 | source = "registry+https://github.com/rust-lang/crates.io-index" 1677 | checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e" 1678 | dependencies = [ 1679 | "form_urlencoded", 1680 | "idna", 1681 | "matches", 1682 | "percent-encoding", 1683 | ] 1684 | 1685 | [[package]] 1686 | name = "vcpkg" 1687 | version = "0.2.11" 1688 | source = "registry+https://github.com/rust-lang/crates.io-index" 1689 | checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb" 1690 | 1691 | [[package]] 1692 | name = "vec_map" 1693 | version = "0.8.2" 1694 | source = "registry+https://github.com/rust-lang/crates.io-index" 1695 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 1696 | 1697 | [[package]] 1698 | name = "version_check" 1699 | version = "0.9.2" 1700 | source = "registry+https://github.com/rust-lang/crates.io-index" 1701 | checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" 1702 | 1703 | [[package]] 1704 | name = "wait-timeout" 1705 | version = "0.2.0" 1706 | source = "registry+https://github.com/rust-lang/crates.io-index" 1707 | checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" 1708 | dependencies = [ 1709 | "libc", 1710 | ] 1711 | 1712 | [[package]] 1713 | name = "want" 1714 | version = "0.3.0" 1715 | source = "registry+https://github.com/rust-lang/crates.io-index" 1716 | checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" 1717 | dependencies = [ 1718 | "log", 1719 | "try-lock", 1720 | ] 1721 | 1722 | [[package]] 1723 | name = "wasi" 1724 | version = "0.9.0+wasi-snapshot-preview1" 1725 | source = "registry+https://github.com/rust-lang/crates.io-index" 1726 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 1727 | 1728 | [[package]] 1729 | name = "wasi" 1730 | version = "0.10.0+wasi-snapshot-preview1" 1731 | source = "registry+https://github.com/rust-lang/crates.io-index" 1732 | checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" 1733 | 1734 | [[package]] 1735 | name = "wasm-bindgen" 1736 | version = "0.2.69" 1737 | source = "registry+https://github.com/rust-lang/crates.io-index" 1738 | checksum = "3cd364751395ca0f68cafb17666eee36b63077fb5ecd972bbcd74c90c4bf736e" 1739 | dependencies = [ 1740 | "cfg-if 1.0.0", 1741 | "serde", 1742 | "serde_json", 1743 | "wasm-bindgen-macro", 1744 | ] 1745 | 1746 | [[package]] 1747 | name = "wasm-bindgen-backend" 1748 | version = "0.2.69" 1749 | source = "registry+https://github.com/rust-lang/crates.io-index" 1750 | checksum = "1114f89ab1f4106e5b55e688b828c0ab0ea593a1ea7c094b141b14cbaaec2d62" 1751 | dependencies = [ 1752 | "bumpalo", 1753 | "lazy_static", 1754 | "log", 1755 | "proc-macro2", 1756 | "quote", 1757 | "syn", 1758 | "wasm-bindgen-shared", 1759 | ] 1760 | 1761 | [[package]] 1762 | name = "wasm-bindgen-futures" 1763 | version = "0.4.19" 1764 | source = "registry+https://github.com/rust-lang/crates.io-index" 1765 | checksum = "1fe9756085a84584ee9457a002b7cdfe0bfff169f45d2591d8be1345a6780e35" 1766 | dependencies = [ 1767 | "cfg-if 1.0.0", 1768 | "js-sys", 1769 | "wasm-bindgen", 1770 | "web-sys", 1771 | ] 1772 | 1773 | [[package]] 1774 | name = "wasm-bindgen-macro" 1775 | version = "0.2.69" 1776 | source = "registry+https://github.com/rust-lang/crates.io-index" 1777 | checksum = "7a6ac8995ead1f084a8dea1e65f194d0973800c7f571f6edd70adf06ecf77084" 1778 | dependencies = [ 1779 | "quote", 1780 | "wasm-bindgen-macro-support", 1781 | ] 1782 | 1783 | [[package]] 1784 | name = "wasm-bindgen-macro-support" 1785 | version = "0.2.69" 1786 | source = "registry+https://github.com/rust-lang/crates.io-index" 1787 | checksum = "b5a48c72f299d80557c7c62e37e7225369ecc0c963964059509fbafe917c7549" 1788 | dependencies = [ 1789 | "proc-macro2", 1790 | "quote", 1791 | "syn", 1792 | "wasm-bindgen-backend", 1793 | "wasm-bindgen-shared", 1794 | ] 1795 | 1796 | [[package]] 1797 | name = "wasm-bindgen-shared" 1798 | version = "0.2.69" 1799 | source = "registry+https://github.com/rust-lang/crates.io-index" 1800 | checksum = "7e7811dd7f9398f14cc76efd356f98f03aa30419dea46aa810d71e819fc97158" 1801 | 1802 | [[package]] 1803 | name = "web-sys" 1804 | version = "0.3.46" 1805 | source = "registry+https://github.com/rust-lang/crates.io-index" 1806 | checksum = "222b1ef9334f92a21d3fb53dc3fd80f30836959a90f9274a626d7e06315ba3c3" 1807 | dependencies = [ 1808 | "js-sys", 1809 | "wasm-bindgen", 1810 | ] 1811 | 1812 | [[package]] 1813 | name = "winapi" 1814 | version = "0.2.8" 1815 | source = "registry+https://github.com/rust-lang/crates.io-index" 1816 | checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" 1817 | 1818 | [[package]] 1819 | name = "winapi" 1820 | version = "0.3.9" 1821 | source = "registry+https://github.com/rust-lang/crates.io-index" 1822 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1823 | dependencies = [ 1824 | "winapi-i686-pc-windows-gnu", 1825 | "winapi-x86_64-pc-windows-gnu", 1826 | ] 1827 | 1828 | [[package]] 1829 | name = "winapi-build" 1830 | version = "0.1.1" 1831 | source = "registry+https://github.com/rust-lang/crates.io-index" 1832 | checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" 1833 | 1834 | [[package]] 1835 | name = "winapi-i686-pc-windows-gnu" 1836 | version = "0.4.0" 1837 | source = "registry+https://github.com/rust-lang/crates.io-index" 1838 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1839 | 1840 | [[package]] 1841 | name = "winapi-x86_64-pc-windows-gnu" 1842 | version = "0.4.0" 1843 | source = "registry+https://github.com/rust-lang/crates.io-index" 1844 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1845 | 1846 | [[package]] 1847 | name = "winreg" 1848 | version = "0.7.0" 1849 | source = "registry+https://github.com/rust-lang/crates.io-index" 1850 | checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" 1851 | dependencies = [ 1852 | "winapi 0.3.9", 1853 | ] 1854 | 1855 | [[package]] 1856 | name = "ws2_32-sys" 1857 | version = "0.2.1" 1858 | source = "registry+https://github.com/rust-lang/crates.io-index" 1859 | checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" 1860 | dependencies = [ 1861 | "winapi 0.2.8", 1862 | "winapi-build", 1863 | ] 1864 | 1865 | [[package]] 1866 | name = "zip" 1867 | version = "0.5.9" 1868 | source = "registry+https://github.com/rust-lang/crates.io-index" 1869 | checksum = "cc2896475a242c41366941faa27264df2cb935185a92e059a004d0048feb2ac5" 1870 | dependencies = [ 1871 | "byteorder", 1872 | "bzip2", 1873 | "crc32fast", 1874 | "flate2", 1875 | "thiserror", 1876 | "time", 1877 | ] 1878 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright © 2021 Peter M. Stahl pemistahl@gmail.com 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either expressed or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | [package] 17 | name = "chr" 18 | version = "1.0.0" 19 | authors = ["Peter M. Stahl "] 20 | description = """ 21 | A command-line tool that gives information about Unicode characters 22 | """ 23 | homepage = "https://github.com/pemistahl/chr" 24 | repository = "https://github.com/pemistahl/chr" 25 | license = "Apache-2.0" 26 | readme = "README.md" 27 | edition = "2018" 28 | categories = ["command-line-utilities"] 29 | keywords = ["unicode", "character", "text"] 30 | 31 | [dependencies] 32 | colored = "2.0.0" 33 | dirs = "3.0.1" 34 | minus = { version = "2.1.0", features = ["static_output"] } 35 | rusqlite = { version = "0.24.2", features = ["bundled"] } 36 | structopt = "0.3.21" 37 | strum = "0.20.0" 38 | strum_macros = "0.20.1" 39 | zip = "0.5.9" 40 | 41 | [dev-dependencies] 42 | assert_cmd = "1.0.2" 43 | indoc = "1.0.3" 44 | predicates = "1.0.6" 45 | 46 | [build-dependencies] 47 | csv = "1.1.5" 48 | reqwest = { version = "0.10.10", features = ["blocking"] } 49 | rusqlite = { version = "0.24.2", features = ["bundled"] } 50 | serde = { version = "1.0.118", features = ["derive"] } 51 | serde_json = "1.0.61" 52 | zip = "0.5.9" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![chr](logo.png) 2 | 3 |
4 | 5 | [![Build Status](https://github.com/pemistahl/chr/workflows/chr%20CI/badge.svg?branch=master)](https://github.com/pemistahl/chr/actions?query=workflow%3A%22chr+CI%22+branch%3Amaster) 6 | [![dependency status](https://deps.rs/crate/chr/1.0.0/status.svg)](https://deps.rs/crate/chr/1.0.0) 7 | [![lines of code](https://tokei.rs/b1/github/pemistahl/chr?category=code)](https://github.com/XAMPPRocky/tokei) 8 | [![Downloads](https://img.shields.io/crates/d/chr.svg)](https://crates.io/crates/chr) 9 | 10 | [![Crates.io](https://img.shields.io/crates/v/chr.svg)](https://crates.io/crates/chr) 11 | [![Lib.rs](https://img.shields.io/badge/lib.rs-v1.0.0-blue)](https://lib.rs/crates/chr) 12 | [![license](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) 13 | 14 | [![Linux Download](https://img.shields.io/badge/Linux%20Download-v1.0.0-blue?logo=Linux)](https://github.com/pemistahl/chr/releases/download/v1.0.0/chr-v1.0.0-x86_64-unknown-linux-musl.tar.gz) 15 | [![MacOS Download](https://img.shields.io/badge/macOS%20Download-v1.0.0-blue?logo=Apple)](https://github.com/pemistahl/chr/releases/download/v1.0.0/chr-v1.0.0-x86_64-apple-darwin.tar.gz) 16 | [![Windows Download](https://img.shields.io/badge/Windows%20Download-v1.0.0-blue?logo=Windows)](https://github.com/pemistahl/chr/releases/download/v1.0.0/chr-v1.0.0-x86_64-pc-windows-msvc.zip) 17 | 18 | ## Table of Contents 19 | 1. [What does this tool do?](#what-does-tool-do) 20 | 2. [How to install?](#how-to-install) 21 | 3. [How to use?](#how-to-use) 22 | 4. [How to build?](#how-to-build) 23 | 5. [Alternatives](#alternatives) 24 | 6. [Do you want to contribute?](#contribution) 25 | 26 | ## 1. What does this tool do? [Top ▲](#table-of-contents) 27 | 28 | *chr* is a command-line utility that is meant to give various information about Unicode characters. 29 | Currently, this information includes a character's Unicode escape sequence and its name, block, 30 | category and age as stated in the [Unicode Character Database (UCD)](https://www.unicode.org/reports/tr44). 31 | The UCD in its current version 13.0 is the main and only source of information for now. More properties 32 | and sources will be added in later releases. 33 | 34 | ## 2. How to install? [Top ▲](#table-of-contents) 35 | 36 | You can download the self-contained executable for your platform above and put it in a place of your choice. 37 | 38 | *chr* is also hosted on [crates.io](https://crates.io/crates/chr), 39 | the official Rust package registry. If you are a Rust developer and already have the Rust 40 | toolchain installed, you can install by compiling from source using 41 | [*cargo*](https://doc.rust-lang.org/cargo/), the Rust package manager: 42 | 43 | ``` 44 | cargo install chr 45 | ``` 46 | 47 | Package managers such as [Scoop](https://scoop.sh) (for Windows) and 48 | [Homebrew](https://brew.sh) (for macOS and Linux) will be supported soon. 49 | 50 | ## 3. How to use? [Top ▲](#table-of-contents) 51 | 52 | When running the executable for the first time, an SQLite database that is bundled with it will be unzipped 53 | to the current user's home directory. It can be found in a hidden directory under the path `/.chr/chr_1_0_0.db`. 54 | This database is queried each time *chr* is used. It is initially created when building the source code. 55 | Various UCD files are downloaded from the internet, the relevant information is copied into the SQLite database and 56 | then the database is zipped and included within the executable. 57 | 58 | ``` 59 | $ chr -h 60 | 61 | chr 1.0.0 62 | © 2021 Peter M. Stahl 63 | Licensed under the Apache License, Version 2.0 64 | Downloadable from https://crates.io/crates/chr 65 | Source code at https://github.com/pemistahl/chr 66 | 67 | chr is a command-line tool that gives 68 | information about Unicode characters. 69 | 70 | USAGE: 71 | chr [FLAGS] ... --name 72 | 73 | FLAGS: 74 | --no-paging Disables paging for the terminal output 75 | -c, --colorize Provides syntax highlighting for the terminal output 76 | -h, --help Prints help information 77 | -v, --version Prints version information 78 | 79 | OPTIONS: 80 | -n, --name Searches for characters by their name as 81 | stated in the Unicode Character Database 82 | 83 | ARGS: 84 | ... One or more characters separated by blank space 85 | ``` 86 | 87 | The tool is mainly meant to search for information about specific characters. 88 | All characters of interest, separated by blank space, can be given to *chr* at the same time. 89 | The entries are sorted by their Unicode escape sequence in ascending order: 90 | 91 | ``` 92 | $ chr Ä @ $ ß ! 93 | 94 | 1. ! U+0021 95 | EXCLAMATION MARK 96 | Basic Latin Other Punctuation 97 | since 1.1 98 | 99 | 2. $ U+0024 100 | DOLLAR SIGN 101 | Basic Latin Currency Sign 102 | since 1.1 103 | 104 | 3. @ U+0040 105 | COMMERCIAL AT 106 | Basic Latin Other Punctuation 107 | since 1.1 108 | 109 | 4. Ä U+00C4 110 | LATIN CAPITAL LETTER A WITH DIAERESIS 111 | Latin-1 Supplement Uppercase Letter 112 | since 1.1 113 | 114 | 5. ß U+00DF 115 | LATIN SMALL LETTER SHARP S 116 | Latin-1 Supplement Lowercase Letter 117 | since 1.1 118 | ``` 119 | 120 | It is also possible to search for characters by their official name in the UCD: 121 | 122 | ``` 123 | $ chr --name honey 124 | 125 | >>> 2 results found 126 | 127 | 1. 🍯 U+1F36F 128 | HONEY POT 129 | Miscellaneous Symbols and Pictographs Other Symbol 130 | since 6.0 131 | 132 | 2. 🐝 U+1F41D 133 | HONEYBEE 134 | Miscellaneous Symbols and Pictographs Other Symbol 135 | since 6.0 136 | ``` 137 | 138 | Long result lists are paged automatically in supported terminals for easier browsing. 139 | The [minus](https://github.com/arijit79/minus) crate is used for this purpose. 140 | Its key controls are documented in a 141 | [subsection of the project's readme](https://github.com/arijit79/minus#end-user-help). 142 | If the paging is not deactivated automatically in unsupported terminals or if it does 143 | not work as expected, it can be explicitly switched off using the `--no-paging` flag. 144 | The result lists can be colorized as well with the `--colorize` flag which produces 145 | nicer looking output in supported terminals. 146 | 147 | ## 4. How to build? [Top ▲](#table-of-contents) 148 | 149 | In order to build the source code yourself, you need the 150 | [stable Rust toolchain](https://www.rust-lang.org/tools/install) installed on your machine 151 | so that [*cargo*](https://doc.rust-lang.org/cargo/), the Rust package manager is available. 152 | 153 | ``` 154 | git clone https://github.com/pemistahl/chr.git 155 | cd chr 156 | cargo build 157 | ``` 158 | 159 | The source code is accompanied by some integration tests. For running them, simply say: 160 | 161 | ``` 162 | cargo test 163 | ``` 164 | 165 | ## 5. Alternatives [Top ▲](#table-of-contents) 166 | 167 | An alternative tool named [*cha(rs)*](https://github.com/antifuchs/chars) already exists that targets 168 | a similar purpose. Currently, it offers more character properties than *chr*. Unfortunately, it lacks 169 | a proper command-line interface, paging, colorization, and the presented information is not neatly arranged, 170 | in my opinion. It also bundles all the UCD files in its repository which is not necessary and impedes 171 | maintenance and future updates. 172 | 173 | With *chr*, only the url to the new UCD version needs to be updated. Assuming that the format of the UCD 174 | files does not change between versions, this is all there is to it to provide *chr* with the newest data. 175 | The data presentation is focused on readability and concentrates on the most essential properties for now. 176 | Last but not least, the fact that *chr* is backed by an SQL database allows for both complex and performant 177 | queries, especially in later releases when the functionality gets extended. 178 | 179 | ## 6. Do you want to contribute? [Top ▲](#table-of-contents) 180 | 181 | In case you want to contribute something to *chr* even though it's in a very early stage of development, 182 | then I encourage you to do so nevertheless. Do you have ideas for cool features? Or have you found any bugs so far? 183 | Feel free to open an issue or send a pull request. It's very much appreciated. :-) -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 Peter M. Stahl pemistahl@gmail.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either expressed or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | use csv::{Reader, ReaderBuilder}; 18 | use reqwest::blocking::Client; 19 | use rusqlite::{params, Connection, Error, NO_PARAMS}; 20 | use serde::Deserialize; 21 | use std::collections::BTreeMap; 22 | use std::env::var_os; 23 | use std::fs::File; 24 | use std::io::Write; 25 | use std::io::{BufReader, Read}; 26 | use std::ops::RangeInclusive; 27 | use std::option::Option::Some; 28 | use std::path::Path; 29 | use zip::write::FileOptions; 30 | use zip::ZipWriter; 31 | 32 | const UCD_URL: &str = "http://ftp.unicode.org/Public/13.0.0/ucd"; 33 | 34 | const BLOCKS_FILE_NAME: &str = "Blocks.txt"; 35 | const DERIVED_AGE_FILE_NAME: &str = "DerivedAge.txt"; 36 | const UNICODE_DATA_FILE_NAME: &str = "UnicodeData.txt"; 37 | const DATABASE_FILE_NAME: &str = "chr.db"; 38 | const ZIP_FILE_NAME: &str = "chr.db.zip"; 39 | 40 | fn main() { 41 | let out_dir = var_os("OUT_DIR").unwrap(); 42 | let target_directory_path = Path::new(&out_dir); 43 | 44 | download_files(target_directory_path); 45 | 46 | let mut unicode_char_data_map = process_unicode_data_file(target_directory_path); 47 | 48 | process_blocks_file(target_directory_path, &mut unicode_char_data_map); 49 | process_derived_age_file(target_directory_path, &mut unicode_char_data_map); 50 | 51 | save_to_database(target_directory_path, unicode_char_data_map); 52 | compress_database(target_directory_path); 53 | } 54 | 55 | fn download_files(target_directory_path: &Path) { 56 | let ucd_base_url = Path::new(UCD_URL); 57 | let file_urls = vec![ 58 | ucd_base_url.join(BLOCKS_FILE_NAME), 59 | ucd_base_url.join(DERIVED_AGE_FILE_NAME), 60 | ucd_base_url.join(UNICODE_DATA_FILE_NAME), 61 | ]; 62 | let client = Client::new(); 63 | 64 | for file_url in file_urls { 65 | let file_path = target_directory_path.join(file_url.file_name().unwrap()); 66 | 67 | if !file_path.exists() { 68 | let mut file_data = client 69 | .get(file_url.to_str().unwrap()) 70 | .send() 71 | .expect("File download failed"); 72 | let mut file = 73 | File::create(file_path).expect("New file could not be created at the given path"); 74 | 75 | file_data 76 | .copy_to(&mut file) 77 | .expect("Downloaded file data could not be written to disk"); 78 | } 79 | } 80 | } 81 | 82 | fn process_unicode_data_file(target_directory_path: &Path) -> BTreeMap { 83 | let mut csv_file_reader = open_csv_file_reader(target_directory_path, UNICODE_DATA_FILE_NAME); 84 | let mut csv_row_iterator = csv_file_reader.deserialize::(); 85 | let mut unicode_char_data_map = BTreeMap::::new(); 86 | 87 | while let Some(row) = csv_row_iterator.next() { 88 | let unicode_data_file_row = row.expect("UnicodeDataFileRow could not be deserialized"); 89 | let codepoint = to_decimal_number(&unicode_data_file_row.hexcode); 90 | 91 | if unicode_data_file_row.name.ends_with("First>") { 92 | let name = &unicode_data_file_row.name.split(',').next().unwrap()[1..]; 93 | let next_row = csv_row_iterator.next().unwrap().unwrap(); 94 | let last_codepoint = to_decimal_number(&next_row.hexcode); 95 | 96 | for point in codepoint..=last_codepoint { 97 | let unicode_char_data = UnicodeCharData::from(&unicode_data_file_row, point, name); 98 | unicode_char_data_map.insert(point, unicode_char_data); 99 | } 100 | } else { 101 | let unicode_char_data = UnicodeCharData::from( 102 | &unicode_data_file_row, 103 | codepoint, 104 | &unicode_data_file_row.name, 105 | ); 106 | unicode_char_data_map.insert(codepoint, unicode_char_data); 107 | } 108 | } 109 | 110 | unicode_char_data_map 111 | } 112 | 113 | fn process_blocks_file( 114 | target_directory_path: &Path, 115 | unicode_char_data_map: &mut BTreeMap, 116 | ) { 117 | let mut csv_file_reader = open_csv_file_reader(target_directory_path, BLOCKS_FILE_NAME); 118 | 119 | for result in csv_file_reader.records() { 120 | let row = result.expect("CSV row could not be unwrapped"); 121 | 122 | if row.len() == 2 { 123 | let codepoints = row.get(0).unwrap().trim(); 124 | let block_name = row.get(1).unwrap().trim(); 125 | 126 | if !codepoints.starts_with('#') { 127 | for codepoint in codepoint_range(codepoints) { 128 | if unicode_char_data_map.contains_key(&codepoint) { 129 | unicode_char_data_map.get_mut(&codepoint).unwrap().block = 130 | block_name.to_string(); 131 | } 132 | } 133 | } 134 | } 135 | } 136 | } 137 | 138 | fn process_derived_age_file( 139 | target_directory_path: &Path, 140 | unicode_char_data_map: &mut BTreeMap, 141 | ) { 142 | let mut csv_file_reader = open_csv_file_reader(target_directory_path, DERIVED_AGE_FILE_NAME); 143 | 144 | for result in csv_file_reader.records() { 145 | let row = result.expect("CSV row could not be unwrapped"); 146 | 147 | if row.len() == 2 { 148 | let codepoints = row.get(0).unwrap().trim(); 149 | let unicode_version = row.get(1).unwrap().split('#').next().unwrap().trim(); 150 | 151 | if !codepoints.starts_with('#') { 152 | for codepoint in codepoint_range(codepoints) { 153 | if unicode_char_data_map.contains_key(&codepoint) { 154 | unicode_char_data_map.get_mut(&codepoint).unwrap().age = 155 | unicode_version.to_string(); 156 | } 157 | } 158 | } 159 | } 160 | } 161 | } 162 | 163 | fn save_to_database( 164 | target_directory_path: &Path, 165 | unicode_char_data_map: BTreeMap, 166 | ) { 167 | let database_path = target_directory_path.join(DATABASE_FILE_NAME); 168 | let database = Connection::open(database_path).expect("Database could not be created"); 169 | 170 | database 171 | .execute( 172 | r#" 173 | CREATE TABLE IF NOT EXISTS UnicodeData ( 174 | codepoint INTEGER NOT NULL PRIMARY KEY, 175 | name TEXT NOT NULL, 176 | category TEXT NOT NULL, 177 | block TEXT NOT NULL, 178 | age TEXT NOT NULL 179 | ) WITHOUT ROWID; 180 | "#, 181 | NO_PARAMS, 182 | ) 183 | .expect("Database table could not be created"); 184 | 185 | let entry_count: Result = 186 | database.query_row("SELECT COUNT(*) FROM UnicodeData", NO_PARAMS, |row| { 187 | row.get(0) 188 | }); 189 | 190 | if entry_count.unwrap() > 0 { 191 | return; 192 | } 193 | 194 | let mut insert_statement = database 195 | .prepare_cached( 196 | r#"INSERT INTO UnicodeData VALUES ( 197 | ?,?,?,?,? 198 | )"#, 199 | ) 200 | .unwrap(); 201 | 202 | for (codepoint, char_data) in unicode_char_data_map.iter() { 203 | insert_statement 204 | .execute(params![ 205 | *codepoint, 206 | &char_data.name, 207 | &char_data.category, 208 | &char_data.block, 209 | &char_data.age, 210 | ]) 211 | .expect("Database insert statement failed"); 212 | } 213 | } 214 | 215 | fn compress_database(target_directory_path: &Path) { 216 | let database_path = target_directory_path.join(DATABASE_FILE_NAME); 217 | let database_file = 218 | File::open(database_path).expect("Database file could not be opened for compression"); 219 | let mut database_reader = BufReader::new(database_file); 220 | let mut database = vec![]; 221 | 222 | database_reader 223 | .read_to_end(&mut database) 224 | .expect("Database could not be read as bytes"); 225 | 226 | let zip_file_path = target_directory_path.join(ZIP_FILE_NAME); 227 | let zip_file = File::create(zip_file_path).expect("Empty zip file could not be created"); 228 | let mut zip = ZipWriter::new(zip_file); 229 | 230 | zip.start_file(DATABASE_FILE_NAME, FileOptions::default()) 231 | .expect("Zip content file could not be created"); 232 | zip.write_all(&database) 233 | .expect("Database could not be written to zip file"); 234 | } 235 | 236 | fn open_csv_file_reader(target_directory_path: &Path, file_name: &str) -> Reader { 237 | ReaderBuilder::new() 238 | .delimiter(b';') 239 | .has_headers(false) 240 | .flexible(true) 241 | .from_path(target_directory_path.join(file_name)) 242 | .unwrap_or_else(|_| panic!("File {} could not be opened for reading", file_name)) 243 | } 244 | 245 | fn to_decimal_number(hexcode: &str) -> u32 { 246 | u32::from_str_radix(hexcode, 16).expect("Could not convert hex to decimal number") 247 | } 248 | 249 | fn codepoint_range(codepoints: &str) -> RangeInclusive { 250 | if codepoints.contains("..") { 251 | let start_and_end = codepoints 252 | .split("..") 253 | .map(|hexcode| to_decimal_number(hexcode)) 254 | .collect::>(); 255 | let start = *start_and_end.get(0).unwrap(); 256 | let end = *start_and_end.get(1).unwrap(); 257 | start..=end 258 | } else { 259 | let start = to_decimal_number(codepoints); 260 | start..=start 261 | } 262 | } 263 | 264 | #[derive(Deserialize)] 265 | #[allow(dead_code)] 266 | struct UnicodeDataFileRow { 267 | hexcode: String, 268 | name: String, 269 | category: String, 270 | canonical_combining_class: u32, 271 | bidi_class: String, 272 | decomposition_type: Option, 273 | numeric_value_1: Option, 274 | numeric_value_2: Option, 275 | numeric_value_3: Option, 276 | bidi_mirrored: String, 277 | unicode_1_name: Option, 278 | iso_comment: Option, 279 | uppercase_mapping: Option, 280 | lowercase_mapping: Option, 281 | titlecase_mapping: Option, 282 | } 283 | 284 | #[derive(Default)] 285 | struct UnicodeCharData { 286 | codepoint: u32, 287 | name: String, 288 | category: String, 289 | block: String, 290 | age: String, 291 | } 292 | 293 | impl UnicodeCharData { 294 | fn from(unicode_data_file_row: &UnicodeDataFileRow, codepoint: u32, name: &str) -> Self { 295 | let mut unicode_char_data = UnicodeCharData::default(); 296 | unicode_char_data.codepoint = codepoint; 297 | unicode_char_data.name = name.to_string(); 298 | unicode_char_data.category = unicode_data_file_row.category.clone(); 299 | unicode_char_data 300 | } 301 | } 302 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pemistahl/chr/f85581b8696f9033c41c0c35e693ae3c8f8db13b/logo.png -------------------------------------------------------------------------------- /src/category.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 Peter M. Stahl pemistahl@gmail.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either expressed or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | use strum_macros::EnumString; 18 | 19 | #[derive(EnumString)] 20 | pub enum Category { 21 | Lu, 22 | Ll, 23 | Lt, 24 | Lm, 25 | Lo, 26 | Mn, 27 | Mc, 28 | Me, 29 | Nd, 30 | Nl, 31 | No, 32 | Pc, 33 | Pd, 34 | Ps, 35 | Pe, 36 | Pi, 37 | Pf, 38 | Po, 39 | Sm, 40 | Sc, 41 | Sk, 42 | So, 43 | Zs, 44 | Zl, 45 | Zp, 46 | Cc, 47 | Cf, 48 | Cs, 49 | Co, 50 | Cn, 51 | } 52 | 53 | impl Category { 54 | pub fn description(&self) -> &'static str { 55 | match self { 56 | Category::Lu => "Uppercase Letter", 57 | Category::Ll => "Lowercase Letter", 58 | Category::Lt => "Titlecase Letter", 59 | Category::Lm => "Modifier Letter", 60 | Category::Lo => "Other Letter", 61 | Category::Mn => "Non-spacing Mark", 62 | Category::Mc => "Spacing Mark", 63 | Category::Me => "Enclosing Mark", 64 | Category::Nd => "Decimal Number", 65 | Category::Nl => "Letter Number", 66 | Category::No => "Other Number", 67 | Category::Pc => "Connector Punctuation", 68 | Category::Pd => "Dash Punctuation", 69 | Category::Ps => "Opening Punctuation", 70 | Category::Pe => "Closing Punctuation", 71 | Category::Pi => "Initial Quotation Mark", 72 | Category::Pf => "Final Quotation Mark", 73 | Category::Po => "Other Punctuation", 74 | Category::Sm => "Mathematical Symbol", 75 | Category::Sc => "Currency Sign", 76 | Category::Sk => "Non-letter Modifier Symbol", 77 | Category::So => "Other Symbol", 78 | Category::Zs => "Space Separator", 79 | Category::Zl => "Line Separator", 80 | Category::Zp => "Paragraph Separator", 81 | Category::Cc => "Control Character", 82 | Category::Cf => "Format Control Character", 83 | Category::Cs => "Surrogate Code Point", 84 | Category::Co => "Private-use Character", 85 | Category::Cn => "Reserved Unassigned Code Point", 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 Peter M. Stahl pemistahl@gmail.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either expressed or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | mod category; 18 | 19 | use crate::category::Category; 20 | use colored::Colorize; 21 | use dirs::home_dir; 22 | use rusqlite::{Connection, Row, ToSql, NO_PARAMS}; 23 | use std::char; 24 | use std::fmt::Write as FmtWrite; 25 | use std::fs::{create_dir, File}; 26 | use std::io::{Cursor, Read, Write}; 27 | use std::iter; 28 | use std::path::Path; 29 | use std::str::FromStr; 30 | use structopt::clap::AppSettings::ColoredHelp; 31 | use structopt::StructOpt; 32 | use zip::ZipArchive; 33 | 34 | const DATABASE_DIRECTORY_NAME: &str = ".chr"; 35 | const DATABASE_FILE_NAME: &str = "chr_1_0_0.db"; 36 | 37 | #[derive(StructOpt)] 38 | #[structopt( 39 | author = "© 2021 Peter M. Stahl ", 40 | about = "Licensed under the Apache License, Version 2.0\n\ 41 | Downloadable from https://crates.io/crates/chr\n\ 42 | Source code at https://github.com/pemistahl/chr\n\n\ 43 | chr is a command-line tool that gives\n\ 44 | information about Unicode characters.", 45 | version_short = "v", 46 | global_settings = &[ColoredHelp] 47 | )] 48 | struct CLI { 49 | // -------------------- 50 | // ARGS 51 | // -------------------- 52 | #[structopt( 53 | value_name = "CHARS", 54 | required_unless = "name", 55 | conflicts_with = "name", 56 | help = "One or more characters separated by blank space" 57 | )] 58 | chars: Vec, 59 | 60 | // -------------------- 61 | // FLAGS 62 | // -------------------- 63 | #[structopt( 64 | name = "no-paging", 65 | long, 66 | help = "Disables paging for the terminal output", 67 | display_order = 1 68 | )] 69 | is_paging_disabled: bool, 70 | 71 | #[structopt( 72 | name = "colorize", 73 | short, 74 | long, 75 | help = "Provides syntax highlighting for the terminal output", 76 | display_order = 2 77 | )] 78 | is_output_colorized: bool, 79 | 80 | // -------------------- 81 | // OPTIONS 82 | // -------------------- 83 | #[structopt( 84 | name = "name", 85 | value_name = "NAME", 86 | short, 87 | long, 88 | required_unless = "chars", 89 | help = "Searches for characters by their name as\n\ 90 | stated in the Unicode Character Database" 91 | )] 92 | name: Option, 93 | } 94 | 95 | fn main() { 96 | let cli: CLI = CLI::from_args(); 97 | let database = connect_to_database(); 98 | let results = search_database(database, &cli); 99 | 100 | render(results, &cli); 101 | } 102 | 103 | fn connect_to_database() -> Connection { 104 | let home_directory = home_dir().expect("Home directory could not be found"); 105 | let database_file_path = home_directory 106 | .join(DATABASE_DIRECTORY_NAME) 107 | .join(DATABASE_FILE_NAME); 108 | 109 | if !database_file_path.is_file() { 110 | println!("Preparing Unicode character database, please wait a moment..."); 111 | unzip_database(&home_directory); 112 | println!("Database is ready\n"); 113 | } 114 | 115 | Connection::open(database_file_path).expect("Database connection could not be established") 116 | } 117 | 118 | fn unzip_database(home_directory: &Path) { 119 | let zip_file_bytes = include_bytes!(concat!(env!("OUT_DIR"), "/chr.db.zip")); 120 | let zip_file_reader = Cursor::new(zip_file_bytes); 121 | let mut zip_archive = 122 | ZipArchive::new(zip_file_reader).expect("Zip archive could not be opened"); 123 | let mut zip_file_content = zip_archive 124 | .by_index(0) 125 | .expect("Database could not be found within zip archive"); 126 | let mut database_file_bytes = vec![]; 127 | 128 | zip_file_content 129 | .read_to_end(&mut database_file_bytes) 130 | .expect("Bytes of database file could not be read"); 131 | 132 | let database_directory = home_directory.join(DATABASE_DIRECTORY_NAME); 133 | 134 | if !database_directory.is_dir() { 135 | create_dir(&database_directory).expect("Database directory could not be created"); 136 | } 137 | 138 | let database_file_path = database_directory.join(DATABASE_FILE_NAME); 139 | let mut database_file = 140 | File::create(database_file_path).expect("Database file could not be created"); 141 | 142 | database_file 143 | .write_all(&database_file_bytes) 144 | .expect("Database content could not be written to file"); 145 | } 146 | 147 | fn search_database(database: Connection, cli: &CLI) -> Vec { 148 | let mut sql = 149 | String::from("SELECT codepoint, name, category, block, age FROM UnicodeData WHERE "); 150 | 151 | if !cli.chars.is_empty() { 152 | let chars_as_decimals = convert_chars_to_decimals(&cli.chars); 153 | let params = iter::repeat("?") 154 | .take(chars_as_decimals.len()) 155 | .collect::>() 156 | .join(","); 157 | 158 | sql.push_str(&format!("codepoint IN ({})", params)); 159 | retrieve_results(database, sql, chars_as_decimals, cli) 160 | } else { 161 | sql.push_str(&format!("name LIKE '%{}%'", cli.name.as_ref().unwrap())); 162 | retrieve_results(database, sql, NO_PARAMS, cli) 163 | } 164 | } 165 | 166 | fn retrieve_results

(database: Connection, sql: String, params: P, cli: &CLI) -> Vec 167 | where 168 | P: IntoIterator, 169 | P::Item: ToSql, 170 | { 171 | let mut statement = database.prepare(&sql).unwrap(); 172 | let mut rows = statement.query(params).unwrap(); 173 | let mut results = vec![]; 174 | let mut idx = 1; 175 | 176 | while let Some(row) = rows.next().unwrap() { 177 | let result = convert_database_row_to_result(row, idx, cli); 178 | results.push(result); 179 | idx += 1; 180 | } 181 | 182 | results 183 | } 184 | 185 | fn convert_database_row_to_result(row: &Row, idx: u32, cli: &CLI) -> String { 186 | let codepoint_column_index = row.column_index("codepoint").unwrap(); 187 | let name_column_index = row.column_index("name").unwrap(); 188 | let category_column_index = row.column_index("category").unwrap(); 189 | let block_column_index = row.column_index("block").unwrap(); 190 | let age_column_index = row.column_index("age").unwrap(); 191 | 192 | let c = char::from_u32(row.get_unwrap(codepoint_column_index)).unwrap(); 193 | let hex_code = format!("U+{:04x}", to_decimal_number(c)).to_uppercase(); 194 | let name: String = row.get_unwrap(name_column_index); 195 | let category: String = row.get_unwrap(category_column_index); 196 | let category_description: &str = Category::from_str(&category).unwrap().description(); 197 | let block: String = row.get_unwrap(block_column_index); 198 | let age: String = row.get_unwrap(age_column_index); 199 | let formatted_age = format!("since {}", age); 200 | 201 | if cli.is_output_colorized { 202 | let idx_str = format!("{}.", idx); 203 | let colored_idx = idx_str.as_str().bright_white().on_bright_blue(); 204 | let colored_hex_code = hex_code.as_str().green(); 205 | let colored_name = name.as_str().cyan(); 206 | let colored_category = category_description.red(); 207 | let colored_block = block.as_str().purple(); 208 | let colored_age = formatted_age.as_str().yellow(); 209 | 210 | format!( 211 | "{}\t{}\t{}\n{}\n{}\t{}\n{}", 212 | colored_idx, 213 | c, 214 | colored_hex_code, 215 | colored_name, 216 | colored_block, 217 | colored_category, 218 | colored_age 219 | ) 220 | } else { 221 | format!( 222 | "{}.\t{}\t{}\n{}\n{}\t{}\n{}", 223 | idx, c, hex_code, name, block, category_description, formatted_age 224 | ) 225 | } 226 | } 227 | 228 | fn convert_chars_to_decimals(chars: &[char]) -> Vec { 229 | chars.iter().map(|&c| to_decimal_number(c)).collect() 230 | } 231 | 232 | fn to_decimal_number(c: char) -> u32 { 233 | u32::from_str_radix(&to_hex_code(c), 16).expect("Could not convert hex to decimal number") 234 | } 235 | 236 | fn to_hex_code(c: char) -> String { 237 | let escaped_char = c.escape_unicode().to_string(); 238 | escaped_char[3..escaped_char.len() - 1].to_string() 239 | } 240 | 241 | fn render(mut results: Vec, cli: &CLI) { 242 | if !cli.is_paging_disabled && cli.name.is_some() { 243 | results.insert(0, format!(">>> {} results found", results.len())); 244 | } 245 | 246 | let result = results.join("\n\n"); 247 | 248 | if cli.is_paging_disabled { 249 | println!("{}", result); 250 | } else { 251 | let mut output = minus::Pager::new(); 252 | writeln!(output.lines, "{}", result) 253 | .expect("Terminal output could not be written to pager"); 254 | minus::page_all(output).expect("Pager could not be initialized"); 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /tests/cli_integration_tests.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2021 Peter M. Stahl pemistahl@gmail.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either expressed or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | use assert_cmd::prelude::*; 18 | use indoc::indoc; 19 | use predicates::prelude::*; 20 | use std::process::Command; 21 | 22 | #[test] 23 | fn succeeds_with_character_search_option() { 24 | let mut chr = init_command(); 25 | chr.args(&["--no-paging", "Ä", "@", "$", "ß", "!"]); 26 | chr.assert() 27 | .success() 28 | .stdout(predicate::str::contains(indoc!( 29 | " 30 | 1. ! U+0021 31 | EXCLAMATION MARK 32 | Basic Latin Other Punctuation 33 | since 1.1 34 | 35 | 2. $ U+0024 36 | DOLLAR SIGN 37 | Basic Latin Currency Sign 38 | since 1.1 39 | 40 | 3. @ U+0040 41 | COMMERCIAL AT 42 | Basic Latin Other Punctuation 43 | since 1.1 44 | 45 | 4. Ä U+00C4 46 | LATIN CAPITAL LETTER A WITH DIAERESIS 47 | Latin-1 Supplement Uppercase Letter 48 | since 1.1 49 | 50 | 5. ß U+00DF 51 | LATIN SMALL LETTER SHARP S 52 | Latin-1 Supplement Lowercase Letter 53 | since 1.1 54 | " 55 | ))); 56 | } 57 | 58 | #[test] 59 | fn succeeds_with_name_search_option() { 60 | let mut chr = init_command(); 61 | chr.args(&["--no-paging", "--name", "honey"]); 62 | chr.assert() 63 | .success() 64 | .stdout(predicate::str::contains(indoc!( 65 | " 66 | 1. 🍯 U+1F36F 67 | HONEY POT 68 | Miscellaneous Symbols and Pictographs Other Symbol 69 | since 6.0 70 | 71 | 2. 🐝 U+1F41D 72 | HONEYBEE 73 | Miscellaneous Symbols and Pictographs Other Symbol 74 | since 6.0 75 | " 76 | ))); 77 | } 78 | 79 | #[test] 80 | fn fails_with_string_instead_of_chars() { 81 | let mut chr = init_command(); 82 | chr.args(&["Ä@"]); 83 | chr.assert().failure().stderr(predicate::str::contains( 84 | "Invalid value for '...': too many characters in string", 85 | )); 86 | } 87 | 88 | #[test] 89 | fn fails_with_both_character_and_name_search_option() { 90 | let mut chr = init_command(); 91 | chr.args(&["Ä@", "--name", "honey"]); 92 | chr.assert().failure().stderr(predicate::str::contains( 93 | "error: The argument '--name ' cannot be used with 'chars'", 94 | )); 95 | } 96 | 97 | fn init_command() -> Command { 98 | Command::cargo_bin("chr").unwrap() 99 | } 100 | --------------------------------------------------------------------------------