├── .github ├── CODEOWNERS ├── FUNDING.yml ├── dependabot.yml ├── mergify.yml └── workflows │ ├── cd.yml │ ├── ci-beta-nightly.yml │ └── ci.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── cliff.toml ├── config.toml ├── img ├── demo.gif └── logo.png ├── man └── rpaste.1 ├── src ├── args.rs ├── config.rs ├── error.rs ├── lib.rs ├── main.rs └── upload.rs └── tests └── token_file_parsing ├── token.txt └── token_whitespaced.txt /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/about-codeowners/ 2 | # for more info about CODEOWNERS file 3 | 4 | # It uses the same pattern rule for gitignore file 5 | # https://git-scm.com/docs/gitignore#_pattern_format 6 | 7 | # Core 8 | * @orhun @tessus 9 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: orhun 2 | patreon: orhunp 3 | custom: ["https://www.buymeacoffee.com/orhun"] 4 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # Maintain dependencies for Cargo 4 | - package-ecosystem: cargo 5 | directory: "/" 6 | schedule: 7 | interval: daily 8 | open-pull-requests-limit: 10 9 | 10 | # Maintain dependencies for GitHub Actions 11 | - package-ecosystem: github-actions 12 | directory: "/" 13 | schedule: 14 | interval: daily 15 | open-pull-requests-limit: 10 16 | -------------------------------------------------------------------------------- /.github/mergify.yml: -------------------------------------------------------------------------------- 1 | pull_request_rules: 2 | - name: Automatic merge for Dependabot pull requests 3 | conditions: 4 | - author=dependabot[bot] 5 | actions: 6 | merge: 7 | method: squash 8 | 9 | - name: Automatic update to the main branch for pull requests 10 | conditions: 11 | - -conflict # skip PRs with conflicts 12 | - -draft # skip GH draft PRs 13 | - -author=dependabot[bot] # skip dependabot PRs 14 | actions: 15 | update: 16 | -------------------------------------------------------------------------------- /.github/workflows/cd.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Deployment 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*.*.*" 7 | 8 | jobs: 9 | publish-github: 10 | name: Publish on GitHub 11 | runs-on: ${{ matrix.config.OS }} 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | config: 16 | - { OS: ubuntu-latest, TARGET: "x86_64-unknown-linux-gnu" } 17 | - { OS: ubuntu-latest, TARGET: "x86_64-unknown-linux-musl" } 18 | - { OS: ubuntu-latest, TARGET: "i686-unknown-linux-gnu" } 19 | - { OS: ubuntu-latest, TARGET: "i686-unknown-linux-musl" } 20 | - { OS: ubuntu-latest, TARGET: "armv5te-unknown-linux-gnueabi" } 21 | - { OS: ubuntu-latest, TARGET: "armv7-unknown-linux-gnueabihf" } 22 | - { OS: ubuntu-latest, TARGET: "aarch64-unknown-linux-gnu" } 23 | - { OS: ubuntu-latest, TARGET: "aarch64-unknown-linux-musl" } 24 | - { OS: macos-latest, TARGET: "x86_64-apple-darwin" } 25 | - { OS: macos-latest, TARGET: "aarch64-apple-darwin" } 26 | - { OS: windows-latest, TARGET: "x86_64-pc-windows-msvc" } 27 | - { OS: windows-latest, TARGET: "i686-pc-windows-msvc" } 28 | 29 | steps: 30 | - name: Checkout the repository 31 | uses: actions/checkout@v4 32 | 33 | - name: Set the release version 34 | shell: bash 35 | run: echo "RELEASE_VERSION=${GITHUB_REF:11}" >> $GITHUB_ENV 36 | 37 | - name: Install musl-tools 38 | if: ${{ endsWith(matrix.config.TARGET, 'musl') }} 39 | run: | 40 | sudo apt-get update 41 | sudo apt-get install -y --no-install-recommends \ 42 | --allow-unauthenticated musl-tools 43 | 44 | - name: Install Rust toolchain 45 | uses: dtolnay/rust-toolchain@stable 46 | with: 47 | target: ${{ matrix.config.TARGET }} 48 | 49 | - name: Install cross 50 | uses: taiki-e/install-action@cross 51 | 52 | - name: Build 53 | run: cross build --release --locked --target ${{ matrix.config.TARGET }} 54 | 55 | - name: Prepare release assets 56 | shell: bash 57 | run: | 58 | mkdir release/ 59 | cp {LICENSE,README.md,CHANGELOG.md,config.toml} release/ 60 | cp target/${{ matrix.config.TARGET }}/release/rpaste release/ 61 | mv release/ rustypaste-cli-${{ env.RELEASE_VERSION }}/ 62 | 63 | - name: Create release artifacts 64 | shell: bash 65 | run: | 66 | if [ "${{ matrix.config.OS }}" = "windows-latest" ]; then 67 | 7z a -tzip "rustypaste-cli-${{ env.RELEASE_VERSION }}-${{ matrix.config.TARGET }}.zip" \ 68 | rustypaste-cli-${{ env.RELEASE_VERSION }} 69 | else 70 | tar -czvf rustypaste-cli-${{ env.RELEASE_VERSION }}-${{ matrix.config.TARGET }}.tar.gz \ 71 | rustypaste-cli-${{ env.RELEASE_VERSION }}/ 72 | shasum -a 512 rustypaste-cli-${{ env.RELEASE_VERSION }}-${{ matrix.config.TARGET }}.tar.gz \ 73 | > rustypaste-cli-${{ env.RELEASE_VERSION }}-${{ matrix.config.TARGET }}.tar.gz.sha512 74 | fi 75 | 76 | - name: Upload the release 77 | uses: svenstaro/upload-release-action@v2 78 | with: 79 | repo_token: ${{ secrets.GITHUB_TOKEN }} 80 | file: rustypaste-cli-${{ env.RELEASE_VERSION }}-${{ matrix.config.TARGET }}.* 81 | file_glob: true 82 | overwrite: true 83 | tag: ${{ github.ref }} 84 | 85 | publish-crates-io: 86 | name: Publish on crates.io 87 | needs: publish-github 88 | runs-on: ubuntu-latest 89 | steps: 90 | - name: Checkout the repository 91 | uses: actions/checkout@v4 92 | - name: Install Rust toolchain 93 | uses: dtolnay/rust-toolchain@stable 94 | - name: Publish 95 | run: cargo publish --locked --token ${{ secrets.CARGO_TOKEN }} 96 | -------------------------------------------------------------------------------- /.github/workflows/ci-beta-nightly.yml: -------------------------------------------------------------------------------- 1 | name: Build Test (Beta and Nightly) 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | build-beta-nightly: 13 | name: "Build for ${{ matrix.config.TARGET }} (on ${{ matrix.config.OS }}) [args: ${{ matrix.BUILD_ARGS }}]" 14 | runs-on: ${{ matrix.config.OS }} 15 | strategy: 16 | fail-fast: false 17 | matrix: 18 | config: 19 | - { OS: ubuntu-latest, TARGET: "x86_64-unknown-linux-gnu" } 20 | - { OS: ubuntu-latest, TARGET: "x86_64-unknown-linux-musl" } 21 | - { OS: ubuntu-latest, TARGET: "i686-unknown-linux-gnu" } 22 | - { OS: ubuntu-latest, TARGET: "i686-unknown-linux-musl" } 23 | # - { OS: ubuntu-latest, TARGET: "armv5te-unknown-linux-gnueabi" } 24 | - { 25 | OS: ubuntu-latest, 26 | TARGET: "armv7-unknown-linux-gnueabihf" 27 | } 28 | - { OS: ubuntu-latest, TARGET: "aarch64-unknown-linux-gnu" } 29 | - { OS: ubuntu-latest, TARGET: "aarch64-unknown-linux-musl" } 30 | # - { OS: ubuntu-latest, TARGET: "x86_64-pc-windows-gnu" } 31 | - { OS: macos-latest, TARGET: "x86_64-apple-darwin" } 32 | - { OS: macos-latest, TARGET: "aarch64-apple-darwin" } 33 | - { OS: windows-latest, TARGET: "x86_64-pc-windows-msvc" } 34 | - { OS: windows-latest, TARGET: "i686-pc-windows-msvc" } 35 | TOOLCHAIN: [ beta, nightly ] 36 | BUILD_ARGS: [ "", "--features use-native-certs" ] 37 | 38 | steps: 39 | - name: Checkout the repository 40 | uses: actions/checkout@v4 41 | 42 | - name: Install Rust 43 | uses: dtolnay/rust-toolchain@master 44 | with: 45 | toolchain: ${{ matrix.TOOLCHAIN }} 46 | target: ${{ matrix.config.TARGET }} 47 | components: rustfmt, clippy 48 | 49 | - name: Cache Cargo dependencies 50 | uses: Swatinem/rust-cache@v2 51 | 52 | - name: Install cross 53 | uses: taiki-e/install-action@cross 54 | 55 | - name: Build 56 | run: cross build --target ${{ matrix.config.TARGET }} ${{ matrix.BUILD_ARGS }} 57 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | schedule: 11 | - cron: "0 0 * * 0" 12 | 13 | jobs: 14 | build: 15 | name: "Build for ${{ matrix.config.TARGET }} using Rust ${{ matrix.TOOLCHAIN }} (on ${{ matrix.config.OS }}) [args: ${{ matrix.BUILD_ARGS }}]" 16 | runs-on: ${{ matrix.config.OS }} 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | config: 21 | - { OS: ubuntu-latest, TARGET: "x86_64-unknown-linux-gnu" } 22 | - { OS: ubuntu-latest, TARGET: "x86_64-unknown-linux-musl" } 23 | - { OS: ubuntu-latest, TARGET: "i686-unknown-linux-gnu" } 24 | - { OS: ubuntu-latest, TARGET: "i686-unknown-linux-musl" } 25 | # - { OS: ubuntu-latest, TARGET: "armv5te-unknown-linux-gnueabi" } 26 | - { 27 | OS: ubuntu-latest, 28 | TARGET: "armv7-unknown-linux-gnueabihf" 29 | } 30 | - { OS: ubuntu-latest, TARGET: "aarch64-unknown-linux-gnu" } 31 | - { OS: ubuntu-latest, TARGET: "aarch64-unknown-linux-musl" } 32 | - { OS: ubuntu-latest, TARGET: "x86_64-pc-windows-gnu" } 33 | - { OS: macos-latest, TARGET: "x86_64-apple-darwin" } 34 | - { OS: macos-latest, TARGET: "aarch64-apple-darwin" } 35 | - { OS: windows-latest, TARGET: "x86_64-pc-windows-msvc" } 36 | - { OS: windows-latest, TARGET: "i686-pc-windows-msvc" } 37 | TOOLCHAIN: [ stable ] 38 | BUILD_ARGS: [ "", "--features use-native-certs" ] 39 | 40 | steps: 41 | - name: Checkout the repository 42 | uses: actions/checkout@v4 43 | 44 | - name: Install Rust 45 | uses: dtolnay/rust-toolchain@master 46 | with: 47 | toolchain: ${{ matrix.TOOLCHAIN }} 48 | target: ${{ matrix.config.TARGET }} 49 | components: rustfmt, clippy 50 | 51 | - name: Cache Cargo dependencies 52 | uses: Swatinem/rust-cache@v2 53 | 54 | - name: Install cross 55 | uses: taiki-e/install-action@cross 56 | 57 | - name: Build 58 | run: cross build --target ${{ matrix.config.TARGET }} ${{ matrix.BUILD_ARGS }} 59 | 60 | - name: Check formatting 61 | run: cargo fmt -- --check --verbose 62 | 63 | - name: Check lints 64 | run: cargo clippy --tests --verbose -- -D warnings 65 | 66 | audit: 67 | name: Perform audit for security 68 | runs-on: ubuntu-latest 69 | steps: 70 | - name: Checkout the repository 71 | uses: actions/checkout@v4 72 | - name: Run cargo-audit 73 | uses: rustsec/audit-check@v2.0.0 74 | with: 75 | token: ${{ secrets.GITHUB_TOKEN }} 76 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled files and executables 2 | **/target/ 3 | 4 | # Backup files generated by rustfmt 5 | **/*.rs.bk 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [0.9.4] - 2025-04-25 9 | 10 | ### Added 11 | 12 | - Honor XDG_CONFIG_HOME on macOS (additionally search for the config file at the same locations as on Linux) by @tessus in [#185](https://github.com/orhun/rustypaste-cli/pull/185) 13 | 14 | ### Fixed 15 | 16 | - No panic when creation_date_utc is null by @kanna5 in [#182](https://github.com/orhun/rustypaste-cli/pull/182) 17 | 18 | ### Changed 19 | 20 | - Bump dependencies 21 | 22 | ## [0.9.3] - 2025-03-13 23 | 24 | ### Added 25 | 26 | - Add option to set token from file by @nydragon in [#171](https://github.com/orhun/rustypaste-cli/pull/171) 27 | 28 | ### Changed 29 | 30 | - Bump dependencies 31 | 32 | ## [0.9.2] - 2024-12-11 33 | 34 | ### Changed 35 | 36 | - Bump dependencies 37 | 38 | ### Fixed 39 | 40 | - Omit explicit lifetime by @tessus in [#156](https://github.com/orhun/rustypaste-cli/pull/156) 41 | 42 | ## [0.9.1] - 2024-08-01 43 | 44 | ### Added 45 | 46 | - Show creation date in list output by @tessus in [#117](https://github.com/orhun/rustypaste-cli/pull/117) 47 | 48 | See the latest rustypaste server release ([0.15.1](https://github.com/orhun/rustypaste/releases/tag/v0.15.1)) for more information. 49 | 50 | - Add @tessus as a maintainer 51 | 52 | ### Changed 53 | 54 | - Update license copyright years by @orhun 55 | - Bump dependencies 56 | 57 | ## [0.9.0] - 2024-03-27 58 | 59 | ### Added 60 | 61 | - Add argument to specify filename by @tessus in [#88](https://github.com/orhun/rustypaste-cli/pull/88) 62 | 63 | `rustypaste` >=0.15.0 supports overriding the file name by using `filename` header. You can also use this functionality in `rpaste` as follows: 64 | 65 | ```sh 66 | rpaste -n filename-on-server.txt awesome.txt 67 | ``` 68 | 69 | ### Changed 70 | 71 | - Simplify reading content from stdin by @tranzystorekk in [#96](https://github.com/orhun/rustypaste-cli/pull/96) 72 | - Split workflow into stable and beta/nightly by @tessus in [#99](https://github.com/orhun/rustypaste-cli/pull/99) 73 | - Get rid of the unmaintained actions by @tessus in [#102](https://github.com/orhun/rustypaste-cli/pull/102) 74 | - Add Mergify config by @orhun 75 | - Bump dependencies by @orhun 76 | 77 | ### Removed 78 | 79 | - Remove deprecated actions by @tessus in [#101](https://github.com/orhun/rustypaste-cli/pull/101) 80 | 81 | ### New Contributors 82 | 83 | - @tranzystorekk made their first contribution in [#96](https://github.com/orhun/rustypaste-cli/pull/96) 84 | 85 | ## [0.8.0] - 2023-09-05 86 | 87 | ### Added 88 | 89 | - Add option to delete file(s) from server (#54) 90 | 91 | `rustpaste` now supports deleting files starting from [`0.14.0`](https://github.com/orhun/rustypaste/releases/tag/v0.14.0) thus a new flag (`-d`) is added to `rpaste`. 92 | 93 | ```sh 94 | rpaste -d file.txt 95 | ``` 96 | 97 | To use this feature, set tokens for both `rustypaste` and `rustypaste-cli` in the configuration file via `delete_tokens` / `delete_token` option. 98 | 99 | ### Changed 100 | 101 | - Use IsTerminal from stdlib (#55) 102 | - Disable Rust beta builds 103 | - Upgrade dependencies 104 | 105 | ## [0.7.0] - 2023-08-12 106 | 107 | ### Added 108 | 109 | - Added `-l` flag for retrieving file list from the server (#45) 110 | 111 | For example: 112 | 113 | ```sh 114 | rpaste -l # JSON output 115 | rpaste -lp # Table output (pretty) 116 | ``` 117 | 118 | `[server].expose_list` option should be set to `true` on `rustypaste` server for this flag to work. 119 | 120 | ### Removed 121 | 122 | - Remove extra newline from version output (#36) 123 | 124 | ## [0.6.0] - 2023-07-08 125 | 126 | ### Changed 127 | 128 | - Automatically detect if the data is piped (#28) 129 | 130 | Now when data is piped into `rpaste`, there is no reason to add `-` as a file. 131 | 132 | Before: 133 | 134 | ``` 135 | cat whatever.txt | rpaste - 136 | ``` 137 | 138 | After: 139 | 140 | ``` 141 | cat whatever.txt | rpaste 142 | ``` 143 | 144 | - Upgrade dependencies 145 | 146 | ## [0.5.0] - 2023-07-01 147 | 148 | ### Added 149 | 150 | - Support using the OS TLS trust store (#18) 151 | - Added `use-native-certs` feature flag for enabling the default TLS implementation. 152 | 153 | ### Changed 154 | 155 | - Mention the platform-specific configuration directory in the documentation (#10) 156 | - Upgrade dependencies 157 | 158 | ### Fixed 159 | 160 | - Fix the server version retrieval (#17) 161 | 162 | ## [0.4.0] - 2023-05-31 163 | 164 | ### Added 165 | 166 | - Support uploading one shot URLs 167 | 168 | `rustypaste` supports one shot URL uploads since [`0.10.0`](https://github.com/orhun/rustypaste/releases/tag/v0.10.0). To use this feature: 169 | 170 | ```sh 171 | rpaste -ou https://example.com/some/long/url 172 | ``` 173 | 174 | - Add example for using the stdin 175 | - Add installation instructions for Alpine Linux 176 | 177 | ### Changed 178 | 179 | - Update funding options 180 | - [Buy me a coffee to support my open source endeavours!](https://www.buymeacoffee.com/orhun) ☕ 181 | 182 | ## [0.3.0] - 2022-12-31 183 | 184 | ### Added 185 | 186 | - Add a progress bar for upload 187 | - Now you can track the upload status for big files! 188 | 189 | ![demo](https://user-images.githubusercontent.com/24392180/210139218-7c309398-1e4c-4323-ace7-ba30baf3c9d2.gif) 190 | 191 | ### Updated 192 | 193 | - Upgrade dependencies 194 | 195 | ## [0.2.0] - 2022-10-04 196 | 197 | ### Added 198 | 199 | - Add `--server-version` flag 200 | - With the last release of `rustypaste`, it is now possible to retrieve the server version via `/version` endpoint. 201 | - You can print the server version with using `-V`/`--server-version` flag with `rustypaste-cli`. 202 | 203 | ### Updated 204 | 205 | - Upgrade dependencies 206 | - Enable [GitHub Sponsors](https://github.com/sponsors/orhun) for funding 207 | - Consider supporting me for my open-source work 💖 208 | 209 | ## [0.1.8 ... 0.1.11] - 2022-06-18 210 | 211 | ### Updated 212 | 213 | - Build/release for more platforms (MacOS & Windows) 214 | - (0.1.9) Upgrade transitive dependencies 215 | - (0.1.9) Fix deployment workflow (remove `x86_64-pc-windows-gnu` target) 216 | - (0.1.10) Fix deployment workflow (use compatible commands for MacOS & Windows) 217 | - (0.1.11) Fix deployment workflow (set the correct artifact name for Windows assets) 218 | 219 | ## [0.1.7] - 2022-05-29 220 | 221 | ### Updated 222 | 223 | - Upgrade dependencies 224 | 225 | ## [0.1.6] - 2022-03-31 226 | 227 | ### Updated 228 | 229 | - Fix typo in the manpage identifier 230 | - Use `url::Url` for parsing URLs 231 | 232 | ## [0.1.5] - 2022-03-15 233 | 234 | ### Added 235 | 236 | - Allow specifying `prettify` in config 237 | - Add a manpage 238 | 239 | ### Changed 240 | 241 | - Respect `XDG_CONFIG_HOME` as global config location 242 | - Exit with a more informative message if no address is given 243 | 244 | ## [0.1.4] - 2022-03-13 245 | 246 | ### Added 247 | 248 | - Add instructions for installing on Arch Linux 249 | 250 | ### Updated 251 | 252 | - Update license copyright years 253 | - Upgrade dependencies 254 | 255 | ### Fixed 256 | 257 | - Apply clippy::map_flatten suggestion 258 | 259 | ## [0.1.3] - 2021-11-07 260 | 261 | ### Added 262 | 263 | - Add argument for uploading files from remote URL 264 | 265 | ## [0.1.2] - 2021-09-19 266 | 267 | ### Fixed 268 | 269 | - Read raw bytes from stdin. 270 | - Fixes "stream did not contain valid UTF-8" error 271 | 272 | ## [0.1.1] - 2021-09-19 273 | 274 | Initial release. 275 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "adler" 7 | version = "1.0.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 10 | 11 | [[package]] 12 | name = "base64" 13 | version = "0.21.7" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" 16 | 17 | [[package]] 18 | name = "base64" 19 | version = "0.22.1" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 22 | 23 | [[package]] 24 | name = "bitflags" 25 | version = "1.3.2" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 28 | 29 | [[package]] 30 | name = "bitflags" 31 | version = "2.5.0" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" 34 | 35 | [[package]] 36 | name = "bumpalo" 37 | version = "3.16.0" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" 40 | 41 | [[package]] 42 | name = "cc" 43 | version = "1.2.16" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" 46 | dependencies = [ 47 | "shlex", 48 | ] 49 | 50 | [[package]] 51 | name = "cfg-if" 52 | version = "1.0.0" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 55 | 56 | [[package]] 57 | name = "colored" 58 | version = "3.0.0" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e" 61 | dependencies = [ 62 | "windows-sys 0.52.0", 63 | ] 64 | 65 | [[package]] 66 | name = "console" 67 | version = "0.15.8" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" 70 | dependencies = [ 71 | "encode_unicode", 72 | "lazy_static", 73 | "libc", 74 | "unicode-width 0.1.11", 75 | "windows-sys 0.52.0", 76 | ] 77 | 78 | [[package]] 79 | name = "core-foundation" 80 | version = "0.9.4" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" 83 | dependencies = [ 84 | "core-foundation-sys", 85 | "libc", 86 | ] 87 | 88 | [[package]] 89 | name = "core-foundation-sys" 90 | version = "0.8.6" 91 | source = "registry+https://github.com/rust-lang/crates.io-index" 92 | checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" 93 | 94 | [[package]] 95 | name = "crc32fast" 96 | version = "1.4.0" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" 99 | dependencies = [ 100 | "cfg-if", 101 | ] 102 | 103 | [[package]] 104 | name = "dirs" 105 | version = "5.0.1" 106 | source = "registry+https://github.com/rust-lang/crates.io-index" 107 | checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" 108 | dependencies = [ 109 | "dirs-sys", 110 | ] 111 | 112 | [[package]] 113 | name = "dirs-next" 114 | version = "2.0.0" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" 117 | dependencies = [ 118 | "cfg-if", 119 | "dirs-sys-next", 120 | ] 121 | 122 | [[package]] 123 | name = "dirs-sys" 124 | version = "0.4.1" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" 127 | dependencies = [ 128 | "libc", 129 | "option-ext", 130 | "redox_users", 131 | "windows-sys 0.48.0", 132 | ] 133 | 134 | [[package]] 135 | name = "dirs-sys-next" 136 | version = "0.1.2" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" 139 | dependencies = [ 140 | "libc", 141 | "redox_users", 142 | "winapi", 143 | ] 144 | 145 | [[package]] 146 | name = "displaydoc" 147 | version = "0.2.5" 148 | source = "registry+https://github.com/rust-lang/crates.io-index" 149 | checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" 150 | dependencies = [ 151 | "proc-macro2", 152 | "quote", 153 | "syn", 154 | ] 155 | 156 | [[package]] 157 | name = "encode_unicode" 158 | version = "0.3.6" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" 161 | 162 | [[package]] 163 | name = "equivalent" 164 | version = "1.0.1" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 167 | 168 | [[package]] 169 | name = "errno" 170 | version = "0.3.8" 171 | source = "registry+https://github.com/rust-lang/crates.io-index" 172 | checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" 173 | dependencies = [ 174 | "libc", 175 | "windows-sys 0.52.0", 176 | ] 177 | 178 | [[package]] 179 | name = "fastrand" 180 | version = "2.0.2" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" 183 | 184 | [[package]] 185 | name = "flate2" 186 | version = "1.0.28" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" 189 | dependencies = [ 190 | "crc32fast", 191 | "miniz_oxide", 192 | ] 193 | 194 | [[package]] 195 | name = "form_urlencoded" 196 | version = "1.2.1" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 199 | dependencies = [ 200 | "percent-encoding", 201 | ] 202 | 203 | [[package]] 204 | name = "getopts" 205 | version = "0.2.21" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" 208 | dependencies = [ 209 | "unicode-width 0.1.11", 210 | ] 211 | 212 | [[package]] 213 | name = "getrandom" 214 | version = "0.2.12" 215 | source = "registry+https://github.com/rust-lang/crates.io-index" 216 | checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" 217 | dependencies = [ 218 | "cfg-if", 219 | "libc", 220 | "wasi", 221 | ] 222 | 223 | [[package]] 224 | name = "hashbrown" 225 | version = "0.15.2" 226 | source = "registry+https://github.com/rust-lang/crates.io-index" 227 | checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" 228 | 229 | [[package]] 230 | name = "icu_collections" 231 | version = "1.5.0" 232 | source = "registry+https://github.com/rust-lang/crates.io-index" 233 | checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" 234 | dependencies = [ 235 | "displaydoc", 236 | "yoke", 237 | "zerofrom", 238 | "zerovec", 239 | ] 240 | 241 | [[package]] 242 | name = "icu_locid" 243 | version = "1.5.0" 244 | source = "registry+https://github.com/rust-lang/crates.io-index" 245 | checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" 246 | dependencies = [ 247 | "displaydoc", 248 | "litemap", 249 | "tinystr", 250 | "writeable", 251 | "zerovec", 252 | ] 253 | 254 | [[package]] 255 | name = "icu_locid_transform" 256 | version = "1.5.0" 257 | source = "registry+https://github.com/rust-lang/crates.io-index" 258 | checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" 259 | dependencies = [ 260 | "displaydoc", 261 | "icu_locid", 262 | "icu_locid_transform_data", 263 | "icu_provider", 264 | "tinystr", 265 | "zerovec", 266 | ] 267 | 268 | [[package]] 269 | name = "icu_locid_transform_data" 270 | version = "1.5.0" 271 | source = "registry+https://github.com/rust-lang/crates.io-index" 272 | checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" 273 | 274 | [[package]] 275 | name = "icu_normalizer" 276 | version = "1.5.0" 277 | source = "registry+https://github.com/rust-lang/crates.io-index" 278 | checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" 279 | dependencies = [ 280 | "displaydoc", 281 | "icu_collections", 282 | "icu_normalizer_data", 283 | "icu_properties", 284 | "icu_provider", 285 | "smallvec", 286 | "utf16_iter", 287 | "utf8_iter", 288 | "write16", 289 | "zerovec", 290 | ] 291 | 292 | [[package]] 293 | name = "icu_normalizer_data" 294 | version = "1.5.0" 295 | source = "registry+https://github.com/rust-lang/crates.io-index" 296 | checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" 297 | 298 | [[package]] 299 | name = "icu_properties" 300 | version = "1.5.1" 301 | source = "registry+https://github.com/rust-lang/crates.io-index" 302 | checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" 303 | dependencies = [ 304 | "displaydoc", 305 | "icu_collections", 306 | "icu_locid_transform", 307 | "icu_properties_data", 308 | "icu_provider", 309 | "tinystr", 310 | "zerovec", 311 | ] 312 | 313 | [[package]] 314 | name = "icu_properties_data" 315 | version = "1.5.0" 316 | source = "registry+https://github.com/rust-lang/crates.io-index" 317 | checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" 318 | 319 | [[package]] 320 | name = "icu_provider" 321 | version = "1.5.0" 322 | source = "registry+https://github.com/rust-lang/crates.io-index" 323 | checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" 324 | dependencies = [ 325 | "displaydoc", 326 | "icu_locid", 327 | "icu_provider_macros", 328 | "stable_deref_trait", 329 | "tinystr", 330 | "writeable", 331 | "yoke", 332 | "zerofrom", 333 | "zerovec", 334 | ] 335 | 336 | [[package]] 337 | name = "icu_provider_macros" 338 | version = "1.5.0" 339 | source = "registry+https://github.com/rust-lang/crates.io-index" 340 | checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" 341 | dependencies = [ 342 | "proc-macro2", 343 | "quote", 344 | "syn", 345 | ] 346 | 347 | [[package]] 348 | name = "idna" 349 | version = "1.0.3" 350 | source = "registry+https://github.com/rust-lang/crates.io-index" 351 | checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" 352 | dependencies = [ 353 | "idna_adapter", 354 | "smallvec", 355 | "utf8_iter", 356 | ] 357 | 358 | [[package]] 359 | name = "idna_adapter" 360 | version = "1.2.0" 361 | source = "registry+https://github.com/rust-lang/crates.io-index" 362 | checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" 363 | dependencies = [ 364 | "icu_normalizer", 365 | "icu_properties", 366 | ] 367 | 368 | [[package]] 369 | name = "indexmap" 370 | version = "2.7.1" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" 373 | dependencies = [ 374 | "equivalent", 375 | "hashbrown", 376 | ] 377 | 378 | [[package]] 379 | name = "indicatif" 380 | version = "0.17.11" 381 | source = "registry+https://github.com/rust-lang/crates.io-index" 382 | checksum = "183b3088984b400f4cfac3620d5e076c84da5364016b4f49473de574b2586235" 383 | dependencies = [ 384 | "console", 385 | "number_prefix", 386 | "portable-atomic", 387 | "unicode-width 0.2.0", 388 | "web-time", 389 | ] 390 | 391 | [[package]] 392 | name = "itoa" 393 | version = "1.0.11" 394 | source = "registry+https://github.com/rust-lang/crates.io-index" 395 | checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" 396 | 397 | [[package]] 398 | name = "js-sys" 399 | version = "0.3.72" 400 | source = "registry+https://github.com/rust-lang/crates.io-index" 401 | checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" 402 | dependencies = [ 403 | "wasm-bindgen", 404 | ] 405 | 406 | [[package]] 407 | name = "lazy_static" 408 | version = "1.4.0" 409 | source = "registry+https://github.com/rust-lang/crates.io-index" 410 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 411 | 412 | [[package]] 413 | name = "libc" 414 | version = "0.2.170" 415 | source = "registry+https://github.com/rust-lang/crates.io-index" 416 | checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" 417 | 418 | [[package]] 419 | name = "libredox" 420 | version = "0.0.1" 421 | source = "registry+https://github.com/rust-lang/crates.io-index" 422 | checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" 423 | dependencies = [ 424 | "bitflags 2.5.0", 425 | "libc", 426 | "redox_syscall", 427 | ] 428 | 429 | [[package]] 430 | name = "linux-raw-sys" 431 | version = "0.4.13" 432 | source = "registry+https://github.com/rust-lang/crates.io-index" 433 | checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" 434 | 435 | [[package]] 436 | name = "litemap" 437 | version = "0.7.3" 438 | source = "registry+https://github.com/rust-lang/crates.io-index" 439 | checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" 440 | 441 | [[package]] 442 | name = "log" 443 | version = "0.4.21" 444 | source = "registry+https://github.com/rust-lang/crates.io-index" 445 | checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" 446 | 447 | [[package]] 448 | name = "memchr" 449 | version = "2.7.1" 450 | source = "registry+https://github.com/rust-lang/crates.io-index" 451 | checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" 452 | 453 | [[package]] 454 | name = "mime" 455 | version = "0.3.17" 456 | source = "registry+https://github.com/rust-lang/crates.io-index" 457 | checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 458 | 459 | [[package]] 460 | name = "mime_guess" 461 | version = "2.0.4" 462 | source = "registry+https://github.com/rust-lang/crates.io-index" 463 | checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" 464 | dependencies = [ 465 | "mime", 466 | "unicase", 467 | ] 468 | 469 | [[package]] 470 | name = "miniz_oxide" 471 | version = "0.7.2" 472 | source = "registry+https://github.com/rust-lang/crates.io-index" 473 | checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" 474 | dependencies = [ 475 | "adler", 476 | ] 477 | 478 | [[package]] 479 | name = "multipart" 480 | version = "0.18.0" 481 | source = "registry+https://github.com/rust-lang/crates.io-index" 482 | checksum = "00dec633863867f29cb39df64a397cdf4a6354708ddd7759f70c7fb51c5f9182" 483 | dependencies = [ 484 | "log", 485 | "mime", 486 | "mime_guess", 487 | "rand", 488 | "tempfile", 489 | ] 490 | 491 | [[package]] 492 | name = "number_prefix" 493 | version = "0.4.0" 494 | source = "registry+https://github.com/rust-lang/crates.io-index" 495 | checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" 496 | 497 | [[package]] 498 | name = "once_cell" 499 | version = "1.19.0" 500 | source = "registry+https://github.com/rust-lang/crates.io-index" 501 | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 502 | 503 | [[package]] 504 | name = "openssl-probe" 505 | version = "0.1.5" 506 | source = "registry+https://github.com/rust-lang/crates.io-index" 507 | checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" 508 | 509 | [[package]] 510 | name = "option-ext" 511 | version = "0.2.0" 512 | source = "registry+https://github.com/rust-lang/crates.io-index" 513 | checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" 514 | 515 | [[package]] 516 | name = "percent-encoding" 517 | version = "2.3.1" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 520 | 521 | [[package]] 522 | name = "portable-atomic" 523 | version = "1.6.0" 524 | source = "registry+https://github.com/rust-lang/crates.io-index" 525 | checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" 526 | 527 | [[package]] 528 | name = "ppv-lite86" 529 | version = "0.2.17" 530 | source = "registry+https://github.com/rust-lang/crates.io-index" 531 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 532 | 533 | [[package]] 534 | name = "proc-macro2" 535 | version = "1.0.88" 536 | source = "registry+https://github.com/rust-lang/crates.io-index" 537 | checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" 538 | dependencies = [ 539 | "unicode-ident", 540 | ] 541 | 542 | [[package]] 543 | name = "quote" 544 | version = "1.0.35" 545 | source = "registry+https://github.com/rust-lang/crates.io-index" 546 | checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" 547 | dependencies = [ 548 | "proc-macro2", 549 | ] 550 | 551 | [[package]] 552 | name = "rand" 553 | version = "0.8.5" 554 | source = "registry+https://github.com/rust-lang/crates.io-index" 555 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 556 | dependencies = [ 557 | "libc", 558 | "rand_chacha", 559 | "rand_core", 560 | ] 561 | 562 | [[package]] 563 | name = "rand_chacha" 564 | version = "0.3.1" 565 | source = "registry+https://github.com/rust-lang/crates.io-index" 566 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 567 | dependencies = [ 568 | "ppv-lite86", 569 | "rand_core", 570 | ] 571 | 572 | [[package]] 573 | name = "rand_core" 574 | version = "0.6.4" 575 | source = "registry+https://github.com/rust-lang/crates.io-index" 576 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 577 | dependencies = [ 578 | "getrandom", 579 | ] 580 | 581 | [[package]] 582 | name = "redox_syscall" 583 | version = "0.4.1" 584 | source = "registry+https://github.com/rust-lang/crates.io-index" 585 | checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" 586 | dependencies = [ 587 | "bitflags 1.3.2", 588 | ] 589 | 590 | [[package]] 591 | name = "redox_users" 592 | version = "0.4.4" 593 | source = "registry+https://github.com/rust-lang/crates.io-index" 594 | checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" 595 | dependencies = [ 596 | "getrandom", 597 | "libredox", 598 | "thiserror 1.0.68", 599 | ] 600 | 601 | [[package]] 602 | name = "ring" 603 | version = "0.17.14" 604 | source = "registry+https://github.com/rust-lang/crates.io-index" 605 | checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" 606 | dependencies = [ 607 | "cc", 608 | "cfg-if", 609 | "getrandom", 610 | "libc", 611 | "untrusted", 612 | "windows-sys 0.52.0", 613 | ] 614 | 615 | [[package]] 616 | name = "rustix" 617 | version = "0.38.32" 618 | source = "registry+https://github.com/rust-lang/crates.io-index" 619 | checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" 620 | dependencies = [ 621 | "bitflags 2.5.0", 622 | "errno", 623 | "libc", 624 | "linux-raw-sys", 625 | "windows-sys 0.52.0", 626 | ] 627 | 628 | [[package]] 629 | name = "rustls" 630 | version = "0.23.19" 631 | source = "registry+https://github.com/rust-lang/crates.io-index" 632 | checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1" 633 | dependencies = [ 634 | "log", 635 | "once_cell", 636 | "ring", 637 | "rustls-pki-types", 638 | "rustls-webpki", 639 | "subtle", 640 | "zeroize", 641 | ] 642 | 643 | [[package]] 644 | name = "rustls-native-certs" 645 | version = "0.7.0" 646 | source = "registry+https://github.com/rust-lang/crates.io-index" 647 | checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792" 648 | dependencies = [ 649 | "openssl-probe", 650 | "rustls-pemfile", 651 | "rustls-pki-types", 652 | "schannel", 653 | "security-framework", 654 | ] 655 | 656 | [[package]] 657 | name = "rustls-pemfile" 658 | version = "2.1.1" 659 | source = "registry+https://github.com/rust-lang/crates.io-index" 660 | checksum = "f48172685e6ff52a556baa527774f61fcaa884f59daf3375c62a3f1cd2549dab" 661 | dependencies = [ 662 | "base64 0.21.7", 663 | "rustls-pki-types", 664 | ] 665 | 666 | [[package]] 667 | name = "rustls-pki-types" 668 | version = "1.10.0" 669 | source = "registry+https://github.com/rust-lang/crates.io-index" 670 | checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" 671 | 672 | [[package]] 673 | name = "rustls-webpki" 674 | version = "0.102.8" 675 | source = "registry+https://github.com/rust-lang/crates.io-index" 676 | checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" 677 | dependencies = [ 678 | "ring", 679 | "rustls-pki-types", 680 | "untrusted", 681 | ] 682 | 683 | [[package]] 684 | name = "rustypaste-cli" 685 | version = "0.9.4" 686 | dependencies = [ 687 | "colored", 688 | "dirs-next", 689 | "getopts", 690 | "indicatif", 691 | "multipart", 692 | "serde", 693 | "shellexpand", 694 | "thiserror 2.0.12", 695 | "toml", 696 | "ureq", 697 | "url", 698 | ] 699 | 700 | [[package]] 701 | name = "ryu" 702 | version = "1.0.17" 703 | source = "registry+https://github.com/rust-lang/crates.io-index" 704 | checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" 705 | 706 | [[package]] 707 | name = "schannel" 708 | version = "0.1.23" 709 | source = "registry+https://github.com/rust-lang/crates.io-index" 710 | checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" 711 | dependencies = [ 712 | "windows-sys 0.52.0", 713 | ] 714 | 715 | [[package]] 716 | name = "security-framework" 717 | version = "2.9.2" 718 | source = "registry+https://github.com/rust-lang/crates.io-index" 719 | checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" 720 | dependencies = [ 721 | "bitflags 1.3.2", 722 | "core-foundation", 723 | "core-foundation-sys", 724 | "libc", 725 | "security-framework-sys", 726 | ] 727 | 728 | [[package]] 729 | name = "security-framework-sys" 730 | version = "2.9.1" 731 | source = "registry+https://github.com/rust-lang/crates.io-index" 732 | checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" 733 | dependencies = [ 734 | "core-foundation-sys", 735 | "libc", 736 | ] 737 | 738 | [[package]] 739 | name = "serde" 740 | version = "1.0.219" 741 | source = "registry+https://github.com/rust-lang/crates.io-index" 742 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 743 | dependencies = [ 744 | "serde_derive", 745 | ] 746 | 747 | [[package]] 748 | name = "serde_derive" 749 | version = "1.0.219" 750 | source = "registry+https://github.com/rust-lang/crates.io-index" 751 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 752 | dependencies = [ 753 | "proc-macro2", 754 | "quote", 755 | "syn", 756 | ] 757 | 758 | [[package]] 759 | name = "serde_json" 760 | version = "1.0.115" 761 | source = "registry+https://github.com/rust-lang/crates.io-index" 762 | checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" 763 | dependencies = [ 764 | "itoa", 765 | "ryu", 766 | "serde", 767 | ] 768 | 769 | [[package]] 770 | name = "serde_spanned" 771 | version = "0.6.8" 772 | source = "registry+https://github.com/rust-lang/crates.io-index" 773 | checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" 774 | dependencies = [ 775 | "serde", 776 | ] 777 | 778 | [[package]] 779 | name = "shellexpand" 780 | version = "3.1.1" 781 | source = "registry+https://github.com/rust-lang/crates.io-index" 782 | checksum = "8b1fdf65dd6331831494dd616b30351c38e96e45921a27745cf98490458b90bb" 783 | dependencies = [ 784 | "dirs", 785 | ] 786 | 787 | [[package]] 788 | name = "shlex" 789 | version = "1.3.0" 790 | source = "registry+https://github.com/rust-lang/crates.io-index" 791 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 792 | 793 | [[package]] 794 | name = "smallvec" 795 | version = "1.13.2" 796 | source = "registry+https://github.com/rust-lang/crates.io-index" 797 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 798 | 799 | [[package]] 800 | name = "stable_deref_trait" 801 | version = "1.2.0" 802 | source = "registry+https://github.com/rust-lang/crates.io-index" 803 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 804 | 805 | [[package]] 806 | name = "subtle" 807 | version = "2.5.0" 808 | source = "registry+https://github.com/rust-lang/crates.io-index" 809 | checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" 810 | 811 | [[package]] 812 | name = "syn" 813 | version = "2.0.87" 814 | source = "registry+https://github.com/rust-lang/crates.io-index" 815 | checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" 816 | dependencies = [ 817 | "proc-macro2", 818 | "quote", 819 | "unicode-ident", 820 | ] 821 | 822 | [[package]] 823 | name = "synstructure" 824 | version = "0.13.1" 825 | source = "registry+https://github.com/rust-lang/crates.io-index" 826 | checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" 827 | dependencies = [ 828 | "proc-macro2", 829 | "quote", 830 | "syn", 831 | ] 832 | 833 | [[package]] 834 | name = "tempfile" 835 | version = "3.10.1" 836 | source = "registry+https://github.com/rust-lang/crates.io-index" 837 | checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" 838 | dependencies = [ 839 | "cfg-if", 840 | "fastrand", 841 | "rustix", 842 | "windows-sys 0.52.0", 843 | ] 844 | 845 | [[package]] 846 | name = "thiserror" 847 | version = "1.0.68" 848 | source = "registry+https://github.com/rust-lang/crates.io-index" 849 | checksum = "02dd99dc800bbb97186339685293e1cc5d9df1f8fae2d0aecd9ff1c77efea892" 850 | dependencies = [ 851 | "thiserror-impl 1.0.68", 852 | ] 853 | 854 | [[package]] 855 | name = "thiserror" 856 | version = "2.0.12" 857 | source = "registry+https://github.com/rust-lang/crates.io-index" 858 | checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" 859 | dependencies = [ 860 | "thiserror-impl 2.0.12", 861 | ] 862 | 863 | [[package]] 864 | name = "thiserror-impl" 865 | version = "1.0.68" 866 | source = "registry+https://github.com/rust-lang/crates.io-index" 867 | checksum = "a7c61ec9a6f64d2793d8a45faba21efbe3ced62a886d44c36a009b2b519b4c7e" 868 | dependencies = [ 869 | "proc-macro2", 870 | "quote", 871 | "syn", 872 | ] 873 | 874 | [[package]] 875 | name = "thiserror-impl" 876 | version = "2.0.12" 877 | source = "registry+https://github.com/rust-lang/crates.io-index" 878 | checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" 879 | dependencies = [ 880 | "proc-macro2", 881 | "quote", 882 | "syn", 883 | ] 884 | 885 | [[package]] 886 | name = "tinystr" 887 | version = "0.7.6" 888 | source = "registry+https://github.com/rust-lang/crates.io-index" 889 | checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" 890 | dependencies = [ 891 | "displaydoc", 892 | "zerovec", 893 | ] 894 | 895 | [[package]] 896 | name = "toml" 897 | version = "0.8.22" 898 | source = "registry+https://github.com/rust-lang/crates.io-index" 899 | checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" 900 | dependencies = [ 901 | "serde", 902 | "serde_spanned", 903 | "toml_datetime", 904 | "toml_edit", 905 | ] 906 | 907 | [[package]] 908 | name = "toml_datetime" 909 | version = "0.6.9" 910 | source = "registry+https://github.com/rust-lang/crates.io-index" 911 | checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" 912 | dependencies = [ 913 | "serde", 914 | ] 915 | 916 | [[package]] 917 | name = "toml_edit" 918 | version = "0.22.26" 919 | source = "registry+https://github.com/rust-lang/crates.io-index" 920 | checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" 921 | dependencies = [ 922 | "indexmap", 923 | "serde", 924 | "serde_spanned", 925 | "toml_datetime", 926 | "toml_write", 927 | "winnow", 928 | ] 929 | 930 | [[package]] 931 | name = "toml_write" 932 | version = "0.1.1" 933 | source = "registry+https://github.com/rust-lang/crates.io-index" 934 | checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" 935 | 936 | [[package]] 937 | name = "unicase" 938 | version = "2.7.0" 939 | source = "registry+https://github.com/rust-lang/crates.io-index" 940 | checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" 941 | dependencies = [ 942 | "version_check", 943 | ] 944 | 945 | [[package]] 946 | name = "unicode-ident" 947 | version = "1.0.12" 948 | source = "registry+https://github.com/rust-lang/crates.io-index" 949 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 950 | 951 | [[package]] 952 | name = "unicode-width" 953 | version = "0.1.11" 954 | source = "registry+https://github.com/rust-lang/crates.io-index" 955 | checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" 956 | 957 | [[package]] 958 | name = "unicode-width" 959 | version = "0.2.0" 960 | source = "registry+https://github.com/rust-lang/crates.io-index" 961 | checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" 962 | 963 | [[package]] 964 | name = "untrusted" 965 | version = "0.9.0" 966 | source = "registry+https://github.com/rust-lang/crates.io-index" 967 | checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" 968 | 969 | [[package]] 970 | name = "ureq" 971 | version = "2.12.1" 972 | source = "registry+https://github.com/rust-lang/crates.io-index" 973 | checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d" 974 | dependencies = [ 975 | "base64 0.22.1", 976 | "flate2", 977 | "log", 978 | "once_cell", 979 | "rustls", 980 | "rustls-native-certs", 981 | "rustls-pki-types", 982 | "serde", 983 | "serde_json", 984 | "url", 985 | "webpki-roots", 986 | ] 987 | 988 | [[package]] 989 | name = "url" 990 | version = "2.5.4" 991 | source = "registry+https://github.com/rust-lang/crates.io-index" 992 | checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" 993 | dependencies = [ 994 | "form_urlencoded", 995 | "idna", 996 | "percent-encoding", 997 | ] 998 | 999 | [[package]] 1000 | name = "utf16_iter" 1001 | version = "1.0.5" 1002 | source = "registry+https://github.com/rust-lang/crates.io-index" 1003 | checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" 1004 | 1005 | [[package]] 1006 | name = "utf8_iter" 1007 | version = "1.0.4" 1008 | source = "registry+https://github.com/rust-lang/crates.io-index" 1009 | checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" 1010 | 1011 | [[package]] 1012 | name = "version_check" 1013 | version = "0.9.4" 1014 | source = "registry+https://github.com/rust-lang/crates.io-index" 1015 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 1016 | 1017 | [[package]] 1018 | name = "wasi" 1019 | version = "0.11.0+wasi-snapshot-preview1" 1020 | source = "registry+https://github.com/rust-lang/crates.io-index" 1021 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1022 | 1023 | [[package]] 1024 | name = "wasm-bindgen" 1025 | version = "0.2.95" 1026 | source = "registry+https://github.com/rust-lang/crates.io-index" 1027 | checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" 1028 | dependencies = [ 1029 | "cfg-if", 1030 | "once_cell", 1031 | "wasm-bindgen-macro", 1032 | ] 1033 | 1034 | [[package]] 1035 | name = "wasm-bindgen-backend" 1036 | version = "0.2.95" 1037 | source = "registry+https://github.com/rust-lang/crates.io-index" 1038 | checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" 1039 | dependencies = [ 1040 | "bumpalo", 1041 | "log", 1042 | "once_cell", 1043 | "proc-macro2", 1044 | "quote", 1045 | "syn", 1046 | "wasm-bindgen-shared", 1047 | ] 1048 | 1049 | [[package]] 1050 | name = "wasm-bindgen-macro" 1051 | version = "0.2.95" 1052 | source = "registry+https://github.com/rust-lang/crates.io-index" 1053 | checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" 1054 | dependencies = [ 1055 | "quote", 1056 | "wasm-bindgen-macro-support", 1057 | ] 1058 | 1059 | [[package]] 1060 | name = "wasm-bindgen-macro-support" 1061 | version = "0.2.95" 1062 | source = "registry+https://github.com/rust-lang/crates.io-index" 1063 | checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" 1064 | dependencies = [ 1065 | "proc-macro2", 1066 | "quote", 1067 | "syn", 1068 | "wasm-bindgen-backend", 1069 | "wasm-bindgen-shared", 1070 | ] 1071 | 1072 | [[package]] 1073 | name = "wasm-bindgen-shared" 1074 | version = "0.2.95" 1075 | source = "registry+https://github.com/rust-lang/crates.io-index" 1076 | checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" 1077 | 1078 | [[package]] 1079 | name = "web-time" 1080 | version = "1.1.0" 1081 | source = "registry+https://github.com/rust-lang/crates.io-index" 1082 | checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" 1083 | dependencies = [ 1084 | "js-sys", 1085 | "wasm-bindgen", 1086 | ] 1087 | 1088 | [[package]] 1089 | name = "webpki-roots" 1090 | version = "0.26.1" 1091 | source = "registry+https://github.com/rust-lang/crates.io-index" 1092 | checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009" 1093 | dependencies = [ 1094 | "rustls-pki-types", 1095 | ] 1096 | 1097 | [[package]] 1098 | name = "winapi" 1099 | version = "0.3.9" 1100 | source = "registry+https://github.com/rust-lang/crates.io-index" 1101 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1102 | dependencies = [ 1103 | "winapi-i686-pc-windows-gnu", 1104 | "winapi-x86_64-pc-windows-gnu", 1105 | ] 1106 | 1107 | [[package]] 1108 | name = "winapi-i686-pc-windows-gnu" 1109 | version = "0.4.0" 1110 | source = "registry+https://github.com/rust-lang/crates.io-index" 1111 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1112 | 1113 | [[package]] 1114 | name = "winapi-x86_64-pc-windows-gnu" 1115 | version = "0.4.0" 1116 | source = "registry+https://github.com/rust-lang/crates.io-index" 1117 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1118 | 1119 | [[package]] 1120 | name = "windows-sys" 1121 | version = "0.48.0" 1122 | source = "registry+https://github.com/rust-lang/crates.io-index" 1123 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 1124 | dependencies = [ 1125 | "windows-targets 0.48.5", 1126 | ] 1127 | 1128 | [[package]] 1129 | name = "windows-sys" 1130 | version = "0.52.0" 1131 | source = "registry+https://github.com/rust-lang/crates.io-index" 1132 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 1133 | dependencies = [ 1134 | "windows-targets 0.52.4", 1135 | ] 1136 | 1137 | [[package]] 1138 | name = "windows-targets" 1139 | version = "0.48.5" 1140 | source = "registry+https://github.com/rust-lang/crates.io-index" 1141 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 1142 | dependencies = [ 1143 | "windows_aarch64_gnullvm 0.48.5", 1144 | "windows_aarch64_msvc 0.48.5", 1145 | "windows_i686_gnu 0.48.5", 1146 | "windows_i686_msvc 0.48.5", 1147 | "windows_x86_64_gnu 0.48.5", 1148 | "windows_x86_64_gnullvm 0.48.5", 1149 | "windows_x86_64_msvc 0.48.5", 1150 | ] 1151 | 1152 | [[package]] 1153 | name = "windows-targets" 1154 | version = "0.52.4" 1155 | source = "registry+https://github.com/rust-lang/crates.io-index" 1156 | checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" 1157 | dependencies = [ 1158 | "windows_aarch64_gnullvm 0.52.4", 1159 | "windows_aarch64_msvc 0.52.4", 1160 | "windows_i686_gnu 0.52.4", 1161 | "windows_i686_msvc 0.52.4", 1162 | "windows_x86_64_gnu 0.52.4", 1163 | "windows_x86_64_gnullvm 0.52.4", 1164 | "windows_x86_64_msvc 0.52.4", 1165 | ] 1166 | 1167 | [[package]] 1168 | name = "windows_aarch64_gnullvm" 1169 | version = "0.48.5" 1170 | source = "registry+https://github.com/rust-lang/crates.io-index" 1171 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 1172 | 1173 | [[package]] 1174 | name = "windows_aarch64_gnullvm" 1175 | version = "0.52.4" 1176 | source = "registry+https://github.com/rust-lang/crates.io-index" 1177 | checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" 1178 | 1179 | [[package]] 1180 | name = "windows_aarch64_msvc" 1181 | version = "0.48.5" 1182 | source = "registry+https://github.com/rust-lang/crates.io-index" 1183 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 1184 | 1185 | [[package]] 1186 | name = "windows_aarch64_msvc" 1187 | version = "0.52.4" 1188 | source = "registry+https://github.com/rust-lang/crates.io-index" 1189 | checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" 1190 | 1191 | [[package]] 1192 | name = "windows_i686_gnu" 1193 | version = "0.48.5" 1194 | source = "registry+https://github.com/rust-lang/crates.io-index" 1195 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 1196 | 1197 | [[package]] 1198 | name = "windows_i686_gnu" 1199 | version = "0.52.4" 1200 | source = "registry+https://github.com/rust-lang/crates.io-index" 1201 | checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" 1202 | 1203 | [[package]] 1204 | name = "windows_i686_msvc" 1205 | version = "0.48.5" 1206 | source = "registry+https://github.com/rust-lang/crates.io-index" 1207 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 1208 | 1209 | [[package]] 1210 | name = "windows_i686_msvc" 1211 | version = "0.52.4" 1212 | source = "registry+https://github.com/rust-lang/crates.io-index" 1213 | checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" 1214 | 1215 | [[package]] 1216 | name = "windows_x86_64_gnu" 1217 | version = "0.48.5" 1218 | source = "registry+https://github.com/rust-lang/crates.io-index" 1219 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 1220 | 1221 | [[package]] 1222 | name = "windows_x86_64_gnu" 1223 | version = "0.52.4" 1224 | source = "registry+https://github.com/rust-lang/crates.io-index" 1225 | checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" 1226 | 1227 | [[package]] 1228 | name = "windows_x86_64_gnullvm" 1229 | version = "0.48.5" 1230 | source = "registry+https://github.com/rust-lang/crates.io-index" 1231 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 1232 | 1233 | [[package]] 1234 | name = "windows_x86_64_gnullvm" 1235 | version = "0.52.4" 1236 | source = "registry+https://github.com/rust-lang/crates.io-index" 1237 | checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" 1238 | 1239 | [[package]] 1240 | name = "windows_x86_64_msvc" 1241 | version = "0.48.5" 1242 | source = "registry+https://github.com/rust-lang/crates.io-index" 1243 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 1244 | 1245 | [[package]] 1246 | name = "windows_x86_64_msvc" 1247 | version = "0.52.4" 1248 | source = "registry+https://github.com/rust-lang/crates.io-index" 1249 | checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" 1250 | 1251 | [[package]] 1252 | name = "winnow" 1253 | version = "0.7.7" 1254 | source = "registry+https://github.com/rust-lang/crates.io-index" 1255 | checksum = "6cb8234a863ea0e8cd7284fcdd4f145233eb00fee02bbdd9861aec44e6477bc5" 1256 | dependencies = [ 1257 | "memchr", 1258 | ] 1259 | 1260 | [[package]] 1261 | name = "write16" 1262 | version = "1.0.0" 1263 | source = "registry+https://github.com/rust-lang/crates.io-index" 1264 | checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" 1265 | 1266 | [[package]] 1267 | name = "writeable" 1268 | version = "0.5.5" 1269 | source = "registry+https://github.com/rust-lang/crates.io-index" 1270 | checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" 1271 | 1272 | [[package]] 1273 | name = "yoke" 1274 | version = "0.7.4" 1275 | source = "registry+https://github.com/rust-lang/crates.io-index" 1276 | checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" 1277 | dependencies = [ 1278 | "serde", 1279 | "stable_deref_trait", 1280 | "yoke-derive", 1281 | "zerofrom", 1282 | ] 1283 | 1284 | [[package]] 1285 | name = "yoke-derive" 1286 | version = "0.7.4" 1287 | source = "registry+https://github.com/rust-lang/crates.io-index" 1288 | checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" 1289 | dependencies = [ 1290 | "proc-macro2", 1291 | "quote", 1292 | "syn", 1293 | "synstructure", 1294 | ] 1295 | 1296 | [[package]] 1297 | name = "zerofrom" 1298 | version = "0.1.4" 1299 | source = "registry+https://github.com/rust-lang/crates.io-index" 1300 | checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" 1301 | dependencies = [ 1302 | "zerofrom-derive", 1303 | ] 1304 | 1305 | [[package]] 1306 | name = "zerofrom-derive" 1307 | version = "0.1.4" 1308 | source = "registry+https://github.com/rust-lang/crates.io-index" 1309 | checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" 1310 | dependencies = [ 1311 | "proc-macro2", 1312 | "quote", 1313 | "syn", 1314 | "synstructure", 1315 | ] 1316 | 1317 | [[package]] 1318 | name = "zeroize" 1319 | version = "1.7.0" 1320 | source = "registry+https://github.com/rust-lang/crates.io-index" 1321 | checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" 1322 | 1323 | [[package]] 1324 | name = "zerovec" 1325 | version = "0.10.4" 1326 | source = "registry+https://github.com/rust-lang/crates.io-index" 1327 | checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" 1328 | dependencies = [ 1329 | "yoke", 1330 | "zerofrom", 1331 | "zerovec-derive", 1332 | ] 1333 | 1334 | [[package]] 1335 | name = "zerovec-derive" 1336 | version = "0.10.3" 1337 | source = "registry+https://github.com/rust-lang/crates.io-index" 1338 | checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" 1339 | dependencies = [ 1340 | "proc-macro2", 1341 | "quote", 1342 | "syn", 1343 | ] 1344 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rustypaste-cli" 3 | version = "0.9.4" 4 | edition = "2021" 5 | description = "A CLI tool for rustypaste" 6 | authors = ["Orhun Parmaksız "] 7 | license = "MIT" 8 | readme = "README.md" 9 | homepage = "https://github.com/orhun/rustypaste" 10 | repository = "https://github.com/orhun/rustypaste-cli" 11 | keywords = ["paste", "pastebin", "upload"] 12 | categories = ["command-line-utilities"] 13 | default-run = "rpaste" 14 | 15 | [[bin]] 16 | name = "rpaste" 17 | path = "src/main.rs" 18 | 19 | [features] 20 | default = [] 21 | use-native-certs = ["ureq/native-certs"] 22 | 23 | [dependencies] 24 | serde = { version = "1.0.219", default-features = false, features = ["derive"] } 25 | toml = "0.8.22" 26 | dirs-next = "2.0.0" 27 | thiserror = "2.0.12" 28 | getopts = "0.2.21" 29 | ureq = { version = "2.12.1", features = ["json"] } 30 | multipart = { version = "0.18.0", default-features = false, features = [ 31 | "client", 32 | ] } 33 | colored = "3.0.0" 34 | url = "2.5.4" 35 | indicatif = "0.17.11" 36 | shellexpand = "3.1.1" 37 | 38 | [profile.release] 39 | opt-level = 3 40 | debug = false 41 | panic = "unwind" 42 | lto = true 43 | codegen-units = 1 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021-2024 Orhun Parmaksız 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | A CLI tool for [**rustypaste**](https://github.com/orhun/rustypaste). 4 | 5 | ![demo](img/demo.gif) 6 | 7 | ## Installation 8 | 9 | ### crates.io 10 | 11 | ```sh 12 | cargo install rustypaste-cli 13 | ``` 14 | 15 | #### Features 16 | 17 | - `use-native-certs`: makes the default TLS implementation use the OS' trust store. [\*](https://github.com/algesten/ureq#https--tls--ssl) (disabled) 18 | 19 | To enable crate features, use the `--features` flag as follows: 20 | 21 | ```sh 22 | cargo install rustypaste-cli --features use-native-certs 23 | ``` 24 | 25 | ### Arch Linux 26 | 27 | ``` 28 | pacman -S rustypaste-cli 29 | ``` 30 | 31 | ### Alpine Linux 32 | 33 | `rustypaste-cli` is available for [Alpine Edge](https://pkgs.alpinelinux.org/packages?name=rustypaste-cli&branch=edge). It can be installed via [apk](https://wiki.alpinelinux.org/wiki/Alpine_Package_Keeper) after enabling the [testing repository](https://wiki.alpinelinux.org/wiki/Repositories). 34 | 35 | ```sh 36 | apk add rustypaste-cli 37 | ``` 38 | 39 | ### Binary releases 40 | 41 | See the available binaries on [releases](https://github.com/orhun/rustypaste-cli/releases/) page. 42 | 43 | ### Build from source 44 | 45 | ```sh 46 | git clone https://github.com/orhun/rustypaste-cli.git 47 | cd rustypaste-cli/ 48 | cargo build --release 49 | ``` 50 | 51 | ## Usage 52 | 53 | `rpaste [options] ` 54 | 55 | ``` 56 | -h, --help prints help information 57 | -v, --version prints version information 58 | -V, --server-version retrieves the server version 59 | -l, --list lists files on the server 60 | -d, --delete delete files from server 61 | -o, --oneshot generates one shot links 62 | -p, --pretty prettifies the output 63 | -c, --config CONFIG sets the configuration file 64 | -s, --server SERVER sets the address of the rustypaste server 65 | -a, --auth TOKEN sets the authentication or delete token 66 | -u, --url URL sets the URL to shorten 67 | -r, --remote URL sets the remote URL for uploading 68 | -e, --expire TIME sets the expiration time for the link 69 | -n, --filename NAME sets and overrides the filename 70 | ``` 71 | 72 | ### Set credentials 73 | 74 | Either set the credentials on the command line (not recommended): 75 | 76 | ```sh 77 | rpaste -s "https://paste.example.com" -a "" 78 | ``` 79 | 80 | or specify them in the [configuration file](#configuration) as follows: 81 | 82 | ```toml 83 | [server] 84 | auth_token = "" 85 | delete_token = "" 86 | ``` 87 | 88 | It is also possible to use these tokens from a file: 89 | 90 | ```toml 91 | [server] 92 | auth_token_file = "~/example/auth-token" 93 | delete_token_file = "~/example/delete-token" 94 | ``` 95 | 96 | The contents should be only the token, all whitespace will be trimmed. 97 | 98 | ### Upload files 99 | 100 | ```sh 101 | rpaste awesome.txt other.txt 102 | ``` 103 | 104 | ### Upload from stdin 105 | 106 | ```sh 107 | echo "Hello World" | rpaste - 108 | ``` 109 | 110 | ### Shorten URLs 111 | 112 | ```sh 113 | rpaste -u https://example.com/some/long/url 114 | ``` 115 | 116 | ### Paste from URL 117 | 118 | ```sh 119 | rpaste -r https://example.com/file.txt 120 | ``` 121 | 122 | ### One shot 123 | 124 | ```sh 125 | rpaste -o disappear_after_seen.txt 126 | ``` 127 | 128 | ### One shot URL 129 | 130 | ```sh 131 | rpaste -ou https://example.com/some/long/url 132 | ``` 133 | 134 | ### Expiration 135 | 136 | ```sh 137 | rpaste -e 10min expires_in_10_minutes.txt 138 | ``` 139 | 140 | ```sh 141 | rpaste -e 1hour -u https://example.com/expire/1hour 142 | ``` 143 | 144 | \* Supported units: `ns`, `us`, `ms`, `sec`, `min`, `hours`, `days`, `weeks`, `months`, `years` 145 | 146 | ### List uploaded files 147 | 148 | ```sh 149 | rpaste -l 150 | ``` 151 | 152 | \* Use `-p` for table output instead of JSON. 153 | 154 | ### Delete files from server 155 | 156 | ```sh 157 | rpaste -d awesome.UA86.txt other.JSNI.txt 158 | ``` 159 | 160 | ### Override the filename 161 | 162 | ```sh 163 | rpaste -n filename-on-server.txt awesome.txt 164 | ``` 165 | 166 | \* rustypaste >=0.15.0 is required for this argument to work, otherwise the filename will not be overridden. 167 | 168 | ### Extras 169 | 170 | - Show a _prettier_ output: `rpaste -p [...]` 171 | - [Disable colors](https://no-color.org/) in the output: `NO_COLOR=1 rpaste -p [...]` 172 | 173 | ## Configuration 174 | 175 | The configuration file can be specified via `--config` argument and `RPASTE_CONFIG` environment variable or it can be placed to the following global locations: 176 | 177 | - `` `/` `rustypaste/config.toml` 178 | - `` `/` `.rustypaste/config.toml` 179 | 180 | `` depends on the platform as shown in the following table: 181 | 182 | | Platform | Value | Example | 183 | |----------|----------------------------------------------------------------------------------|-----------------------------------------------------------------------| 184 | | Linux | • $XDG_CONFIG_HOME
• $HOME/.config | /home/alice/.config | 185 | | macOS | • $XDG_CONFIG_HOME
• $HOME/.config
• $HOME/Library/Application Support | • /home/alice/.config
• /Users/Alice/Library/Application Support | 186 | | Windows | {FOLDERID_RoamingAppData} | C:\Users\Alice\AppData\Roaming | 187 | 188 | See [config.toml](./config.toml) for configuration options. 189 | 190 | ## Contributing 191 | 192 | Pull requests are welcome! 193 | 194 | #### License 195 | 196 | 197 | All code is licensed under The MIT License. 198 | 199 | -------------------------------------------------------------------------------- /cliff.toml: -------------------------------------------------------------------------------- 1 | # git-cliff ~ configuration file 2 | # https://git-cliff.org/docs/configuration 3 | 4 | [changelog] 5 | # template for the changelog header 6 | header = """ 7 | # Changelog\n 8 | All notable changes to this project will be documented in this file. 9 | 10 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 11 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n 12 | """ 13 | # template for the changelog body 14 | # https://keats.github.io/tera/docs/#introduction 15 | body = """ 16 | {%- macro remote_url() -%} 17 | https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }} 18 | {%- endmacro -%} 19 | 20 | {% if version -%} 21 | ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }} 22 | {% else -%} 23 | ## [Unreleased] 24 | {% endif -%} 25 | 26 | {% for group, commits in commits | group_by(attribute="group") %} 27 | ### {{ group | upper_first }} 28 | {%- for commit in commits %} 29 | - {{ commit.message | split(pat="\n") | first | upper_first | trim }}\ 30 | {% if commit.remote.username %} by @{{ commit.remote.username }}{%- endif -%} 31 | {% if commit.remote.pr_number %} in \ 32 | [#{{ commit.remote.pr_number }}]({{ self::remote_url() }}/pull/{{ commit.remote.pr_number }}) \ 33 | {%- endif -%} 34 | {% endfor %} 35 | {% endfor %} 36 | 37 | {%- if github.contributors | filter(attribute="is_first_time", value=true) | length != 0 %} 38 | ### New Contributors 39 | {%- endif -%} 40 | {% for contributor in github.contributors | filter(attribute="is_first_time", value=true) %} 41 | - @{{ contributor.username }} made their first contribution 42 | {%- if contributor.pr_number %} in \ 43 | [#{{ contributor.pr_number }}]({{ self::remote_url() }}/pull/{{ contributor.pr_number }}) \ 44 | {%- endif %} 45 | {%- endfor %}\n\n 46 | """ 47 | # template for the changelog footer 48 | footer = """ 49 | {%- macro remote_url() -%} 50 | https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }} 51 | {%- endmacro -%} 52 | 53 | {% for release in releases -%} 54 | {% if release.version -%} 55 | {% if release.previous.version -%} 56 | [{{ release.version | trim_start_matches(pat="v") }}]: \ 57 | {{ self::remote_url() }}/compare/{{ release.previous.version }}..{{ release.version }} 58 | {% endif -%} 59 | {% else -%} 60 | [unreleased]: {{ self::remote_url() }}/compare/{{ release.previous.version }}..HEAD 61 | {% endif -%} 62 | {% endfor %} 63 | 64 | """ 65 | # remove the leading and trailing whitespace from the templates 66 | trim = true 67 | 68 | [git] 69 | # parse the commits based on https://www.conventionalcommits.org 70 | conventional_commits = true 71 | # filter out the commits that are not conventional 72 | filter_unconventional = false 73 | # regex for preprocessing the commit messages 74 | commit_preprocessors = [ 75 | # remove issue numbers from commits 76 | { pattern = '\((\w+\s)?#([0-9]+)\)', replace = "" }, 77 | ] 78 | # regex for parsing and grouping commits 79 | commit_parsers = [ 80 | { message = "^[a|A]dd", group = "Added" }, 81 | { message = "^[s|S]upport", group = "Added" }, 82 | { message = "^[r|R]emove", group = "Removed" }, 83 | { message = "^.*: add", group = "Added" }, 84 | { message = "^.*: support", group = "Added" }, 85 | { message = "^.*: remove", group = "Removed" }, 86 | { message = "^.*: delete", group = "Removed" }, 87 | { message = "^test", group = "Fixed" }, 88 | { message = "^fix", group = "Fixed" }, 89 | { message = "^.*: fix", group = "Fixed" }, 90 | { message = "^.*", group = "Changed" }, 91 | ] 92 | # filter out the commits that are not matched by commit parsers 93 | filter_commits = false 94 | # sort the tags topologically 95 | topo_order = false 96 | # sort the commits inside sections by oldest/newest order 97 | sort_commits = "newest" 98 | -------------------------------------------------------------------------------- /config.toml: -------------------------------------------------------------------------------- 1 | [server] 2 | # Server address. 3 | address = "https://paste.example.com" 4 | # Server authentication token. 5 | auth_token = "" 6 | # A file that contains an authentication token. 7 | auth_token_file = "~/example/auth-token" 8 | # Server deletion token. 9 | delete_token = "" 10 | # A file that contains a deletion token. 11 | delete_token_file = "~/example/delete-token" 12 | 13 | [paste] 14 | # Delete the paste after it has been accessed for the first time. 15 | oneshot = false 16 | # Override the server's default expiration time. 17 | #expire = "10min" 18 | 19 | [style] 20 | # Prettify the output. 21 | prettify = false 22 | -------------------------------------------------------------------------------- /img/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orhun/rustypaste-cli/20916025a0dfedd3efbf0443879ab29992f0ccc7/img/demo.gif -------------------------------------------------------------------------------- /img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orhun/rustypaste-cli/20916025a0dfedd3efbf0443879ab29992f0ccc7/img/logo.png -------------------------------------------------------------------------------- /man/rpaste.1: -------------------------------------------------------------------------------- 1 | .\" Manpage for rpaste 2 | 3 | .TH RPASTE "1" "Apr 2025" "rustypaste-cli 0.9.4" "User Commands" 4 | .SH NAME 5 | .PP 6 | rpaste \- a CLI tool for rustypaste 7 | 8 | .SH SYNOPSIS 9 | .PP 10 | .B rpaste 11 | [OPTIONS] 12 | 13 | .SH OPTIONS 14 | .TP 15 | \fB\-h\fR, \fB\-\-help\fR 16 | prints help information 17 | .TP 18 | \fB\-v\fR, \fB\-\-version\fR 19 | prints version information 20 | .TP 21 | \fB\-V\fR, \fB\-\-server\-version\fR 22 | retrieves the server version 23 | .TP 24 | \fB\-l\fR, \fB\-\-list\fR 25 | lists files on the server 26 | .TP 27 | \fB\-d\fR, \fB\-\-delete\fR 28 | delete files from server 29 | .TP 30 | \fB\-o\fR, \fB\-\-oneshot\fR 31 | generates one shot links 32 | .TP 33 | \fB\-p\fR, \fB\-\-pretty\fR 34 | prettifies the output 35 | .HP 36 | \fB\-c\fR, \fB\-\-config\fR CONFIG sets the configuration file 37 | .HP 38 | \fB\-s\fR, \fB\-\-server\fR SERVER sets the address of the rustypaste server 39 | .TP 40 | \fB\-a\fR, \fB\-\-auth\fR TOKEN 41 | sets the authentication token 42 | .TP 43 | \fB\-u\fR, \fB\-\-url\fR URL 44 | sets the URL to shorten 45 | .TP 46 | \fB\-r\fR, \fB\-\-remote\fR URL 47 | sets the remote URL for uploading 48 | .TP 49 | \fB\-e\fR, \fB\-\-expire\fR TIME 50 | sets the expiration time for the link 51 | .TP 52 | \fB\-n\fR, \fB\-\-filename\fR NAME 53 | sets and overrides the filename 54 | 55 | .SH CONFIGURATION 56 | A TOML file can be used to configure rpaste settings. 57 | .TP 58 | \fB[server]\fP 59 | .RS 60 | .IP \(bu 2 61 | \fBaddress\fP: address of the rustypaste server 62 | .IP \(bu 2 63 | \fBauth_token\fP: authentication token 64 | .RE 65 | .TP 66 | \fB[paste]\fP 67 | .RS 68 | .IP \(bu 2 69 | \fBoneshot\fP: generate one shot links if set to true 70 | .IP \(bu 2 71 | \fBexpire\fP: expiration time for the links 72 | .RE 73 | .TP 74 | \fB[style]\fP 75 | .RS 76 | .IP \(bu 2 77 | \fBprettify\fP: prettify the output if set to true 78 | 79 | .SH BUGS 80 | Report bugs at or contact the author via email. 81 | 82 | .SH ABOUT 83 | .P 84 | \f[I]rustypaste-cli\f[R] is maintained by Orhun Parmaksız , 85 | and released under the MIT license. 86 | .PP 87 | See the project homepage at for full documentation. 88 | 89 | .SH SEE ALSO 90 | .PP 91 | 92 | -------------------------------------------------------------------------------- /src/args.rs: -------------------------------------------------------------------------------- 1 | use getopts::Options; 2 | use std::env; 3 | use std::io::IsTerminal; 4 | use std::path::PathBuf; 5 | use std::process; 6 | 7 | /// Command-line arguments to parse. 8 | #[derive(Debug, Default)] 9 | pub struct Args { 10 | /// Configuration file. 11 | pub config: Option, 12 | /// Server address. 13 | pub server: Option, 14 | /// Authentication or delete token. 15 | pub auth: Option, 16 | /// URL to shorten. 17 | pub url: Option, 18 | /// Remote URL to download file. 19 | pub remote: Option, 20 | /// Files to upload. 21 | pub files: Vec, 22 | /// Whether if the file will disappear after being viewed once. 23 | pub oneshot: bool, 24 | /// Expiration time for the link. 25 | pub expire: Option, 26 | /// Prettify the program output. 27 | pub prettify: bool, 28 | /// Whether if the server version should be printed. 29 | pub print_server_version: bool, 30 | /// List files on the server (file name, file size, expiry timestamp). 31 | pub list_files: bool, 32 | /// Delete files from server. 33 | pub delete: bool, 34 | /// Send filename header (give uploaded file a specific name). 35 | pub filename: Option, 36 | } 37 | 38 | impl Args { 39 | /// Parses the command-line arguments. 40 | pub fn parse() -> Self { 41 | let mut opts = Options::new(); 42 | opts.optflag("h", "help", "prints help information"); 43 | opts.optflag("v", "version", "prints version information"); 44 | opts.optflag("V", "server-version", "retrieves the server version"); 45 | opts.optflag("l", "list", "lists files on the server"); 46 | opts.optflag("d", "delete", "delete files from server"); 47 | opts.optflag("o", "oneshot", "generates one shot links"); 48 | opts.optflag("p", "pretty", "prettifies the output"); 49 | opts.optopt("c", "config", "sets the configuration file", "CONFIG"); 50 | opts.optopt( 51 | "s", 52 | "server", 53 | "sets the address of the rustypaste server", 54 | "SERVER", 55 | ); 56 | opts.optopt( 57 | "a", 58 | "auth", 59 | "sets the authentication or delete token", 60 | "TOKEN", 61 | ); 62 | opts.optopt("u", "url", "sets the URL to shorten", "URL"); 63 | opts.optopt("r", "remote", "sets the remote URL for uploading", "URL"); 64 | opts.optopt( 65 | "e", 66 | "expire", 67 | "sets the expiration time for the link", 68 | "TIME", 69 | ); 70 | opts.optopt("n", "filename", "sets and overrides the filename", "NAME"); 71 | 72 | let env_args: Vec = env::args().collect(); 73 | let matches = match opts.parse(&env_args[1..]) { 74 | Ok(m) => m, 75 | Err(e) => { 76 | eprintln!("Argument error: `{e}`"); 77 | process::exit(1); 78 | } 79 | }; 80 | 81 | if matches.opt_present("h") 82 | || (matches.free.is_empty() 83 | && !matches.opt_present("u") 84 | && !matches.opt_present("r") 85 | && !matches.opt_present("V") 86 | && !matches.opt_present("l") 87 | && !matches.opt_present("d") 88 | && !matches.opt_present("v") 89 | && std::io::stdin().is_terminal()) 90 | { 91 | let usage = format!( 92 | "\n{} {} \u{2014} {}.\ 93 | \n\u{221F} written by {}\ 94 | \n\u{221F} licensed under MIT <{}>\ 95 | \n\nUsage:\n {} [options] ", 96 | env!("CARGO_PKG_NAME"), 97 | env!("CARGO_PKG_VERSION"), 98 | env!("CARGO_PKG_DESCRIPTION"), 99 | env!("CARGO_PKG_AUTHORS"), 100 | env!("CARGO_PKG_REPOSITORY"), 101 | "rpaste", 102 | ); 103 | println!("{}", opts.usage(&usage)); 104 | process::exit(0) 105 | } 106 | 107 | if matches.opt_present("v") { 108 | println!("{} {}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")); 109 | process::exit(0) 110 | } 111 | 112 | Args { 113 | config: env::var("RPASTE_CONFIG") 114 | .ok() 115 | .or_else(|| matches.opt_str("c")) 116 | .map(PathBuf::from), 117 | server: matches.opt_str("s"), 118 | auth: matches.opt_str("a"), 119 | url: matches.opt_str("u"), 120 | remote: matches.opt_str("r"), 121 | oneshot: matches.opt_present("o"), 122 | expire: matches.opt_str("e"), 123 | prettify: matches.opt_present("p"), 124 | print_server_version: matches.opt_present("V"), 125 | list_files: matches.opt_present("l"), 126 | delete: matches.opt_present("d"), 127 | filename: matches.opt_str("n"), 128 | files: matches.free, 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/config.rs: -------------------------------------------------------------------------------- 1 | use crate::args::Args; 2 | use serde::{Deserialize, Serialize}; 3 | use std::fs; 4 | #[cfg(target_os = "macos")] 5 | use std::{env, path::PathBuf}; 6 | 7 | /// Configuration values. 8 | #[derive(Debug, Clone, Default, Serialize, Deserialize)] 9 | pub struct Config { 10 | /// Server configuration. 11 | pub server: ServerConfig, 12 | /// Paste configuration. 13 | pub paste: PasteConfig, 14 | /// Style configuration. 15 | pub style: Option, 16 | } 17 | 18 | /// Server configuration. 19 | #[derive(Debug, Clone, Default, Serialize, Deserialize)] 20 | pub struct ServerConfig { 21 | /// Server address. 22 | pub address: String, 23 | /// Token for authentication. 24 | pub auth_token: Option, 25 | /// A file containing the token for authentication. 26 | /// 27 | /// Leading and trailing whitespace will be trimmed. 28 | pub auth_token_file: Option, 29 | /// Token for deleting files. 30 | pub delete_token: Option, 31 | /// A file containing the token for deleting files. 32 | /// 33 | /// Leading and trailing whitespace will be trimmed. 34 | pub delete_token_file: Option, 35 | } 36 | 37 | /// Paste configuration. 38 | #[derive(Debug, Clone, Default, Serialize, Deserialize)] 39 | pub struct PasteConfig { 40 | /// Whether if the file will disappear after being viewed once. 41 | pub oneshot: Option, 42 | /// Expiration time for the link. 43 | pub expire: Option, 44 | /// Filename. 45 | #[serde(skip_deserializing)] 46 | pub filename: Option, 47 | } 48 | 49 | /// Style configuration. 50 | #[derive(Debug, Clone, Default, Serialize, Deserialize)] 51 | pub struct StyleConfig { 52 | /// Whether if the output will be prettified. 53 | pub prettify: bool, 54 | } 55 | 56 | impl Config { 57 | /// Override the configuration file with arguments. 58 | pub fn update_from_args(&mut self, args: &Args) { 59 | if let Some(server_address) = &args.server { 60 | self.server.address = server_address.to_string(); 61 | } 62 | if args.auth.is_some() { 63 | self.server.auth_token = args.auth.as_ref().cloned(); 64 | if args.delete { 65 | self.server.delete_token = args.auth.as_ref().cloned(); 66 | } 67 | } 68 | if args.oneshot { 69 | self.paste.oneshot = Some(true); 70 | } 71 | if args.expire.is_some() { 72 | self.paste.expire = args.expire.as_ref().cloned(); 73 | } 74 | if args.filename.is_some() { 75 | self.paste.filename = args.filename.as_ref().cloned(); 76 | } 77 | } 78 | 79 | /// Parses the files referenced by [Config::auth_token_file] and [Config::delete_token_file]. 80 | /// 81 | /// Updates the respective token variables with the contents of the files. 82 | pub fn parse_token_files(&mut self) { 83 | if let Some(path) = &self.server.auth_token_file { 84 | let path = shellexpand::tilde(path).to_string(); 85 | match fs::read_to_string(path) { 86 | Ok(token) => self.server.auth_token = Some(token.trim().to_string()), 87 | Err(e) => eprintln!("Error while reading token file: {e}"), 88 | }; 89 | }; 90 | 91 | if let Some(path) = &self.server.delete_token_file { 92 | let path = shellexpand::tilde(path).to_string(); 93 | match fs::read_to_string(path) { 94 | Ok(token) => self.server.delete_token = Some(token.trim().to_string()), 95 | Err(e) => eprintln!("Error while reading token file: {e}"), 96 | }; 97 | }; 98 | } 99 | 100 | /// Find a special config path on macOS. 101 | /// 102 | /// The `dirs-next` crate ignores the `XDG_CONFIG_HOME` env var on macOS and only considers 103 | /// `Library/Application Support` as the config dir, which is primarily used by GUI apps. 104 | /// 105 | /// This function determines the config path and honors the `XDG_CONFIG_HOME` env var. 106 | /// If it is not set, it will fall back to `~/.config` 107 | #[cfg(target_os = "macos")] 108 | pub(crate) fn retrieve_xdg_config_on_macos(&self) -> PathBuf { 109 | let config_dir = env::var("XDG_CONFIG_HOME").map_or_else( 110 | |_| dirs_next::home_dir().unwrap_or_default().join(".config"), 111 | PathBuf::from, 112 | ); 113 | config_dir.join("rustypaste") 114 | } 115 | } 116 | 117 | #[cfg(test)] 118 | mod tests { 119 | use super::*; 120 | 121 | #[test] 122 | /// Test that the token file is being properly processed. 123 | fn test_parse_token_files_no_whitespace() { 124 | let mut cfg = Config::default(); 125 | let token = "KBRRHMxlJfFo1".to_string(); 126 | 127 | cfg.server.auth_token_file = Some("tests/token_file_parsing/token.txt".to_string()); 128 | cfg.parse_token_files(); 129 | assert_eq!(cfg.server.auth_token, Some(token)); 130 | } 131 | 132 | #[test] 133 | /// Test that whitespace is being properly trimmed. 134 | fn test_parse_token_files_whitespaced() { 135 | let mut cfg = Config::default(); 136 | let token = "nhJuLuY5vxUrO".to_string(); 137 | 138 | cfg.server.auth_token_file = 139 | Some("tests/token_file_parsing/token_whitespaced.txt".to_string()); 140 | cfg.parse_token_files(); 141 | assert_eq!(cfg.server.auth_token, Some(token)); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use thiserror::Error as ThisError; 2 | 3 | /// Custom error type. 4 | #[derive(Debug, ThisError)] 5 | pub enum Error { 6 | /// Error that might occur while handling I/O operations. 7 | #[error("IO error: `{0}`")] 8 | IoError(#[from] std::io::Error), 9 | /// Error that might occur while parsing the configuration file. 10 | #[error("TOML parsing error: `{0}`")] 11 | TomlError(#[from] toml::de::Error), 12 | /// Error that might occur while processing/sending requests. 13 | #[error("Request error: `{0}`")] 14 | RequestError(#[from] Box), 15 | /// Error that might occur while uploading files. 16 | #[error("Upload error: `{0}`")] 17 | UploadError(String), 18 | /// Error that might occur while deleting files from server. 19 | #[error("Delete error: `{0}`")] 20 | DeleteError(String), 21 | /// Error that might occur when no server address is provided. 22 | #[error("No rustypaste server address is given.")] 23 | NoServerAddressError, 24 | /// Error that might occur during the preparation of the multipart data. 25 | #[error("Multipart IO error: `{0}`")] 26 | MultipartIOError(#[from] multipart::client::lazy::LazyError<'static, std::io::Error>), 27 | /// Error that might occur during parsing URLs. 28 | #[error("URL parsing error: `{0}`")] 29 | UrlParseError(#[from] url::ParseError), 30 | /// Error that might occur during parsing a progress bar template. 31 | #[error("Template parsing error: `{0}`")] 32 | TemplateParseError(#[from] indicatif::style::TemplateError), 33 | } 34 | 35 | /// Type alias for the Result type. 36 | pub type Result = std::result::Result; 37 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A CLI tool for [`rustypaste`]. 2 | //! 3 | //! [`rustypaste`]: https://github.com/orhun/rustypaste 4 | #![warn(missing_docs, clippy::unwrap_used)] 5 | 6 | /// Command-line argument parser. 7 | pub mod args; 8 | /// Configuration file parser. 9 | pub mod config; 10 | /// Custom error implementation. 11 | pub mod error; 12 | /// Upload handler. 13 | pub mod upload; 14 | 15 | use crate::args::Args; 16 | use crate::config::Config; 17 | use crate::error::{Error, Result}; 18 | use crate::upload::Uploader; 19 | use colored::Colorize; 20 | use std::fs; 21 | use std::io::IsTerminal; 22 | use std::io::{self, Read}; 23 | 24 | /// Default name of the configuration file. 25 | const CONFIG_FILE: &str = "config.toml"; 26 | 27 | /// Runs `rpaste`. 28 | pub fn run(args: Args) -> Result<()> { 29 | let mut config = Config::default(); 30 | if let Some(ref config_path) = args.config { 31 | config = toml::from_str(&fs::read_to_string(config_path)?)? 32 | } else { 33 | for path in [ 34 | dirs_next::home_dir().map(|p| p.join(".rustypaste").join(CONFIG_FILE)), 35 | #[cfg(target_os = "macos")] 36 | Some(config.retrieve_xdg_config_on_macos().join(CONFIG_FILE)), 37 | dirs_next::config_dir().map(|p| p.join("rustypaste").join(CONFIG_FILE)), 38 | ] 39 | .iter() 40 | .filter_map(|v| v.as_ref()) 41 | { 42 | if path.exists() { 43 | config = toml::from_str(&fs::read_to_string(path)?)?; 44 | break; 45 | } 46 | } 47 | } 48 | config.parse_token_files(); 49 | config.update_from_args(&args); 50 | if config.server.address.is_empty() { 51 | return Err(Error::NoServerAddressError); 52 | } 53 | 54 | let uploader = Uploader::new(&config); 55 | if args.print_server_version { 56 | println!("rustypaste-server {}", uploader.retrieve_version()?.trim()); 57 | return Ok(()); 58 | } 59 | 60 | if args.list_files { 61 | let prettify = args.prettify 62 | || config 63 | .style 64 | .as_ref() 65 | .map(|style| style.prettify) 66 | .unwrap_or(false); 67 | uploader.retrieve_list(&mut io::stdout(), prettify)?; 68 | return Ok(()); 69 | } 70 | 71 | let mut results = Vec::new(); 72 | if let Some(ref url) = args.url { 73 | results.push(uploader.upload_url(url)); 74 | } else if let Some(ref remote_url) = args.remote { 75 | results.push(uploader.upload_remote_url(remote_url)); 76 | } else if !std::io::stdin().is_terminal() || args.files.contains(&String::from("-")) { 77 | let mut buffer = Vec::new(); 78 | let mut stdin = io::stdin(); 79 | stdin.read_to_end(&mut buffer)?; 80 | results.push(uploader.upload_stream(&*buffer)); 81 | } else { 82 | for file in args.files.iter() { 83 | if !args.delete { 84 | results.push(uploader.upload_file(file)) 85 | } else { 86 | results.push(uploader.delete_file(file)) 87 | } 88 | } 89 | } 90 | let prettify = args.prettify 91 | || config 92 | .style 93 | .as_ref() 94 | .map(|style| style.prettify) 95 | .unwrap_or(false); 96 | let format_padding = prettify 97 | .then(|| results.iter().map(|v| v.0.len()).max()) 98 | .flatten() 99 | .unwrap_or(1); 100 | for (data, result) in results.iter().map(|v| (v.0, v.1.as_ref())) { 101 | let data = if prettify { 102 | format!( 103 | "{:p$} {} ", 104 | data, 105 | if result.is_ok() { 106 | "=>".green().bold() 107 | } else { 108 | "=>".red().bold() 109 | }, 110 | p = format_padding, 111 | ) 112 | } else { 113 | String::new() 114 | }; 115 | match result { 116 | Ok(url) => println!("{}{}", data, url.trim()), 117 | Err(e) => eprintln!("{data}{e}"), 118 | } 119 | } 120 | 121 | Ok(()) 122 | } 123 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use rustypaste_cli::args::Args; 2 | use std::process; 3 | 4 | pub fn main() { 5 | let args = Args::parse(); 6 | match rustypaste_cli::run(args) { 7 | Ok(_) => process::exit(0), 8 | Err(e) => { 9 | eprintln!("{e}"); 10 | process::exit(1) 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/upload.rs: -------------------------------------------------------------------------------- 1 | use crate::config::Config; 2 | use crate::error::{Error, Result}; 3 | use indicatif::{ProgressBar, ProgressStyle}; 4 | use multipart::client::lazy::Multipart; 5 | use serde::Deserialize; 6 | use std::io::{Read, Result as IoResult, Write}; 7 | use std::time::Duration; 8 | use ureq::Error as UreqError; 9 | use ureq::{Agent, AgentBuilder}; 10 | use url::Url; 11 | 12 | /// Default file name to use for multipart stream. 13 | const DEFAULT_FILE_NAME: Option<&str> = Some("file"); 14 | 15 | /// HTTP header to use for specifying expiration times. 16 | const EXPIRATION_HEADER: &str = "expire"; 17 | 18 | /// HTTP header for specifying the filename. 19 | const FILENAME_HEADER: &str = "filename"; 20 | 21 | /// File entry item for list endpoint. 22 | #[derive(Deserialize, Debug)] 23 | pub struct ListItem { 24 | /// Uploaded file name. 25 | pub file_name: String, 26 | /// Size of the file in bytes. 27 | pub file_size: u64, 28 | /// ISO8601 formatted date-time string of the creation timestamp. 29 | pub creation_date_utc: Option, 30 | /// ISO8601 formatted date-time string of the expiration timestamp if one exists for this file. 31 | pub expires_at_utc: Option, 32 | } 33 | 34 | /// Wrapper around raw data and result. 35 | #[derive(Debug)] 36 | pub struct UploadResult<'a, T>(pub &'a str, pub Result); 37 | 38 | /// Upload progress tracker. 39 | #[derive(Debug)] 40 | pub struct UploadTracker<'a, R: Read> { 41 | /// Inner type for the upload stream. 42 | inner: R, 43 | /// Progress bar. 44 | progress_bar: &'a ProgressBar, 45 | /// Uploaded size. 46 | uploaded: usize, 47 | } 48 | 49 | impl<'a, R: Read> UploadTracker<'a, R> { 50 | /// Constructs a new instance. 51 | pub fn new(progress_bar: &'a ProgressBar, total: u64, reader: R) -> Result { 52 | progress_bar.set_style( 53 | ProgressStyle::default_bar() 54 | .template("{msg:.green.bold} {spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({bytes_per_sec}, {eta})")? 55 | .progress_chars("#>-"), 56 | ); 57 | progress_bar.set_length(total); 58 | progress_bar.reset_elapsed(); 59 | Ok(Self { 60 | inner: reader, 61 | progress_bar, 62 | uploaded: 0, 63 | }) 64 | } 65 | } 66 | 67 | impl Read for UploadTracker<'_, R> { 68 | fn read(&mut self, buf: &mut [u8]) -> IoResult { 69 | let bytes_read = self.inner.read(buf)?; 70 | self.uploaded += bytes_read; 71 | self.progress_bar.set_position(self.uploaded as u64); 72 | Ok(bytes_read) 73 | } 74 | } 75 | 76 | /// Upload handler. 77 | #[derive(Debug)] 78 | pub struct Uploader<'a> { 79 | /// HTTP client. 80 | client: Agent, 81 | /// Server configuration. 82 | config: &'a Config, 83 | } 84 | 85 | impl<'a> Uploader<'a> { 86 | /// Constructs a new instance. 87 | pub fn new(config: &'a Config) -> Self { 88 | Self { 89 | client: AgentBuilder::new() 90 | .user_agent(&format!( 91 | "{}/{}", 92 | env!("CARGO_PKG_NAME"), 93 | env!("CARGO_PKG_VERSION") 94 | )) 95 | .build(), 96 | config, 97 | } 98 | } 99 | 100 | /// Uploads the given file to the server. 101 | pub fn upload_file(&self, file: &'a str) -> UploadResult<'a, String> { 102 | let field = if self.config.paste.oneshot == Some(true) { 103 | "oneshot" 104 | } else { 105 | "file" 106 | }; 107 | let mut multipart = Multipart::new(); 108 | multipart.add_file(field, file); 109 | 110 | UploadResult(file, self.upload(multipart)) 111 | } 112 | 113 | /// Uploads the given URL (stream) to the server. 114 | pub fn upload_url(&self, url: &'a str) -> UploadResult<'a, String> { 115 | let field = if self.config.paste.oneshot == Some(true) { 116 | "oneshot_url" 117 | } else { 118 | "url" 119 | }; 120 | 121 | if let Err(e) = Url::parse(url) { 122 | UploadResult(url, Err(e.into())) 123 | } else { 124 | let mut multipart = Multipart::new(); 125 | multipart.add_stream::<_, &[u8], &str>(field, url.as_bytes(), None, None); 126 | UploadResult(url, self.upload(multipart)) 127 | } 128 | } 129 | 130 | /// Uploads the given remote URL (stream) to the server. 131 | pub fn upload_remote_url(&self, url: &'a str) -> UploadResult<'a, String> { 132 | if let Err(e) = Url::parse(url) { 133 | UploadResult(url, Err(e.into())) 134 | } else { 135 | let mut multipart = Multipart::new(); 136 | multipart.add_stream::<_, &[u8], &str>("remote", url.as_bytes(), None, None); 137 | UploadResult(url, self.upload(multipart)) 138 | } 139 | } 140 | 141 | /// Uploads a stream to the server. 142 | pub fn upload_stream(&self, stream: S) -> UploadResult<'a, String> { 143 | let field = if self.config.paste.oneshot == Some(true) { 144 | "oneshot" 145 | } else { 146 | "file" 147 | }; 148 | let mut multipart = Multipart::new(); 149 | multipart.add_stream(field, stream, DEFAULT_FILE_NAME, None); 150 | 151 | UploadResult("stream", self.upload(multipart)) 152 | } 153 | 154 | /// Uploads the given multipart data. 155 | fn upload(&self, mut multipart: Multipart<'static, '_>) -> Result { 156 | let multipart_data = multipart.prepare()?; 157 | let mut request = self.client.post(&self.config.server.address).set( 158 | "Content-Type", 159 | &format!( 160 | "multipart/form-data; boundary={}", 161 | multipart_data.boundary() 162 | ), 163 | ); 164 | if let Some(content_len) = multipart_data.content_len() { 165 | request = request.set("Content-Length", &content_len.to_string()); 166 | } 167 | if let Some(auth_token) = &self.config.server.auth_token { 168 | request = request.set("Authorization", auth_token); 169 | } 170 | if let Some(expiration_time) = &self.config.paste.expire { 171 | request = request.set(EXPIRATION_HEADER, expiration_time); 172 | } 173 | if let Some(filename) = &self.config.paste.filename { 174 | request = request.set(FILENAME_HEADER, filename); 175 | } 176 | let progress_bar = ProgressBar::new_spinner(); 177 | progress_bar.enable_steady_tick(Duration::from_millis(80)); 178 | progress_bar.set_message("Uploading"); 179 | let upload_tracker = UploadTracker::new( 180 | &progress_bar, 181 | multipart_data.content_len().unwrap_or_default(), 182 | multipart_data, 183 | )?; 184 | let result = match request.send(upload_tracker) { 185 | Ok(response) => { 186 | let status = response.status(); 187 | let response_text = response.into_string()?; 188 | if response_text.lines().count() != 1 { 189 | Err(Error::UploadError(format!( 190 | "server returned invalid body (status code: {status})" 191 | ))) 192 | } else if status == 200 { 193 | Ok(response_text) 194 | } else { 195 | Err(Error::UploadError(format!( 196 | "unknown error (status code: {status})" 197 | ))) 198 | } 199 | } 200 | Err(UreqError::Status(code, response)) => Err(Error::UploadError(format!( 201 | "{} (status code: {})", 202 | response.into_string()?.trim(), 203 | code 204 | ))), 205 | Err(e) => Err(Error::RequestError(Box::new(e))), 206 | }; 207 | progress_bar.finish_and_clear(); 208 | result 209 | } 210 | 211 | /// Wrapper: Delete the given file from the server. 212 | pub fn delete_file(&self, file: &'a str) -> UploadResult<'a, String> { 213 | UploadResult(file, self.delete(file)) 214 | } 215 | 216 | /// Delete the given file from the server. 217 | fn delete(&self, file: &'a str) -> Result { 218 | let url = self.retrieve_url(file)?; 219 | let mut request = self.client.delete(url.as_str()); 220 | if let Some(delete_token) = &self.config.server.delete_token { 221 | request = request.set("Authorization", delete_token); 222 | } 223 | let result = match request.call() { 224 | Ok(response) => { 225 | let status = response.status(); 226 | let response_text = response.into_string()?; 227 | if status == 200 { 228 | Ok(response_text) 229 | } else { 230 | Err(Error::DeleteError(format!( 231 | "unknown error (status code: {status})" 232 | ))) 233 | } 234 | } 235 | Err(UreqError::Status(code, response)) => { 236 | if code == 404 { 237 | Err(Error::DeleteError( 238 | response.into_string()?.trim().to_string(), 239 | )) 240 | } else { 241 | Err(Error::DeleteError(format!( 242 | "{} (status code: {})", 243 | response.into_string()?.trim(), 244 | code 245 | ))) 246 | } 247 | } 248 | Err(e) => Err(Error::RequestError(Box::new(e))), 249 | }; 250 | result 251 | } 252 | 253 | /// Returns a valid request URL for an endpoint. 254 | pub fn retrieve_url(&self, endpoint: &str) -> Result { 255 | let mut url = Url::parse(&self.config.server.address)?; 256 | if !url.path().to_string().ends_with('/') { 257 | url = url.join(&format!("{}/", url.path()))?; 258 | } 259 | url = url.join(endpoint)?; 260 | Ok(url) 261 | } 262 | 263 | /// Returns the server version. 264 | pub fn retrieve_version(&self) -> Result { 265 | let url = self.retrieve_url("version")?; 266 | let mut request = self.client.get(url.as_str()); 267 | if let Some(auth_token) = &self.config.server.auth_token { 268 | request = request.set("Authorization", auth_token); 269 | } 270 | Ok(request 271 | .call() 272 | .map_err(|e| Error::RequestError(Box::new(e)))? 273 | .into_string()?) 274 | } 275 | 276 | /// Retrieves and prints the files on server. 277 | pub fn retrieve_list(&self, output: &mut Output, prettify: bool) -> Result<()> { 278 | let url = self.retrieve_url("list")?; 279 | let mut request = self.client.get(url.as_str()); 280 | if let Some(auth_token) = &self.config.server.auth_token { 281 | request = request.set("Authorization", auth_token); 282 | } 283 | let response = request 284 | .call() 285 | .map_err(|e| Error::RequestError(Box::new(e)))?; 286 | if !prettify { 287 | writeln!(output, "{}", response.into_string()?)?; 288 | return Ok(()); 289 | } 290 | let items: Vec = response.into_json()?; 291 | if items.is_empty() { 292 | writeln!(output, "No files on server :(")?; 293 | return Ok(()); 294 | } 295 | let filename_width = items 296 | .iter() 297 | .map(|v| v.file_name.len()) 298 | .max() 299 | .unwrap_or_default(); 300 | let mut filesize_width = items 301 | .iter() 302 | .map(|v| v.file_size) 303 | .max() 304 | .unwrap_or_default() 305 | .to_string() 306 | .len(); 307 | if filesize_width < 4 { 308 | filesize_width = 4; 309 | } 310 | writeln!( 311 | output, 312 | "{:^filename_width$} | {:^filesize_width$} | {:^19} | {:^19}", 313 | "Name", "Size", "Creation (UTC)", "Expiry (UTC)" 314 | )?; 315 | writeln!( 316 | output, 317 | "{:-filesize_width$}-|-{:-<19}-|-{:-<19}", 318 | "", "", "", "" 319 | )?; 320 | items.iter().try_for_each(|file_info| { 321 | writeln!( 322 | output, 323 | "{:filesize_width$} | {:<19} | {}", 324 | file_info.file_name, 325 | file_info.file_size, 326 | file_info 327 | .creation_date_utc 328 | .as_deref() 329 | .unwrap_or("info not available"), 330 | file_info.expires_at_utc.as_deref().unwrap_or_default() 331 | ) 332 | })?; 333 | Ok(()) 334 | } 335 | } 336 | -------------------------------------------------------------------------------- /tests/token_file_parsing/token.txt: -------------------------------------------------------------------------------- 1 | KBRRHMxlJfFo1 2 | -------------------------------------------------------------------------------- /tests/token_file_parsing/token_whitespaced.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | nhJuLuY5vxUrO 4 | 5 | 6 | 7 | --------------------------------------------------------------------------------