diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..fb96391 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,36 @@ +version: 2 +jobs: + build: + docker: + - image: rust:1.31 + steps: + - checkout + # Load cargo target from cache if possible. + # Multiple caches are used to increase the chance of a cache hit. + - restore_cache: + keys: + - v1-cargo-cache-{{ arch }}-{{ .Branch }} + - v1-cargo-cache-{{ arch }} + + # Show versions + - run: rustc --version && cargo --version + + # Build + - run: cargo build + - run: cargo build --features logging + - run: cargo build --no-default-features + + # Run tests + - run: cargo test + - run: cargo test --no-default-features + + - save_cache: + key: v1-cargo-cache-{{ arch }}-{{ .Branch }} + paths: + - target + - /usr/local/cargo + - save_cache: + key: v1-cargo-cache-{{ arch }} + paths: + - target + - /usr/local/cargo diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 953567c..0000000 --- a/.gitattributes +++ /dev/null @@ -1,4 +0,0 @@ -* text=auto - -*.md eol=lf -*.expected eol=lf diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 8ac6b8c..0000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,6 +0,0 @@ -version: 2 -updates: - - package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "monthly" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index faf3111..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,93 +0,0 @@ -name: CI -on: - push: - branches: - - main - - "v*.x" - pull_request: - schedule: - - cron: '30 3 * * 2' - workflow_dispatch: - -jobs: - test: - name: run tests - strategy: - matrix: - platform: [ubuntu-latest, macos-latest, windows-latest] - toolchain: [stable, 1.87.0] # MSRV - include: - - platform: windows-latest - exe_suffix: .exe - runs-on: ${{ matrix.platform }} - steps: - - uses: actions/checkout@v7 - - uses: dtolnay/rust-toolchain@master - with: - toolchain: ${{ matrix.toolchain }} - - run: mkdir artifacts - - name: Build with default features - run: | - cargo build - cp target/debug/tldr${{ matrix.exe_suffix}} artifacts/tldr-default${{ matrix.exe_suffix}} - - name: Build with logging and Rustls with webpki roots - run: | - cargo build --features logging,rustls-with-webpki-roots --no-default-features - cp target/debug/tldr${{ matrix.exe_suffix}} artifacts/tldr-logging-rustls-webpki${{ matrix.exe_suffix}} - - name: Build with native TLS backend - run: | - # expects runners have the proper Native SSL library - cargo build --features native-tls --no-default-features - cp target/debug/tldr${{ matrix.exe_suffix}} artifacts/tldr-native-tls${{ matrix.exe_suffix}} - - uses: actions/upload-artifact@v7 - with: - name: tldr-debug-build-${{ matrix.platform }}-rust-${{ matrix.toolchain }} - path: artifacts/ - - name: Run tests - run: cargo test -- --test-threads 1 - - clippy: - name: run clippy lints - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v7 - - uses: dtolnay/rust-toolchain@master - with: - toolchain: stable - components: clippy - - name: run clippy lints - run: cargo clippy --all-targets --features logging - - fmt: - name: run rustfmt - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v7 - - uses: dtolnay/rust-toolchain@master - with: - toolchain: stable - components: rustfmt - - name: run rustfmt - run: cargo fmt --all -- --check - - docs: - name: build docs - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v7 - - name: Setup mdBook - uses: peaceiris/actions-mdbook@v2 - with: - mdbook-version: '0.4.4' - - name: Setup toolchain - uses: dtolnay/rust-toolchain@master - with: - toolchain: stable - - name: Build - run: cargo build - - name: Ensure that docs can be built - run: cd docs && mdbook build - - name: Generate usage string - run: cargo run -- --help > docs/src/usage-actual.txt - - name: Ensure that usage string is up to date - run: diff docs/src/usage{,-actual}.txt diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml deleted file mode 100644 index ae9ae93..0000000 --- a/.github/workflows/gh-pages.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: GitHub Pages -on: - push: - tags: - - "v[1-9]*" # push events matching `v` followed by anything larger than 0, e.g. v1.0, v20.15.10 - workflow_dispatch: - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v7 - - - name: Setup mdBook - uses: peaceiris/actions-mdbook@v2 - with: - mdbook-version: '0.4.4' - - - run: cd docs && mdbook build - - - name: Deploy - uses: peaceiris/actions-gh-pages@v4 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./docs/book diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 9337d6f..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,160 +0,0 @@ -name: Release -on: - push: - tags: - - "v*" # push events to matching v*, i.e. v1.0, v20.15.10 - -jobs: - create-release: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v7 - - name: Create release for tag - if: startsWith(github.ref, 'refs/tags/') - run: | - source ./scripts/upload-asset.sh - # Create: - create_release ${{ secrets.GITHUB_TOKEN }} ${{ github.repository }} ${GITHUB_REF#refs/*/} "Tealdeer version ${GITHUB_REF#refs/*/v}.\n\nFor the full changelog, see https://github.com/tealdeer-rs/tealdeer/blob/main/CHANGELOG.md.\n\nBinaries were generated automatically in CI, and are therefore unsigned. For a fully trusted release, please build from source." - - upload-completions: - needs: - - create-release - runs-on: ubuntu-latest - strategy: - matrix: - target: ["bash", "fish", "zsh"] - steps: - - uses: actions/checkout@v7 - - name: Upload completion - if: startsWith(github.ref, 'refs/tags/') - run: | - source ./scripts/upload-asset.sh - # Upload: - upload_release_file ${{ secrets.GITHUB_TOKEN }} ${{ github.repository }} ${GITHUB_REF#refs/*/} completion/${{ matrix.target }}_tealdeer completions_${{ matrix.target }} - - upload-license: - needs: - - create-release - runs-on: ubuntu-latest - strategy: - matrix: - target: ["MIT", "APACHE"] - steps: - - uses: actions/checkout@v7 - - name: Upload license - if: startsWith(github.ref, 'refs/tags/') - run: | - source ./scripts/upload-asset.sh - # Upload: - upload_release_file ${{ secrets.GITHUB_TOKEN }} ${{ github.repository }} ${GITHUB_REF#refs/*/} LICENSE-${{ matrix.target }} LICENSE-${{ matrix.target }}.txt - - build-linux: - runs-on: ubuntu-latest - strategy: - matrix: - include: - - arch: "x86_64" - libc: "musl" - - arch: "aarch64" - libc: "musl" - - arch: "i686" - libc: "musl" - - arch: "armv7" - libc: "musleabihf" - - arch: "arm" - libc: "musleabi" - - arch: "arm" - libc: "musleabihf" - steps: - - uses: actions/checkout@v7 - - name: Pull Docker image - run: docker pull messense/rust-musl-cross:${{ matrix.arch }}-${{ matrix.libc }} - - name: Build in Docker - run: docker run --rm -i -v "$(pwd)":/home/rust/src messense/rust-musl-cross:${{ matrix.arch }}-${{ matrix.libc }} cargo build --release - - name: Strip binary - run: docker run --rm -i -v "$(pwd)":/home/rust/src messense/rust-musl-cross:${{ matrix.arch }}-${{ matrix.libc }} musl-strip -s /home/rust/src/target/${{ matrix.arch }}-unknown-linux-${{ matrix.libc }}/release/tldr - - uses: actions/upload-artifact@v7 - with: - name: "tealdeer-linux-${{ matrix.arch }}-${{ matrix.libc }}" - path: "target/${{ matrix.arch }}-unknown-linux-${{ matrix.libc }}/release/tldr" - - build-macos: - runs-on: macos-latest - strategy: - matrix: - include: - - arch: "x86_64" - - arch: "aarch64" - steps: - - uses: actions/checkout@v7 - - name: Setup toolchain - uses: dtolnay/rust-toolchain@master - with: - toolchain: stable - targets: "${{ matrix.arch }}-apple-darwin" - - name: Build - run: cargo build --release --target ${{ matrix.arch }}-apple-darwin - - uses: actions/upload-artifact@v7 - with: - name: "tealdeer-macos-${{ matrix.arch }}" - path: "target/${{ matrix.arch }}-apple-darwin/release/tldr" - - build-windows: - runs-on: windows-latest - steps: - - uses: actions/checkout@v7 - - name: Setup toolchain - uses: dtolnay/rust-toolchain@master - with: - toolchain: stable - - name: Build - run: cargo build --release --target x86_64-pc-windows-msvc - - uses: actions/upload-artifact@v7 - with: - name: "tealdeer-windows-x86_64-msvc" - path: "target/x86_64-pc-windows-msvc/release/tldr.exe" - - upload-release: - needs: - - create-release - - build-linux - - build-macos - - build-windows - runs-on: ubuntu-latest - strategy: - matrix: - target: - - linux-x86_64-musl - - linux-aarch64-musl - - linux-i686-musl - - linux-armv7-musleabihf - - linux-arm-musleabi - - linux-arm-musleabihf - - macos-x86_64 - - macos-aarch64 - - windows-x86_64-msvc - steps: - - uses: actions/checkout@v7 - - uses: actions/download-artifact@v8 - - name: Upload binary - if: startsWith(github.ref, 'refs/tags/') - run: | - source ./scripts/upload-asset.sh - - # Move/rename file - mkdir out && cd out - if [[ "${{ matrix.target }}" == *windows* ]]; then - src="../tealdeer-${{ matrix.target }}/tldr.exe" - filename="tealdeer-${{ matrix.target }}.exe" - else - src="../tealdeer-${{ matrix.target }}/tldr" - filename="tealdeer-${{ matrix.target }}" - fi - cp $src $filename - - # Create checksum - sha256sum "$filename" > "$filename.sha256" - - # Upload: - upload_release_file ${{ secrets.GITHUB_TOKEN }} ${{ github.repository }} ${GITHUB_REF#refs/*/} $filename $filename - upload_release_file ${{ secrets.GITHUB_TOKEN }} ${{ github.repository }} ${GITHUB_REF#refs/*/} $filename.sha256 $filename.sha256 diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..3bf39a9 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,7 @@ +language: rust +os: osx +rust: + - 1.31.0 + - stable +cache: cargo +script: cargo test diff --git a/CHANGELOG.md b/CHANGELOG.md index 644a30a..ed03204 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,480 +10,6 @@ Possible log types: - `[removed]` for deprecated features removed in this release. - `[fixed]` for any bug fixes. - `[security]` to invite users to upgrade in case of vulnerabilities. -- `[docs]` for documentation changes. -- `[chore]` for maintenance work. - -### [v1.5.1][v1.5.1], [v1.6.2][v1.6.2], [v1.7.3][v1.7.3] (2026-01-25) - -Today I am releasing three patch updates for outdated versions of tealdeer. -They are minimal patches for Linux distributions that ship old versions of -tealdeer which recently broke due to an upstream change. If you can choose -freely which version of tealdeer to use, I recommend using the latest version of -tealdeer, 1.8.1. For more details, see the "Notes to package maintainers" -section below. - -All three updates contain only a single change compared to their respective -previous versions which changes the `ARCHIVE_URL` constant used for updating the -page cache. The reason for this change is that the upstream tldr-pages -repository shut down the domain that clients were previously required to use. - -Note that this issue is already fixed in tealdeer 1.8.0 where we introduced a -config file option for changing the URL used at runtime. The versions 1.8.0 and -1.8.1 also use the new domain of the tldr-pages archive by default, so no action -is needed for users of those versions. - -#### Changes - -- [fixed] Update `ARCHIVE_URL` - -#### Notes to package maintainers - -I have _not_ updated the lockfile for any of these releases, so the locked -dependency versions are still the same as they were for the previous release in -the respective v1.x series. Updating the lockfile for tealdeer 1.5.0 to remove -any `cargo audit` warnings while also maintaining compatibility with Rust 1.54 -also brings larger changes through transitive dependencies, which contradicts my -plan to make this update easy to plug into existing build pipelines. - -If you want to build / distribute tealdeer v1.5.1, v1.6.2, or v1.7.3, please use -an up to date Rust toolchain to permit updates to newer versions of (transitive) -dependencies. Do not use the lockfile, instead update to the newest available -dependency versions. - -For the same reason, there are no artifacts attached to the GitHub releases of -these versions. - -### [v1.8.1][v1.8.1] (2025-11-11) - -This patch release tweaks the enabled features for ureq, the library we use to -perform HTTP requests when updating the cache. In particular, support for socks -proxies is now enabled. - -#### Changes: - -- [added] Enable ureq's socks-proxy feature ([#451]) - -### [v1.8.0][v1.8.0] (2025-10-03) - -One year and one day have passed since tealdeer version 1.7.0 was released, so -it's time for an update! Tealdeer 1.8 comes with a complete rewrite of the page -cache and contains many long awaited improvements around it. - -Firstly, tealdeer now supports language-specific downloads. This means that only -the pages matching the configured languages are downloaded when updating the -cache. The languages used for searching pages can be configured separately to -the ones used for updating, so it is possible to download pages in languages -that are not usually queried. - -Next to configuring which languages are used for searching, it is now also -possible to specify which platforms are used in the config file. Importantly, -the default behavior for page search has changed so that all platforms are -searched if no page is found for the platform that tealdeer is running on. To -restore the behavior of tealdeer 1.7, users should set -```toml -[search] -platforms = ["current", "common"] -``` -in their config file. - -Coming back to updating, the default build configuration of tealdeer now -includes multiple TLS backends. This means that tealdeer does not have to be -rebuilt to try out a different TLS backend. The used backend can be chosen in -the config file. By default, tealdeer comes with support for rustls using webpki -certificates or system certificates. Native TLS is supported, but not enabled by -default to avoid build troubles with OpenSSL and musl. - -For details, please refer to the [user documentation]. - -#### Changes: - -- [added] Resolve paths in config `[directories]` relative to the config directory ([#306]) -- [added] Add `common` platform to CLI ([#401]) -- [added] Add configuration option for `archive_source` ([#337]) -- [added] Allows configuring TLS backend ([#386]) -- [added] Add args: `--edit-page` and `--edit-patch` ([#388]) -- [added] Add an option to specify a custom config file to be used ([#422]) -- [added] Upload binaries from build step as artifact ([#423]) -- [added] Add `search.languages` and `updates.download_languages` settings ([#430]) -- [added] Add `search.platforms` config option and search all platforms by default ([#435]) -- [added] Add `display.show_title` option to display command titles in output ([#439]) -- [chore] Various test improvements ([#399]) -- [chore] Add tests for osx/macos alias ([#407]) -- [chore] Move most of `main` to `try_main` ([#400]) -- [chore] Only create a single temporary directory in integration tests ([#411]) -- [chore] Replace reqwest with ureq ([#417]) -- [chore] Introduce Language struct ([#425]) -- [chore] Cache rewrite ([#416]) -- [chore] Allow references in `Config` ([#429]) -- [docs] Highlight code examples in user docs ([#440]) -- [removed] Remove native-tls from default feature set ([#436]) - -#### Contributors to this version: - -- [Christoph Loy][@beatbrot] -- [Erick Guan][@erickguan] -- [@MHS-0][@MHS-0] -- [Matěj Kafka][@MatejKafka] -- [Nachiket Kanore][@nachiketkanore] -- [Niklas Mohrin][@niklasmohrin] -- [Predrag Minic][@mipedja] -- [@hex1c][@hex1c] -- [lyj][@lengyijun] - -Thanks! - -#### Notes to package maintainers - -1. The MSRV has been bumped to 1.85. -2. Consider whether you want to include the `native-tls` feature in your build - of tealdeer. The feature is disabled for the binaries in the GitHub release - because we target musl, but it might work out of the box for your - distribution. -3. We have added the `ignore-online-tests` feature to automatically mark all - tests that require an internet connection as skipped, so you can use this - feature instead of maintaining a list of these tests yourself. - -### [v1.7.2][v1.7.2] (2025-03-18) - -This patch release updates the `zip` dependency to mitigate a potential security -vulnerability. A successful attack against tealdeer users would require -manipulation of the tldr pages archive downloaded during an update. As the -archive is downloaded from a trusted source (the tldr-pages organization), it -seems very unlikely that running a version of tealdeer prior to 1.7.2 poses a -security risk. Nevertheless, it cannot hurt to rule out any chance of an attack -by updating tealdeer to version 1.7.2. - -For more details, please see https://github.com/advisories/GHSA-94vh-gphv-8pm8. - -- [security] Require `zip >= 2.3.0` -- [chore] Run CI on backport branches and on dispatch - -### [v1.7.1][v1.7.1] (2024-11-14) - -This patch release updates the `yansi` dependency to version 1, so that the -previous versions of `yansi` can be removed from the package sets of Linux -distributions. This change should not impact the behavior of tealdeer. - -#### Changes: - -- [chore] Upgrade yansi: 0.5.1 -> 1.0.1 ([#389]) - -#### Contributors to this version: - -- [Blair Noctis][@nc7s] - -Thanks! - -### [v1.7.0][v1.7.0] (2024-10-02) - -It's been 24 months since the last release, time for tealdeer 1.7.0! Thanks to -16 individual contributors, a few nice changes and features are included in -this release. - -One change is that you can **query multiple platforms at once**. For example: - - tldr --platform openbsd --platform linux df - -This will show the `df` page for OpenBSD (if available), followed by Linux (if -available), with fallback to the current platform on which tealdeer runs. - -What's that `openbsd` thing up there? Yes, there's now **support for the BSD -platforms `freebsd`, `netbsd` and `openbsd`**. - -And since we're already talking about platform support: Our **binary releases -now include builds for ARM64 (aka `aarch64`) on macOS (Apple Silicon, M1/M2/M3) -and Linux**. _(Keep in mind that binary releases are generated in CI and are -unsigned. For a trusted build, please compile from source.)_ - -There's also a breaking change for the folks using [custom pages and -patches](https://tealdeer-rs.github.io/tealdeer/usage_custom_pages.html): These -files now use a `.md` extension. Old files will continue to work, but will -result a deprecation warning being printed when used. - -On a personal note, this will be the last release from me -([Danilo](https://github.com/dbrgn/)) as primary maintainer of tealdeer. For -details, see [#376](https://github.com/tealdeer-rs/tealdeer/issues/376). - -#### Changes: - -- [added] Allow querying multiple platforms ([#300]) -- [added] Add BSD platform support ([#354]) -- [added] Allow building with native-tls in addition to rustls ([#303]) -- [changed] Change custom page files to use a `.md` file extension ([#322]) -- [changed] Update to clap v4 for doing command line parsing ([#298]) -- [changed] Performance optimization in LineIterator ([#314]) -- [changed] Performance optimizations by tweaking Cargo flags ([#355]) -- [changed] Include completions in published crate ([#333]) -- [changed] Minimal supported Rust version is now 1.75 ([#298]) -- [fixed] Fix bash/zsh/fish completions when cache is empty ([#327], [#331]) -- [docs] Publish docs only when tagging a release ([#362]) -- [docs] List Scoop and Debian packages ([#305], [#315]) -- [docs] Add "Tips and Tricks" chapter to user manual ([#342]) -- [docs] Various docs improvements ([#293]) -- [chore] Improvements to CI workflows ([#324]) -- [chore] Update Cargo.toml license field following SPDX 2.1 ([#336]) -- [chore] Dependency updates - -#### Contributors to this version: - -- [Adam Henley][@adamazing] -- [Andrea Frigido][@frisoft] -- [Blair Noctis][@nc7s] -- [Danilo Bargen][@dbrgn] -- [Felix Yan][@felixonmars] -- [Iliia Maleki][@iliya-malecki] -- [JJ Style][@jj-style] -- [K.B.Dharun Krishna][@kbdharun] -- [Linus Walker][@Walker-00] -- [Mohit Raj][@agrmohit] -- [Nicolai Fröhlich][@nifr] -- [Niklas Mohrin][@niklasmohrin] -- [@qknogxxb][@qknogxxb] -- [@tveness][@tveness] -- [Y.D.X.][@YDX-2147483647] -- [Zacchary Dempsey-Plante][@zedseven] - -Thanks! - - -### [v1.6.1][v1.6.1] (2022-10-24) - -#### Changes: - -- [fixed] Fix path source for custom pages dir ([#297]) -- [chore] Update dependendencies ([#299]) - -#### Contributors to this version: - -- [Cyrus Yip][@CyrusYip] -- [Danilo Bargen][@dbrgn] - -Thanks! - - -### [v1.6.0][v1.6.0] (2022-10-02) - -It's been 9 months since the last release already! This is not a huge update -feature-wise, but it still contains a few nice new improvements and a few -bugfixes, contributed by 11 different people. The most important new feature is -probably the option to override the cache directory through the config file. -The `TEALDEER_CACHE_DIR` env variable is now deprecated. - -A note to packagers: Shell completions have been moved to the `completion/` -subdirectory! Packaging scripts might need to be updated. - -#### Changes: - -- [added] Allow overriding cache directory through config ([#276]) -- [added] Add `--no-auto-update` CLI flag ([#257]) -- [added] Show note about auto-updates when cache is missing ([#254]) -- [added] Add support for android platform ([#274]) -- [added] Add custom pages to list output ([#285]) -- [fixed] Cache: Return error if HTTP client cannot be created ([#247]) -- [fixed] Handle cache download errors ([#253]) -- [fixed] Do not page output of `tldr --update` ([#231]) -- [fixed] Create macOS release builds with bundled root certificates ([#272]) -- [fixed] Clean up and fix shell completions ([#262]) -- [deprecated] The `TEALDEER_CACHE_DIR` env variable is now deprecated ([#276]) -- [removed] The `--config-path` command was removed, use `--show-paths` instead ([#290]) -- [removed] The `-o/--os` command was removed, use `-p/--platform` instead ([#290]) -- [removed] The `-m/--markdown` command was removed, use `-r/--raw` instead ([#290]) -- [chore] Move shell completion scripts to their own directory ([#259]) -- [chore] Update dependencies ([#271], [#287], [#291]) -- [chore] Use anyhow for error handling ([#249]) -- [chore] Switch to Rust 2021 edition ([#284]) - -#### Contributors to this version: - -- [@bagohart][@bagohart] -- [@cyqsimon][@cyqsimon] -- [Danilo Bargen][@dbrgn] -- [Danny Mösch][@SimplyDanny] -- [Evan Lloyd New-Schmidt][@newsch] -- [Hans Gaiser][@hgaiser] -- [Kian-Meng Ang][@kianmeng] -- [Marcin Puc][@tranzystorek-io] -- [Niklas Mohrin][@niklasmohrin] -- [Olav de Haas][@Olavhaasie] -- [Simon Perdrisat][@gagarine] - -Thanks! - - -### [v1.5.0][v1.5.0] (2021-12-31) - -This is quite a big release with many new features. In the 15 months since the -last release, 59 pull requests from 16 different contributors were merged! - -The highlights: - -- **Custom pages and patches**: You can now create your own local-only tldr - pages. But not just that, you can also extend existing upstream pages with - your own examples. For more details, see - [the docs](https://tealdeer-rs.github.io/tealdeer/usage_custom_pages.html). -- **Change argument parsing from docopt to clap**: We replaced docopt.rs as - argument parsing library with clap v3, resulting in almost 1 MiB smaller - binaries and a 22% speed increase when rendering a tldr page. -- **Multi-language support**: You can now override the language with `-L/--language`. -- **A new `--show-paths` command**: By running `tldr --show-paths`, you can list - the currently used config dir, cache dir, upstream pages dir and custom pages dir. -- **Compliance with the tldr client spec v1.5**: We renamed `-o/--os` to - `-p/--platform` and implemented transparent lowercasing of the page names. -- **Docs**: The README based documentation has reached its limits. There are - now new mdbook based docs over at - [tealdeer-rs.github.io/tealdeer/](https://tealdeer-rs.github.io/tealdeer/), we hope these - make using tealdeer easier. Of course, documentation improvements are - welcome! Also, if you're confused about how to use a certain feature, feel - free to open an issue, this way we can improve the docs. - -Note that the MSRV (Minimal Supported Rust Version) of the project -[changed][i190]: - -> When publishing a tealdeer release, the Rust version required to build it -> should be stable for at least a month. - -#### Changes: - -- [added] Support custom pages and patches ([#142][i142]) -- [added] Multi-language support ([#125][i125], [#161][i161]) -- [added] Add support for ANSI code and RGB colors ([#148][i148]) -- [added] Implement new `--show-paths` command ([#162][i162]) -- [added] Support for italic text styling ([#197][i197]) -- [added] Allow SunOS platform override ([#176][i176]) -- [added] Automatically lowercase page names before lookup ([#227][i227]) -- [added] Add "macos" alias for "osx" ([#215][i215]) -- [fixed] Consider only standalone command names for styling ([#157][i157]) -- [fixed] Fixed and improved zsh completions ([#168][i168]) -- [fixed] Create cache directory path if it does not exist ([#174][i174]) -- [fixed] Use default style if user-defined style is missing ([#210][i210]) -- [changed] Switch from docopt to clap for argument parsing ([#108][i108]) -- [changed] Switch from OpenSSL to Rustls ([#187][i187]) -- [changed] Performance improvements ([#187][i187]) -- [changed] Send all progress logging messages to stderr ([#171][i171]) -- [changed] Rename `-o/--os` to `-p/--platform` ([#217][i217]) -- [changed] Rename `-m/--markdown` to `-r/--raw` ([#108][i108]) -- [deprecated] The `--config-path` command is deprecated, use `--show-paths` instead ([#162][i162]) -- [deprecated] The `-o/--os` command is deprecated, use `-p/--platform` instead ([#217][i217]) -- [deprecated] The `-m/--markdown` command is deprecated, use `-r/--raw` instead ([#108][i108]) -- [docs] New docs at [tealdeer-rs.github.io/tealdeer/](https://tealdeer-rs.github.io/tealdeer/) -- [docs] Add comparative benchmarks with hyperfine ([#163][i163], [README](https://github.com/tealdeer-rs/tealdeer#goals)) -- [chore] Download tldr pages archive from their website, not from GitHub ([#213][i213]) -- [chore] Bump MSRV to 1.54 and change MSRV policy ([#190][i190]) -- [chore] The `master` branch was renamed to `main` -- [chore] All release binaries are now generated in CI. Binaries for macOS and Windows are also provided. ([#240][i240]) -- [chore] Update all dependencies - -#### Contributors to this version: - -- [@bl-ue][@bl-ue] -- [Cameron Tod][@cam8001] -- [Dalton][@dmaahs2017] -- [Danilo Bargen][@dbrgn] -- [Danny Mösch][@SimplyDanny] -- [Marcin Puc][@tranzystorek-io] -- [Michael Cho][@cho-m] -- [MS_Y][@black7375] -- [Niklas Mohrin][@niklasmohrin] -- [Rithvik Vibhu][@rithvikvibhu] -- [rnd][@0ndorio] -- [Sondre Nilsen][@sondr3] -- [Tomás Farías Santana][@tomasfarias] -- [Tsvetomir Bonev][@invakid404] -- [@tveness][@tveness] -- [ギャラ][@laxect] - -Thanks! - -Last but not least, [Niklas Mohrin][@niklasmohrin] has joined the project as -co-maintainer. Thank you for your help! - - -### [v1.4.1][v1.4.1] (2020-09-04) - -- [fixed] Syntax error in zsh completion file ([#138][i138]) - -#### Contributors to this version: - -- [Danilo Bargen][@dbrgn] -- [Bruno A. Muciño][@mucinoab] -- [Francesco][@BachoSeven] - -Thanks! - - -### [v1.4.0][v1.4.0] (2020-09-03) - -- [added] Configurable automatic cache updates ([#115][i115]) -- [added] Improved color detection and support for `--color` argument and - `NO_COLOR` env variable ([#111][i111]) -- [changed] Make `--list` option comply with official spec ([#112][i112]) -- [changed] Move cache age warning to stderr ([#113][i113]) - -#### Contributors to this version: - -- [Atul Bhosale][@Atul9] -- [Danilo Bargen][@dbrgn] -- [Danny Mösch][@SimplyDanny] -- [Ilaï Deutel][@ilai-deutel] -- [Kornel][@kornelski] -- [@LovecraftianHorror][@LovecraftianHorror] -- [@michaeldel][@michaeldel] -- [Niklas Mohrin][@niklasmohrin] - -Thanks! - - -### [v1.3.0][v1.3.0] (2020-02-28) - -- [added] New config option for compact output mode ([#89][i89]) -- [added] New -m/--markdown parameter for raw rendering ([#95][i95]) -- [added] Provide zsh autocompletion ([#86][i86]) -- [changed] Require at least Rust 1.39 to build (previous: 1.32) -- [changed] Switch to GitHub actions, CI testing now covers Windows as well ([#99][i99]) -- [changed] Tweak the "outdated cache" warning message ([#97][i97]) -- [changed] General maintenance: Upgrade dependencies, fix linter warnings -- [fixed] Fix Fish autocompletion on macOS ([#87][i87]) -- [fixed] Fix compilation on Windows by disabling pager ([#99][i99]) - -#### Contributors to this version: - -- [Bruno Heridet][@Delapouite] -- [Danilo Bargen][@dbrgn] -- [Hugo Locurcio][@Calinou] -- [Isak Johansson][@Plommonsorbet] -- [James Doyle][@james2doyle] -- [Jesús Trinidad Díaz Ramírez][@jesdazrez] -- [@korrat][@korrat] -- [Marc-André Renaud][@ma-renaud] - -Thanks! - - -### [v1.2.0][v1.2.0] (2019-08-10) - -- [added] Add Windows support ([#77][i77]) -- [added] Add support for spaces in commands ([#75][i75]) -- [added] Add support for Fish-based autocompletion ([#71][i71]) -- [added] Add pager support ([#44][i44]) -- [added] Print detected OS with `-v` / `--version` ([#57][i57]) -- [changed] OS detection: Treat BSDs as "osx" ([#58][i58]) -- [changed] Move from curl to reqwest ([#61][i61]) -- [changed] Move to Rust 2018, require Rust 1.32 ([#69][i69] / [#84][i84]) -- [fixed] Add (back) support for proxies ([#68][i68]) - -#### Contributors to this version: - -- [Bar Hatsor][@Bassets] -- [Danilo Bargen][@dbrgn] -- [Gabriel Martinez][@mystal] -- [Ivan Smirnov][@aldanor] -- [Jan Christian Grünhage][@jcgruenhage] -- [Jonathan Dahan][@jedahan] -- [Juan D. Vega][@jdvr] -- [Natalie Pendragon][@natpen] -- [Raphael Das Gupta][@das-g] - -Thanks! ### [v1.1.0][v1.1.0] (2018-10-22) @@ -494,9 +20,8 @@ Thanks! - [changed] Require at least Rust 1.28 to build (previous: 1.19) - [fixed] Fix building on systems with openssl 1.1.1 ([#47][i47]) -#### Contributors to this version: +Contributors to this version: -- [Danilo Bargen][@dbrgn] - [@equal-l2][@equal-l2] - [Jonathan Dahan][@jedahan] - [Lukas Bergdoll][@Voultapher] @@ -527,198 +52,15 @@ Thanks! - First crates.io release -[user documentation]: https://tealdeer-rs.github.io/tealdeer/ -[@0ndorio]: https://github.com/0ndorio -[@adamazing]: https://github.com/adamazing -[@agrmohit]: https://github.com/agrmohit -[@aldanor]: https://github.com/aldanor -[@Atul9]: https://github.com/Atul9 -[@BachoSeven]: https://github.com/BachoSeven -[@bagohart]: https://github.com/bagohart -[@Bassets]: https://github.com/Bassets -[@black7375]: https://github.com/black7375 -[@bl-ue]: https://github.com/bl-ue -[@Calinou]: https://github.com/Calinou -[@cam8001]: https://github.com/cam8001 -[@cho-m]: https://github.com/cho-m -[@cyqsimon]: https://github.com/cyqsimon -[@CyrusYip]: https://github.com/CyrusYip -[@das-g]: https://github.com/das-g -[@dbrgn]: https://github.com/dbrgn -[@Delapouite]: https://github.com/Delapouite -[@dmaahs2017]: https://github.com/dmaahs2017 [@equal-l2]: https://github.com/equal-l2 -[@felixonmars]: https://github.com/felixonmars -[@frisoft]: https://github.com/frisoft -[@gagarine]: https://github.com/gagarine -[@hgaiser]: https://github.com/hgaiser -[@ilai-deutel]: https://github.com/ilai-deutel -[@iliya-malecki]: https://github.com/iliya-malecki -[@invakid404]: https://github.com/invakid404 -[@james2doyle]: https://github.com/james2doyle -[@jcgruenhage]: https://github.com/jcgruenhage -[@jdvr]: https://github.com/jdvr [@jedahan]: https://github.com/jedahan -[@jesdazrez]: https://github.com/jesdazrez -[@jj-style]: https://github.com/jj-style -[@kbdharun]: https://github.com/kbdharun -[@kianmeng]: https://github.com/kianmeng -[@kornelski]: https://github.com/kornelski -[@korrat]: https://github.com/korrat -[@laxect]: https://github.com/laxect -[@LovecraftianHorror]: https://github.com/LovecraftianHorror -[@ma-renaud]: https://github.com/ma-renaud -[@michaeldel]: https://github.com/michaeldel -[@mucinoab]: https://github.com/mucinoab -[@mystal]: https://github.com/mystal -[@natpen]: https://github.com/natpen -[@nc7s]: https://github.com/nc7s -[@newsch]: https://github.com/newsch -[@nifr]: https://github.com/nifr -[@niklasmohrin]: https://github.com/niklasmohrin -[@Olavhaasie]: https://github.com/Olavhaasie -[@Plommonsorbet]: https://github.com/Plommonsorbet -[@qknogxxb]: https://github.com/qknogxxb -[@rithvikvibhu]: https://github.com/rithvikvibhu -[@SimplyDanny]: https://github.com/SimplyDanny -[@sondr3]: https://github.com/sondr3 -[@tomasfarias]: https://github.com/tomasfarias -[@tranzystorek-io]: https://github.com/tranzystorek-io -[@tveness]: https://github.com/tveness [@Voultapher]: https://github.com/Voultapher -[@Walker-00]: https://github.com/Walker-00 -[@YDX-2147483647]: https://github.com/YDX-2147483647 -[@zedseven]: https://github.com/zedseven -[@beatbrot]: https://github.com/beatbrot -[@erickguan]: https://github.com/erickguan -[@MHS-0]: https://github.com/MHS-0 -[@MatejKafka]: https://github.com/MatejKafka -[@nachiketkanore]: https://github.com/nachiketkanore -[@mipedja]: https://github.com/mipedja -[@hex1c]: https://github.com/hex1c -[@lengyijun]: https://github.com/lengyijun -[v1.0.0]: https://github.com/tealdeer-rs/tealdeer/compare/v0.4.0...v1.0.0 -[v1.1.0]: https://github.com/tealdeer-rs/tealdeer/compare/v1.0.0...v1.1.0 -[v1.2.0]: https://github.com/tealdeer-rs/tealdeer/compare/v1.1.0...v1.2.0 -[v1.3.0]: https://github.com/tealdeer-rs/tealdeer/compare/v1.2.0...v1.3.0 -[v1.4.0]: https://github.com/tealdeer-rs/tealdeer/compare/v1.3.0...v1.4.0 -[v1.4.1]: https://github.com/tealdeer-rs/tealdeer/compare/v1.4.0...v1.4.1 -[v1.5.0]: https://github.com/tealdeer-rs/tealdeer/compare/v1.4.1...v1.5.0 -[v1.5.1]: https://github.com/tealdeer-rs/tealdeer/compare/v1.5.0...v1.5.1 -[v1.6.0]: https://github.com/tealdeer-rs/tealdeer/compare/v1.5.0...v1.6.0 -[v1.6.1]: https://github.com/tealdeer-rs/tealdeer/compare/v1.6.0...v1.6.1 -[v1.6.2]: https://github.com/tealdeer-rs/tealdeer/compare/v1.6.1...v1.6.2 -[v1.7.0]: https://github.com/tealdeer-rs/tealdeer/compare/v1.6.1...v1.7.0 -[v1.7.1]: https://github.com/tealdeer-rs/tealdeer/compare/v1.7.0...v1.7.1 -[v1.7.2]: https://github.com/tealdeer-rs/tealdeer/compare/v1.7.1...v1.7.2 -[v1.7.3]: https://github.com/tealdeer-rs/tealdeer/compare/v1.7.2...v1.7.3 -[v1.8.0]: https://github.com/tealdeer-rs/tealdeer/compare/v1.7.2...v1.8.0 -[v1.8.1]: https://github.com/tealdeer-rs/tealdeer/compare/v1.8.0...v1.8.1 +[v1.0.0]: https://github.com/dbrgn/tealdeer/compare/v0.4.0...v1.0.0 +[v1.1.0]: https://github.com/dbrgn/tealdeer/compare/v1.0.0...v1.1.0 -[i34]: https://github.com/tealdeer-rs/tealdeer/issues/34 -[i43]: https://github.com/tealdeer-rs/tealdeer/issues/43 -[i44]: https://github.com/tealdeer-rs/tealdeer/issues/44 -[i47]: https://github.com/tealdeer-rs/tealdeer/issues/47 -[i48]: https://github.com/tealdeer-rs/tealdeer/issues/48 -[i57]: https://github.com/tealdeer-rs/tealdeer/issues/57 -[i58]: https://github.com/tealdeer-rs/tealdeer/issues/58 -[i61]: https://github.com/tealdeer-rs/tealdeer/issues/61 -[i68]: https://github.com/tealdeer-rs/tealdeer/issues/68 -[i69]: https://github.com/tealdeer-rs/tealdeer/issues/69 -[i71]: https://github.com/tealdeer-rs/tealdeer/issues/71 -[i75]: https://github.com/tealdeer-rs/tealdeer/issues/75 -[i77]: https://github.com/tealdeer-rs/tealdeer/issues/77 -[i84]: https://github.com/tealdeer-rs/tealdeer/issues/84 -[i86]: https://github.com/tealdeer-rs/tealdeer/issues/86 -[i87]: https://github.com/tealdeer-rs/tealdeer/issues/87 -[i89]: https://github.com/tealdeer-rs/tealdeer/issues/89 -[i95]: https://github.com/tealdeer-rs/tealdeer/issues/95 -[i97]: https://github.com/tealdeer-rs/tealdeer/issues/97 -[i99]: https://github.com/tealdeer-rs/tealdeer/issues/99 -[i108]: https://github.com/tealdeer-rs/tealdeer/pull/108 -[i111]: https://github.com/tealdeer-rs/tealdeer/issues/111 -[i112]: https://github.com/tealdeer-rs/tealdeer/issues/112 -[i113]: https://github.com/tealdeer-rs/tealdeer/issues/113 -[i115]: https://github.com/tealdeer-rs/tealdeer/issues/115 -[i125]: https://github.com/tealdeer-rs/tealdeer/pull/125 -[i138]: https://github.com/tealdeer-rs/tealdeer/issues/138 -[i142]: https://github.com/tealdeer-rs/tealdeer/pull/142 -[i148]: https://github.com/tealdeer-rs/tealdeer/pull/148 -[i157]: https://github.com/tealdeer-rs/tealdeer/pull/157 -[i161]: https://github.com/tealdeer-rs/tealdeer/pull/161 -[i162]: https://github.com/tealdeer-rs/tealdeer/pull/162 -[i163]: https://github.com/tealdeer-rs/tealdeer/pull/163 -[i168]: https://github.com/tealdeer-rs/tealdeer/pull/168 -[i171]: https://github.com/tealdeer-rs/tealdeer/pull/171 -[i174]: https://github.com/tealdeer-rs/tealdeer/pull/174 -[i176]: https://github.com/tealdeer-rs/tealdeer/pull/176 -[i187]: https://github.com/tealdeer-rs/tealdeer/pull/187 -[i190]: https://github.com/tealdeer-rs/tealdeer/issues/190 -[i197]: https://github.com/tealdeer-rs/tealdeer/pull/197 -[i210]: https://github.com/tealdeer-rs/tealdeer/pull/210 -[i213]: https://github.com/tealdeer-rs/tealdeer/pull/213 -[i215]: https://github.com/tealdeer-rs/tealdeer/pull/215 -[i217]: https://github.com/tealdeer-rs/tealdeer/pull/217 -[i227]: https://github.com/tealdeer-rs/tealdeer/pull/227 -[#231]: https://github.com/tealdeer-rs/tealdeer/pull/231 -[i240]: https://github.com/tealdeer-rs/tealdeer/pull/240 -[#247]: https://github.com/tealdeer-rs/tealdeer/pull/247 -[#249]: https://github.com/tealdeer-rs/tealdeer/pull/249 -[#253]: https://github.com/tealdeer-rs/tealdeer/pull/253 -[#254]: https://github.com/tealdeer-rs/tealdeer/pull/254 -[#257]: https://github.com/tealdeer-rs/tealdeer/pull/257 -[#259]: https://github.com/tealdeer-rs/tealdeer/pull/259 -[#262]: https://github.com/tealdeer-rs/tealdeer/pull/262 -[#271]: https://github.com/tealdeer-rs/tealdeer/pull/271 -[#272]: https://github.com/tealdeer-rs/tealdeer/pull/272 -[#274]: https://github.com/tealdeer-rs/tealdeer/pull/274 -[#276]: https://github.com/tealdeer-rs/tealdeer/pull/276 -[#284]: https://github.com/tealdeer-rs/tealdeer/pull/284 -[#285]: https://github.com/tealdeer-rs/tealdeer/pull/285 -[#287]: https://github.com/tealdeer-rs/tealdeer/pull/287 -[#290]: https://github.com/tealdeer-rs/tealdeer/pull/290 -[#291]: https://github.com/tealdeer-rs/tealdeer/pull/291 -[#293]: https://github.com/tealdeer-rs/tealdeer/pull/293 -[#297]: https://github.com/tealdeer-rs/tealdeer/pull/297 -[#298]: https://github.com/tealdeer-rs/tealdeer/pull/298 -[#299]: https://github.com/tealdeer-rs/tealdeer/pull/299 -[#300]: https://github.com/tealdeer-rs/tealdeer/pull/300 -[#303]: https://github.com/tealdeer-rs/tealdeer/pull/303 -[#305]: https://github.com/tealdeer-rs/tealdeer/pull/305 -[#306]: https://github.com/tealdeer-rs/tealdeer/pull/306 -[#314]: https://github.com/tealdeer-rs/tealdeer/pull/314 -[#315]: https://github.com/tealdeer-rs/tealdeer/pull/315 -[#322]: https://github.com/tealdeer-rs/tealdeer/pull/322 -[#324]: https://github.com/tealdeer-rs/tealdeer/pull/324 -[#327]: https://github.com/tealdeer-rs/tealdeer/pull/327 -[#331]: https://github.com/tealdeer-rs/tealdeer/pull/331 -[#333]: https://github.com/tealdeer-rs/tealdeer/pull/333 -[#336]: https://github.com/tealdeer-rs/tealdeer/pull/336 -[#337]: https://github.com/tealdeer-rs/tealdeer/pull/337 -[#342]: https://github.com/tealdeer-rs/tealdeer/pull/342 -[#354]: https://github.com/tealdeer-rs/tealdeer/pull/354 -[#355]: https://github.com/tealdeer-rs/tealdeer/pull/355 -[#362]: https://github.com/tealdeer-rs/tealdeer/pull/362 -[#386]: https://github.com/tealdeer-rs/tealdeer/pull/386 -[#388]: https://github.com/tealdeer-rs/tealdeer/pull/388 -[#389]: https://github.com/tealdeer-rs/tealdeer/pull/389 -[#399]: https://github.com/tealdeer-rs/tealdeer/pull/399 -[#400]: https://github.com/tealdeer-rs/tealdeer/pull/400 -[#401]: https://github.com/tealdeer-rs/tealdeer/pull/401 -[#407]: https://github.com/tealdeer-rs/tealdeer/pull/407 -[#411]: https://github.com/tealdeer-rs/tealdeer/pull/411 -[#416]: https://github.com/tealdeer-rs/tealdeer/pull/416 -[#417]: https://github.com/tealdeer-rs/tealdeer/pull/417 -[#422]: https://github.com/tealdeer-rs/tealdeer/pull/422 -[#423]: https://github.com/tealdeer-rs/tealdeer/pull/423 -[#425]: https://github.com/tealdeer-rs/tealdeer/pull/425 -[#426]: https://github.com/tealdeer-rs/tealdeer/pull/426 -[#429]: https://github.com/tealdeer-rs/tealdeer/pull/429 -[#430]: https://github.com/tealdeer-rs/tealdeer/pull/430 -[#435]: https://github.com/tealdeer-rs/tealdeer/pull/435 -[#436]: https://github.com/tealdeer-rs/tealdeer/pull/436 -[#439]: https://github.com/tealdeer-rs/tealdeer/pull/439 -[#440]: https://github.com/tealdeer-rs/tealdeer/pull/440 -[#451]: https://github.com/tealdeer-rs/tealdeer/pull/451 +[i34]: https://github.com/dbrgn/tealdeer/issues/34 +[i43]: https://github.com/dbrgn/tealdeer/issues/43 +[i47]: https://github.com/dbrgn/tealdeer/issues/47 +[i48]: https://github.com/dbrgn/tealdeer/issues/48 diff --git a/Cargo.lock b/Cargo.lock index 4fba42b..16e09e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,1671 +1,1859 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 +[[package]] +name = "MacTypes-sys" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] -name = "adler2" -version = "2.0.1" +name = "adler32" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aho-corasick" -version = "1.1.3" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ - "memchr", + "memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "anstream" -version = "0.6.21" +name = "ansi_term" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "arrayvec" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" - -[[package]] -name = "anstyle-parse" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" -dependencies = [ - "windows-sys 0.60.2", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" -dependencies = [ - "anstyle", - "once_cell_polyfill", - "windows-sys 0.60.2", -] - -[[package]] -name = "anyhow" -version = "1.0.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" - -[[package]] -name = "arbitrary" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" -dependencies = [ - "derive_arbitrary", + "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "assert_cmd" -version = "2.0.17" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bd389a4b2970a01282ee455294913c0a43724daedcd1a24c3eb0ec1c1320b66" dependencies = [ - "anstyle", - "bstr", - "doc-comment", - "libc", - "predicates", - "predicates-core", - "predicates-tree", - "wait-timeout", + "escargot 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "predicates 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "predicates-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "predicates-tree 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "autocfg" -version = "1.5.0" +name = "atty" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +dependencies = [ + "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "base64" -version = "0.22.1" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "base64ct" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" +dependencies = [ + "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "bitflags" -version = "2.9.4" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" [[package]] -name = "bstr" -version = "1.12.0" +name = "bitflags" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" -dependencies = [ - "memchr", - "regex-automata", - "serde", -] [[package]] -name = "bumpalo" -version = "3.19.0" +name = "build_const" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "byteorder" -version = "1.5.0" +version = "1.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.10.1" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +dependencies = [ + "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cargo_metadata" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "cc" -version = "1.2.40" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d05d92f4b1fd76aad469d46cdd858ca761576082cd37df81416691e50199fb" -dependencies = [ - "find-msvc-tools", - "shlex", -] - -[[package]] -name = "cesu8" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" [[package]] name = "cfg-if" -version = "1.0.3" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" [[package]] -name = "clap" -version = "4.5.48" +name = "clippy" +version = "0.0.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae" dependencies = [ - "clap_builder", - "clap_derive", + "cargo_metadata 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "clippy_lints 0.0.174 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "clap_builder" -version = "4.5.48" +name = "clippy_lints" +version = "0.0.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9" dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "terminal_size", + "if_chain 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "itertools 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "pulldown-cmark 0.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "quine-mc_cluskey 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "clap_derive" -version = "4.5.47" +name = "cloudabi" +version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c" dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "clap_lex" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" - -[[package]] -name = "colorchoice" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" - -[[package]] -name = "combine" -version = "4.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" -dependencies = [ - "bytes", - "memchr", + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "core-foundation" -version = "0.9.4" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" -dependencies = [ - "core-foundation-sys", - "libc", + "core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "core-foundation-sys" -version = "0.8.7" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +dependencies = [ + "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crc" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "crc32fast" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-deque" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-epoch 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-utils" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "difference" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "docopt" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", + "strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "dtoa" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "either" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" + +[[package]] +name = "encoding_rs" +version = "0.8.13" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if", -] - -[[package]] -name = "der" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" -dependencies = [ - "pem-rfc7468", - "zeroize", -] - -[[package]] -name = "derive_arbitrary" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "difflib" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" - -[[package]] -name = "doc-comment" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" - -[[package]] -name = "env_filter" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" -dependencies = [ - "log", - "regex", + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "env_logger" -version = "0.11.8" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" dependencies = [ - "anstream", - "anstyle", - "env_filter", - "jiff", - "log", -] - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "errno" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" -dependencies = [ - "errno-dragonfly", - "libc", - "winapi", -] - -[[package]] -name = "errno" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" -dependencies = [ - "libc", - "windows-sys 0.61.1", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", + "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "escargot" -version = "0.5.15" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11c3aea32bc97b500c9ca6a72b768a26e558264303d101d3409cf6d57a9ed0cf" dependencies = [ - "log", - "serde", - "serde_json", + "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "etcetera" -version = "0.11.0" +name = "escargot" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de48cc4d1c1d97a20fd819def54b890cadde72ed3ad0c614822a0a433361be96" dependencies = [ - "cfg-if", - "windows-sys 0.61.1", + "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "fastrand" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" - [[package]] name = "filetime" -version = "0.2.26" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc0505cd1b6fa6580283f6bdf70a73fcf4aba1184038c90902b92b3dd0df63ed" dependencies = [ - "cfg-if", - "libc", - "libredox", - "windows-sys 0.60.2", + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.50 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "find-msvc-tools" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0399f9d26e5191ce32c498bebd31e7a3ceabc2745f0ac54af3f335126c3f24b3" - [[package]] name = "flate2" -version = "1.1.2" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" dependencies = [ - "crc32fast", - "libz-rs-sys", - "miniz_oxide", + "crc32fast 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "miniz-sys 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "miniz_oxide_c_api 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "float-cmp" -version = "0.10.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8" dependencies = [ - "num-traits", + "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "fnv" -version = "1.0.7" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foreign-types" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" dependencies = [ - "foreign-types-shared", + "foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "foreign-types-shared" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] -name = "getrandom" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.11.1+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" +name = "fuchsia-zircon" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ - "cfg-if", - "libc", - "r-efi", - "wasi 0.14.7+wasi-0.2.4", + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "hashbrown" -version = "0.16.0" +name = "fuchsia-zircon-sys" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" [[package]] -name = "heck" -version = "0.5.0" +name = "futures" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "futures-cpupool" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "getopts" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "h2" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "string 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "http" -version = "1.3.1" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ - "bytes", - "fnv", - "itoa", + "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "httparse" -version = "1.10.1" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "humantime" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hyper" +version = "0.12.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "h2 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tcp 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "want 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hyper-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.12.19 (registry+https://github.com/rust-lang/crates.io-index)", + "native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "idna" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "if_chain" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "indexmap" -version = "2.11.4" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "iovec" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" dependencies = [ - "equivalent", - "hashbrown", + "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "is_terminal_polyfill" -version = "1.70.1" +name = "itertools" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +dependencies = [ + "either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "itoa" -version = "1.0.15" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] -name = "jiff" -version = "0.2.15" +name = "kernel32-sys" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49" dependencies = [ - "jiff-static", - "log", - "portable-atomic", - "portable-atomic-util", - "serde", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "jiff-static" -version = "0.2.15" +name = "lazy_static" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] [[package]] -name = "jni" -version = "0.21.1" +name = "lazy_static" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" -dependencies = [ - "cesu8", - "cfg-if", - "combine", - "jni-sys", - "log", - "thiserror", - "walkdir", - "windows-sys 0.45.0", -] [[package]] -name = "jni-sys" -version = "0.3.0" +name = "lazycell" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "libc" -version = "0.2.176" +version = "0.2.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" [[package]] -name = "libredox" -version = "0.1.10" +name = "libflate" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" dependencies = [ - "bitflags", - "libc", - "redox_syscall", + "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "crc32fast 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "libz-rs-sys" -version = "0.5.2" +name = "lock_api" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "840db8cf39d9ec4dd794376f38acc40d0fc65eec2a8f484f7fd375b84602becd" dependencies = [ - "zlib-rs", + "owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "linux-raw-sys" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" - [[package]] name = "log" -version = "0.4.28" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "matches" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] name = "memchr" -version = "2.7.6" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +dependencies = [ + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "memoffset" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "mime" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicase 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "mime_guess" +version = "2.0.0-alpha.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "mime 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", + "phf 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_codegen 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", + "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "miniz-sys" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "miniz_oxide" -version = "0.8.9" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ - "adler2", + "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "miniz_oxide_c_api" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", + "crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "miniz_oxide 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "mio" +version = "0.6.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "mio-uds" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "miow" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "native-tls" -version = "0.2.14" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" dependencies = [ - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework 2.11.1", - "security-framework-sys", - "tempfile", + "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.10.16 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.40 (registry+https://github.com/rust-lang/crates.io-index)", + "schannel 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "net2" +version = "0.2.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "nodrop" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "normalize-line-endings" -version = "0.3.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" [[package]] name = "num-traits" -version = "0.2.19" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "num_cpus" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ - "autocfg", + "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "once_cell" -version = "1.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" - -[[package]] -name = "once_cell_polyfill" -version = "1.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" - [[package]] name = "openssl" -version = "0.10.73" +version = "0.10.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" dependencies = [ - "bitflags", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn", + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.40 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "openssl-probe" -version = "0.1.6" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.109" +version = "0.9.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", + "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "pager" -version = "0.16.1" +name = "owning_ref" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2599211a5c97fbbb1061d3dc751fa15f404927e4846e07c643287d6d1f462880" dependencies = [ - "errno 0.2.8", - "libc", + "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "pem-rfc7468" -version = "0.7.0" +name = "parking_lot" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" dependencies = [ - "base64ct", + "lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parking_lot_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "percent-encoding" -version = "2.3.2" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] -name = "pkg-config" -version = "0.3.32" +name = "phf" +version = "0.7.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" - -[[package]] -name = "portable-atomic" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" - -[[package]] -name = "portable-atomic-util" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" dependencies = [ - "portable-atomic", + "phf_shared 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "predicates" -version = "3.1.3" +name = "phf_codegen" +version = "0.7.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573" dependencies = [ - "anstyle", - "difflib", - "float-cmp", - "normalize-line-endings", - "predicates-core", - "regex", + "phf_generator 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_shared 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "phf_generator" +version = "0.7.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "phf_shared 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "phf_shared" +version = "0.7.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pkg-config" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "predicates" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "float-cmp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "normalize-line-endings 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "predicates-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "predicates-core" -version = "1.0.9" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa" [[package]] name = "predicates-tree" -version = "1.0.12" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c" dependencies = [ - "predicates-core", - "termtree", + "predicates-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "treeline 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "proc-macro2" -version = "1.0.101" +version = "0.4.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ - "unicode-ident", + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "pulldown-cmark" +version = "0.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "getopts 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quick-error" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "quine-mc_cluskey" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "quote" -version = "1.0.41" +version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" dependencies = [ - "proc-macro2", + "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "r-efi" -version = "5.3.0" +name = "rand" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +dependencies = [ + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_xorshift 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_chacha" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rand_hc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_isaac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_pcg" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_xorshift" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "redox_syscall" -version = "0.5.17" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "redox_termios" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ - "bitflags", + "redox_syscall 0.1.50 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "regex" -version = "1.11.3" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b5288124840bee7b386bc413c487869b360b2b4ec421ea56425128692f2a82c" dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", + "aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "regex-automata" -version = "0.4.11" +name = "regex" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "833eb9ce86d40ef33cb1306d8accf7bc8ec2bfea4355cbdebb3df68b40925cad" dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", + "aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "regex-syntax" -version = "0.8.6" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" [[package]] -name = "ring" -version = "0.17.14" +name = "regex-syntax" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ - "cc", - "cfg-if", - "getrandom 0.2.16", - "libc", - "untrusted", - "windows-sys 0.52.0", + "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "rustix" -version = "1.1.2" +name = "regex-syntax" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ - "bitflags", - "errno 0.3.14", - "libc", - "linux-raw-sys", - "windows-sys 0.61.1", + "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "rustls" -version = "0.23.32" +name = "remove_dir_all" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd3c25631629d034ce7cd9940adc9d45762d46de2b0f57193c4443b92c6d4d40" dependencies = [ - "log", - "once_cell", - "ring", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "rustls-native-certs" -version = "0.8.1" +name = "reqwest" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" dependencies = [ - "openssl-probe", - "rustls-pki-types", - "schannel", - "security-framework 3.5.1", + "base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "encoding_rs 0.8.13 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.12.19 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper-tls 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libflate 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", + "mime_guess 2.0.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", + "native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_urlencoded 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "uuid 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "rustls-pemfile" -version = "2.2.0" +name = "rustc_version" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "rustls-pki-types" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" -dependencies = [ - "zeroize", -] - -[[package]] -name = "rustls-platform-verifier" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be59af91596cac372a6942530653ad0c3a246cdd491aaa9dcaee47f88d67d5a0" -dependencies = [ - "core-foundation 0.10.1", - "core-foundation-sys", - "jni", - "log", - "once_cell", - "rustls", - "rustls-native-certs", - "rustls-platform-verifier-android", - "rustls-webpki", - "security-framework 3.5.1", - "security-framework-sys", - "webpki-root-certs", - "windows-sys 0.59.0", -] - -[[package]] -name = "rustls-platform-verifier-android" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" - -[[package]] -name = "rustls-webpki" -version = "0.103.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10b3f4191e8a80e6b43eebabfac91e5dcecebb27a71f04e820c47ec41d314bf" -dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "ryu" -version = "1.0.20" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "safemem" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "same-file" -version = "1.0.6" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ - "winapi-util", + "winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "schannel" -version = "0.1.28" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" dependencies = [ - "windows-sys 0.61.1", + "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "security-framework" -version = "2.11.1" +name = "scopeguard" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" -dependencies = [ - "bitflags", - "core-foundation 0.9.4", - "core-foundation-sys", - "libc", - "security-framework-sys", -] [[package]] name = "security-framework" -version = "3.5.1" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" dependencies = [ - "bitflags", - "core-foundation 0.10.1", - "core-foundation-sys", - "libc", - "security-framework-sys", + "core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "security-framework-sys" -version = "2.15.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" dependencies = [ - "core-foundation-sys", - "libc", + "MacTypes-sys 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "semver" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "serde" -version = "1.0.228" +version = "1.0.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" -dependencies = [ - "serde_derive", + "serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_derive" -version = "1.0.228" +version = "1.0.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_json" -version = "1.0.145" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ - "itoa", - "memchr", - "ryu", - "serde", - "serde_core", + "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "serde_spanned" -version = "0.6.9" +name = "serde_urlencoded" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" dependencies = [ - "serde", + "dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "shlex" -version = "1.3.0" +name = "siphasher" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] -name = "simd-adler32" -version = "0.3.7" +name = "slab" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" [[package]] -name = "socks" -version = "0.3.4" +name = "smallvec" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c3dbbd9ae980613c6dd8e28a9407b50509d3803b57624d5dfe8315218cd58b" dependencies = [ - "byteorder", - "libc", - "winapi", + "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "subtle" -version = "2.6.1" +name = "stable_deref_trait" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "string" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "strsim" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.106" +version = "0.15.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", + "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tar" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "filetime 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.50 (registry+https://github.com/rust-lang/crates.io-index)", + "xattr 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tealdeer" -version = "1.8.1" +version = "1.1.0" dependencies = [ - "anyhow", - "assert_cmd", - "clap", - "env_logger", - "escargot", - "etcetera", - "filetime", - "log", - "pager", - "predicates", - "serde", - "serde_derive", - "tempfile", - "toml", - "ureq", - "yansi", - "zip", + "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", + "assert_cmd 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", + "clippy 0.0.174 (registry+https://github.com/rust-lang/crates.io-index)", + "docopt 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", + "escargot 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "flate2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "predicates 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "reqwest 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", + "tar 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", + "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "utime 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tempdir" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tempfile" -version = "3.23.0" +version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" dependencies = [ - "fastrand", - "getrandom 0.3.3", - "once_cell", - "rustix", - "windows-sys 0.61.1", + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.50 (registry+https://github.com/rust-lang/crates.io-index)", + "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "terminal_size" -version = "0.4.3" +name = "termcolor" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b8cb979cb11c32ce1603f8137b22262a9d131aaa5c37b5678025f22b8becd0" dependencies = [ - "rustix", - "windows-sys 0.60.2", + "wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "termtree" -version = "0.5.1" +name = "termion" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.50 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "thiserror-impl" -version = "1.0.69" +name = "thread_local" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ - "proc-macro2", - "quote", - "syn", + "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "time" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.50 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-current-thread 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-fs 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tcp 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-timer 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-udp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-uds 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-codec" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-current-thread" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-executor" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-fs" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-io" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-reactor" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-tcp" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-threadpool" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-timer" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-udp" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-uds" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", + "mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-reactor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "toml" -version = "0.8.23" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", + "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "toml_datetime" -version = "0.6.11" +name = "treeline" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" -dependencies = [ - "serde", -] [[package]] -name = "toml_edit" -version = "0.22.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" -dependencies = [ - "indexmap", - "serde", - "serde_spanned", - "toml_datetime", - "toml_write", - "winnow", -] - -[[package]] -name = "toml_write" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" - -[[package]] -name = "unicode-ident" -version = "1.0.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "ureq" -version = "3.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99ba1025f18a4a3fc3e9b48c868e9beb4f24f4b4b1a325bada26bd4119f46537" -dependencies = [ - "base64", - "der", - "flate2", - "log", - "native-tls", - "percent-encoding", - "rustls", - "rustls-pemfile", - "rustls-pki-types", - "rustls-platform-verifier", - "socks", - "ureq-proto", - "utf-8", - "webpki-root-certs", - "webpki-roots", -] - -[[package]] -name = "ureq-proto" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b4531c118335662134346048ddb0e54cc86bd7e81866757873055f0e38f5d2" -dependencies = [ - "base64", - "http", - "httparse", - "log", -] - -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - -[[package]] -name = "utf8parse" +name = "try-lock" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "ucd-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicase" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicase" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-width" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unreachable" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "url" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "utf8-ranges" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "utime" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "uuid" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "vcpkg" -version = "0.2.15" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] -name = "wait-timeout" -version = "0.2.1" +name = "version_check" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "void" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" -dependencies = [ - "libc", -] [[package]] name = "walkdir" -version = "2.5.0" +version = "2.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ - "same-file", - "winapi-util", + "same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" +name = "want" +version = "0.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - -[[package]] -name = "wasi" -version = "0.14.7+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" dependencies = [ - "wasip2", -] - -[[package]] -name = "wasip2" -version = "1.0.1+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" -dependencies = [ - "wit-bindgen", -] - -[[package]] -name = "webpki-root-certs" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4ffd8df1c57e87c325000a3d6ef93db75279dc3a231125aac571650f22b12a" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "webpki-roots" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" -dependencies = [ - "rustls-pki-types", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "winapi" -version = "0.3.9" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.11" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.1", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows-link" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.4", -] - -[[package]] -name = "windows-sys" -version = "0.61.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f109e41dd4a3c848907eb83d5a42ea98b3769495597450cf6d153507b166f0f" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.53.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d42b7b7f66d2a06854650af09cfdf8713e427a439c97ad65a6375318033ac4b" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnu" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_i686_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" - -[[package]] -name = "winnow" -version = "0.7.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" -dependencies = [ - "memchr", -] - -[[package]] -name = "wit-bindgen" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" - -[[package]] -name = "yansi" +name = "wincolor" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" - -[[package]] -name = "zeroize" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" - -[[package]] -name = "zip" -version = "5.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f852905151ac8d4d06fdca66520a661c09730a74c6d4e2b0f27b436b382e532" dependencies = [ - "arbitrary", - "crc32fast", - "flate2", - "indexmap", - "memchr", - "zopfli", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "zlib-rs" -version = "0.5.2" +name = "ws2_32-sys" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f06ae92f42f5e5c42443fd094f245eb656abf56dd7cce9b8b263236565e00f2" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] -name = "zopfli" -version = "0.8.2" +name = "xattr" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edfc5ee405f504cd4984ecc6f14d02d55cfda60fa4b689434ef4102aae150cd7" dependencies = [ - "bumpalo", - "crc32fast", - "log", - "simd-adler32", + "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", ] + +[[package]] +name = "xdg" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum MacTypes-sys 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7dbbe033994ae2198a18517c7132d952a29fb1db44249a1234779da7c50f4698" +"checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" +"checksum aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9a933f4e58658d7b12defcf96dc5c720f20832deebe3e0a19efd3b6aaeeb9e" +"checksum ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6b3568b48b7cefa6b8ce125f9bb4989e52fbcc29ebea88df04cc7c5f12f70455" +"checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71" +"checksum assert_cmd 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b7ac5c260f75e4e4ba87b7342be6edcecbcb3eb6741a0507fda7ad115845cc65" +"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" +"checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" +"checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" +"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" +"checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39" +"checksum byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "94f88df23a25417badc922ab0f5716cc1330e87f71ddd9203b3a3ccd9cedf75d" +"checksum bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "40ade3d27603c2cb345eb0912aec461a6dec7e06a4ae48589904e808335c7afa" +"checksum cargo_metadata 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "be1057b8462184f634c3a208ee35b0f935cfd94b694b26deadccd98732088d7b" +"checksum cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4a8b715cb4597106ea87c7c84b2f1d452c7492033765df7f32651e66fcf749" +"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4" +"checksum clippy 0.0.174 (registry+https://github.com/rust-lang/crates.io-index)" = "1b03ded6eba74b16dbeb598be58e874bc72f6cf12f7ccc577b328091e1d4392a" +"checksum clippy_lints 0.0.174 (registry+https://github.com/rust-lang/crates.io-index)" = "9b8e508648d6d41040e0061f45fc5562b3af5c8a7d67853f15841fb531c8e984" +"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +"checksum core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "286e0b41c3a20da26536c6000a280585d519fd07b3956b43aed8a79e9edce980" +"checksum core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "716c271e8613ace48344f723b60b900a93150271e5be206212d052bbc0883efa" +"checksum crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" +"checksum crc32fast 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e91d5240c6975ef33aeb5f148f35275c25eda8e8a5f95abe421978b05b8bf192" +"checksum crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "05e44b8cf3e1a625844d1750e1f7820da46044ff6d28f4d43e455ba3e5bb2c13" +"checksum crossbeam-epoch 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f10a4f8f409aaac4b16a5474fb233624238fcdeefb9ba50d5ea059aab63ba31c" +"checksum crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "41ee4864f4797060e52044376f7d107429ce1fb43460021b126424b7180ee21a" +"checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" +"checksum docopt 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d8acd393692c503b168471874953a2531df0e9ab77d0b6bbc582395743300a4a" +"checksum dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6d301140eb411af13d3115f9a562c85cc6b541ade9dfa314132244aaee7489dd" +"checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0" +"checksum encoding_rs 0.8.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1a8fa54e6689eb2549c4efed8d00d7f3b2b994a064555b0e8df4ae3764bcc4be" +"checksum env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)" = "15b0a4d2e39f8420210be8b27eeda28029729e2fd4291019455016c348240c38" +"checksum escargot 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "19db1f7e74438642a5018cdf263bb1325b2e792f02dd0a3ca6d6c0f0d7b1d5a5" +"checksum escargot 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ceb9adbf9874d5d028b5e4c5739d22b71988252b25c9c98fe7cf9738bee84597" +"checksum filetime 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a2df5c1a8c4be27e7707789dc42ae65976e60b394afd293d1419ab915833e646" +"checksum flate2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2291c165c8e703ee54ef3055ad6188e3d51108e2ded18e9f2476e774fc5ad3d4" +"checksum float-cmp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "134a8fa843d80a51a5b77d36d42bc2def9edcb0262c914861d08129fd1926600" +"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" +"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +"checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +"checksum futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)" = "49e7653e374fe0d0c12de4250f0bdb60680b8c80eed558c5c7538eec9c89e21b" +"checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" +"checksum getopts 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "0a7292d30132fb5424b354f5dc02512a86e4c516fe544bb7a25e7f266951b797" +"checksum h2 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "1ac030ae20dee464c5d0f36544d8b914a6bc606da44a57e052d2b0f5dae129e0" +"checksum http 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "02096a6d2c55e63f7fcb800690e4f889a25f6ec342e3adb4594e293b625215ab" +"checksum httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e8734b0cfd3bc3e101ec59100e101c2eecd19282202e87808b3037b442777a83" +"checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114" +"checksum hyper 0.12.19 (registry+https://github.com/rust-lang/crates.io-index)" = "f1ebec079129e43af5e234ef36ee3d7e6085687d145b7ea653b262d16c6b65f1" +"checksum hyper-tls 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "32cd73f14ad370d3b4d4b7dce08f69b81536c82e39fcc89731930fe5788cd661" +"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" +"checksum if_chain 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4bac95d9aa0624e7b78187d6fb8ab012b41d9f6f54b1bcb61e61c4845f8357ec" +"checksum indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e81a7c05f79578dbc15793d8b619db9ba32b4577003ef3af1a91c416798c58d" +"checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" +"checksum itertools 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d3f2be4da1690a039e9ae5fd575f706a63ad5a2120f161b1d653c9da3930dd21" +"checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b" +"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +"checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" +"checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1" +"checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" +"checksum libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "2d2857ec59fadc0773853c664d2d18e7198e83883e7060b63c924cb077bd5c74" +"checksum libflate 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)" = "bff3ac7d6f23730d3b533c35ed75eef638167634476a499feef16c428d74b57b" +"checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" +"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" +"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" +"checksum memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "db4c41318937f6e76648f42826b1d9ade5c09cafb5aef7e351240a70f39206e9" +"checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" +"checksum mime 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)" = "0a907b83e7b9e987032439a387e187119cddafc92d5c2aaeb1d92580a793f630" +"checksum mime_guess 2.0.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "30de2e4613efcba1ec63d8133f344076952090c122992a903359be5a4f99c3ed" +"checksum miniz-sys 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "0300eafb20369952951699b68243ab4334f4b10a88f411c221d444b36c40e649" +"checksum miniz_oxide 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5ad30a47319c16cde58d0314f5d98202a80c9083b5f61178457403dfb14e509c" +"checksum miniz_oxide_c_api 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "28edaef377517fd9fe3e085c37d892ce7acd1fbeab9239c5a36eec352d8a8b7e" +"checksum mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)" = "71646331f2619b1026cc302f87a2b8b648d5c6dd6937846a16cc8ce0f347f432" +"checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" +"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" +"checksum native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ff8e08de0070bbf4c31f452ea2a70db092f36f6f2e4d897adf5674477d488fb2" +"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" +"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" +"checksum normalize-line-endings 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2e0a1a39eab95caf4f5556da9289b9e68f0aafac901b2ce80daaf020d3b733a8" +"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" +"checksum num_cpus 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5a69d464bdc213aaaff628444e99578ede64e9c854025aa43b9796530afa9238" +"checksum openssl 0.10.16 (registry+https://github.com/rust-lang/crates.io-index)" = "ec7bd7ca4cce6dbdc77e7c1230682740d307d1218a87fb0349a571272be749f9" +"checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" +"checksum openssl-sys 0.9.40 (registry+https://github.com/rust-lang/crates.io-index)" = "1bb974e77de925ef426b6bc82fce15fd45bdcbeb5728bffcfc7cdeeb7ce1c2d6" +"checksum owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13" +"checksum parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f0802bff09003b291ba756dc7e79313e51cc31667e94afbe847def490424cde5" +"checksum parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad7f7e6ebdc79edff6fdcb87a55b620174f7a989e3eb31b65231f4af57f00b8c" +"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" +"checksum phf 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)" = "cec29da322b242f4c3098852c77a0ca261c9c01b806cae85a5572a1eb94db9a6" +"checksum phf_codegen 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)" = "7d187f00cd98d5afbcd8898f6cf181743a449162aeb329dcd2f3849009e605ad" +"checksum phf_generator 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)" = "03dc191feb9b08b0dc1330d6549b795b9d81aec19efe6b4a45aec8d4caee0c4b" +"checksum phf_shared 0.7.23 (registry+https://github.com/rust-lang/crates.io-index)" = "b539898d22d4273ded07f64a05737649dc69095d92cb87c7097ec68e3f150b93" +"checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c" +"checksum predicates 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fa984b7cd021a0bf5315bcce4c4ae61d2a535db2a8d288fc7578638690a7b7c3" +"checksum predicates-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "06075c3a3e92559ff8929e7a280684489ea27fe44805174c3ebd9328dcb37178" +"checksum predicates-tree 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8e63c4859013b38a76eca2414c64911fba30def9e3202ac461a2d22831220124" +"checksum proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)" = "77619697826f31a02ae974457af0b29b723e5619e113e9397b8b82c6bd253f09" +"checksum pulldown-cmark 0.0.15 (registry+https://github.com/rust-lang/crates.io-index)" = "378e941dbd392c101f2cb88097fa4d7167bc421d4b88de3ff7dbee503bc3233b" +"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" +"checksum quine-mc_cluskey 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "07589615d719a60c8dd8a4622e7946465dfef20d1a428f969e3443e7386d5f45" +"checksum quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "53fa22a1994bd0f9372d7a816207d8a2677ad0325b073f5c5332760f0fb62b5c" +"checksum rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8356f47b32624fef5b3301c1be97e5944ecdd595409cc5da11d05f211db6cfbd" +"checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c" +"checksum rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae9d223d52ae411a33cf7e54ec6034ec165df296ccd23533d671a28252b6f66a" +"checksum rand_chacha 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "771b009e3a508cb67e8823dda454aaa5368c7bc1c16829fb77d3e980440dd34a" +"checksum rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1961a422c4d189dfb50ffa9320bf1f2a9bd54ecb92792fb9477f99a1045f3372" +"checksum rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0905b6b7079ec73b314d4c748701f6931eb79fd97c668caa3f1899b22b32c6db" +"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" +"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" +"checksum rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "086bd09a33c7044e56bb44d5bdde5a60e7f119a9e95b0775f545de759a32fe05" +"checksum rand_xorshift 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "effa3fcaa47e18db002bdde6060944b6d2f9cfd8db471c30e873448ad9187be3" +"checksum redox_syscall 0.1.50 (registry+https://github.com/rust-lang/crates.io-index)" = "52ee9a534dc1301776eff45b4fa92d2c39b1d8c3d3357e6eb593e0d795506fc2" +"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" +"checksum regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9329abc99e39129fcceabd24cf5d85b4671ef7c29c50e972bc5afe32438ec384" +"checksum regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37e7cbbd370869ce2e8dff25c7018702d10b21a20ef7135316f8daecd6c25b7f" +"checksum regex-syntax 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8e931c58b93d86f080c734bfd2bce7dd0079ae2331235818133c8be7f422e20e" +"checksum regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7d707a4fa2637f2dca2ef9fd02225ec7661fe01a53623c1e6515b6916511f7a7" +"checksum regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4e47a2ed29da7a9e1960e1639e7a982e6edc6d49be308a3b02daf511504a16d1" +"checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" +"checksum reqwest 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ab52e462d1e15891441aeefadff68bdea005174328ce3da0a314f2ad313ec837" +"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +"checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7" +"checksum safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dca453248a96cb0749e36ccdfe2b0b4e54a61bfef89fb97ec621eb8e0a93dd9" +"checksum same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8f20c4be53a8a1ff4c1f1b2bd14570d2f634628709752f0702ecdd2b3f9a5267" +"checksum schannel 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "0e1a231dc10abf6749cfa5d7767f25888d484201accbd919b66ab5413c502d56" +"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" +"checksum security-framework 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "697d3f3c23a618272ead9e1fb259c1411102b31c6af8b93f1d64cca9c3b0e8e0" +"checksum security-framework-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "40d95f3d7da09612affe897f320d78264f0d2320f3e8eea27d12bd1bd94445e2" +"checksum semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a3186ec9e65071a2095434b1f5bb24838d4e8e130f584c790f6033c79943537" +"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +"checksum serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)" = "0e732ed5a5592c17d961555e3b552985baf98d50ce418b7b655f31f6ba7eb1b7" +"checksum serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d6115a3ca25c224e409185325afc16a0d5aaaabc15c42b09587d6f1ba39a5b" +"checksum serde_json 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)" = "bdf540260cfee6da923831f4776ddc495ada940c30117977c70f1313a6130545" +"checksum serde_urlencoded 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d48f9f99cd749a2de71d29da5f948de7f2764cc5a9d7f3c97e3514d4ee6eabf2" +"checksum siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" +"checksum slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5f9776d6b986f77b35c6cf846c11ad986ff128fe0b2b63a3628e3755e8d3102d" +"checksum smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "b73ea3738b47563803ef814925e69be00799a8c07420be8b996f8e98fb2336db" +"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" +"checksum string 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "98998cced76115b1da46f63388b909d118a37ae0be0f82ad35773d4a4bc9d18d" +"checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694" +"checksum syn 0.15.23 (registry+https://github.com/rust-lang/crates.io-index)" = "9545a6a093a3f0bd59adb472700acc08cad3776f860f16a897dfce8c88721cbc" +"checksum tar 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)" = "a303ba60a099fcd2aaa646b14d2724591a96a75283e4b7ed3d1a1658909d9ae2" +"checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" +"checksum tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "7e91405c14320e5c79b3d148e1c86f40749a36e490642202a31689cb1a3452b2" +"checksum termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4096add70612622289f2fdcdbd5086dc81c1e2675e6ae58d6c4f62a16c6d7f2f" +"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" +"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" +"checksum time 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "847da467bf0db05882a9e2375934a8a55cffdc9db0d128af1518200260ba1f6c" +"checksum tokio 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "a7817d4c98cc5be21360b3b37d6036fe9b7aefa5b7a201b7b16ff33423822f7d" +"checksum tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c501eceaf96f0e1793cf26beb63da3d11c738c4a943fdf3746d81d64684c39f" +"checksum tokio-current-thread 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "331c8acc267855ec06eb0c94618dcbbfea45bed2d20b77252940095273fb58f6" +"checksum tokio-executor 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c117b6cf86bb730aab4834f10df96e4dd586eff2c3c27d3781348da49e255bde" +"checksum tokio-fs 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "60ae25f6b17d25116d2cba342083abe5255d3c2c79cb21ea11aa049c53bf7c75" +"checksum tokio-io 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "7392fe0a70d5ce0c882c4778116c519bd5dbaa8a7c3ae3d04578b3afafdcda21" +"checksum tokio-reactor 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "502b625acb4ee13cbb3b90b8ca80e0addd263ddacf6931666ef751e610b07fb5" +"checksum tokio-tcp 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7ad235e9dadd126b2d47f6736f65aa1fdcd6420e66ca63f44177bc78df89f912" +"checksum tokio-threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "56c5556262383032878afad66943926a1d1f0967f17e94bd7764ceceb3b70e7f" +"checksum tokio-timer 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "4f37f0111d76cc5da132fe9bc0590b9b9cfd079bc7e75ac3846278430a299ff8" +"checksum tokio-udp 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "66268575b80f4a4a710ef83d087fdfeeabdce9b74c797535fbac18a2cb906e92" +"checksum tokio-uds 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "99ce87382f6c1a24b513a72c048b2c8efe66cb5161c9061d00bee510f08dc168" +"checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" +"checksum treeline 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41" +"checksum try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" +"checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86" +"checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" +"checksum unicase 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9d3218ea14b4edcaccfa0df0a64a3792a2c32cc706f1b336e48867f9d3147f90" +"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +"checksum unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6a0180bc61fc5a987082bfa111f4cc95c4caff7f9799f3e46df09163a937aa25" +"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" +"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" +"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" +"checksum url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" +"checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737" +"checksum utime 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "055058552ca15c566082fc61da433ae678f78986a6f16957e33162d1b218792a" +"checksum uuid 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dab5c5526c5caa3d106653401a267fed923e7046f35895ffcb5ca42db64942e6" +"checksum vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "def296d3eb3b12371b2c7d0e83bfe1403e4db2d7a0bba324a12b21c4ee13143d" +"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" +"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +"checksum walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9d9d7ed3431229a144296213105a390676cc49c9b6a72bd19f3176c98e129fa1" +"checksum want 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "797464475f30ddb8830cc529aaaae648d581f99e2036a928877dfde027ddf6b3" +"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" +"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0" +"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "afc5508759c5bf4285e61feb862b6083c8480aec864fa17a81fdec6f69b461ab" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +"checksum wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "561ed901ae465d6185fa7864d63fbd5720d0ef718366c9a4dc83cf6170d7e9ba" +"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +"checksum xattr 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c" +"checksum xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57" diff --git a/Cargo.toml b/Cargo.toml index 1d98992..a8c99bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,61 +1,46 @@ [package] -authors = [ - "Danilo Bargen ", - "Niklas Mohrin ", -] +authors = ["Danilo Bargen "] description = "Fetch and show tldr help pages for many CLI commands. Full featured offline client with caching support." -homepage = "https://github.com/tealdeer-rs/tealdeer/" -license = "MIT OR Apache-2.0" +homepage = "https://github.com/dbrgn/tealdeer/" +license = "MIT/Apache-2.0" name = "tealdeer" readme = "README.md" -repository = "https://github.com/tealdeer-rs/tealdeer/" -documentation = "https://tealdeer-rs.github.io/tealdeer/" -version = "1.8.1" -include = ["/src/**/*", "/tests/**/*", "/Cargo.toml", "/README.md", "/LICENSE-*", "/screenshot.png", "completion/*"] -rust-version = "1.87" # MSRV -edition = "2021" +repository = "https://github.com/dbrgn/tealdeer/" +version = "1.1.0" +include = ["/src/**/*", "/tests/**/*", "/Cargo.toml", "/README.md", "/LICENSE-*", "/screenshot.png", "/bash_tealdeer"] +edition = "2018" [[bin]] name = "tldr" path = "src/main.rs" [dependencies] -anyhow = "1" -clap = { version = "4", features = ["std", "derive", "help", "usage", "cargo", "error-context", "color", "wrap_help"], default-features = false } -env_logger = { version = "0.11", optional = true } -etcetera = "0.11.0" +ansi_term = "0.10.2" +clippy = { version = "0.0.174", optional = true } +docopt = "0.8.1" +env_logger = { version = "0.5", optional = true } +flate2 = "1.0" log = "0.4" serde = "1.0.21" serde_derive = "1.0.21" -ureq = { version = "3.0.8", default-features = false, features = ["gzip", "socks-proxy"] } -toml = "0.8.19" -yansi = "1" -zip = { version = "5.1.1", default-features = false, features = ["deflate"] } - -[target.'cfg(not(windows))'.dependencies] -pager = "0.16" +tar = "0.4.14" +time = "0.1.38" +toml = "0.4.6" +walkdir = "2.0.1" +xdg = "2.1.0" +reqwest = { version = "0.9.5", optional = true } [dev-dependencies] -assert_cmd = "2.0.1" -escargot = "0.5" -predicates = "3.1.2" -tempfile = "3.1.0" -filetime = "0.2.10" +assert_cmd = "0.10" +escargot = "0.4" +predicates = "1.0" +tempdir = "^0.3" +utime = "0.2.0" [features] -# native-tls is not enabled by default, because it is difficult to build for musl -default = ["rustls-with-webpki-roots", "rustls-with-native-roots"] +default = ["networking"] logging = ["env_logger"] - -# At least one of variants for `ureq` HTTP client must be selected. -native-tls = ["ureq/native-tls", "ureq/platform-verifier"] -rustls-with-webpki-roots = ["ureq/rustls"] # ureq uses WebPKI roots by default -rustls-with-native-roots = ["ureq/rustls", "ureq/platform-verifier"] - -ignore-online-tests = [] +networking = ["reqwest"] [profile.release] -strip = true -opt-level = 3 lto = true -codegen-units = 1 diff --git a/LICENSE-MIT b/LICENSE-MIT index 1c952f1..2249a66 100644 --- a/LICENSE-MIT +++ b/LICENSE-MIT @@ -1,4 +1,4 @@ -Copyright (C) 2015-2021 Danilo Bargen and contributors +Copyright (C) 2015-2018 Danilo Bargen and contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/README.md b/README.md index 859d06f..dbefa2a 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,20 @@ # tealdeer -![teal deer](docs/src/deer.png) +![teal deer](deer.png) -|Crate|CI (Linux/macOS/Windows)| -|:---:|:---:| -|[![Crates.io][crates-io-badge]][crates-io]|[![GitHub CI][github-actions-badge]][github-actions]| +|Crate|Linux|macOS| +|:---:|:---:|:---:| +|[![Crates.io][crates-io-badge]][crates-io]|[![Circle CI][circle-ci-badge]][circle-ci]|[![Travis CI][travis-ci-badge]][travis-ci]| A very fast implementation of [tldr](https://github.com/tldr-pages/tldr) in Rust: Simplified, example based and community-driven man pages. -Screenshot of tldr command +Screenshot of tldr command If you pronounce "tldr" in English, it sounds somewhat like "tealdeer". Hence the project name :) In case you're in a hurry and just want to quickly try tealdeer, you can find static -binaries on the [GitHub releases page](https://github.com/tealdeer-rs/tealdeer/releases/)! - - -## Docs (Installing, Usage, Configuration) - -User documentation is available at ! - -The docs are generated using [mdbook](https://rust-lang.github.io/mdBook/index.html). -They can be edited through the markdown files in the `docs/src/` directory. +binaries on the [GitHub releases page](https://github.com/dbrgn/tealdeer/releases/)! ## Goals @@ -31,40 +23,103 @@ High level project goals: - [x] Download and cache pages - [x] Don't require a network connection for anything besides updating the cache -- [x] Command line interface similar or equivalent to the [NodeJS client][node-gh] -- [x] Comply with the [tldr client specification][client-spec] -- [x] Advanced highlighting and configuration +- [x] Command line interface similar or equivalent to the [NodeJS client][tldr-node-client] - [x] Be fast -A tool like `tldr` should be as frictionless as possible to use and show the -output as fast as possible. +A tool like `tldr` should be as frictionless as possible to use. It should be +easy to invoke (just `tldr tar`, not using another subcommand like `tldr find +tar`) and it should show the output as fast as possible. -We think that `tealdeer` reaches these goals. We put together a (more or less) -reproducible benchmark that compiles a handful of clients from source and -measures the execution times on a cold disk cache. The benchmarking is run in a -Docker container using sharkdp's [`hyperfine`][hyperfine-gh] -([Dockerfile][benchmark-dockerfile]). +tealdeer reaches these goals. During a (highly non-scientific) test (see +[#38](https://github.com/dbrgn/tealdeer/issues/38) for details), I tested the +invocation speed of `tldr ` for a few of the existing clients: -| Client (50 runs, 17.10.2021) | Programming Language | Mean in ms | Deviation in ms | Comments | -| :---: | :---: | :---: | :---: | :---: | -| [`outfieldr`][outfieldr-gh] | Zig | 9.1 | 0.5 | no user configuration | -| `tealdeer` | Rust | 13.2 | 0.5 | | -| [`fast-tldr`][fast-tldr-gh] | Haskell | 17.0 | 0.6 | no example highlighting | -| [`tldr-hs`][hs-gh] | Haskell | 25.1 | 0.5 | no example highlighting | -| [`tldr-bash`][bash-gh] | Bash | 30.0 | 0.8 | | -| [`tldr-python-client`][python-gh] | Python | 87.0 | 2.4 | | -| [`tldr-node-client`][node-gh] | JavaScript / NodeJS | 407.1 | 12.9 | | +| Client | Times (ms) | Avg of 5 (ms) | +| --- | --- | --- | +| [Tealdeer](https://github.com/dbrgn/tealdeer/) | `15/11/5/5/11` | `9.4` (100%) | +| [C client](https://github.com/tldr-pages/tldr-cpp-client) | `11/5/12/11/15` | `10.8` (115%) | +| [Bash client](https://github.com/pepa65/tldr-bash-client) | `15/19/22/25/24` | `21.0` (223%) | +| [Go client by k3mist](https://github.com/k3mist/tldr/) | `98/96/100/95/101` | `98.8` (1'051%) | +| [Python client](https://github.com/lord63/tldr.py) | `152/148/151/158/140` | `149.8` (1'594%) | +| [NodeJS client](https://github.com/tldr-pages/tldr-node-client) | `169/171/170/170/170` | `170.0` (1'809%) | -As you can see, `tealdeer` is one of the fastest of the tested clients. -However, we strive for useful features and code quality over raw performance, -even if that means that we don't come out on top in this friendly competition. -That said, we are still optimizing the code, for example when the `outfieldr` -developers [suggested to switch][outfieldr-comment-tls] to a native TLS -implementation instead of the native libraries. +tealdeer was the winner here, although the C client and the Bash client are in +the same speed class. Interpreted languages are clearly much slower to invoke, +a delay of 170 milliseconds is definitely noticeable and increases friction for +the user. -## Development +These are the clients I tried but failed to compile or run: +[Haskell client](https://github.com/psibi/tldr-hs), +[Ruby client](https://github.com/YellowApple/tldrb), +[Perl client](https://github.com/skaji/perl-tldr), +[Go client by anoopengineer](https://github.com/anoopengineer/tldr/), +[PHP client](https://github.com/BrainMaestro/tldr-php). -Creating a debug build with logging enabled: + +## Usage + + tldr [options] + tldr [options] + + Options: + + -h --help Show this screen + -v --version Show version information + -l --list List all commands in the cache + -f --render Render a specific markdown file + -o --os Override the operating system [linux, osx, sunos] + -u --update Update the local cache + -c --clear-cache Clear the local cache + -q --quiet Suppress informational messages + --config-path Show config file path + --seed-config Create a basic config + + Examples: + + $ tldr tar + $ tldr --list + + To control the cache: + + $ tldr --update + $ tldr --clear-cache + + To render a local file (for testing): + + $ tldr --render /path/to/file.md + + +## Installing + +### Static Binaries (Linux) + +Static binary builds (currently for Linux only) are available on the +[GitHub releases page](https://github.com/dbrgn/tealdeer/releases). +Simply download the binary for your platform and run it! + +Builds for other platforms are planned. + +### Cargo Install (any platform) + +Build and install the tool via cargo... + + $ cargo install tealdeer + +### From Package Manager + +tealdeer has been added to a few package managers: + +- Arch Linux AUR: [`tealdeer`](https://aur.archlinux.org/packages/tealdeer/) + or [`tealdeer-git`](https://aur.archlinux.org/packages/tealdeer-git/) +- macOS Homebrew: [`tealdeer`](https://formulae.brew.sh/formula/tealdeer) +- Nix: [`tealdeer`](https://nixos.org/nixos/packages.html#tealdeer) +- Void Linux XBPS: [`tealdeer`](https://github.com/void-linux/void-packages/tree/master/srcpkgs/tealdeer) + +### From Source (any platform) + +tealdeer requires at least Rust 1.31. + +Debug build with logging enabled: $ cargo build --features logging @@ -76,31 +131,82 @@ To enable the log output, set the `RUST_LOG` env variable: $ export RUST_LOG=tldr=debug + + +## Configuration + +The tldr command can be customized with a config file called `config.toml`. +Creating the config file can be done manually or with the help of tldr: + + $ tldr --seed-config + +The configuration file path follows OS conventions. It can be queried with the following command: + + $ tldr --config-path + +### Style + +Using the config file, the style (e.g. colors or underlines) can be customized. + +Possible styles: + +- `description`: The initial description text +- `command_name`: The command name as part of the example code +- `example_text`: The text that describes an example +- `example_code`: The example itself, except the `command_name` and `example_variable` +- `example_variable`: The variables in the example + +Currently supported attributes: + +- `foreground` (color string, see below) +- `background` (color string, see below) +- `underline` (`true` or `false`) +- `bold` (`true` or `false`) + +The currently supported colors are: + +- `black` +- `red` +- `green` +- `yellow` +- `blue` +- `purple` +- `cyan` +- `white` + +Example customization: + +Screenshot of customized version + + +## Autocompletion + +- *Bash*: copy `bash_tealdeer` to `/usr/share/bash-completion/completions/tldr` +- *Fish*: copy `fish_tealdeer` to `~/.config/fish/completions/tldr.fish` + +## Development + To run tests: $ cargo test +(Note that integration tests are a bit slow, since they invoke `cargo build` in different configurations.) + To run lints: $ rustup component add clippy $ cargo clean && cargo clippy -### AI Policy +## Build Flags -Using AI is generally discouraged. However, if it is used as part of a contribution, the contributor MUST: +tealdeer knows the following feature flags: -1. Clearly mark what parts (if any) of a contribution were created with the help of AI tools. This includes issue and pull request comments. -2. Check all output of AI tools before sharing it with others in the tealdeer project. -3. Not post slop, spam, or low quality contributions. This includes pull request descriptions and comments with excessive text and markdown flair. -4. Leave small or easy tasks to new contributors who want to learn without the use of AI. This is to maintain the presence of the `good-first-issue` tag. -5. Be respectful of everyone's time: *maintainers and other contributors will be reviewing your PRs.* +- `logging`: This enables logging support through [env_logger](https://docs.rs/env_logger/*/env_logger/) +- `networking`: This enables support for updating the cache from the internet - -## MSRV (Minimally Supported Rust Version) - -When publishing a tealdeer release, the Rust version required to build it -should be stable for at least a month. +By default, only the `networking` feature is enabled. To build tealdeer without +networking support, use the `--no-default-features` Cargo flag. ## License @@ -119,23 +225,15 @@ Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. -Thanks to @severen for coming up with the name "tealdeer"! +Thanks to @SShrike for coming up with the name "tealdeer"! -[node-gh]: https://github.com/tldr-pages/tldr-node-client -[hs-gh]: https://github.com/psibi/tldr-hs -[fast-tldr-gh]: https://github.com/gutjuri/fast-tldr -[bash-gh]: https://4e4.win/tldr -[outfieldr-gh]: https://gitlab.com/ve-nt/outfieldr -[python-gh]: https://github.com/tldr-pages/tldr-python-client - -[benchmark-dockerfile]: https://github.com/tealdeer-rs/tealdeer/blob/main/benchmarks/Dockerfile -[client-spec]: https://github.com/tldr-pages/tldr/blob/main/CLIENT-SPECIFICATION.md -[hyperfine-gh]: https://github.com/sharkdp/hyperfine -[outfieldr-comment-tls]: https://github.com/tealdeer-rs/tealdeer/issues/129#issuecomment-833596765 +[tldr-node-client]: https://github.com/tldr-pages/tldr-node-client -[github-actions]: https://github.com/tealdeer-rs/tealdeer/actions?query=branch%3Amain -[github-actions-badge]: https://github.com/tealdeer-rs/tealdeer/actions/workflows/ci.yml/badge.svg?branch=main +[circle-ci]: https://circleci.com/gh/dbrgn/tealdeer/tree/master +[circle-ci-badge]: https://circleci.com/gh/dbrgn/tealdeer/tree/master.svg?style=shield +[travis-ci]: https://travis-ci.org/dbrgn/tealdeer +[travis-ci-badge]: https://travis-ci.org/dbrgn/tealdeer.svg?branch=master [crates-io]: https://crates.io/crates/tealdeer [crates-io-badge]: https://img.shields.io/crates/v/tealdeer.svg diff --git a/RELEASING.md b/RELEASING.md index f182519..aed6a9b 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -7,16 +7,12 @@ Run linting: Set variables: $ export VERSION=X.Y.Z - $ export GPG_KEY=20EE002D778AE197EF7D0D2CB993FF98A90C9AB1 + $ export GPG_KEY=EA456E8BAF0109429583EED83578F667F2F3A5FA Update version numbers: $ vim Cargo.toml - $ cargo update -p tealdeer - -Update docs: - - $ cargo run -- --help > docs/src/usage.txt + $ cargo update Update changelog: @@ -32,4 +28,6 @@ Publish: $ cargo publish $ git push && git push --tags -Then publish the release on GitHub. +Create release binaries: + + $ ./release-build.sh diff --git a/bash_tealdeer b/bash_tealdeer new file mode 100644 index 0000000..64efb08 --- /dev/null +++ b/bash_tealdeer @@ -0,0 +1,30 @@ +# tealdeer bash completion + +_tealdeer() +{ + local cur prev words cword + _init_completion || return + + case $prev in + -h|--help|-v|--version|-l|--list|-u|--update|-c|--clear-cache|--config-path|--seed-config|-q|--quiet) + return + ;; + -f|--render) + _filedir + return + ;; + -o|--os) + COMPREPLY=( $(compgen -W 'linux osx sunos' -- "${cur}") ) + return + ;; + esac + + if [[ $cur == -* ]]; then + COMPREPLY=( $( compgen -W '$( _parse_help "$1" )' -- "$cur" ) ) + return + fi + + COMPREPLY=( $(compgen -W '$( tldr -l | tr -d , )' -- "${cur}") ) +} + +complete -F _tealdeer tldr diff --git a/benchmarks/Dockerfile b/benchmarks/Dockerfile deleted file mode 100644 index 2a36fe0..0000000 --- a/benchmarks/Dockerfile +++ /dev/null @@ -1,139 +0,0 @@ -# Benchmark Dockerfile for tealdeer -# -# To run the benchmarks, execute -# -# docker build --pull -t tldr-benchmark . -# docker run --privileged --rm -it tldr-benchmark -# -# as root in the directory of this Dockerfile. This will build the compared -# clients and benchmark them with `hyperfine` at the end. -# -# The `--privileged` flag is needed to drop the disk caches before every run. If -# you want to test with hot caches or don't want to use this flag, you will have -# to remove the `--prepare` line from the `hyperfine` command at the end of this -# file and rebuild the image. - -################################################################################ - -FROM rust AS tealdeer-builder - -WORKDIR /build -RUN git clone https://github.com/tealdeer-rs/tealdeer.git \ - && cd tealdeer \ - && cargo build --release \ - && mkdir /build-outputs \ - && cp target/release/tldr /build-outputs/tealdeer - -################################################################################ - -FROM ubuntu:latest AS tldr-c-builder - -WORKDIR /build -RUN apt-get update && apt-get install -y build-essential git && rm -rf /var/lib/apt/lists/* -RUN git clone https://github.com/tldr-pages/tldr-c-client.git \ - && cd tldr-c-client \ - && DEBIAN_FRONTEND=noninteractive ./deps.sh \ - && make \ - && mkdir /build-outputs /deps \ - && cp tldr /build-outputs/tldr-c \ - && cp deps.sh /deps/tldr-c-deps.sh - -################################################################################ - -FROM haskell AS haskell-builder - -WORKDIR /build - -RUN git clone https://github.com/psibi/tldr-hs.git \ - && cd tldr-hs \ - && stack build --install-ghc - -RUN git clone https://github.com/gutjuri/fast-tldr \ - && cd fast-tldr \ - && stack build --install-ghc - -RUN mkdir /build-outputs \ - && find tldr-hs/.stack-work/dist -type f -iname tldr -exec mv '{}' /build-outputs/tldr-hs \; \ - && find fast-tldr/.stack-work/dist -type f -iname tldr -exec mv '{}' /build-outputs/fast-tldr \; - -################################################################################ - -FROM node:slim AS node-builder - -WORKDIR /build-outputs -RUN npm install tldr \ - && cp $(which node) . \ - && echo './node -- ./node_modules/.bin/tldr "$@"' > tldr-node \ - && chmod +x tldr-node - -################################################################################ - -FROM euantorano/zig:0.8.0 AS zig-builder - -WORKDIR /build -RUN apk add git \ - && git clone https://gitlab.com/ve-nt/outfieldr.git \ - && cd outfieldr \ - && git submodule init \ - && git submodule update \ - && zig build -Drelease-safe \ - && mkdir /build-outputs \ - && cp bin/tldr /build-outputs/outfieldr - -################################################################################ - -FROM ubuntu:latest AS benchmark - -ENV LANG="en_US.UTF-8" - -WORKDIR /deps -RUN apt-get update && apt-get install -y wget unzip python3 python3-venv && rm -rf /var/lib/apt/lists/* -COPY --from=tldr-c-builder /deps/* ./ -RUN for file in *; do DEBIAN_FRONTEND=noninteractive sh $file; done - -WORKDIR /clients -COPY --from=tealdeer-builder /build-outputs/* ./ -COPY --from=tldr-c-builder /build-outputs/* ./ -COPY --from=haskell-builder /build-outputs/* ./ -RUN wget -qO tldr-bash https://4e4.win/tldr && chmod +x tldr-bash -COPY --from=node-builder /build-outputs/node /build-outputs/tldr-node ./ -COPY --from=node-builder /build-outputs/node_modules/ ./node_modules/ -COPY --from=zig-builder /build-outputs/* ./ - -# python is really hard to isolate in a package, using pyinstaller didn't really work either, so for now we just use it like this -RUN python3 -m venv tldr-python \ - && cd tldr-python \ - && bash -c 'source bin/activate; pip install wheel; pip install tldr; deactivate' \ - && cd .. \ - && echo '#!/bin/bash' > tldr-python.bash \ - && echo 'source tldr-python/bin/activate; tldr $@' >> tldr-python.bash \ - && chmod +x tldr-python.bash - -# Update all the individual caches -RUN bash -c 'mkdir -p /caches/{tealdeer,tldr-c,tldr-hs,fast-tldr,tldr-bash,tldr-node,tldr-python,outfieldr/.local/share}' \ - && TEALDEER_CACHE_DIR=/caches/tealdeer ./tealdeer -u \ - && TLDR_CACHE_DIR=/caches/tldr-c ./tldr-c -u \ - && XDG_DATA_HOME=/caches/tldr-hs ./tldr-hs -u \ - && XDG_DATA_HOME=/caches/fast-tldr ./fast-tldr -u \ - && XDG_DATA_HOME=/caches/tldr-bash ./tldr-bash -u \ - && HOME=/caches/tldr-node ./tldr-node -u \ - && HOME=/caches/tldr-python ./tldr-python.bash -u \ - && HOME=/caches/outfieldr ./outfieldr -u - -WORKDIR /tools -RUN wget -q https://github.com/sharkdp/hyperfine/releases/download/v1.11.0/hyperfine_1.11.0_amd64.deb && dpkg -i hyperfine_1.11.0_amd64.deb - -ENV PAGE="tar" -WORKDIR /clients -CMD hyperfine \ - --warmup 10 \ - --runs 50 \ - --prepare 'sync; echo 3 | tee /proc/sys/vm/drop_caches' \ - "TEALDEER_CACHE_DIR=/caches/tealdeer ./tealdeer $PAGE" \ - "TLDR_CACHE_DIR=/caches/tldr-c ./tldr-c $PAGE" \ - "XDG_DATA_HOME=/caches/tldr-hs ./tldr-hs $PAGE" \ - "XDG_DATA_HOME=/caches/fast-tldr ./fast-tldr $PAGE" \ - "XDG_DATA_HOME=/caches/tldr-bash TLDR_LESS=0 ./tldr-bash $PAGE" \ - "HOME=/caches/tldr-python ./tldr-python.bash $PAGE" \ - "HOME=/caches/outfieldr ./outfieldr $PAGE" \ - "HOME=/caches/tldr-node ./tldr-node $PAGE" diff --git a/completion/bash_tealdeer b/completion/bash_tealdeer deleted file mode 100644 index d5420b6..0000000 --- a/completion/bash_tealdeer +++ /dev/null @@ -1,35 +0,0 @@ -# tealdeer bash completion - -_tealdeer() -{ - local cur prev words cword - _init_completion || return - - case $prev in - -h|--help|-v|--version|-l|--list|-u|--update|--no-auto-update|-c|--clear-cache|--pager|-r|--raw|--show-paths|--seed-config|-q|--quiet) - return - ;; - -f|--render) - _filedir - return - ;; - -p|--platform) - COMPREPLY=( $(compgen -W 'linux macos sunos windows android freebsd netbsd openbsd' -- "${cur}") ) - return - ;; - --color) - COMPREPLY=( $(compgen -W 'always auto never' -- "${cur}") ) - return - ;; - esac - - if [[ $cur == -* ]]; then - COMPREPLY=( $( compgen -W '$( _parse_help "$1" )' -- "$cur" ) ) - return - fi - if tldrlist=$(tldr -l 2>/dev/null); then - COMPREPLY=( $(compgen -W '$( echo "$tldrlist" | tr -d , )' -- "${cur}") ) - fi -} - -complete -F _tealdeer tldr diff --git a/completion/fish_tealdeer b/completion/fish_tealdeer deleted file mode 100644 index 528be44..0000000 --- a/completion/fish_tealdeer +++ /dev/null @@ -1,28 +0,0 @@ -# -# Completions for the tealdeer implementation of tldr -# https://github.com/tealdeer-rs/tealdeer/ -# - -complete -c tldr -s h -l help -d 'Print the help message.' -f -complete -c tldr -s v -l version -d 'Show version information.' -f -complete -c tldr -s l -l list -d 'List all commands in the cache.' -f -complete -c tldr -s f -l render -d 'Render a specific markdown file.' -r -complete -c tldr -s p -l platform -d 'Override the operating system.' -xa 'linux macos sunos windows android freebsd netbsd openbsd' -complete -c tldr -s L -l language -d 'Override the language' -x -complete -c tldr -s u -l update -d 'Update the local cache.' -f -complete -c tldr -l no-auto-update -d 'If auto update is configured, disable it for this run.' -f -complete -c tldr -s c -l clear-cache -d 'Clear the local cache.' -f -complete -c tldr -l pager -d 'Use a pager to page output.' -f -complete -c tldr -s r -l raw -d 'Display the raw markdown instead of rendering it.' -f -complete -c tldr -s q -l quiet -d 'Suppress informational messages.' -f -complete -c tldr -l show-paths -d 'Show file and directory paths used by tealdeer.' -f -complete -c tldr -l seed-config -d 'Create a basic config.' -f -complete -c tldr -l color -d 'Controls when to use color.' -xa 'always auto never' - -function __tealdeer_entries - if set entries (tldr --list 2>/dev/null) - string replace -a -i -r "\,\s" "\n" $entries - end -end - -complete -f -c tldr -a '(__tealdeer_entries)' diff --git a/completion/zsh_tealdeer b/completion/zsh_tealdeer deleted file mode 100644 index fbab749..0000000 --- a/completion/zsh_tealdeer +++ /dev/null @@ -1,51 +0,0 @@ -#compdef tldr - -_applications() { - local -a commands - if commands=(${(uonzf)"$(tldr --list 2>/dev/null)"//:/\\:}); then - _describe -t commands 'command' commands - fi -} - -_tealdeer() { - local I="-h --help -v --version" - integer ret=1 - local -a args - - args+=( - "($I -l --list)"{-l,--list}"[List all commands in the cache]" - "($I -f --render)"{-f,--render}"[Render a specific markdown file]:file:_files" - "($I -p --platform)"{-p,--platform}'[Override the operating system]:platform:(( - linux - macos - sunos - windows - android - freebsd - netbsd - openbsd - ))' - "($I -L --language)"{-L,--language}"[Override the language settings]:lang" - "($I -u --update)"{-u,--update}"[Update the local cache]" - "($I)--no-auto-update[If auto update is configured, disable it for this run]" - "($I -c --clear-cache)"{-c,--clear-cache}"[Clear the local cache]" - "($I)--pager[Use a pager to page output]" - "($I -r --raw)"{-r,--raw}"[Display the raw markdown instead of rendering it]" - "($I -q --quiet)"{-q,--quiet}"[Suppress informational messages]" - "($I)--show-paths[Show file and directory paths used by tealdeer]" - "($I)--seed-config[Create a basic config]" - "($I)--color[Controls when to use color]:when:(( - always - auto - never - ))" - '(- *)'{-h,--help}'[Display help]' - '(- *)'{-v,--version}'[Show version information]' - '1: :_applications' - ) - - _arguments $args[@] && ret=0 - return ret -} - -_tealdeer diff --git a/docs/.gitignore b/docs/.gitignore deleted file mode 100644 index 7585238..0000000 --- a/docs/.gitignore +++ /dev/null @@ -1 +0,0 @@ -book diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index 7b63a06..0000000 --- a/docs/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# Tealdeer Docs - -To build the docs, install [mdbook](https://github.com/rust-lang/mdBook). - -You can build the HTML with `mdbook build`. - -To serve the docs on `localhost:3000` and watch for changes, use `mdbook -serve`. diff --git a/docs/book.toml b/docs/book.toml deleted file mode 100644 index ae63f23..0000000 --- a/docs/book.toml +++ /dev/null @@ -1,6 +0,0 @@ -[book] -authors = ["Danilo Bargen"] -language = "en" -multilingual = false -src = "src" -title = "Tealdeer User Manual" diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md deleted file mode 100644 index 4649382..0000000 --- a/docs/src/SUMMARY.md +++ /dev/null @@ -1,14 +0,0 @@ -# Summary - -[Introduction](./intro.md) - -- [Installing](./installing.md) -- [Usage](./usage.md) - - [Custom Pages and Patches](./usage_custom_pages.md) -- [Configuration](./config.md) - - [Section: \[display\]](./config_display.md) - - [Section: \[style\]](./config_style.md) - - [Section: \[search\]](./config_search.md) - - [Section: \[updates\]](./config_updates.md) - - [Section: \[directories\]](./config_directories.md) -- [Tips and Tricks](./tips_and_tricks.md) diff --git a/docs/src/config.md b/docs/src/config.md deleted file mode 100644 index a662b57..0000000 --- a/docs/src/config.md +++ /dev/null @@ -1,59 +0,0 @@ -# Configuration - -Tealdeer can be customized with a config file in [TOML -format](https://toml.io/) called `config.toml`. - -## Configfile Path - -The configuration file path follows OS conventions (e.g. -`$XDG_CONFIG_HOME/tealdeer/config.toml` on Linux). The paths can be queried -with the following command: - -```shell -$ tldr --show-paths -``` - -Creating the config file can be done manually or with the help of `tldr`: - -```shell -$ tldr --seed-config -``` - -On Linux, this will usually be `~/.config/tealdeer/config.toml`. - -## Config Example - -Here's an example configuration file. Note that this example does not contain -all possible config options. For details on the things that can be configured, -please refer to the subsections of this documentation page -([display](config_display.html), [style](config_style.html), [search](config_search.html), -[updates](config_updates.html) or [directories](config_directories.html)). - -```toml -[display] -compact = false -use_pager = true -show_title = false - -[style.command_name] -foreground = "red" - -[style.example_text] -foreground = "green" - -[style.example_code] -foreground = "blue" - -[style.example_variable] -foreground = "blue" -underline = true - -[updates] -auto_update = true -``` - -## Override Config Directory - -The directory where the configuration file resides may be overwritten by the -environment variable `TEALDEER_CONFIG_DIR`. Remember to use an absolute path. -Variable expansion will not be performed on the path. diff --git a/docs/src/config_directories.md b/docs/src/config_directories.md deleted file mode 100644 index 507bd27..0000000 --- a/docs/src/config_directories.md +++ /dev/null @@ -1,29 +0,0 @@ -# Section: \[directories\] - -This section allows overriding some directory paths. - -## `cache_dir` - -Override the cache directory. Remember to use an absolute path. Variable -expansion will not be performed on the path. If the directory does not yet -exist, it will be created. - -```toml -[directories] -cache_dir = "/home/myuser/.tealdeer-cache/" -``` - -If no `cache_dir` is specified, tealdeer will fall back to a location that -follows OS conventions. On Linux, it will usually be at `~/.cache/tealdeer/`. -Use `tldr --show-paths` to show the path that is being used. - -## `custom_pages_dir` - -Set the directory to be used to look up [custom -pages](usage_custom_pages.html). Remember to use an absolute path. Variable -expansion will not be performed on the path. - -```toml -[directories] -custom_pages_dir = "/home/myuser/custom-tldr-pages/" -``` diff --git a/docs/src/config_display.md b/docs/src/config_display.md deleted file mode 100644 index 007d64b..0000000 --- a/docs/src/config_display.md +++ /dev/null @@ -1,71 +0,0 @@ -# Section: \[display\] - -In the `display` section you can configure the output format. - -## `use_pager` - -Specifies whether the pager should be used by default or not (default `false`). - -```toml -[display] -use_pager = true -``` - -When enabled, `less -R` is used as pager. To override the pager command used, -set the `PAGER` environment variable. - -NOTE: This feature is not available on Windows. - -## `compact` - -Set this to enforce more compact output, where empty lines are stripped out -(default `false`). - -```toml -[display] -compact = true -``` - -## `show_title` - -Display the command name at the top of the page output (default `false`). - -```toml -[display] -show_title = true -``` - -When enabled, the command name will be displayed at the top of the output, -styled with the `command_name` style configuration. - -## `indent` - -Controls the indentation of the output via two sub-keys. - -### `indent.base` - -Specifies the number of spaces used to indent descriptions, example text, and titles (default `2`). - -```toml -[display.indent] -base = 2 -``` - -### `indent.command` - -Specifies the number of spaces used to indent example code lines (default `6`). - -```toml -[display.indent] -command = 6 -``` - -You can also configure both subkeys in a single line like this: - -```toml -[display] -indent = { - base = 2, - command = 6, -} -``` diff --git a/docs/src/config_search.md b/docs/src/config_search.md deleted file mode 100644 index 28e00d6..0000000 --- a/docs/src/config_search.md +++ /dev/null @@ -1,33 +0,0 @@ -# Section: \[search\] - -This config section is used to configure the page search in the cache. -The settings apply to `tldr ` and `tldr --list`. - -## `languages` - -The list of languages that should be considered when searching. -If unspecified, the list of languages will be inferred from the `LANG` and `LANGUAGE` environment variables. -Either way, the language used can be overwritten using the `--language` command line flag. - -```toml -[search] -# Show pages in German if available, otherwise show in English -languages = ["de", "en"] -``` - -## `platforms` - -The list of platforms that should be considered when searching. -In addition to the platforms listed in the help text of the `--platform` flag, there are two special platforms available: -- `"current"`: equals the platform that tealdeer was compiled for -- `"all"`: adds all remaining platforms to the list - -Tealdeer searches the platforms in order of appearance in this list. -The default list of platforms is `["current", "common", "all"]`. -The list of platforms can be overwritten using the `--platform` command line flag. - -```toml -[search] -# Search for linux and common, and then search windows before trying the remaining platforms -platforms = ["linux", "common", "windows", "all"] -``` diff --git a/docs/src/config_style.md b/docs/src/config_style.md deleted file mode 100644 index 593a5b5..0000000 --- a/docs/src/config_style.md +++ /dev/null @@ -1,47 +0,0 @@ -# Section: \[style\] - -Using the config file, the style (e.g. colors or underlines) can be customized. - -Screenshot of customized version - -## Style Targets - -- `description`: The initial description text -- `command_name`: The command name as part of the example code -- `example_text`: The text that describes an example -- `example_code`: The example itself (except the `command_name` and `example_variable`) -- `example_variable`: The variables in the example - -## Attributes - -- `foreground` (color string, ANSI code, or RGB, see below) -- `background` (color string, ANSI code, or RGB, see below) -- `underline` (`true` or `false`) -- `bold` (`true` or `false`) -- `italic` (`true` or `false`) - -Colors can be specified in one of three ways: - -- Color string (`black`, `red`, `green`, `yellow`, `blue`, `magenta`, `cyan`, `white`): - - Example: - - ```toml - foreground = "green" - ``` - -- 256 color ANSI code (*tealdeer v1.5.0+*) - - Example: - - ```toml - foreground = { ansi = 4 } - ``` - -- 24-bit RGB color (*tealdeer v1.5.0+*) - - Example: - - ```toml - background = { rgb = { r = 255, g = 255, b = 255 } } - ``` diff --git a/docs/src/config_updates.md b/docs/src/config_updates.md deleted file mode 100644 index a8a10c8..0000000 --- a/docs/src/config_updates.md +++ /dev/null @@ -1,91 +0,0 @@ -# Section: \[updates\] - -This config section contains settings related to updating the tealdeer cache. - -## Automatic updates - -Tealdeer can refresh the cache automatically when it is outdated. This -behavior can be configured in the `updates` section and is disabled by -default. - -### `auto_update` - -Specifies whether the auto-update feature should be enabled (defaults to -`false`). - -```toml -[updates] -auto_update = true -``` - -### `auto_update_interval_hours` - -Duration, since the last cache update, after which the cache will be -refreshed (defaults to 720 hours). This parameter is ignored if `auto_update` -is set to `false`. - -```toml -[updates] -auto_update = true -auto_update_interval_hours = 24 -``` - -### `warn_cache_age` - -Controls when a warning is printed if the cache has not been updated in a while. -By default, the warning is shown once the cache is older than 30 days. Set this -to `"never"` to silence the warning. This is useful if, for some reason, the -modification time does not reflect its actual age. - -```toml -[updates] -warn_cache_age = "never" -``` - -## Download configuration - -### `download_languages` - -The list of languages which should be downloaded when updating. -If unspecified, the languages listed in the `search.languages` setting are used. -Thus, this setting is the most useful to instruct tealdeer to download pages in additional languages that are not searched by default. -Either way, the language used can be overwritten using the `--language` command line flag. - -```toml -[search] -languages = ["de", "en"] - -[updates] -# sometimes I like to read the Italian description -download_languages = ["de", "en", "it"] -``` - -### `archive_source` - -URL for the location of the tldr pages archive. By default the pages are -fetched from the latest `tldr-pages/tldr` GitHub release. - -```toml -[updates] -archive_source = "https://my-company.example.com/tldr/" -``` - -### `tls_backend` - -Specifies which TLS backend to use. Try changing this setting if you encounter certificate errors. - -Available options: -- `rustls-with-native-roots` - [Rustls][rustls] (a TLS library in Rust) with native roots -- `rustls-with-webpki-roots` - Rustls with [WebPKI][rustls-webpki] roots -- `native-tls` - Native TLS - - SChannel on Windows - - Secure Transport on macOS - - OpenSSL on other platforms - -```toml -[updates] -tls_backend = "native-tls" -``` - -[rustls]: https://github.com/rustls/rustls -[rustls-webpki]: https://github.com/rustls/webpki diff --git a/docs/src/deer.png b/docs/src/deer.png deleted file mode 100644 index ebc9c9d..0000000 Binary files a/docs/src/deer.png and /dev/null differ diff --git a/docs/src/deer.svg b/docs/src/deer.svg deleted file mode 100644 index b686b4b..0000000 --- a/docs/src/deer.svg +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - diff --git a/docs/src/installing.md b/docs/src/installing.md deleted file mode 100644 index f0ca823..0000000 --- a/docs/src/installing.md +++ /dev/null @@ -1,74 +0,0 @@ -# Installing - -There are a few different ways to install tealdeer: - -- Through [package managers](#package-managers) -- Through [static binaries](#static-binaries-linux) -- Through [cargo install](#through-cargo-install) -- By [building from source](#build-from-source) - -Additionally, when not using system packages, you can [manually install -autocompletions](#autocompletion). - -## Package Managers - -Tealdeer has been added to a few package managers: - -- Arch Linux: [`tealdeer`](https://archlinux.org/packages/extra/x86_64/tealdeer/) -- Debian: [`tealdeer`](https://tracker.debian.org/tealdeer) -- Fedora: [`tealdeer`](https://src.fedoraproject.org/rpms/rust-tealdeer) -- FreeBSD: [`sysutils/tealdeer`](https://www.freshports.org/sysutils/tealdeer/) -- Funtoo: [`app-misc/tealdeer`](https://github.com/funtoo/core-kit/tree/1.4-release/app-misc/tealdeer) -- Homebrew: [`tealdeer`](https://formulae.brew.sh/formula/tealdeer) -- MacPorts: [`tealdeer`](https://ports.macports.org/port/tealdeer/) -- NetBSD: [`sysutils/tealdeer`](https://pkgsrc.se/sysutils/tealdeer) -- Nix: [`tealdeer`](https://search.nixos.org/packages?query=tealdeer) -- openSUSE: [`tealdeer`](https://software.opensuse.org/package/tealdeer?search_term=tealdeer) -- Scoop: [`tealdeer`](https://github.com/ScoopInstaller/Main/blob/master/bucket/tealdeer.json) -- Solus: [`tealdeer`](https://packages.getsol.us/shannon/t/tealdeer/) -- Void Linux: [`tealdeer`](https://github.com/void-linux/void-packages/tree/master/srcpkgs/tealdeer) - -## Static Binaries (Linux) - -Static binary builds (currently for Linux only) are available on the -[GitHub releases page](https://github.com/tealdeer-rs/tealdeer/releases). -Simply download the binary for your platform and run it! - -## Through `cargo install` - -Build and install the tool via cargo... - -```shell -$ cargo install tealdeer -``` - -## Build From Source - -Release build: - -```shell -$ cargo build --release -``` - -Release build with native TLS support: - -```shell -$ cargo build --release --features native-tls -``` - -Debug build with logging support: - -```shell -$ cargo build --features logging -``` - -(To enable logging at runtime, export the `RUST_LOG=tldr=debug` env variable.) - -## Autocompletion - -Shell completion scripts are located in the folder `completion`. -Just copy them to their designated location: - -- *Bash*: `cp completion/bash_tealdeer /usr/share/bash-completion/completions/tldr` -- *Fish*: `cp completion/fish_tealdeer ~/.config/fish/completions/tldr.fish` -- *Zsh*: `cp completion/zsh_tealdeer /usr/share/zsh/site-functions/_tldr` diff --git a/docs/src/intro.md b/docs/src/intro.md deleted file mode 100644 index a90ecec..0000000 --- a/docs/src/intro.md +++ /dev/null @@ -1,14 +0,0 @@ -# Tealdeer: Introduction - -Tealdeer is a very fast implementation of -[tldr](https://github.com/tldr-pages/tldr) in Rust: Simplified, example based -and community-driven man pages. - -![Screenshot](screenshot-default.png) - -This documentation shows how to install, use and configure tealdeer. - -## Links - -- [GitHub Project Page](https://github.com/tealdeer-rs/tealdeer) -- [TLDR Pages Project](https://tldr.sh/) diff --git a/docs/src/tips_and_tricks.md b/docs/src/tips_and_tricks.md deleted file mode 100644 index 29f7216..0000000 --- a/docs/src/tips_and_tricks.md +++ /dev/null @@ -1,52 +0,0 @@ -# Tips and Tricks - -This page features some example use cases of Tealdeer. - -## Showing a random page on shell start - -To display a randomly selected page, you can invoke `tldr` twice: One time to -select a page and a second time to display this page. To randomly select a page, -we use `shuf` from the GNU coreutils: - -```bash -tldr --quiet $(tldr --quiet --list | shuf -n1) -``` - -You can also add the above command to your `.bashrc` (or similar shell -configuration file) to display a random page every time you start a new shell -session. - -## Displaying all pages with their summary - -If you want to extend the output of `tldr --list` with the first line summary of -each page, you can run the following Python script: - -```python -#!/usr/bin/env python3 - -import subprocess - -commands = subprocess.run( - ["tldr", "--quiet", "--list"], - capture_output=True, - encoding="utf-8", -).stdout.splitlines() - -for command in commands: - output = subprocess.run( - ["tldr", "--quiet", command], - capture_output=True, - encoding="utf-8", - ).stdout - description = output.lstrip().split("\n\n")[0] - description = " ".join(description.split()) - print(f"{command} => {description}") -``` - -Note that there are a lot of pages and the script will run Tealdeer once for -every page, so the script may take a couple of seconds to finish. - -## Extending this chapter - -If you have an interesting setup with Tealdeer, feel free to share your -configuration on [our Github repository](https://github.com/tealdeer-rs/tealdeer). diff --git a/docs/src/usage.md b/docs/src/usage.md deleted file mode 100644 index 6fba933..0000000 --- a/docs/src/usage.md +++ /dev/null @@ -1,10 +0,0 @@ -# Usage - -Tealdeer is straightforward to use, through the binary named `tldr`. - -You can view the available options using `tldr --help`: - - -``` -{{#include usage.txt}} -``` diff --git a/docs/src/usage.txt b/docs/src/usage.txt deleted file mode 100644 index 6a04de7..0000000 --- a/docs/src/usage.txt +++ /dev/null @@ -1,33 +0,0 @@ -tealdeer 1.8.1: A fast TLDR client -Danilo Bargen , Niklas Mohrin - -Usage: tldr [OPTIONS] [COMMAND]... - -Arguments: - [COMMAND]... The command to show (e.g. `tar` or `git log`) - -Options: - -l, --list List all commands in the cache - --edit-page Edit custom page with `EDITOR` - --edit-patch Edit custom patch with `EDITOR` - -f, --render Render a specific markdown file - -p, --platform Override the operating system, can be specified multiple times in order - of preference [possible values: linux, macos, sunos, windows, android, - freebsd, netbsd, openbsd, common] - -L, --language Override the language - -u, --update Update the local cache - --no-auto-update If auto update is configured, disable it for this run - -c, --clear-cache Clear the local cache - --config-path Override config file location - --pager Use a pager to page output - -r, --raw Display the raw markdown instead of rendering it - -q, --quiet Suppress informational messages - --show-paths Show file and directory paths used by tealdeer - --seed-config Create a basic config - --color Control whether to use color [possible values: always, auto, never] - -v, --version Print the version - -h, --help Print help - -To view the user documentation, please visit https://tealdeer-rs.github.io/tealdeer/. - -To view usage examples, run tldr tldr or tldr tealdeer. diff --git a/docs/src/usage_custom_pages.md b/docs/src/usage_custom_pages.md deleted file mode 100644 index c73bf90..0000000 --- a/docs/src/usage_custom_pages.md +++ /dev/null @@ -1,58 +0,0 @@ -# Custom Pages and Patches - -> ⚠️ **Breaking change in version 1.7.0:** The file name extension for custom -> pages and patches was changed: -> -> - `.page` → `.page.md` -> - `.patch` → `.patch.md` -> -> If you have custom pages or patches, you need to rename them. - -Tealdeer allows creating new custom pages, overriding existing pages as well as -extending existing pages. - -The directory, where these custom pages and patches can be placed, follows OS -conventions. On Linux for instance, the default location is -`~/.local/share/tealdeer/pages/`. To print the path used on your system, simply -run `tldr --show-paths`. - -The custom pages directory can be [overridden by the config -file](config_directories.html). - -## Custom Pages - -To document internal command line tools, or if you want to replace an existing -tldr page with one that's better suited for you, place a file with the name -`.page.md` in the custom pages directory. When calling `tldr `, -your custom page will be shown instead of the upstream version in the cache. - -Path: - -```plain -$CUSTOM_PAGES_DIR/.page.md -``` - -Example: - -```plain -~/.local/share/tealdeer/pages/ufw.page.md -``` - -## Custom Patches - -Sometimes you don't want to fully replace an existing upstream page, but just -want to extend it with your own examples that you frequently need. In this -case, use a file called `.patch.md`, it will be appended to existing -pages. - -Path: - -```plain -$CUSTOM_PAGES_DIR/.patch.md -``` - -Example: - -```plain -~/.local/share/tealdeer/pages/ufw.patch.md -``` diff --git a/fish_tealdeer b/fish_tealdeer new file mode 100644 index 0000000..9276e9a --- /dev/null +++ b/fish_tealdeer @@ -0,0 +1,21 @@ +# +# Completions for the tealdeer implementation of tldr +# https://github.com/dbrgn/tealdeer/ +# + +complete -c tldr -s h -l help -d 'Print the help message.' -f +complete -c tldr -s v -l version -d 'Show version information.' -f +complete -c tldr -s l -l list -d 'List all commands in the cache.' -f +complete -c tldr -s f -l render -d 'Render a specific markdown file.' -r +complete -c tldr -s o -l os -d 'Override the operating system.' -xa 'linux osx sunos other' +complete -c tldr -s u -l update -d 'Update the local cache.' -f +complete -c tldr -s c -l clear-cache -d 'Clear the local cache.' -f +complete -c tldr -s q -l quiet -d 'Suppress informational messages.' -f +complete -c tldr -l config-path -d 'Show config file path.' -f +complete -c tldr -l seed-config -d 'Create a basic config.' -f + +function __tealdeer_entries + tldr --list | sed -e 's/, /\n/g' +end + +complete -f -c tldr -a '(__tealdeer_entries)' diff --git a/pages/tealdeer.md b/pages/tealdeer.md deleted file mode 100644 index 948b278..0000000 --- a/pages/tealdeer.md +++ /dev/null @@ -1,42 +0,0 @@ -# tldr - -> This is a builtin page that shows information for your installed tealdeer version. -> More information: . - -> This page shows tealdeer specific functionality. See tldr tldr for more examples. - -- Render a local markdown file as a tldr page: - -`tldr --render {{path/to/file.md}}` - -- Show the raw markdown source of a page instead of rendering it: - -`tldr --raw {{command}}` - -- Show file and directory paths used by tealdeer: - -`tldr --show-paths` - -- Create an initial config file: - -`tldr --seed-config` - -- Override config file location: - -`tldr --config-path ` - -- Open a custom page for a command in `$EDITOR` (creates it if it doesn't exist): - -`tldr --edit-page {{command}}` - -- Open a custom patch for a command in `$EDITOR` (appended to the existing page): - -`tldr --edit-patch {{command}}` - -- Clear the local cache: - -`tldr --clear-cache` - -- If auto update is configured, disable it for this run: - -`tldr --no-auto-update` diff --git a/release-build.sh b/release-build.sh new file mode 100755 index 0000000..42e2f6d --- /dev/null +++ b/release-build.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash + +set -euo pipefail + +VERSION=$(grep '^version = ' Cargo.toml | sed 's/.*"\([0-9\.]*\)".*/\1/') +GPG_KEY=EA456E8BAF0109429583EED83578F667F2F3A5FA + +declare -a targets=( + "x86_64-musl" + "i686-musl" + "armv7-musleabihf" + "arm-musleabi" + "arm-musleabihf" +) + +declare -a rusttargets=( + "x86_64-unknown-linux-musl" + "i686-unknown-linux-musl" + "armv7-unknown-linux-musleabihf" + "arm-unknown-linux-musleabi" + "arm-unknown-linux-musleabihf" +) + +function docker-download { + echo "==> Downloading Docker image: messense/rust-musl-cross:$1" + docker pull messense/rust-musl-cross:$1 +} + +function docker-build { + echo "==> Building target: $1" + docker run --rm -it -v "$(pwd)":/home/rust/src messense/rust-musl-cross:$1 cargo build --release +} + +echo -e "==> Version $VERSION\n" + +for target in ${targets[@]}; do docker-download $target; done +echo "" +for target in ${targets[@]}; do docker-build $target; done +echo "" + +rm -rf "dist-$VERSION" +mkdir "dist-$VERSION" + +for i in ${!targets[@]}; do + echo "==> Copying ${targets[$i]}" + cp "target/${rusttargets[$i]}/release/tldr" "dist-$VERSION/tldr-${targets[$i]}" +done +echo "" + +for target in ${targets[@]}; do + echo "==> Stripping $target" + docker run --rm -it -v "$(pwd)":/home/rust/src messense/rust-musl-cross:$target musl-strip -s /home/rust/src/dist-$VERSION/tldr-$target +done +echo "" + +for target in ${targets[@]}; do + echo "==> Signing $target" + gpg -a --output "dist-$VERSION/tldr-$target.sig" --detach-sig "dist-$VERSION/tldr-$target" +done +echo "" + +echo "Done." diff --git a/rustfmt.toml b/rustfmt.toml deleted file mode 100644 index f857430..0000000 --- a/rustfmt.toml +++ /dev/null @@ -1 +0,0 @@ -# Empty file, use defaults and disregard global settings diff --git a/docs/src/screenshot-custom.png b/screenshot-custom.png similarity index 100% rename from docs/src/screenshot-custom.png rename to screenshot-custom.png diff --git a/docs/src/screenshot-default.png b/screenshot-default.png similarity index 100% rename from docs/src/screenshot-default.png rename to screenshot-default.png diff --git a/scripts/upload-asset.sh b/scripts/upload-asset.sh deleted file mode 100644 index 4f9de09..0000000 --- a/scripts/upload-asset.sh +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env bash -# -# Upload artifacts to GitHub Actions. -# -# Based on: https://gist.github.com/schell/2fe896953b6728cc3c5d8d5f9f3a17a3 -# -# Requires curl and jq on PATH - -# Args: -# token: GitHub API user token -# repo: GitHub username/reponame -# tag: Name of the tag for which to create a release -# description: Release description -create_release() { - # Args - token=$1 - repo=$2 - tag=$3 - description=$4 - echo "Creating release:" - echo " repo=$repo" - echo " tag=$tag" - echo "" - - # Create release - http_code=$( - curl -s -o create.json -w '%{http_code}' \ - --header "Accept: application/vnd.github.v3+json" \ - --header "Authorization: Bearer $token" \ - --header "Content-Type:application/json" \ - "https://api.github.com/repos/$repo/releases" \ - -d '{"tag_name":"'"$tag"'","name":"'"${tag/v/Version }"'","draft":true,"body":"'"${description/\"/\\\"}"'"}' - ) - if [ "$http_code" == "201" ]; then - echo "Release for tag $tag created." - else - echo "Asset upload failed with code '$http_code'." - return 1 - fi -} - -# Args: -# token: GitHub API user token -# repo: GitHub username/reponame -# tag: Name of the tag for which to upload the assets -# file: Path to the asset file to upload -# name: Name to use for the uploaded asset -upload_release_file() { - # Args - token=$1 - repo=$2 - tag=$3 - file=$4 - name=$5 - echo "Uploading:" - echo " repo=$repo" - echo " tag=$tag" - echo " file=$file" - echo " name=$name" - echo "" - - # Determine upload URL of latest draft release for the specified tag - upload_url=$( - curl -s \ - --header "Accept: application/vnd.github.v3+json" \ - --header "Authorization: Bearer $token" \ - "https://api.github.com/repos/$repo/releases" \ - | jq -r '[.[] | select(.tag_name == "'"$tag"'" and .draft)][0].upload_url' \ - | cut -d"{" -f'1' - ) - echo "Determined upload URL: $upload_url" - http_code=$( - curl -s -o upload.json -w '%{http_code}' \ - --request POST \ - --header "Accept: application/vnd.github.v3+json" \ - --header "Authorization: Bearer $token" \ - --header "Content-Type: application/octet-stream" \ - --data-binary "@$file" "$upload_url?name=$name" - ) - if [ "$http_code" == "201" ]; then - echo "Asset $name uploaded:" - jq -r .browser_download_url upload.json - else - echo "Asset upload failed with code '$http_code':" - cat upload.json - return 1 - fi -} diff --git a/src/cache.rs b/src/cache.rs index afa3603..eef99a7 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -1,240 +1,157 @@ -use std::{ - fs::{self, File}, - io::{Cursor, ErrorKind, Read}, - path::{Path, PathBuf}, - time::{Duration, SystemTime}, -}; +#[cfg(feature = "networking")] +use std::borrow::Cow; +use std::env; +use std::fs; +use std::io::Read; +use std::path::{Path, PathBuf}; -use anyhow::{anyhow, bail, ensure, Context, Result}; -use log::{debug, info}; -use ureq::{ - http::StatusCode, - tls::{RootCerts, TlsConfig, TlsProvider}, - Agent, -}; -use zip::ZipArchive; +#[cfg(all(unix, feature = "networking"))] +use std::os::unix::fs::MetadataExt; -use crate::{ - config::{Language, TlsBackend}, - types::PlatformType, -}; +#[cfg(feature = "networking")] +use reqwest::{Client, Proxy}; +use flate2::read::GzDecoder; +use log::debug; +use tar::Archive; +#[cfg(feature = "networking")] +use time; +use walkdir::{DirEntry, WalkDir}; +use xdg::BaseDirectories; -pub static TLDR_PAGES_DIR: &str = "tldr-pages"; -pub static TLDR_OLD_PAGES_DIR: &str = "tldr-master"; +use crate::error::TealdeerError::{self, CacheError, UpdateError}; +use crate::types::OsType; -#[derive(Clone)] -pub struct CacheConfig<'a> { - pub pages_directory: &'a Path, - pub custom_pages_directory: Option<&'a Path>, - pub platforms: &'a [PlatformType], - pub search_languages: &'a [Language<'a>], - pub download_languages: &'a [Language<'a>], -} +/// A cache update source. +#[derive(Debug)] +pub enum Source { + /// Load the archive from the file system. + File(PathBuf), -/// The directory backing this cache is checked to be populated at construction. -pub struct Cache<'a> { - config: CacheConfig<'a>, + /// Load the archive from the network. + #[cfg(feature = "networking")] + Url(Cow<'static, str>), + + /// No source is defined. + #[cfg(not(feature = "networking"))] + None, } #[derive(Debug)] -pub struct PageLookupResult { - pub page_path: PathBuf, - pub patch_path: Option, +pub struct Cache { + /// The cache source. Either an URL or a file path. + source: Source, + /// The target OS type. + os: OsType, } -impl<'a> Cache<'a> { - /// Try opening a cache at the location given by `config.pages_directory`. If no directory - /// exists at this location, `Ok(None)` is returned. - pub fn open(config: CacheConfig<'a>) -> Result> { - match config.pages_directory.metadata() { - Ok(md) => { - ensure!( - md.is_dir(), - "Cache directory `{}` exists, but is not a directory.", - config.pages_directory.display(), - ); - Ok(Some(Cache { config })) - } - Err(err) if err.kind() == ErrorKind::NotFound => Ok(None), - Err(err) => Err(anyhow!(err).context(format!( - "Error getting metdata of cache directory {}", - config.pages_directory.display() - ))), +impl Cache { + pub fn new(source: S, os: OsType) -> Self + where + S: Into, + { + Self { + source: source.into(), + os, } } - /// Open an existing cache at `config.pages_directory` or create one if no cache resides at - /// this location. In case of success, the return value is a tuple with the `Cache` and a - /// boolean indicating whether the cache was newly created. - pub fn open_or_create(config: CacheConfig<'a>) -> Result<(Self, bool)> { - if let Some(cache) = Self::open(config.clone())? { - return Ok((cache, false)); - } + /// Return the path to the cache directory. + fn get_cache_dir(&self) -> Result { + // Allow overriding the cache directory by setting the + // $TEALDEER_CACHE_DIR env variable. + if let Ok(value) = env::var("TEALDEER_CACHE_DIR") { + let path = PathBuf::from(value); - fs::create_dir_all(config.pages_directory).with_context(|| { - format!( - "Cache directory `{}` cannot be created", - config.pages_directory.display(), - ) - })?; - eprintln!( - "Successfully created cache directory `{}`.", - config.pages_directory.display(), - ); - - Ok((Cache { config }, true)) - } - - pub fn age(&self) -> Result { - let mtime = self.config.pages_directory.metadata()?.modified()?; - SystemTime::now() - .duration_since(mtime) - .context("Error comparing cache mtime with current time") - } - - pub fn find_page(&self, command: &str) -> Option { - let page_filename = format!("{command}.md"); - let patch_filename = format!("{command}.patch.md"); - let custom_filename = format!("{command}.page.md"); - - if let Some(custom_pages_dir) = self.config.custom_pages_directory { - let custom_page = custom_pages_dir.join(custom_filename); - if custom_page.is_file() { - return Some(PageLookupResult::with_page(custom_page)); + if path.exists() && path.is_dir() { + return Ok(path); + } else { + return Err(CacheError( + "Path specified by $TEALDEER_CACHE_DIR \ + does not exist or is not a directory." + .into(), + )); } - } - - let patch_path = self - .config - .custom_pages_directory - .map(|dir| dir.join(&patch_filename)) - .filter(|path| path.is_file()); - - for &platform in self.config.platforms { - for language in self.config.search_languages { - let mut search_path = self.config.pages_directory.to_path_buf(); - search_path.push(language.directory_name()); - search_path.push(platform.directory_name()); - search_path.push(&page_filename); - - if search_path.is_file() { - return Some( - PageLookupResult::with_page(search_path).with_optional_patch(patch_path), - ); - } - } - } - - None - } - - pub fn list_pages(&self) -> Result> { - let mut pages = Vec::new(); - - let mut append_all = |directory: &Path, suffix: &str| -> Result<()> { - let Ok(file_iter) = fs::read_dir(directory) else { - return Ok(()); - }; - - for entry in file_iter { - let entry = entry?; - if entry.file_type()?.is_file() { - let mut page_path = entry - .file_name() - .into_string() - .map_err(|_| anyhow!("Found invalid filename: {:?}", entry.path()))?; - - if page_path.ends_with(suffix) { - page_path.truncate(page_path.len() - suffix.len()); - pages.push(page_path); - } else { - debug!( - "Skipping page entry not ending in \".md\": {:?}", - entry.path(), - ); - } - } - } - - Ok(()) }; - let mut search_path = self.config.pages_directory.to_path_buf(); - for language in self.config.search_languages { - search_path.push(language.directory_name()); - for platform in self.config.platforms { - search_path.push(platform.directory_name()); - append_all(&search_path, ".md")?; - search_path.pop(); - } - search_path.pop(); - } - - if let Some(custom_pages_dir) = self.config.custom_pages_directory { - append_all(custom_pages_dir, ".page.md")?; - } - - pages.sort_unstable(); - pages.dedup(); - Ok(pages) + // Otherwise, fall back to $XDG_CACHE_HOME/tealdeer. + let xdg_dirs = match BaseDirectories::with_prefix(crate::NAME) { + Ok(dirs) => dirs, + Err(_) => return Err(CacheError("Could not determine XDG base directory.".into())), + }; + Ok(xdg_dirs.get_cache_home()) } - pub fn old_custom_pages_exist(&self) -> Result { - let Some(directory) = self.config.custom_pages_directory else { - return Ok(false); - }; - let Ok(file_iter) = fs::read_dir(directory) else { - return Ok(false); - }; + /// Load the archive from the file system. + fn load_from_file(path: &Path) -> Result, TealdeerError> { + let mut f = fs::File::open(path) + .map_err(|e| UpdateError(format!("Could not open file: {}", e)))?; + let mut buf: Vec = vec![]; + let bytes_read = f.read_to_end(&mut buf) + .map_err(|e| UpdateError(format!("Could not read file: {}", e)))?; + debug!("{} bytes loaded from filesystem", bytes_read); + Ok(buf) + } - for entry in file_iter { - if let Some(extension) = entry?.path().extension() { - if extension == "page" || extension == "patch" { - return Ok(true); - } + /// Load the archive from the network. + #[cfg(feature = "networking")] + fn load_from_network(url: &str) -> Result, TealdeerError> { + let mut builder = Client::builder(); + if let Ok(ref host) = env::var("HTTP_PROXY") { + if let Ok(proxy) = Proxy::http(host) { + builder = builder.proxy(proxy); } } - - Ok(false) + if let Ok(ref host) = env::var("HTTPS_PROXY") { + if let Ok(proxy) = Proxy::https(host) { + builder = builder.proxy(proxy); + } + } + let client = builder.build().unwrap_or_else(|_| Client::new()); + let mut resp = client.get(url).send()?; + let mut buf: Vec = vec![]; + let bytes_downloaded = resp.copy_to(&mut buf)?; + debug!("{} bytes downloaded", bytes_downloaded); + Ok(buf) } - pub fn clear(self) -> Result<()> { - fs::remove_dir_all(self.config.pages_directory).with_context(|| { - format!( - "Could not remove pages directory at {}", - self.config.pages_directory.display(), - ) - }) + /// Download the archive from the network or load it from a file. + #[cfg(feature = "networking")] + fn load(&self) -> Result, TealdeerError> { + match self.source { + Source::File(ref path) => Self::load_from_file(path), + Source::Url(ref url) => Self::load_from_network(url), + } } - /// Download archives for the languages in `self.config().download_languages` and replace the - /// pages directory with the newly downloaded pages. As not all languages might have pages - /// available (for example, `en_US` instead of `en`), an iterator yielding all languages which - /// were successfully downloaded is returned. - pub fn update( - &mut self, - archive_url: &str, - tls_backend: TlsBackend, - ) -> Result>> { - let client = Self::build_client(tls_backend); + /// Load the archive from the file system. + #[cfg(not(feature = "networking"))] + fn load(&self) -> Result, TealdeerError> { + match self.source { + Source::File(ref path) => Self::load_from_file(path), + Source::None => Err(TealdeerError::UpdateError("No update source defined".into())), + } + } - // Download everything before deleting anything - let mut archives = self - .config - .download_languages - .iter() - .map(|&lang| { - Ok(( - lang, - Self::download( - &client, - &format!("{archive_url}/tldr-{}.zip", lang.directory_name()), - )? - .map(|bytes| ZipArchive::new(Cursor::new(bytes))) - .transpose()?, - )) - }) - .collect::>>()?; + /// Decompress and open the archive + fn decompress(&self, reader: R) -> Archive> { + Archive::new(GzDecoder::new(reader)) + } + + /// Update the pages cache. + pub fn update(&self) -> Result<(), TealdeerError> { + // First, load the compressed data + let bytes: Vec = self.load()?; + + // Decompress the response body into an `Archive` + let mut archive = self.decompress(&bytes[..]); + + // Determine paths + let cache_dir = self.get_cache_dir()?; + + // Make sure that cache directory exists + debug!("Ensure cache directory {:?} exists", &cache_dir); + fs::create_dir_all(&cache_dir) + .map_err(|e| UpdateError(format!("Could not create cache directory: {}", e)))?; // Clear cache directory // Note: This is not the best solution. Ideally we would download the @@ -242,191 +159,143 @@ impl<'a> Cache<'a> { // But renaming a directory doesn't work across filesystems and Rust // does not yet offer a recursive directory copying function. So for // now, we'll use this approach. - fs::remove_dir_all(self.config.pages_directory)?; - fs::create_dir(self.config.pages_directory)?; + self.clear()?; - for (lang, archive) in &mut archives { - if let Some(archive) = archive { - info!("Extracting archive for {lang:?}"); - archive.extract(self.config.pages_directory.join(lang.directory_name()))?; - } else { - info!("No archive found for {lang:?}"); + // Extract archive + archive + .unpack(&cache_dir) + .map_err(|e| UpdateError(format!("Could not unpack compressed data: {}", e)))?; + + Ok(()) + } + + #[cfg(all(unix, feature = "networking"))] + /// Return the number of seconds since the cache directory was last modified. + pub fn last_update(&self) -> Option { + if let Ok(cache_dir) = self.get_cache_dir() { + if let Ok(metadata) = fs::metadata(cache_dir.join("tldr-master")) { + let mtime = metadata.mtime(); + let now = time::now_utc().to_timespec(); + return Some(now.sec - mtime); + }; + }; + None + } + + /// Return the platform directory. + #[allow(clippy::match_same_arms)] + fn get_platform_dir(&self) -> Option<&'static str> { + match self.os { + OsType::Linux => Some("linux"), + OsType::OsX => Some("osx"), + OsType::SunOs => None, // TODO: Does Rust support SunOS? + OsType::Other => None, + } + } + + /// Search for a page and return the path to it. + pub fn find_page(&self, name: &str) -> Option { + // Build page file name + let page_filename = format!("{}.md", name); + + // Get platform dir + let platforms_dir = match self.get_cache_dir() { + Ok(cache_dir) => cache_dir.join("tldr-master").join("pages"), + _ => return None, + }; + + // Determine platform + let platform = self.get_platform_dir(); + + // Search for the page in the platform specific directory + if let Some(pf) = platform { + let path = platforms_dir.join(&pf).join(&page_filename); + if path.exists() && path.is_file() { + return Some(path); } } - Ok(archives - .into_iter() - .filter_map(|(lang, archive)| archive.is_some().then_some(lang))) - } + // If platform is not supported or if platform specific page does not exist, + // look up the page in the "common" directory. + let path = platforms_dir.join("common").join(&page_filename); - pub fn config(&self) -> &CacheConfig<'a> { - &self.config - } -} - -impl PageLookupResult { - pub fn with_page(page_path: PathBuf) -> Self { - Self { - page_path, - patch_path: None, - } - } - - pub fn with_optional_patch(mut self, patch_path: Option) -> Self { - self.patch_path = patch_path; - self - } - - /// Create a reader that sequentially reads from the page and the - /// patch, as if they were concatenated. - /// - /// This will return an error if either the page file or the patch file - /// cannot be opened. - pub fn reader(&self) -> Result> { - // Open page file - let page_file = File::open(&self.page_path) - .with_context(|| format!("Could not open page file at {}", self.page_path.display()))?; - - // Open patch file - let patch_file_opt = match &self.patch_path { - Some(path) => Some( - File::open(path) - .with_context(|| format!("Could not open patch file at {}", path.display()))?, - ), - None => None, - }; - - // Create chained reader from file(s) - // - // Note: It might be worthwhile to create our own struct that accepts - // the page and patch files and that will read them sequentially, - // because it avoids the boxing below. However, the performance impact - // would first need to be shown to be significant using a benchmark. - Ok(if let Some(patch_file) = patch_file_opt { - Box::new(page_file.chain(&b"\n"[..]).chain(patch_file)) as Box + // Return it if it exists, otherwise give up and return `None` + if path.exists() && path.is_file() { + Some(path) } else { - Box::new(page_file) as Box - }) - } -} - -impl Language<'_> { - fn directory_name(&self) -> String { - format!("pages.{}", self.0) - } -} - -impl PlatformType { - fn directory_name(self) -> &'static str { - match self { - PlatformType::Linux => "linux", - PlatformType::OsX => "osx", - PlatformType::SunOs => "sunos", - PlatformType::Windows => "windows", - PlatformType::Android => "android", - PlatformType::FreeBsd => "freebsd", - PlatformType::NetBsd => "netbsd", - PlatformType::OpenBsd => "openbsd", - PlatformType::Common => "common", + None } } -} -impl Cache<'_> { - fn build_client(tls_backend: TlsBackend) -> Agent { - let tls_builder = match tls_backend { - #[cfg(feature = "native-tls")] - TlsBackend::NativeTls => TlsConfig::builder() - .provider(TlsProvider::NativeTls) - .root_certs(RootCerts::PlatformVerifier), - #[cfg(feature = "rustls-with-webpki-roots")] - TlsBackend::RustlsWithWebpkiRoots => TlsConfig::builder() - .provider(TlsProvider::Rustls) - .root_certs(RootCerts::WebPki), - #[cfg(feature = "rustls-with-native-roots")] - TlsBackend::RustlsWithNativeRoots => TlsConfig::builder() - .provider(TlsProvider::Rustls) - .root_certs(RootCerts::PlatformVerifier), + /// Return the available pages. + pub fn list_pages(&self) -> Result, TealdeerError> { + // Determine platforms directory and platform + let cache_dir = self.get_cache_dir()?; + let platforms_dir = cache_dir.join("tldr-master").join("pages"); + let platform_dir = self.get_platform_dir(); + + // Closure that allows the WalkDir instance to traverse platform + // specific and common page directories, but not others. + let should_walk = |entry: &DirEntry| -> bool { + let file_type = entry.file_type(); + let file_name = match entry.file_name().to_str() { + Some(name) => name, + None => return false, + }; + if file_type.is_dir() { + if file_name == "common" { + return true; + } + if let Some(platform) = platform_dir { + return file_name == platform; + } + } else if file_type.is_file() { + return true; + } + false }; - let config = Agent::config_builder() - .http_status_as_error(false) // because we want to handle them - .tls_config(tls_builder.build()) - .build(); - config.into() + // Recursively walk through common and (if applicable) platform specific directory + let mut pages = WalkDir::new(platforms_dir) + .min_depth(1) // Skip root directory + .into_iter() + .filter_entry(|e| should_walk(e)) // Filter out pages for other architectures + .filter_map(|e| e.ok()) // Convert results to options, filter out errors + .filter_map(|e| { + let path = e.path(); + let extension = &path.extension().and_then(|s| s.to_str()).unwrap_or(""); + if e.file_type().is_file() && extension == &"md" { + path.file_stem() + .and_then(|stem| stem.to_str().map(|s| s.into())) + } else { + None + } + }) + .collect::>(); + pages.sort(); + pages.dedup(); + Ok(pages) } - /// Download the archive from the specified URL. - fn download(client: &Agent, archive_url: &str) -> Result>> { - info!("Downloading archive from {archive_url}"); - let response = client.get(archive_url).call(); - match response { - Ok(response) if response.status().is_success() => { - let mut buf: Vec = Vec::new(); - response.into_body().into_reader().read_to_end(&mut buf)?; - debug!("{} bytes downloaded", buf.len()); - Ok(Some(buf)) - } - Ok(response) if response.status() == StatusCode::NOT_FOUND => Ok(None), - _ => { - bail!("Could not download tldr pages from {archive_url}: {response:?}") - } - } - } -} - -/// Unit Tests for cache module -#[cfg(test)] -mod tests { - use super::*; - - use std::{ - fs::File, - io::{Read, Write}, - }; - - #[test] - fn test_reader_with_patch() { - // Write test files - let dir = tempfile::tempdir().unwrap(); - let page_path = dir.path().join("test.page.md"); - let patch_path = dir.path().join("test.patch.md"); - { - let mut f1 = File::create(&page_path).unwrap(); - f1.write_all(b"Hello\n").unwrap(); - let mut f2 = File::create(&patch_path).unwrap(); - f2.write_all(b"World").unwrap(); - } - - // Create chained reader from lookup result - let lr = PageLookupResult::with_page(page_path).with_optional_patch(Some(patch_path)); - let mut reader = lr.reader().unwrap(); - - // Read into a Vec - let mut buf = Vec::new(); - reader.read_to_end(&mut buf).unwrap(); - - assert_eq!(&buf, b"Hello\n\nWorld"); - } - - #[test] - fn test_reader_without_patch() { - // Write test file - let dir = tempfile::tempdir().unwrap(); - let page_path = dir.path().join("test.page.md"); - { - let mut f = File::create(&page_path).unwrap(); - f.write_all(b"Hello\n").unwrap(); - } - - // Create chained reader from lookup result - let lr = PageLookupResult::with_page(page_path); - let mut reader = lr.reader().unwrap(); - - // Read into a Vec - let mut buf = Vec::new(); - reader.read_to_end(&mut buf).unwrap(); - - assert_eq!(&buf, b"Hello\n"); + /// Delete the cache directory. + pub fn clear(&self) -> Result<(), TealdeerError> { + let path = self.get_cache_dir()?; + if path.exists() && path.is_dir() { + fs::remove_dir_all(&path).map_err(|_| CacheError(format!( + "Could not remove cache directory ({}).", + path.display() + )))?; + } else if path.exists() { + return Err(CacheError(format!( + "Cache path ({}) is not a directory.", + path.display() + ))); + } else { + return Err(CacheError(format!( + "Cache path ({}) does not exist.", + path.display() + ))); + }; + Ok(()) } } diff --git a/src/cli.rs b/src/cli.rs deleted file mode 100644 index 161d69d..0000000 --- a/src/cli.rs +++ /dev/null @@ -1,112 +0,0 @@ -//! Definition of the CLI arguments and options. - -use std::path::PathBuf; - -use clap::{builder::ArgAction, ArgGroup, Parser}; - -use crate::types::{ColorOptions, PlatformType}; - -// Note: flag names are specified explicitly in clap attributes -// to improve readability and allow contributors to grep names like "clear-cache" -#[derive(Parser, Debug)] -#[command( - about = "A fast TLDR client", - version, - disable_version_flag = true, - author, - help_template = "{before-help}{name} {version}: {about-with-newline}{author-with-newline} -{usage-heading} {usage} - -{all-args}{after-help}", - after_help = "To view the user documentation, please visit https://tealdeer-rs.github.io/tealdeer/. - -To view usage examples, run tldr tldr or tldr tealdeer.", - arg_required_else_help = true, - help_expected = true, - group = ArgGroup::new("command_or_file").args(&["command", "render"]), -)] -pub(crate) struct Cli { - /// The command to show (e.g. `tar` or `git log`) - #[arg(num_args(1..))] - pub command: Vec, - - /// List all commands in the cache - #[arg(short = 'l', long = "list")] - pub list: bool, - - /// Edit custom page with `EDITOR` - #[arg(long, requires = "command")] - pub edit_page: bool, - - /// Edit custom patch with `EDITOR` - #[arg(long, requires = "command", conflicts_with = "edit_page")] - pub edit_patch: bool, - - /// Render a specific markdown file - #[arg( - short = 'f', - long = "render", - value_name = "FILE", - conflicts_with = "command" - )] - pub render: Option, - - /// Override the operating system, can be specified multiple times in order of preference - #[arg( - short = 'p', - long = "platform", - value_name = "PLATFORM", - action = ArgAction::Append, - )] - pub platforms: Option>, - - /// Override the language - #[arg(short = 'L', long = "language")] - pub language: Option, - - /// Update the local cache - #[arg(short = 'u', long = "update")] - pub update: bool, - - /// If auto update is configured, disable it for this run - #[arg(long = "no-auto-update", requires = "command_or_file")] - pub no_auto_update: bool, - - /// Clear the local cache - #[arg(short = 'c', long = "clear-cache")] - pub clear_cache: bool, - - /// Override config file location - #[arg(long = "config-path", value_name = "FILE")] - pub config_path: Option, - - /// Use a pager to page output - #[arg(long = "pager", requires = "command_or_file")] - pub pager: bool, - - /// Display the raw markdown instead of rendering it - #[arg(short = 'r', long = "raw", requires = "command_or_file")] - pub raw: bool, - - /// Suppress informational messages - #[arg(short = 'q', long = "quiet")] - pub quiet: bool, - - /// Show file and directory paths used by tealdeer - #[arg(long = "show-paths")] - pub show_paths: bool, - - /// Create a basic config - #[arg(long = "seed-config")] - pub seed_config: bool, - - /// Control whether to use color - #[arg(long = "color", value_name = "WHEN")] - pub color: Option, - - /// Print the version - // Note: We override the version flag because clap uses `-V` by default, - // while TLDR specification requires `-v` to be used. - #[arg(short = 'v', long = "version", action = ArgAction::Version)] - pub version: (), -} diff --git a/src/config.rs b/src/config.rs index f0feeb4..787de94 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,138 +1,50 @@ -use std::{ - borrow::Cow, - env, fmt, - fs::{self, File}, - io::{ErrorKind, Write}, - path::{Component, Path, PathBuf}, - sync::LazyLock, - time::Duration, -}; +use std::env; +use std::fs; +use std::io::{Error as IoError, Read, Write}; +use std::path::PathBuf; -use anyhow::{anyhow, ensure, Context, Result}; -use clap::ValueEnum; -use log::info; -use serde::Serialize as _; +use ansi_term::{Color, Style}; +use log::debug; use serde_derive::{Deserialize, Serialize}; -use yansi::{Color, Style}; +use toml; +use xdg::BaseDirectories; -use crate::{ - extensions::Dedup as _, - types::{PathSource, PlatformType}, -}; +use crate::error::TealdeerError::{self, ConfigError}; pub const CONFIG_FILE_NAME: &str = "config.toml"; -pub const MAX_CACHE_AGE: Duration = Duration::from_secs(2_592_000); // 30 days -const DEFAULT_UPDATE_INTERVAL_HOURS: u64 = MAX_CACHE_AGE.as_secs() / 3600; // 30 days -const SUPPORTED_TLS_BACKENDS: &[RawTlsBackend] = &[ - #[cfg(feature = "native-tls")] - RawTlsBackend::NativeTls, - #[cfg(feature = "rustls-with-webpki-roots")] - RawTlsBackend::RustlsWithWebpkiRoots, - #[cfg(feature = "rustls-with-native-roots")] - RawTlsBackend::RustlsWithNativeRoots, -]; - -struct SystemDirectories { - config: PathBuf, - cache: PathBuf, - data: PathBuf, -} - -impl SystemDirectories { - fn discover() -> Result { - use etcetera::{ - app_strategy::choose_native_strategy, choose_app_strategy, AppStrategy, AppStrategyArgs, - }; - - let args = AppStrategyArgs { - top_level_domain: String::new(), - author: String::new(), - app_name: crate::NAME.to_string(), - }; - - // The app strategy prefers XDG on MacOs, whereas the native strategy returns paths which - // are used by installed applications. On Linux and Windows, the strategies are the same. - let app_dirs = choose_app_strategy(args.clone())?; - let native_dirs = choose_native_strategy(args)?; - - // We prefer the XDG paths, but before tealdeer 1.9, we used only the native paths on MacOs. - // So if we find files in these locations, we keep using them. - let fallback = |app_dir: PathBuf, native_dir: PathBuf| { - if !app_dir.exists() && native_dir.exists() { - native_dir - } else { - app_dir - } - }; - - Ok(Self { - config: fallback(app_dirs.config_dir(), native_dirs.config_dir()), - cache: fallback(app_dirs.cache_dir(), native_dirs.cache_dir()), - data: fallback(app_dirs.data_dir(), native_dirs.data_dir()), - }) - } -} -static SYSTEM_DIRECTORIES: LazyLock = LazyLock::new(|| { - SystemDirectories::discover().expect("Failed to initialize system directories.") -}); - -pub(crate) fn supported_tls_backends_string() -> String { - SUPPORTED_TLS_BACKENDS - .iter() - .map(std::string::ToString::to_string) - .collect::>() - .join(", ") -} fn default_underline() -> bool { false } -const fn default_base_indent() -> usize { - 2 -} - -const fn default_command_indent() -> usize { - 6 -} - fn default_bold() -> bool { false } -fn default_italic() -> bool { - false -} - -#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] #[serde(rename_all = "lowercase")] +#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] pub enum RawColor { Black, Red, Green, Yellow, Blue, - Magenta, - Purple, // Backwards compatibility with ansi_term (until tealdeer 1.5.0) + Purple, Cyan, White, - Ansi(u8), - Rgb { r: u8, g: u8, b: u8 }, } impl From for Color { fn from(raw_color: RawColor) -> Self { match raw_color { - RawColor::Black => Self::Black, - RawColor::Red => Self::Red, - RawColor::Green => Self::Green, - RawColor::Yellow => Self::Yellow, - RawColor::Blue => Self::Blue, - RawColor::Magenta | RawColor::Purple => Self::Magenta, - RawColor::Cyan => Self::Cyan, - RawColor::White => Self::White, - RawColor::Ansi(num) => Self::Fixed(num), - RawColor::Rgb { r, g, b } => Self::Rgb(r, g, b), + RawColor::Black => Color::Black, + RawColor::Red => Color::Red, + RawColor::Green => Color::Green, + RawColor::Yellow => Color::Yellow, + RawColor::Blue => Color::Blue, + RawColor::Purple => Color::Purple, + RawColor::Cyan => Color::Cyan, + RawColor::White => Color::White, } } } @@ -145,11 +57,8 @@ struct RawStyle { pub underline: bool, #[serde(default = "default_bold")] pub bold: bool, - #[serde(default = "default_italic")] - pub italic: bool, } -#[allow(clippy::derivable_impls)] // Explicitly control defaults impl Default for RawStyle { fn default() -> Self { Self { @@ -157,10 +66,9 @@ impl Default for RawStyle { background: None, underline: false, bold: false, - italic: false, } } -} +} // impl RawStyle impl From for Style { fn from(raw_style: RawStyle) -> Self { @@ -171,7 +79,7 @@ impl From for Style { } if let Some(background) = raw_style.background { - style = style.bg(Color::from(background)); + style = style.on(Color::from(background)); } if raw_style.underline { @@ -182,10 +90,6 @@ impl From for Style { style = style.bold(); } - if raw_style.italic { - style = style.italic(); - } - style } } @@ -204,197 +108,14 @@ struct RawStyleConfig { pub example_variable: RawStyle, } -impl From<&RawStyleConfig> for StyleConfig { - fn from(raw_style_config: &RawStyleConfig) -> Self { - Self { - command_name: raw_style_config.command_name.into(), - description: raw_style_config.description.into(), - example_text: raw_style_config.example_text.into(), - example_code: raw_style_config.example_code.into(), - example_variable: raw_style_config.example_variable.into(), - } - } -} - #[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq)] -struct RawDisplayConfig { - #[serde(default)] - pub compact: bool, - #[serde(default)] - pub use_pager: bool, - #[serde(default)] - pub show_title: bool, - #[serde(default)] - pub indent: RawIndent, -} - -#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] -struct RawIndent { - #[serde(default = "default_base_indent")] - base: usize, - #[serde(default = "default_command_indent")] - command: usize, -} - -impl Default for RawIndent { - fn default() -> Self { - Self { - base: 2, - command: 6, - } - } -} - -impl From<&RawDisplayConfig> for DisplayConfig { - fn from(raw_display_config: &RawDisplayConfig) -> Self { - Self { - compact: raw_display_config.compact, - use_pager: raw_display_config.use_pager, - show_title: raw_display_config.show_title, - indent: Indent { - base: raw_display_config.indent.base, - command: raw_display_config.indent.command, - }, - } - } -} - -/// Serde doesn't support default values yet (tracking issue: -/// ), so we need to wrap -/// `DEFAULT_UPDATE_INTERVAL_HOURS` in a function to be able to use -/// `#[serde(default = ...)]` -const fn default_auto_update_interval_hours() -> u64 { - DEFAULT_UPDATE_INTERVAL_HOURS -} - -fn default_archive_source() -> String { - "https://github.com/tldr-pages/tldr/releases/latest/download".to_owned() -} - -/// Controls when a warning about an outdated cache is printed. -/// -/// Currently, the only nameable option is `"never"`. In the future, this may -/// be extended to also accept a duration (e.g. `"60d"`), after which the -/// warning should be shown. -#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] -#[serde(rename_all = "lowercase")] -enum RawWarnCacheAge { - Never, -} - -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] -struct RawUpdatesConfig { - #[serde(default)] - pub auto_update: bool, - #[serde(default = "default_auto_update_interval_hours")] - pub auto_update_interval_hours: u64, - #[serde(default = "default_archive_source")] - pub archive_source: String, - #[serde(default)] - pub tls_backend: RawTlsBackend, - #[serde(default)] - pub download_languages: Option>, - #[serde(default)] - pub warn_cache_age: Option, -} - -impl Default for RawUpdatesConfig { - fn default() -> Self { - Self { - auto_update: false, - auto_update_interval_hours: DEFAULT_UPDATE_INTERVAL_HOURS, - archive_source: default_archive_source(), - tls_backend: RawTlsBackend::default(), - download_languages: None, - warn_cache_age: None, - } - } -} - -#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq)] -struct RawDirectoriesConfig { - #[serde(default)] - pub cache_dir: Option, - #[serde(default)] - pub custom_pages_dir: Option, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] -#[serde(rename_all = "lowercase")] -enum RawPlatformType { - Current, - All, - MacOs, // alias for Platform(PlatformType::OsX) - #[serde(untagged)] - Platform(PlatformType), -} - -impl RawPlatformType { - pub fn flatten(raw_platforms: impl IntoIterator) -> Vec { - let mut flattened = Vec::new(); - for raw_platform in raw_platforms { - match raw_platform { - RawPlatformType::Current => flattened.push(PlatformType::current()), - RawPlatformType::Platform(platform) => flattened.push(platform), - RawPlatformType::MacOs => flattened.push(PlatformType::OsX), - RawPlatformType::All => flattened.extend(PlatformType::value_variants()), - } - } - flattened.clear_duplicates(); - flattened - } -} - -#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq)] -struct RawSearchConfig { - pub languages: Option>, - pub platforms: Option>, -} - -impl<'a> From<&'a RawSearchConfig> for SearchConfig<'a> { - fn from(raw_search_config: &'a RawSearchConfig) -> Self { - let languages = raw_search_config - .languages - .as_ref() - .map_or_else(get_languages_from_env, |langs| { - langs.iter().map(|lang| Language(lang)).collect() - }); - let platforms = if let Some(raw_platforms) = raw_search_config.platforms.as_ref() { - RawPlatformType::flatten(raw_platforms.iter().copied()) - } else { - RawPlatformType::flatten([ - RawPlatformType::Current, - RawPlatformType::Platform(PlatformType::Common), - RawPlatformType::All, - ]) - }; - - Self { - languages, - platforms, - } - } -} - -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] -#[serde(default)] struct RawConfig { style: RawStyleConfig, - display: RawDisplayConfig, - updates: RawUpdatesConfig, - directories: RawDirectoriesConfig, - search: RawSearchConfig, } -impl Default for RawConfig { - fn default() -> Self { - let mut raw_config = RawConfig { - style: RawStyleConfig::default(), - display: RawDisplayConfig::default(), - updates: RawUpdatesConfig::default(), - directories: RawDirectoriesConfig::default(), - search: RawSearchConfig::default(), - }; +impl RawConfig { + fn new() -> Self { + let mut raw_config = Self::default(); // Set default config raw_config.style.example_text.foreground = Some(RawColor::Green); @@ -405,9 +126,9 @@ impl Default for RawConfig { raw_config } -} +} // impl RawConfig -#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq)] pub struct StyleConfig { pub description: Style, pub command_name: Style, @@ -416,613 +137,135 @@ pub struct StyleConfig { pub example_variable: Style, } -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct DisplayConfig { - pub compact: bool, - pub use_pager: bool, - pub show_title: bool, - pub indent: Indent, -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct Indent { - pub base: usize, - pub command: usize, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct UpdatesConfig<'a> { - pub auto_update: bool, - pub auto_update_interval: Duration, - pub archive_source: &'a str, - pub tls_backend: TlsBackend, - pub download_languages: Vec>, - pub warn_cache_age: Option, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct PathWithSource { - pub path: PathBuf, - pub source: PathSource, -} - -impl PathWithSource { - pub fn path(&self) -> &Path { - &self.path - } -} - -impl fmt::Display for PathWithSource { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{} ({})", self.path.display(), self.source) - } -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct DirectoriesConfig { - pub cache_dir: PathWithSource, - pub custom_pages_dir: Option, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct SearchConfig<'a> { - pub languages: Vec>, - pub platforms: Vec, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct Language<'a>(pub &'a str); - -fn get_languages<'a>( - env_lang: Option<&'a str>, - env_language: Option<&'a str>, -) -> Vec> { - // Language list according to - // https://github.com/tldr-pages/tldr/blob/main/CLIENT-SPECIFICATION.md#language - - let Some(env_lang) = env_lang else { - return vec![Language("en")]; - }; - - // Create an iterator that contains $LANGUAGE (':' separated list) followed by $LANG (single language) - let locales = env_language.unwrap_or("").split(':').chain([env_lang]); - - let mut lang_list = Vec::new(); - for locale in locales { - if !locale.is_ascii() { - info!("Skipping non-ASCII locale string: {locale}"); - continue; - } - - // Language plus country code (e.g. `en_US`) - if locale.len() >= 5 && locale.chars().nth(2) == Some('_') { - lang_list.push(Language(&locale[..5])); - } - // Language code only (e.g. `en`) - if locale.len() >= 2 && locale != "POSIX" { - lang_list.push(Language(&locale[..2])); - } - } - - lang_list.push(Language("en")); - lang_list.clear_duplicates(); - lang_list -} - -pub fn get_languages_from_env<'a>() -> Vec> { - static LANG: LazyLock> = LazyLock::new(|| std::env::var("LANG").ok()); - static LANGUAGE: LazyLock> = LazyLock::new(|| std::env::var("LANGUAGE").ok()); - get_languages( - LANG.as_ref().map(String::as_str), - LANGUAGE.as_ref().map(String::as_str), - ) -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] -#[serde(rename_all = "kebab-case")] -pub enum RawTlsBackend { - /// Native TLS (`SChannel` on Windows, Secure Transport on macOS and OpenSSL otherwise) - NativeTls, - /// Rustls with `WebPKI` roots. - RustlsWithWebpkiRoots, - /// Rustls with native roots. - RustlsWithNativeRoots, -} - -impl Default for RawTlsBackend { - fn default() -> Self { - *SUPPORTED_TLS_BACKENDS.first().unwrap() - } -} - -impl std::fmt::Display for RawTlsBackend { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - self.serialize(f) - } -} - -/// Allows choosing a `reqwest`'s TLS backend. Available TLS backends: -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum TlsBackend { - /// Native TLS (`SChannel` on Windows, Secure Transport on macOS and OpenSSL otherwise) - #[cfg(feature = "native-tls")] - NativeTls, - /// Rustls with `WebPKI` roots. - #[cfg(feature = "rustls-with-webpki-roots")] - RustlsWithWebpkiRoots, - /// Rustls with native roots. - #[cfg(feature = "rustls-with-native-roots")] - RustlsWithNativeRoots, -} - -impl TryFrom for TlsBackend { - type Error = anyhow::Error; - - fn try_from(raw: RawTlsBackend) -> Result { - match raw { - #[cfg(feature = "native-tls")] - RawTlsBackend::NativeTls => Ok(TlsBackend::NativeTls), - #[cfg(feature = "rustls-with-webpki-roots")] - RawTlsBackend::RustlsWithWebpkiRoots => Ok(TlsBackend::RustlsWithWebpkiRoots), - #[cfg(feature = "rustls-with-native-roots")] - RawTlsBackend::RustlsWithNativeRoots => Ok(TlsBackend::RustlsWithNativeRoots), - // when compiling without all TLS backend features, we want to handle config error. - #[allow(unreachable_patterns)] - _ => Err(anyhow!( - "Unsupported TLS backend: {}. This tealdeer build has support for the following options: {}", - raw, - supported_tls_backends_string(), - )) - } - } -} - -impl TlsBackend { - const fn as_raw(self) -> RawTlsBackend { - match self { - #[cfg(feature = "native-tls")] - Self::NativeTls => RawTlsBackend::NativeTls, - #[cfg(feature = "rustls-with-webpki-roots")] - Self::RustlsWithWebpkiRoots => RawTlsBackend::RustlsWithWebpkiRoots, - #[cfg(feature = "rustls-with-native-roots")] - Self::RustlsWithNativeRoots => RawTlsBackend::RustlsWithNativeRoots, - } - } -} - -impl fmt::Display for TlsBackend { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.as_raw().fmt(f) - } -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Config<'a> { +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct Config { pub style: StyleConfig, - pub display: DisplayConfig, - pub updates: UpdatesConfig<'a>, - pub directories: DirectoriesConfig, - pub search: SearchConfig<'a>, - pub file_path: PathWithSource, } -impl<'a> Config<'a> { - /// Convert a `RawConfig` to a high-level `Config`. - /// - /// For this, some values need to be converted to other types and some - /// defaults need to be set (sometimes based on env variables). - fn from_raw(raw_config: &'a RawConfig, config_file_path: PathWithSource) -> Result { - let style = (&raw_config.style).into(); - let display = (&raw_config.display).into(); - let search: SearchConfig<'a> = (&raw_config.search).into(); - - let updates = UpdatesConfig { - auto_update: raw_config.updates.auto_update, - auto_update_interval: Duration::from_secs( - raw_config.updates.auto_update_interval_hours * 3600, - ), - archive_source: &raw_config.updates.archive_source, - tls_backend: raw_config.updates.tls_backend.try_into()?, - download_languages: raw_config.updates.download_languages.as_ref().map_or_else( - || search.languages.clone(), - |languages| languages.iter().map(|lang| Language(lang)).collect(), - ), - warn_cache_age: match raw_config.updates.warn_cache_age { - None => Some(MAX_CACHE_AGE), - Some(RawWarnCacheAge::Never) => None, +impl From for Config { + fn from(raw_config: RawConfig) -> Self { + Self { + style: StyleConfig { + command_name: raw_config.style.command_name.into(), + description: raw_config.style.description.into(), + example_text: raw_config.style.example_text.into(), + example_code: raw_config.style.example_code.into(), + example_variable: raw_config.style.example_variable.into(), }, - }; + } + } +} - let relative_path_root = config_file_path - .path() - .parent() - .context("Failed to get config directory")?; - let home_path = env::home_dir(); +#[allow(clippy::needless_pass_by_value)] +fn map_io_err_to_config_err(e: IoError) -> TealdeerError { + ConfigError(format!("Io Error: {}", e)) +} - // Determine directories config. For this, we need to take some - // additional factory into account, like env variables, or the - // user config. - let cache_dir_env_var = "TEALDEER_CACHE_DIR"; - let cache_dir = if let Ok(env_var) = env::var(cache_dir_env_var) { - // For backwards compatibility reasons, the cache directory can be - // overridden using an env variable. This is deprecated and will be - // phased out in the future. - eprintln!("Warning: The ${cache_dir_env_var} env variable is deprecated, use the `cache_dir` option in the config file instead."); - PathWithSource { - path: PathBuf::from(env_var), - source: PathSource::EnvVar, - } - } else if let Some(config_value) = &raw_config.directories.cache_dir { - // Resolve possible ~ prefixed path - let expanded_path = expand_home(config_value, home_path.as_deref())?; - // Resolve possible relative path. - let resolved_path = relative_path_root.join(expanded_path); +impl Config { + pub fn load() -> Result { + debug!("Loading config"); - PathWithSource { - path: resolved_path, - source: PathSource::ConfigFile, - } + // Determine path + let config_file_path = get_config_path() + .map_err(|e| ConfigError(format!("Could not determine config path: {}", e)))?; + + // Load config + let raw_config: RawConfig = if config_file_path.exists() && config_file_path.is_file() { + let mut config_file = + fs::File::open(config_file_path).map_err(map_io_err_to_config_err)?; + let mut contents = String::new(); + let _ = config_file + .read_to_string(&mut contents) + .map_err(map_io_err_to_config_err)?; + toml::from_str(&contents) + .map_err(|err| ConfigError(format!("Failed to parse config file: {}", err)))? } else { - PathWithSource { - path: SYSTEM_DIRECTORIES.cache.clone(), - source: PathSource::OsConvention, - } - }; - let custom_pages_dir = raw_config - .directories - .custom_pages_dir - .as_ref() - .map(|path| -> Result { - // Resolve possible ~ prefixed path - let expanded_path = expand_home(path, home_path.as_deref())?; - // Resolve possible relative path. - let resolved_path = relative_path_root.join(expanded_path); - - Ok(PathWithSource { - path: resolved_path, - source: PathSource::ConfigFile, - }) - }) - .transpose()? - .or_else(|| { - // Note: The `join("")` call ensures that there's a trailing slash - Some(PathWithSource { - path: SYSTEM_DIRECTORIES.data.join("pages").join(""), - source: PathSource::OsConvention, - }) - }); - let directories = DirectoriesConfig { - cache_dir, - custom_pages_dir, + RawConfig::new() }; - Ok(Self { - style, - display, - updates, - directories, - search, - file_path: config_file_path, - }) + Ok(Self::from(raw_config)) } -} - -/// Expands tilde (~) prefixed directories into its absolute version -fn expand_home<'a>(input_path: &'a Path, home_path: Option<&Path>) -> Result> { - let mut components = input_path.components(); - - if let Some(Component::Normal(first_component_raw)) = components.next() { - let first_component = first_component_raw - .to_str() - .ok_or(anyhow!("Path contains invalid UTF-8"))?; - - if first_component == "~" { - let home_path = home_path.ok_or(anyhow!("Unable to find user home directory"))?; - let rest: PathBuf = components.collect(); - let expanded = home_path.join(rest); - - return Ok(Cow::Owned(expanded)); - } else if first_component.starts_with('~') { - return Err(anyhow!("Tilde expansion with a login name not supported")); - } - } - - Ok(Cow::Borrowed(input_path)) -} - -/// The [`ConfigLoader`] is used to load a [`Config`] from a file. -/// -/// Since the rich [`Config`] keeps references to [`RawConfig`], the raw config needs to be kept alive outside of the -/// [`Config`]. The [`ConfigLoader`] thus offers the following flow: -/// 1. Read a raw config using [`ConfigLoader::read`] or [`ConfigLoader::read_default_path`]. -/// 2. Validate the contents to a [`Config`] that borrows the [`ConfigLoader`]. -pub struct ConfigLoader { - raw: RawConfig, - path: PathWithSource, -} - -impl ConfigLoader { - fn read_internal(path: PathWithSource, allow_not_found: bool) -> Result { - match fs::read_to_string(&path.path) { - Ok(content) => Ok(Self { - raw: toml::from_str(&content).with_context(|| { - format!( - "Could not parse config file contents as toml from {}.", - path.path.display() - ) - })?, - path, - }), - Err(e) if allow_not_found && e.kind() == ErrorKind::NotFound => Ok(Self { - raw: RawConfig::default(), - path, - }), - Err(e) => Err(e).context(format!( - "Could not read config file contents from {}.", - path.path().display() - )), - } - } - - /// Create a loader that uses the config at `path`. - pub fn read(path: PathBuf) -> Result { - Self::read_internal( - PathWithSource { - path, - source: PathSource::Cli, - }, - false, - ) - } - - /// Create a loader that uses the default config file location. If no file is present at the default location, the - /// default configuration is used. - pub fn read_default_path() -> Result { - let path = get_default_config_path(); - Self::read_internal(path, true) - } - - /// Parse the read [`RawConfig`] into a [`Config`]. - pub fn load(&self) -> Result> { - Config::from_raw(&self.raw, self.path.clone()) - .context("Could not process raw config into rich config") - } -} +} // impl Config /// Return the path to the config directory. /// /// The config dir path can be overridden using the `TEALDEER_CONFIG_DIR` env -/// variable. Otherwise, the user config directory is returned. +/// variable. Otherwise, `$XDG_CONFIG_hOME/tealdeer` is returned. /// /// Note that this function does not verify whether the directory at that -/// location exists, or is a directory. -pub fn get_config_dir() -> (PathBuf, PathSource) { +/// loation exists, or is a directory. +pub fn get_config_dir() -> Result { // Allow overriding the config directory by setting the // $TEALDEER_CONFIG_DIR env variable. if let Ok(value) = env::var("TEALDEER_CONFIG_DIR") { - return (PathBuf::from(value), PathSource::EnvVar); - } + return Ok(PathBuf::from(value)); + }; - (SYSTEM_DIRECTORIES.config.clone(), PathSource::OsConvention) + // Otherwise, fall back to $XDG_CONFIG_HOME/tealdeer. + let xdg_dirs = match BaseDirectories::with_prefix(crate::NAME) { + Ok(dirs) => dirs, + Err(_) => { + return Err(ConfigError("Could not determine XDG base directory.".into())) + } + }; + Ok(xdg_dirs.get_config_home()) } /// Return the path to the config file. /// /// Note that this function does not verify whether the file at that location /// exists, or is a file. -pub fn get_default_config_path() -> PathWithSource { - let (mut path, source) = get_config_dir(); - path.push(CONFIG_FILE_NAME); - PathWithSource { path, source } +pub fn get_config_path() -> Result { + let config_dir = get_config_dir()?; + let config_file_path = config_dir.join(CONFIG_FILE_NAME); + Ok(config_file_path) } /// Create default config file. -/// path: Can be specified to create the config in that path instead of -/// the default path. -pub fn make_default_config(path: Option<&Path>) -> Result { - let config_file_path = if let Some(p) = path { - p.into() - } else { - let (config_dir, _) = get_config_dir(); +pub fn make_default_config() -> Result { + let config_dir = get_config_dir()?; - // Ensure that config directory exists - if config_dir.exists() { - ensure!( - config_dir.is_dir(), - "Config directory could not be created: {} already exists but is not a directory", - config_dir.to_string_lossy(), - ); - } else { - fs::create_dir_all(&config_dir).context("Could not create config directory")?; + // Ensure that config directory exists + if !config_dir.exists() { + if let Err(e) = fs::create_dir_all(&config_dir) { + return Err(ConfigError(format!( + "Could not create config directory: {}", + e + ))); } - - config_dir.join(CONFIG_FILE_NAME) - }; + } else if !config_dir.is_dir() { + return Err(ConfigError(format!( + "Config directory could not be created: {} already exists but is not a directory", + config_dir.to_string_lossy(), + ))); + } // Ensure that a config file doesn't get overwritten - ensure!( - !config_file_path.is_file(), - "A configuration file already exists at {}, no action was taken.", - config_file_path.to_str().unwrap() - ); + let config_file_path = config_dir.join(CONFIG_FILE_NAME); + if config_file_path.is_file() { + return Err(ConfigError(format!( + "A configuration file already exists at {}, no action was taken.", + config_file_path.to_str().unwrap() + ))); + } // Create default config - let serialized_config = - toml::to_string(&RawConfig::default()).context("Failed to serialize default config")?; + let serialized_config = toml::to_string(&RawConfig::new()) + .map_err(|err| ConfigError(format!("Failed to serialize default config: {}", err)))?; // Write default config - let mut config_file = - File::create(&config_file_path).context("Could not create config file")?; + let mut config_file = fs::File::create(&config_file_path).map_err(map_io_err_to_config_err)?; let _wc = config_file .write(serialized_config.as_bytes()) - .context("Could not write to config file")?; + .map_err(map_io_err_to_config_err)?; Ok(config_file_path) } -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn serialize_deserialize() { - let raw_config = RawConfig::default(); - let serialized = toml::to_string(&raw_config).unwrap(); - let deserialized: RawConfig = toml::from_str(&serialized).unwrap(); - assert_eq!(raw_config, deserialized); - } - - #[test] - fn expand_path_with_valid_home() { - let home = Some(PathBuf::from("/foo/bar")); - let path_to_expand = PathBuf::from("~/baz"); - - assert_eq!( - *expand_home(&path_to_expand, home.as_deref()).unwrap(), - PathBuf::from("/foo/bar/baz") - ); - } - - #[test] - fn expand_path_with_absolute_path() { - let home = Some(PathBuf::from("/foo/bar")); - let dir_to_expand = PathBuf::from("/one/two"); - - assert_eq!( - *expand_home(&dir_to_expand, home.as_deref()).unwrap(), - dir_to_expand - ); - } - - #[test] - fn error_with_tilde_username() { - let home = Some(PathBuf::from("/foo/bar")); - let dir_to_expand = PathBuf::from("~baz/foo"); - - assert!(expand_home(&dir_to_expand, home.as_deref()).is_err()); - } - - #[test] - fn expand_tilde_in_config_file() { - let mut raw_config = RawConfig::default(); - raw_config.directories.cache_dir = Some("~/my/custom_cache".into()); - raw_config.directories.custom_pages_dir = Some("~/custom_pages".into()); - - let config = Config::from_raw( - &raw_config, - PathWithSource { - path: PathBuf::from("/path/to/config/config.toml"), - source: PathSource::OsConvention, - }, - ) - .unwrap(); - - let home_dir = env::home_dir().unwrap(); - - assert_eq!( - config.directories.cache_dir.path(), - home_dir.join("my/custom_cache") - ); - assert_eq!( - config.directories.custom_pages_dir.unwrap().path(), - home_dir.join("custom_pages") - ); - } - - #[test] - fn relative_path_resolution() { - let mut raw_config = RawConfig::default(); - raw_config.directories.cache_dir = Some("../cache".into()); - raw_config.directories.custom_pages_dir = Some("../custom_pages".into()); - - let config = Config::from_raw( - &raw_config, - PathWithSource { - path: PathBuf::from("/path/to/config/config.toml"), - source: PathSource::OsConvention, - }, - ) - .unwrap(); - - assert_eq!( - config.directories.cache_dir.path(), - Path::new("/path/to/config/../cache") - ); - assert_eq!( - config.directories.custom_pages_dir.unwrap().path(), - Path::new("/path/to/config/../custom_pages") - ); - } - - mod language { - use super::*; - - #[test] - fn missing_lang_env() { - let lang_list = get_languages(None, Some("de:fr")); - assert_eq!(lang_list, [Language("en")]); - let lang_list = get_languages(None, None); - assert_eq!(lang_list, [Language("en")]); - } - - #[test] - fn missing_language_env() { - let lang_list = get_languages(Some("de"), None); - assert_eq!(lang_list, [Language("de"), Language("en")]); - } - - #[test] - fn preference_order() { - let lang_list = get_languages(Some("de"), Some("fr:cn")); - assert_eq!( - lang_list, - [ - Language("fr"), - Language("cn"), - Language("de"), - Language("en") - ] - ); - } - - #[test] - fn country_code_expansion() { - let lang_list = get_languages(Some("pt_BR"), None); - assert_eq!( - lang_list, - [Language("pt_BR"), Language("pt"), Language("en")] - ); - } - - #[test] - fn with_encoding() { - let lang_list = get_languages(Some("de_DE.UTF-8"), None); - assert_eq!( - lang_list, - [Language("de_DE"), Language("de"), Language("en")] - ); - } - - #[test] - fn ignore_posix_and_c() { - let lang_list = get_languages(Some("POSIX"), None); - assert_eq!(lang_list, [Language("en")]); - let lang_list = get_languages(Some("C"), None); - assert_eq!(lang_list, [Language("en")]); - } - - #[test] - fn no_duplicates() { - let lang_list = get_languages(Some("de"), Some("fr:de:cn:de")); - assert_eq!( - lang_list, - [ - Language("fr"), - Language("de"), - Language("cn"), - Language("en") - ] - ); - } - } +#[test] +fn test_serialize_deserialize() { + let raw_config = RawConfig::new(); + let serialized = toml::to_string(&raw_config).unwrap(); + let deserialized: RawConfig = toml::from_str(&serialized).unwrap(); + assert_eq!(raw_config, deserialized); } diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..c2d1a32 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,29 @@ +use std::fmt; + +#[cfg(feature = "networking")] +use reqwest::Error as ReqwestError; + +#[derive(Debug)] +#[allow(clippy::pub_enum_variant_names)] +pub enum TealdeerError { + CacheError(String), + ConfigError(String), + UpdateError(String), +} + +#[cfg(feature = "networking")] +impl From for TealdeerError { + fn from(err: ReqwestError) -> Self { + TealdeerError::UpdateError(format!("HTTP error: {}", err.to_string())) + } +} + +impl fmt::Display for TealdeerError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + TealdeerError::CacheError(e) => write!(f, "CacheError: {}", e), + TealdeerError::ConfigError(e) => write!(f, "ConfigError: {}", e), + TealdeerError::UpdateError(e) => write!(f, "UpdateError: {}", e), + } + } +} diff --git a/src/extensions.rs b/src/extensions.rs deleted file mode 100644 index e74e9f3..0000000 --- a/src/extensions.rs +++ /dev/null @@ -1,33 +0,0 @@ -use std::mem; - -/// An extension trait to clear duplicates from a collection. -pub(crate) trait Dedup { - fn clear_duplicates(&mut self); -} - -/// Clear duplicates from a collection, keep the first one seen. -/// -/// For small vectors, this will be faster than a `HashSet`. -impl Dedup for Vec { - fn clear_duplicates(&mut self) { - let orig = mem::replace(self, Vec::with_capacity(self.len())); - for item in orig { - if !self.contains(&item) { - self.push(item); - } - } - } -} - -/// Like `str::find`, but starts searching at `start`. -pub(crate) trait FindFrom { - fn find_from(&self, needle: &Self, start: usize) -> Option; -} - -impl FindFrom for str { - fn find_from(&self, needle: &Self, start: usize) -> Option { - self.get(start..) - .and_then(|s| s.find(needle)) - .map(|i| i + start) - } -} diff --git a/src/formatter.rs b/src/formatter.rs index 082f436..488f8ed 100644 --- a/src/formatter.rs +++ b/src/formatter.rs @@ -1,459 +1,78 @@ //! Functions related to formatting and printing lines from a `Tokenizer`. +use std::io::BufRead; + +use ansi_term::{ANSIString, ANSIStrings}; use log::debug; -use crate::{config::Indent, extensions::FindFrom, types::LineType}; +use crate::config::Config; +use crate::tokenizer::Tokenizer; +use crate::types::LineType; -#[derive(Debug, Clone, Copy, Eq)] -/// Represents a snippet from a page of a specific highlighting class. -pub enum PageSnippet { - CommandName(T), - Variable(T), - NormalCode(T), - Description(T), - Text(T), - Title(T), - Linebreak, +fn highlight_command<'a>( + command: &'a str, + example_code: &'a str, + config: &Config, + parts: &mut Vec>, +) { + let mut code_part_end_pos = 0; + while let Some(command_start) = example_code[code_part_end_pos..].find(&command) { + let code_part = &example_code[code_part_end_pos..code_part_end_pos + command_start]; + parts.push(config.style.example_code.paint(code_part)); + parts.push(config.style.command_name.paint(command)); + + code_part_end_pos += command_start + command.len(); + } + parts.push( + config + .style + .example_code + .paint(&example_code[code_part_end_pos..]), + ); } -#[cfg_attr(not(test), allow(dead_code))] -impl PageSnippet { - pub fn map(self, f: F) -> PageSnippet - where - F: FnOnce(T) -> U, - { - match self { - PageSnippet::CommandName(s) => PageSnippet::CommandName(f(s)), - PageSnippet::Variable(s) => PageSnippet::Variable(f(s)), - PageSnippet::NormalCode(s) => PageSnippet::NormalCode(f(s)), - PageSnippet::Description(s) => PageSnippet::Description(f(s)), - PageSnippet::Text(s) => PageSnippet::Text(f(s)), - PageSnippet::Title(s) => PageSnippet::Title(f(s)), - PageSnippet::Linebreak => PageSnippet::Linebreak, +/// Format and highlight code examples including variables in {{ curly braces }}. +fn format_code(command: &str, text: &str, config: &Config) -> String { + let mut parts = Vec::new(); + for between_variables in text.split("}}") { + if let Some(variable_start) = between_variables.find("{{") { + let example_code = &between_variables[..variable_start]; + let example_variable = &between_variables[variable_start + 2..]; + + highlight_command(&command, &example_code, &config, &mut parts); + parts.push(config.style.example_variable.paint(example_variable)); + } else { + highlight_command(&command, &between_variables, &config, &mut parts); } } + + ANSIStrings(&parts).to_string() } -impl, U> PartialEq> for PageSnippet { - fn eq(&self, other: &PageSnippet) -> bool { - match (self, other) { - (PageSnippet::CommandName(s), PageSnippet::CommandName(t)) - | (PageSnippet::Variable(s), PageSnippet::Variable(t)) - | (PageSnippet::NormalCode(s), PageSnippet::NormalCode(t)) - | (PageSnippet::Description(s), PageSnippet::Description(t)) - | (PageSnippet::Text(s), PageSnippet::Text(t)) - | (PageSnippet::Title(s), PageSnippet::Title(t)) => s == t, - (PageSnippet::Linebreak, PageSnippet::Linebreak) => true, - _ => false, - } - } -} - -impl PageSnippet<&str> { - pub fn is_empty(&self) -> bool { - use PageSnippet::*; - - match self { - CommandName(s) | Variable(s) | NormalCode(s) | Description(s) | Text(s) | Title(s) => { - s.is_empty() - } - Linebreak => false, - } - } -} - -/// Parse the content of each line yielded by `lines` and yield `HighLightingSnippet`s accordingly. -pub fn highlight_lines( - lines: L, - process_snippet: &mut F, - keep_empty_lines: bool, - show_title: bool, - indent: Indent, -) -> Result<(), E> +/// Print a token stream to an ANSI terminal. +pub fn print_lines(tokenizer: &mut Tokenizer, config: &Config) where - L: Iterator, - F: for<'snip> FnMut(PageSnippet<&'snip str>) -> Result<(), E>, + R: BufRead, { - let base_indent = " ".repeat(indent.base); - let command_indent = " ".repeat(indent.command); let mut command = String::new(); - for line in lines { - match line { - LineType::Empty => { - if keep_empty_lines { - process_snippet(PageSnippet::Linebreak)?; - } - } + while let Some(token) = tokenizer.next_token() { + match token { + LineType::Empty => println!(), LineType::Title(title) => { - if show_title { - process_snippet(PageSnippet::Linebreak)?; - process_snippet(PageSnippet::Title(&base_indent))?; - process_snippet(PageSnippet::Title(&title))?; - process_snippet(PageSnippet::Linebreak)?; - } else { - debug!("Ignoring title"); - } + debug!("Ignoring title"); + // This is safe as long as the parsed title is only the command, - // and the iterator yields values in order of appearance. + // and tokenizer yields values in order of appearance. command = title; - debug!("Detected command name: {command}"); - } - LineType::Description(text) => { - process_snippet(PageSnippet::Description(&base_indent))?; - process_snippet(PageSnippet::Description(&text))?; - process_snippet(PageSnippet::Linebreak)?; - } - LineType::ExampleText(text) => { - process_snippet(PageSnippet::Text(&base_indent))?; - process_snippet(PageSnippet::Text(&text))?; - process_snippet(PageSnippet::Linebreak)?; + debug!("Detected command name: {}", &command); } + LineType::Description(text) => println!(" {}", config.style.description.paint(text)), + LineType::ExampleText(text) => println!(" {}", config.style.example_text.paint(text)), LineType::ExampleCode(text) => { - process_snippet(PageSnippet::NormalCode(&command_indent))?; - highlight_code(&command, &text, process_snippet)?; - process_snippet(PageSnippet::Linebreak)?; + println!(" {}", &format_code(&command, &text, &config)) } - - LineType::Other(text) => debug!("Unknown line type: {text:?}"), - } - } - process_snippet(PageSnippet::Linebreak)?; - Ok(()) -} - -/// Highlight code examples. -/// - parse placeholders (`{{ curly braces }}`) -/// - replace escaped placeholder markers (`\{\{` and `\}\}`) -fn highlight_code( - command: &str, - mut text: &str, - process_snippet: &mut impl FnMut(PageSnippet<&str>) -> Result<(), E>, -) -> Result<(), E> { - // We replace escaped placeholder markers at the end so that our replacing does not interfere - // with finding the actual markers. - // NOTE: This is not optimal, as it allocates one String for each `replace` - let replace_escaped = |s: &str| s.replace(r"\{\{", "{{").replace(r"\}\}", "}}"); - - loop { - // Find placeholder markers and split into code and placeholder accordingly - - let Some(start_marker) = find_marker(text, "{{", r"\{\{") else { - break; - }; - let Some(mut end_marker) = find_marker(&text[start_marker + 2..], "}}", r"\}\}") else { - break; - }; - end_marker += start_marker + 2; - - // Greedily extend matched range - while end_marker + 2 < text.len() && text.as_bytes()[end_marker + 2] == b'}' { - end_marker += 1; - } - - let placeholder_content = &text[start_marker + 2..end_marker]; - - if start_marker > 0 { - highlight_code_segment( - command, - &replace_escaped(&text[..start_marker]), - process_snippet, - )?; - } - process_snippet(PageSnippet::Variable(&replace_escaped(placeholder_content)))?; - - text = &text[end_marker + 2..]; - } - - if !text.is_empty() { - highlight_code_segment(command, &replace_escaped(text), process_snippet)?; - } - - Ok(()) -} - -/// Find a "{{" (or "}}") substring that does not overlap with a preceding "\{\{" (or "\}\}"). -fn find_marker(s: &str, marker: &str, forbidden_prefix: &str) -> Option { - let mut search_start = 0; - loop { - let marker_index = s.find_from(marker, search_start)?; - - let overlaps_with_prefix = (forbidden_prefix.len() <= marker_index + 1) && { - let prefix_start = marker_index + 1 - forbidden_prefix.len(); - // NOTE: The indices might not be valid character offsets, so we should do this - // comparison on raw bytes. If prefix_start is indeed not a character offset than the - // comparison is guaranteed to return false because forbidden_prefix[0] definitely _is_ - // the start of a (single byte, ASCII) character. - &s.as_bytes()[prefix_start..=marker_index] == forbidden_prefix.as_bytes() - }; - if !overlaps_with_prefix { - return Some(marker_index); - } - - // The next valid marker cannot include the first character of the current match - search_start = marker_index + 1; - } -} - -/// Yields `NormalCode` and `CommandName` in alternating order according to the occurrences of -/// `command_name` in `segment`. Variables are not detected here, see `highlight_code` -/// instead. -fn highlight_code_segment<'a, E>( - command_name: &'a str, - mut segment: &'a str, - process_snippet: &mut impl FnMut(PageSnippet<&'a str>) -> Result<(), E>, -) -> Result<(), E> { - if !command_name.is_empty() { - let mut search_start = 0; - while let Some(match_start) = segment.find_from(command_name, search_start) { - let match_end = match_start + command_name.len(); - if is_freestanding_substring(segment, (match_start, match_end)) { - process_snippet(PageSnippet::NormalCode(&segment[..match_start]))?; - process_snippet(PageSnippet::CommandName(command_name))?; - segment = &segment[match_end..]; - search_start = 0; - } else { - search_start = segment[match_start..] - .char_indices() - .nth(1) - .map_or(segment.len(), |(i, _)| match_start + i); - } - } - } - process_snippet(PageSnippet::NormalCode(segment))?; - Ok(()) -} - -/// Checks whether the characters right before and after the substring (given by half-open index interval) are whitespace (if they exist). -fn is_freestanding_substring(surrounding: &str, substring: (usize, usize)) -> bool { - let (start, end) = substring; - // "okay" meaning or - let char_before_is_okay = surrounding[..start] - .chars() - .last() - .is_none_or(char::is_whitespace); - let char_after_is_okay = surrounding[end..] - .chars() - .next() - .is_none_or(char::is_whitespace); - char_before_is_okay && char_after_is_okay -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_is_freestanding_substring() { - assert!(is_freestanding_substring("I love tldr", (0, 1))); - assert!(is_freestanding_substring("I love tldr", (2, 6))); - assert!(is_freestanding_substring("I love tldr", (7, 11))); - - assert!(is_freestanding_substring("tldr", (0, 4))); - assert!(is_freestanding_substring("tldr ", (0, 4))); - assert!(is_freestanding_substring(" tldr", (1, 5))); - assert!(is_freestanding_substring(" tldr ", (1, 5))); - - assert!(!is_freestanding_substring("tldr", (1, 3))); - assert!(!is_freestanding_substring("tldr ", (1, 4))); - assert!(!is_freestanding_substring(" tldr", (1, 4))); - - assert!(is_freestanding_substring( - " épicé ", - (1, " épicé".len()) // note the missing trailing space - )); - assert!(!is_freestanding_substring( - " épicé ", - (1, " épic".len()) // note the missing trailing space and character - )); - } - - fn run<'a>(cmd: &'a str, segment: &'a str) -> Vec> { - let mut yielded = Vec::new(); - let mut process_snippet = |snip: PageSnippet<&str>| { - if !snip.is_empty() { - yielded.push(snip.map(str::to_string)); - } - Ok::<(), ()>(()) - }; - - highlight_code(cmd, segment, &mut process_snippet).expect("highlight code segment failed"); - yielded - } - - mod highlight_code_segment { - use super::*; - use PageSnippet::*; - - #[test] - fn test_highlight_code_segment() { - assert!(run("make", "").is_empty()); - assert_eq!( - &run("make", "make all CC=clang -q"), - &[CommandName("make"), NormalCode(" all CC=clang -q")] - ); - assert_eq!( - &run("make", " make money --always-make"), - &[ - NormalCode(" "), - CommandName("make"), - NormalCode(" money --always-make") - ] - ); - assert_eq!( - &run("git commit", "git commit -m 'git commit'"), - &[CommandName("git commit"), NormalCode(" -m 'git commit'"),] - ); - } - - #[test] - fn test_i18n() { - assert_eq!( - &run("mäke", "mäke höhlenrätselbücher"), - &[CommandName("mäke"), NormalCode(" höhlenrätselbücher")] - ); - assert_eq!( - &run( - "Müll", - "1000 Gründe warum Müll heute größer ist als Müll früher, ärgerlich" - ), - &[ - NormalCode("1000 Gründe warum "), - CommandName("Müll"), - NormalCode(" heute größer ist als "), - CommandName("Müll"), - NormalCode(" früher, ärgerlich") - ] - ); - assert_eq!( - &run( - "übergang", - "die Zustandsübergangsfunktion übergang Änderungen", - ), - &[ - NormalCode("die Zustandsübergangsfunktion "), - CommandName("übergang"), - NormalCode(" Änderungen") - ], - ); - } - - #[test] - fn test_empty_command() { - let segment = "some code"; - let snippets = [NormalCode(segment)]; - - assert_eq!(run("", segment), snippets); - assert_eq!(run(" ", segment), snippets); - assert_eq!(run(" \t ", segment), snippets); - } - } - - mod placeholders { - use super::*; - use PageSnippet::*; - - #[test] - fn variable_vs_escaped() { - assert_eq!( - run("ping", "ping {{example.com}}"), - [ - CommandName("ping"), - NormalCode(" "), - Variable("example.com"), - ], - ); - assert_eq!( - run( - "docker inspect", - r"docker inspect --format '\{\{range.NetworkSettings.Networks\}\}\{\{.IPAddress\}\}\{\{end\}\}' {{container}}" - ), - [ - CommandName("docker inspect"), - NormalCode( - " --format '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' " - ), - Variable("container"), - ], - ); - assert_eq!( - run("mount", r"mount \\{{computer_name}}\{{share_name}} Z:"), - [ - CommandName("mount"), - NormalCode(r" \\"), - Variable("computer_name"), - NormalCode(r"\"), - Variable("share_name"), - NormalCode(" Z:"), - ], - ); - - assert_eq!(run("", r"\{"), [NormalCode(r"\{")]); - assert_eq!(run("", r"\{{a"), [NormalCode(r"\{{a")]); - assert_eq!(run("", r"\{{a}}"), [NormalCode(r"\"), Variable("a")]); - - // Placeholder has begin marker, but no end marker - assert_eq!(run("", r"{{\}\}}"), [NormalCode("{{}}}")]); - } - - #[test] - fn outer_precedence() { - assert_eq!( - run("git stash", "git stash show --patch {{stash@{0}}}"), - [ - CommandName("git stash"), - NormalCode(" show --patch "), - Variable("stash@{0}"), - ], - ); - - // The following is not listed in the specification, but this is the highlighting I would expect. - assert_eq!( - run("rg", "rg {{}}}"), - [CommandName("rg"), NormalCode(" "), Variable("}")] - ); - - // And these are just to document the current behavior - assert_eq!(run("", "{{{}}}"), [Variable("{}")]); - assert_eq!(run("", "{{{{}}}"), [Variable("{{}")]); - assert_eq!(run("", "{{{}}}}"), [Variable("{}}")]); - } - - #[test] - fn escaped_inside_placeholder() { - assert_eq!( - run( - "playerctl", - r#"playerctl metadata {{[-f|--format]}} "{{Now playing: \{\{artist\}\} - \{\{album\}\} - \{\{title\}\}}}""# - ), - [ - CommandName("playerctl"), - NormalCode(" metadata "), - Variable("[-f|--format]"), - NormalCode(" \""), - Variable("Now playing: {{artist}} - {{album}} - {{title}}"), - NormalCode("\""), - ], - ); - } - - #[test] - fn placeholder_inside_escaped() { - assert_eq!( - run("test", r"test \{\{{{var}} normal\}\}"), - [ - CommandName("test"), - NormalCode(" {{"), - Variable("var"), - NormalCode(" normal}}"), - ], - ); - } - - #[test] - /// Regression test for - fn prefix_check_character_boundary() { - assert_eq!("Ä".len(), 2); - assert_eq!(run("", r"Äxx{{x}}"), [NormalCode("Äxx"), Variable("x")],); + LineType::Other(text) => debug!("Unknown line type: {:?}", text), } } + println!(); } diff --git a/src/line_iterator.rs b/src/line_iterator.rs deleted file mode 100644 index 98088c0..0000000 --- a/src/line_iterator.rs +++ /dev/null @@ -1,122 +0,0 @@ -//! Code to split a `BufRead` instance into an iterator of `LineType`s. - -use std::io::{BufRead, Read}; - -use log::warn; - -use crate::types::LineType; - -#[derive(Debug, PartialEq, Eq)] -pub enum TldrFormat { - /// Not yet clear - Undecided, - /// The original format - V1, - /// The new format (see ) - V2, -} - -/// A `LineIterator` is initialized with a `BufReader` instance that contains the -/// entire Tldr page. It then implements `Iterator`. -#[derive(Debug)] -pub struct LineIterator { - /// An instance of `R: BufRead`. - reader: R, - /// Whether the first line has already been processed or not. - first_line: bool, - /// Buffer for the current line. Used internally. - current_line: String, - /// The tldr page format. - format: TldrFormat, -} - -impl LineIterator -where - R: BufRead, -{ - pub fn new(reader: R) -> Self { - Self { - reader, - first_line: true, - current_line: String::new(), - format: TldrFormat::Undecided, - } - } -} - -impl Iterator for LineIterator { - type Item = LineType; - - fn next(&mut self) -> Option { - self.current_line.clear(); - let bytes_read = self.reader.read_line(&mut self.current_line); - match bytes_read { - Ok(0) => None, - Err(e) => { - warn!("Could not read line from reader: {e:?}"); - None - } - Ok(_) => { - // Handle new titles - if self.first_line { - if self.current_line.starts_with('#') { - // It's the old format. - self.format = TldrFormat::V1; - } else { - // It's the new format! Drop next line. - if let Err(e) = Read::bytes(&mut self.reader) - .find(|b| matches!(b, Ok(b'\n') | Err(_))) - .transpose() - { - warn!("Could not read line from reader: {e:?}"); - return None; - } - self.first_line = false; - self.format = TldrFormat::V2; - return Some(LineType::Title(self.current_line.trim_end().to_string())); - } - } - self.first_line = false; - - // Convert line to a `LineType` instance - match self.format { - TldrFormat::V1 => Some(LineType::from_v1(&self.current_line[..])), - TldrFormat::V2 => Some(LineType::from(&self.current_line[..])), - TldrFormat::Undecided => panic!("Could not determine page format version"), - } - } - } - } -} - -#[cfg(test)] -mod test { - use super::LineIterator; - use crate::types::LineType; - - #[test] - fn test_first_line_old_format() { - let input = "# The Title\n> Description\n"; - let mut lines = LineIterator::new(input.as_bytes()); - let title = lines.next().unwrap(); - assert_eq!(title, LineType::Title("The Title".to_string())); - let description = lines.next().unwrap(); - assert_eq!( - description, - LineType::Description("Description".to_string()) - ); - } - - #[test] - fn test_first_line_new_format() { - let input = "The Title\n=========\n> Description\n"; - let mut lines = LineIterator::new(input.as_bytes()); - let title = lines.next().unwrap(); - assert_eq!(title, LineType::Title("The Title".to_string())); - let description = lines.next().unwrap(); - assert_eq!( - description, - LineType::Description("Description".to_string()) - ); - } -} diff --git a/src/main.rs b/src/main.rs index 6678ceb..459aa3b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ //! An implementation of [tldr](https://github.com/tldr-pages/tldr) in Rust. // -// Copyright (c) 2015-2021 tealdeer developers +// Copyright (c) 2015-2018 tealdeer developers // // Licensed under the Apache License, Version 2.0 or the MIT license @@ -10,135 +10,162 @@ #![deny(clippy::all)] #![warn(clippy::pedantic)] -#![allow(clippy::enum_glob_use)] -#![allow(clippy::module_name_repetitions)] #![allow(clippy::similar_names)] -#![allow(clippy::struct_excessive_bools)] -#![allow(clippy::too_many_lines)] -#![allow(clippy::unnecessary_debug_formatting)] -#![allow(clippy::while_let_loop)] +#![allow(clippy::stutter)] -#[cfg(not(any( - feature = "native-tls", - feature = "rustls-with-webpki-roots", - feature = "rustls-with-native-roots", -)))] -compile_error!( - "at least one of the features \"native-tls\", \"rustls-with-webpki-roots\" or \"rustls-with-native-roots\" must be enabled" -); +#[cfg(feature = "logging")] +extern crate env_logger; -use std::{ - env, - fs::create_dir_all, - io::{self, IsTerminal}, - path::Path, - process::{Command, ExitCode}, -}; +#[cfg(feature = "networking")] +use std::borrow::Cow; +use std::fs::File; +use std::io::BufReader; +use std::path::{Path, PathBuf}; +use std::process; -use anyhow::{anyhow, Context, Result}; -use cache::{CacheConfig, TLDR_OLD_PAGES_DIR}; -use clap::Parser; -use config::{ConfigLoader, Language, StyleConfig, TlsBackend}; -use log::debug; -use types::PlatformType; +#[cfg(feature = "networking")] +use ansi_term::Color; +use docopt::Docopt; +use serde_derive::Deserialize; mod cache; -mod cli; mod config; -pub mod extensions; +mod error; mod formatter; -mod line_iterator; -mod output; +mod tokenizer; mod types; -mod utils; -use crate::{ - cache::{Cache, PageLookupResult, TLDR_PAGES_DIR}, - cli::Cli, - config::{ - get_config_dir, make_default_config, supported_tls_backends_string, Config, PathWithSource, - }, - output::print_page, - types::ColorOptions, - utils::{print_error, print_warning}, -}; +use crate::cache::{Cache, Source}; +use crate::config::{get_config_path, make_default_config, Config}; +use crate::error::TealdeerError::{CacheError, ConfigError, UpdateError}; +use crate::formatter::print_lines; +use crate::tokenizer::Tokenizer; +use crate::types::OsType; const NAME: &str = "tealdeer"; -static TEALDEER_PAGE: &str = - include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/pages/tealdeer.md")); +const VERSION: &str = env!("CARGO_PKG_VERSION"); +const USAGE: &str = " +tealdeer, a fast tldr implementation written in Rust. -/// Clear the cache -fn clear_cache(cache: Cache, quietly: bool) -> Result<()> { - let cache_dir = cache.config().pages_directory.display(); - cache.clear().context("Could not clear cache")?; - if !quietly { - eprintln!("Successfully cleared cache at `{cache_dir}`."); - } - Ok(()) +Usage: + + tldr [options] + tldr [options] + +Options: + + -h --help Show this screen + -v --version Show version information + -l --list List all commands in the cache + -f --render Render a specific markdown file + -o --os Override the operating system [linux, osx, sunos] + -u --update Update the local cache from the network + -U --update-from Update the local cache from the specified URL or path + -c --clear-cache Clear the local cache + -q --quiet Suppress informational messages + --config-path Show config file path + --seed-config Create a basic config + +Examples: + + $ tldr tar + $ tldr --list + +To control the cache: + + $ tldr --update + $ tldr --clear-cache + +You can also manually specify the archive to be used for updating the cache: + + $ tldr --update-from https://github.com/tldr-pages/tldr/archive/master.tar.gz + $ tldr --update-from master.tar.gz + +To render a local file (for testing): + + $ tldr --render /path/to/file.md +"; + +#[cfg(feature = "networking")] +const ARCHIVE_URL: &str = "https://github.com/tldr-pages/tldr/archive/master.tar.gz"; +#[cfg(feature = "networking")] +const MAX_CACHE_AGE: i64 = 2_592_000; // 30 days + +#[derive(Debug, Deserialize)] +struct Args { + arg_command: Option, + flag_help: bool, + flag_version: bool, + flag_list: bool, + flag_render: Option, + flag_os: Option, + flag_update: bool, + flag_update_from: Option, + flag_clear_cache: bool, + flag_quiet: bool, + flag_config_path: bool, + flag_seed_config: bool, } -/// Update the cache -fn update_cache( - cache: &mut Cache, - archive_source: &str, - tls_backend: TlsBackend, - quietly: bool, -) -> Result<()> { - let downloaded_languages = cache - .update(archive_source, tls_backend) - .context("Could not update cache")?; - if !quietly { - eprintln!("Successfully updated cache."); - eprint!("Pages for the following languages were downloaded: "); - let language_strings: Vec<_> = downloaded_languages - .into_iter() - .map(|lang| lang.0) - .collect(); - if language_strings.is_empty() { - eprintln!("(none)"); - } else { - eprintln!("{}", language_strings.join(", ")); +/// Print page by path +fn print_page(path: &Path) -> Result<(), String> { + // Open file + let file = File::open(path).map_err(|msg| format!("Could not open file: {}", msg))?; + let reader = BufReader::new(file); + + // Look up config file, if none is found fall back to default config. + let config = match Config::load() { + Ok(config) => config, + Err(ConfigError(msg)) => { + eprintln!("Could not load config: {}", msg); + process::exit(1); } - } - Ok(()) -} - -/// Show file paths -fn show_paths(config: &Config) { - let config_dir = { - let (mut path, source) = get_config_dir(); - path.push(""); // Trailing path separator - match path.to_str() { - Some(path) => format!("{path} ({source})"), - None => "[Invalid]".to_string(), + Err(e) => { + eprintln!("Could not load config: {}", e); + process::exit(1); } }; - let config_path = config.file_path.to_string(); - let cache_dir = config.directories.cache_dir.to_string(); - let pages_dir = { - let mut path = config.directories.cache_dir.path.clone(); - path.push(TLDR_PAGES_DIR); - path.push(""); // Trailing path separator - path.display().to_string() - }; - let custom_pages_dir = match config.directories.custom_pages_dir { - Some(ref path_with_source) => path_with_source.to_string(), - None => "[None]".to_string(), - }; - println!("Config dir: {config_dir}"); - println!("Config path: {config_path}"); - println!("Cache dir: {cache_dir}"); - println!("Pages dir: {pages_dir}"); - println!("Custom pages dir: {custom_pages_dir}"); + + // Create tokenizer and print output + let mut tokenizer = Tokenizer::new(reader); + print_lines(&mut tokenizer, &config); + + Ok(()) } -fn create_config(path: Option<&Path>) -> Result<()> { - let config_file_path = make_default_config(path).context("Could not create seed config")?; - eprintln!( - "Successfully created seed config file here: {}", - config_file_path.to_str().unwrap() - ); - Ok(()) +/// Check the cache for freshness +#[cfg(feature = "networking")] +fn check_cache(args: &Args, cache: &Cache) { + if !args.flag_update { + match cache.last_update() { + Some(ago) if ago > MAX_CACHE_AGE => { + if args.flag_quiet { + return; + } + println!( + "{}", + Color::Red.paint(format!( + "Cache wasn't updated for more than {} days.\n\ + You should probably run `tldr --update` soon.", + MAX_CACHE_AGE / 24 / 3600 + )) + ); + } + None => { + eprintln!("Cache not found. Please run `tldr --update`."); + process::exit(1); + } + _ => {} + } + }; +} + +/// Check the cache for freshness +/// +/// No-op when networking support is disabled. +#[cfg(not(feature = "networking"))] +fn check_cache(_args: &Args, _cache: &Cache) { + // No-op when no networking support is enabled. } #[cfg(feature = "logging")] @@ -149,296 +176,233 @@ fn init_log() { #[cfg(not(feature = "logging"))] fn init_log() {} -fn spawn_editor(custom_pages_dir: &Path, file_name: &str) -> Result<()> { - create_dir_all(custom_pages_dir).context("Failed to create custom pages directory")?; - - let custom_page_path = custom_pages_dir.join(file_name); - let Some(custom_page_path) = custom_page_path.to_str() else { - return Err(anyhow!("`custom_page_path.to_str()` failed")); - }; - let Ok(editor) = env::var("EDITOR") else { - return Err(anyhow!( - "To edit a custom page, please set the `EDITOR` environment variable." - )); - }; - println!("Editing {custom_page_path:?}"); - - let status = Command::new(&editor).arg(custom_page_path).status()?; - if !status.success() { - return Err(anyhow!("{editor} exit with code {:?}", status.code())); - } - Ok(()) +#[cfg(target_os = "linux")] +fn get_os() -> OsType { + OsType::Linux } -fn main() -> ExitCode { +#[cfg(any(target_os = "macos", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly"))] +fn get_os() -> OsType { + OsType::OsX +} + +#[cfg(not(any(target_os = "linux", + target_os = "macos", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "dragonfly")))] +fn get_os() -> OsType { + OsType::Other +} + +#[cfg(feature = "networking")] +fn get_url_source(source: &str) -> Source { + Source::Url(Cow::Owned(source.to_string())) +} + +#[cfg(not(feature = "networking"))] +fn get_url_source(_source: &str) -> Source { + eprintln!("tealdeer has been compiled without networking support,"); + eprintln!("cannot update the cache from a network URL."); + process::exit(1); +} + +#[cfg(feature = "networking")] +fn get_default_source() -> Source { + Source::Url(Cow::Borrowed(ARCHIVE_URL)) +} + +#[cfg(not(feature = "networking"))] +fn get_default_source() -> Source { + Source::None +} + +fn main() { // Initialize logger init_log(); // Parse arguments - let args = Cli::parse(); + let args: Args = Docopt::new(USAGE) + .and_then(|d| d.deserialize()) + .unwrap_or_else(|e| e.exit()); - // Determine the usage of styles - let enable_styles = match args.color.unwrap_or_default() { - // Attempt to use styling if instructed - ColorOptions::Always => { - yansi::enable(); // disable yansi's automatic detection for ANSI support on Windows - true - } - // Enable styling if: - // * NO_COLOR env var isn't set: https://no-color.org/ - // * The output stream is stdout (not being piped) - ColorOptions::Auto => env::var_os("NO_COLOR").is_none() && io::stdout().is_terminal(), - // Disable styling - ColorOptions::Never => false, - }; - - try_main(args, enable_styles).unwrap_or_else(|error| { - print_error(enable_styles, &error); - ExitCode::FAILURE - }) -} - -fn try_main(args: Cli, enable_styles: bool) -> Result { - // Look up config file, if none is found fall back to default config. - debug!("Loading config"); - let config_loader = match &args.config_path { - Some(path) if !args.seed_config => { - ConfigLoader::read(path.clone()).context("Could not read config from given path")? - } - _ => { - ConfigLoader::read_default_path().context("Could not read config from default path")? - } - }; - let mut config = config_loader.load()?; - - // Override styles if needed - if !enable_styles { - config.style = StyleConfig::default(); + // Show version and exit + if args.flag_version { + let os = get_os(); + println!("{} v{} ({})", NAME, VERSION, os); + process::exit(0); } - let custom_pages_dir = config - .directories - .custom_pages_dir - .as_ref() - .map(PathWithSource::path); + // Specify target OS + let os: OsType = match args.flag_os { + Some(os) => os, + None => get_os(), + }; - // Note: According to the TLDR client spec, page names must be transparently - // lowercased before lookup: - // https://github.com/tldr-pages/tldr/blob/main/CLIENT-SPECIFICATION.md#page-names - let command = args.command.join("-").to_lowercase(); + // Validate cache source + let cache_source: Source = match args.flag_update_from { + Some(ref src) if src.starts_with("http") => get_url_source(src), + Some(ref src) => Source::File(PathBuf::from(src)), + None => get_default_source(), + }; - if args.edit_patch || args.edit_page { - let file_name = if args.edit_patch { - format!("{command}.patch.md") - } else { - format!("{command}.page.md") - }; + // Initialize cache + let cache = Cache::new(cache_source, os); - custom_pages_dir - .context("To edit custom pages/patches, please specify a custom pages directory.") - .and_then(|custom_pages_dir| spawn_editor(custom_pages_dir, &file_name))?; - - return Ok(ExitCode::SUCCESS); + // Clear cache, pass through + if args.flag_clear_cache { + cache.clear().unwrap_or_else(|e| { + match e { + CacheError(msg) | ConfigError(msg) | UpdateError(msg) => { + eprintln!("Could not delete cache: {}", msg) + } + }; + process::exit(1); + }); + if !args.flag_quiet { + println!("Successfully deleted cache."); + } } - // Show various paths - if args.show_paths { - show_paths(&config); + // Update cache, pass through + if args.flag_update || args.flag_update_from.is_some() { + cache.update().unwrap_or_else(|e| { + match e { + CacheError(msg) | ConfigError(msg) | UpdateError(msg) => { + eprintln!("Could not update cache: {}", msg) + } + }; + process::exit(1); + }); + if !args.flag_quiet { + println!("Successfully updated cache."); + } + } + + // Show config file and path, pass through + if args.flag_config_path { + match get_config_path() { + Ok(config_file_path) => { + println!("Config path is: {}", config_file_path.to_str().unwrap()); + } + Err(ConfigError(msg)) => { + eprintln!("Could not look up config_path: {}", msg); + process::exit(1); + } + Err(_) => { + eprintln!("Unknown error"); + process::exit(1); + } + } } // Create a basic config and exit - if args.seed_config { - create_config(args.config_path.as_deref())?; - return Ok(ExitCode::SUCCESS); - } - - // If a local file was passed in, render it and exit - if let Some(file) = args.render { - let reader = PageLookupResult::with_page(file).reader()?; - print_page(reader, args.raw, enable_styles, args.pager, &config)?; - return Ok(ExitCode::SUCCESS); - } - - // The tealdeer page is embedded in the binary, no cache needed - if command == "tealdeer" { - print_page( - TEALDEER_PAGE.as_bytes(), - args.raw, - enable_styles, - args.pager, - &config, - )?; - return Ok(ExitCode::SUCCESS); - } - - if let Some(platforms) = args.platforms { - config.search.platforms = platforms; - if !config.search.platforms.contains(&PlatformType::Common) { - config.search.platforms.push(PlatformType::Common); - } - } - - let (search_languages, download_languages): (&[_], &[_]) = match args.language.as_deref() { - Some(lang) => (&[Language(lang)], &[Language(lang)]), - None => (&config.search.languages, &config.updates.download_languages), - }; - - let cache_config = CacheConfig { - pages_directory: &config.directories.cache_dir.path().join(TLDR_PAGES_DIR), - custom_pages_directory: config - .directories - .custom_pages_dir - .as_ref() - .map(PathWithSource::path), - platforms: &config.search.platforms, - search_languages, - download_languages, - }; - - // TODO: remove in tealdeer 1.9 - let old_config = CacheConfig { - pages_directory: &config.directories.cache_dir.path().join(TLDR_OLD_PAGES_DIR), - ..cache_config - }; - if let Ok(Some(old_cache)) = Cache::open(old_config) { - old_cache.clear()?; - eprintln!("Cleared pages from old cache location."); - } - - if args.clear_cache { - if let Some(cache) = Cache::open(cache_config)? { - clear_cache(cache, args.quiet)?; - } - return Ok(ExitCode::SUCCESS); - } - - let cache = if args.update || config.updates.auto_update && !args.no_auto_update { - let (mut cache, was_created) = Cache::open_or_create(cache_config)?; - if was_created || args.update || cache.age()? >= config.updates.auto_update_interval { - let result = update_cache( - &mut cache, - config.updates.archive_source, - config.updates.tls_backend, - args.quiet, - ); - - if let Err(e) = result { - print_error(enable_styles, &e); - - eprintln!(); - eprintln!("Note: Update errors are often caused by unexpected or missing TLS certificates."); - eprintln!( - "You are currently using the following TLS backend: {}", - config.updates.tls_backend, + if args.flag_seed_config { + match make_default_config() { + Ok(config_file_path) => { + println!( + "Successfully created seed config file here: {}", + config_file_path.to_str().unwrap() ); - eprintln!( - "Try changing the updates.tls_backend setting in the config file, for example:" - ); - eprintln!(); - eprintln!(" [updates]"); - eprintln!(" tls_backend = \"rustls-with-native-roots\""); - eprintln!(); - eprintln!( - "This build of tealdeer has support for the following options: {}", - supported_tls_backends_string(), - ); - - return Ok(ExitCode::FAILURE); + process::exit(0); + } + Err(ConfigError(msg)) => { + eprintln!("Could not create seed config: {}", msg); + process::exit(1); + } + Err(_) => { + eprintln!("Unkown error"); + process::exit(1); } } + } - cache - } else if args.list || !command.is_empty() { - // Cache is needed for these commands to work - let Some(cache) = Cache::open(cache_config)? else { - print_error( - enable_styles, - &anyhow::anyhow!( - "Page cache not found. Please run `tldr --update` to download the cache." - ), - ); - println!("\nNote: You can optionally enable automatic cache updates by adding the"); - println!("following config to your config file:\n"); - println!(" [updates]"); - println!(" auto_update = true\n"); - println!("The path to your config file can be looked up with `tldr --show-paths`."); - println!("To create an initial config file, use `tldr --seed-config`.\n"); - println!("You can find more tips and tricks in our docs:\n"); - println!(" https://tealdeer-rs.github.io/tealdeer/config_updates.html"); - return Ok(ExitCode::FAILURE); + // Render local file and exit + if let Some(ref file) = args.flag_render { + let path = PathBuf::from(file); + if let Err(msg) = print_page(&path) { + eprintln!("{}", msg); + process::exit(1); + } else { + process::exit(0); }; + } - if let Some(max_cache_age) = config.updates.warn_cache_age { - let age = cache.age()?; - if age > max_cache_age && !args.quiet { - print_warning( - enable_styles, - &format!( - "The cache hasn't been updated for {} days.\n\ - You should probably run `tldr --update` soon.", - age.as_secs() / 24 / 3600 - ), - ); + // List cached commands and exit + if args.flag_list { + // Check cache for freshness + check_cache(&args, &cache); + + // Get list of pages + let pages = cache.list_pages().unwrap_or_else(|e| { + match e { + CacheError(msg) | ConfigError(msg) | UpdateError(msg) => { + eprintln!("Could not get list of pages: {}", msg) + } } - } + process::exit(1); + }); - cache - } else { - // There is nothing left to do - return Ok(ExitCode::SUCCESS); - }; - - if args.list { - for page in cache.list_pages()? { - println!("{page}"); - } - - return Ok(ExitCode::SUCCESS); + // Print pages + println!("{}", pages.join(", ")); + process::exit(0); } // Show command from cache - if !command.is_empty() { - // TODO: Remove this check 1 year after version 1.7.0 was released - if cache.old_custom_pages_exist()? { - print_warning( - enable_styles, - &format!( - "Custom pages using the old naming convention were found in {}.\n\ - Please rename them to follow the new convention:\n\ - - `.page` → `.page.md`\n\ - - `.patch` → `.patch.md`", - cache - .config() - .custom_pages_directory - .expect("Old custom pages can only exist in custom pages directory") - .display(), - ), - ); - } + if let Some(ref command) = args.arg_command { + // Check cache for freshness + check_cache(&args, &cache); - let Some(result) = cache.find_page(&command) else { - if !args.quiet { - print_warning( - enable_styles, - &format!( - "Page `{command}` not found in cache.\n\ - Try updating with `tldr --update`, or submit a pull request to:\n\ - https://github.com/tldr-pages/tldr" - ), - ); + // Search for command in cache + if let Some(path) = cache.find_page(&command) { + if let Err(msg) = print_page(&path) { + eprintln!("{}", msg); + process::exit(1); + } else { + process::exit(0); } - return Ok(ExitCode::FAILURE); - }; - - print_page( - result.reader()?, - args.raw, - enable_styles, - args.pager, - &config, - )?; + } else { + if !args.flag_quiet { + println!("Page {} not found in cache", &command); + println!("Try updating with `tldr --update`, or submit a pull request to:"); + println!("https://github.com/tldr-pages/tldr"); + } + process::exit(1); + } } - Ok(ExitCode::SUCCESS) + // Some flags can be run without a command. + if !(args.flag_update || args.flag_clear_cache || args.flag_config_path) { + eprintln!("{}", USAGE); + process::exit(1); + } +} + +#[cfg(test)] +mod test { + use docopt::{Docopt, Error}; + use crate::{Args, OsType, USAGE}; + + fn test_helper(argv: &[&str]) -> Result { + Docopt::new(USAGE).and_then(|d| d.argv(argv.iter()).deserialize()) + } + + #[test] + fn test_docopt_os_case_insensitive() { + let argv = vec!["cp", "--os", "LiNuX"]; + let os = test_helper(&argv).unwrap().flag_os.unwrap(); + assert_eq!(OsType::Linux, os); + } + + #[test] + fn test_docopt_expect_error() { + let argv = vec!["cp", "--os", "lindows"]; + assert!(!test_helper(&argv).is_ok()); + } } diff --git a/src/output.rs b/src/output.rs deleted file mode 100644 index 6243b44..0000000 --- a/src/output.rs +++ /dev/null @@ -1,97 +0,0 @@ -//! Functions for printing pages to the terminal - -use std::io::{self, BufRead, BufReader, Read, Write}; - -use anyhow::{Context, Result}; -use yansi::Paint; - -use crate::{ - config::{Config, StyleConfig}, - formatter::{highlight_lines, PageSnippet}, - line_iterator::LineIterator, -}; - -/// Set up display pager -/// -/// SAFETY: this function may be called multiple times -#[cfg(not(target_os = "windows"))] -fn configure_pager(_: bool) { - use std::sync::Once; - static INIT: Once = Once::new(); - INIT.call_once(|| pager::Pager::with_default_pager("less -R").setup()); -} - -#[cfg(target_os = "windows")] -fn configure_pager(enable_styles: bool) { - use crate::utils::print_warning; - print_warning(enable_styles, "--pager flag not available on Windows!"); -} - -/// Print page by path -pub fn print_page( - reader: impl Read, - enable_markdown: bool, - enable_styles: bool, - use_pager: bool, - config: &Config, -) -> Result<()> { - let reader = BufReader::new(reader); - - // Configure pager if applicable - if use_pager || config.display.use_pager { - configure_pager(enable_styles); - } - - // Lock stdout only once, this improves performance considerably - let stdout = io::stdout(); - let mut handle = stdout.lock(); - - if enable_markdown { - // Print the raw markdown of the file. - for line in reader.lines() { - let line = line.context("Error while reading from a page")?; - writeln!(handle, "{line}").context("Could not write to stdout")?; - } - } else { - // Closure that processes a page snippet and writes it to stdout - let mut process_snippet = |snip: PageSnippet<&str>| { - if snip.is_empty() { - Ok(()) - } else { - print_snippet(&mut handle, snip, &config.style).context("Failed to print snippet") - } - }; - - // Print highlighted lines - highlight_lines( - LineIterator::new(reader), - &mut process_snippet, - !config.display.compact, - config.display.show_title, - config.display.indent, - ) - .context("Could not write to stdout")?; - } - - // We're done outputting data, flush stdout now! - handle.flush().context("Could not flush stdout")?; - - Ok(()) -} - -fn print_snippet( - writer: &mut impl Write, - snip: PageSnippet<&str>, - style: &StyleConfig, -) -> io::Result<()> { - use PageSnippet::*; - - match snip { - CommandName(s) | Title(s) => write!(writer, "{}", s.paint(style.command_name)), - Variable(s) => write!(writer, "{}", s.paint(style.example_variable)), - NormalCode(s) => write!(writer, "{}", s.paint(style.example_code)), - Description(s) => write!(writer, "{}", s.paint(style.description)), - Text(s) => write!(writer, "{}", s.paint(style.example_text)), - Linebreak => writeln!(writer), - } -} diff --git a/src/tokenizer.rs b/src/tokenizer.rs new file mode 100644 index 0000000..7c33740 --- /dev/null +++ b/src/tokenizer.rs @@ -0,0 +1,112 @@ +//! Code to tokenize a `BufRead` instance into an iterator of `LineType`s. + +use std::io::BufRead; + +use log::warn; +use crate::types::LineType; + +#[derive(Debug, PartialEq, Eq)] +pub enum TldrFormat { + /// Not yet clear + Undecided, + /// The original format + V1, + /// The new format (see https://github.com/tldr-pages/tldr/pull/958) + V2, +} + +/// A tokenizer is initialized with a `BufReader` instance that contains the +/// entire Tldr page. It then returns tokens as `Option`. +#[derive(Debug)] +pub struct Tokenizer { + /// An instance of `R: BufRead`. + reader: R, + /// Whether the first line has already been tokenized or not. + first_line: bool, + /// Buffer for the current line. Used internally. + current_line: String, + /// The tldr page format. + format: TldrFormat, +} + +impl Tokenizer +where + R: BufRead, +{ + pub fn new(reader: R) -> Self { + Self { + reader, + first_line: true, + current_line: String::new(), + format: TldrFormat::Undecided, + } + } + + pub fn next_token(&mut self) -> Option { + self.current_line.clear(); + let bytes_read = self.reader.read_line(&mut self.current_line); + match bytes_read { + Ok(0) => None, + Err(e) => { + warn!("Could not read line from token reader: {:?}", e); + None + } + Ok(_) => { + // Handle new titles + if self.first_line && !self.current_line.starts_with('#') { + // It's the new format! Drop next line. + // (Hmm, is there a way to do this without an allocation?) + let mut devnull = String::new(); + if let Err(e) = self.reader.read_line(&mut devnull) { + warn!("Could not read line from token reader: {:?}", e); + return None; + } + self.first_line = false; + self.format = TldrFormat::V2; + return Some(LineType::Title(self.current_line.trim_right().to_string())); + } + + if self.first_line { + // Clear `first_line` flag + self.first_line = false; + + // It's the old format. + self.format = TldrFormat::V1; + } + + // Convert line to a `LineType` instance + match self.format { + TldrFormat::V1 => Some(LineType::from_v1(&self.current_line[..])), + TldrFormat::V2 => Some(LineType::from(&self.current_line[..])), + TldrFormat::Undecided => panic!("Could not determine page format version"), + } + } + } + } +} + +#[cfg(test)] +mod test { + use super::Tokenizer; + use crate::types::LineType; + + #[test] + fn test_first_line_old_format() { + let input = "# The Title\n\n"; + let mut tokenizer = Tokenizer::new(input.as_bytes()); + let title = tokenizer.next_token().unwrap(); + assert_eq!(title, LineType::Title("The Title".to_string())); + let empty = tokenizer.next_token().unwrap(); + assert_eq!(empty, LineType::Empty); + } + + #[test] + fn test_first_line_new_format() { + let input = "The Title\n=========\n\n"; + let mut tokenizer = Tokenizer::new(input.as_bytes()); + let title = tokenizer.next_token().unwrap(); + assert_eq!(title, LineType::Title("The Title".to_string())); + let empty = tokenizer.next_token().unwrap(); + assert_eq!(empty, LineType::Empty); + } +} diff --git a/src/types.rs b/src/types.rs index 7ca6e2d..2e2c3b9 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,131 +1,30 @@ -//! Shared types used in tealdeer. +//! Types used in the client. -use std::{fmt, str}; +use std::fmt; use serde_derive::{Deserialize, Serialize}; #[derive(Debug, Eq, PartialEq, Copy, Clone, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] #[allow(dead_code)] -pub enum PlatformType { +pub enum OsType { Linux, OsX, - Windows, SunOs, - Android, - FreeBsd, - NetBsd, - OpenBsd, - Common, + Other, } -impl fmt::Display for PlatformType { +impl fmt::Display for OsType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - Self::Linux => write!(f, "Linux"), - Self::OsX => write!(f, "macOS / BSD"), - Self::Windows => write!(f, "Windows"), - Self::SunOs => write!(f, "SunOS"), - Self::Android => write!(f, "Android"), - Self::FreeBsd => write!(f, "FreeBSD"), - Self::NetBsd => write!(f, "NetBSD"), - Self::OpenBsd => write!(f, "OpenBSD"), - Self::Common => write!(f, "Common"), + OsType::Linux => write!(f, "Linux"), + OsType::OsX => write!(f, "macOS / BSD"), + OsType::SunOs => write!(f, "SunOS"), + OsType::Other => write!(f, "Unknown OS"), } } } -impl clap::ValueEnum for PlatformType { - fn value_variants<'a>() -> &'a [Self] { - &[ - Self::Linux, - Self::OsX, - Self::SunOs, - Self::Windows, - Self::Android, - Self::FreeBsd, - Self::NetBsd, - Self::OpenBsd, - Self::Common, - ] - } - - fn to_possible_value<'a>(&self) -> Option { - match self { - Self::Linux => Some(clap::builder::PossibleValue::new("linux")), - Self::OsX => Some(clap::builder::PossibleValue::new("macos").alias("osx")), - Self::Windows => Some(clap::builder::PossibleValue::new("windows")), - Self::SunOs => Some(clap::builder::PossibleValue::new("sunos")), - Self::Android => Some(clap::builder::PossibleValue::new("android")), - Self::FreeBsd => Some(clap::builder::PossibleValue::new("freebsd")), - Self::NetBsd => Some(clap::builder::PossibleValue::new("netbsd")), - Self::OpenBsd => Some(clap::builder::PossibleValue::new("openbsd")), - Self::Common => Some(clap::builder::PossibleValue::new("common")), - } - } -} - -impl PlatformType { - #[cfg(target_os = "linux")] - pub fn current() -> Self { - Self::Linux - } - - #[cfg(any(target_os = "macos", target_os = "dragonfly"))] - pub fn current() -> Self { - Self::OsX - } - - #[cfg(target_os = "windows")] - pub fn current() -> Self { - Self::Windows - } - - #[cfg(target_os = "android")] - pub fn current() -> Self { - Self::Android - } - - #[cfg(target_os = "freebsd")] - pub fn current() -> Self { - Self::FreeBsd - } - - #[cfg(target_os = "netbsd")] - pub fn current() -> Self { - Self::NetBsd - } - - #[cfg(target_os = "openbsd")] - pub fn current() -> Self { - Self::OpenBsd - } - - #[cfg(not(any( - target_os = "linux", - target_os = "macos", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", - target_os = "dragonfly", - target_os = "windows", - target_os = "android", - )))] - pub fn current() -> Self { - Self::Other - } -} - -#[derive(Debug, Eq, PartialEq, Copy, Clone, Deserialize, clap::ValueEnum)] -#[serde(rename_all = "lowercase")] -#[derive(Default)] -pub enum ColorOptions { - Always, - #[default] - Auto, - Never, -} - #[derive(Debug, Eq, PartialEq)] pub enum LineType { Empty, @@ -137,24 +36,28 @@ pub enum LineType { } impl<'a> From<&'a str> for LineType { - /// Convert a string slice to a `LineType`. Newlines and trailing whitespace are trimmed. + /// Convert a string slice to a LineType. Newlines and trailing whitespace are trimmed. fn from(line: &'a str) -> Self { - let trimmed: &str = line.trim_end(); + let trimmed: &str = line.trim_right(); let mut chars = trimmed.chars(); match chars.next() { - None => Self::Empty, - Some('#') => Self::Title( + None => LineType::Empty, + Some('#') => LineType::Title( trimmed - .trim_start_matches(|chr: char| chr == '#' || chr.is_whitespace()) + .trim_left_matches(|chr: char| chr == '#' || chr.is_whitespace()) .into(), ), - Some('>') => Self::Description( + Some('>') => LineType::Description( trimmed - .trim_start_matches(|chr: char| chr == '>' || chr.is_whitespace()) + .trim_left_matches(|chr: char| chr == '>' || chr.is_whitespace()) .into(), ), - Some(' ') => Self::ExampleCode(trimmed.trim_start_matches(char::is_whitespace).into()), - Some(_) => Self::ExampleText(trimmed.into()), + Some(' ') => LineType::ExampleCode( + trimmed + .trim_left_matches(|chr: char| chr.is_whitespace()) + .into(), + ), + _ => LineType::ExampleText(trimmed.into()), } } } @@ -166,60 +69,32 @@ impl LineType { let trimmed = line.trim(); let mut chars = trimmed.chars(); match chars.next() { - None => Self::Empty, - Some('#') => Self::Title( + None => LineType::Empty, + Some('#') => LineType::Title( trimmed - .trim_start_matches(|chr: char| chr == '#' || chr.is_whitespace()) + .trim_left_matches(|chr: char| chr == '#' || chr.is_whitespace()) .into(), ), - Some('>') => Self::Description( + Some('>') => LineType::Description( trimmed - .trim_start_matches(|chr: char| chr == '>' || chr.is_whitespace()) + .trim_left_matches(|chr: char| chr == '>' || chr.is_whitespace()) .into(), ), - Some('-') => Self::ExampleText( + Some('-') => LineType::ExampleText( trimmed - .trim_start_matches(|chr: char| chr == '-' || chr.is_whitespace()) + .trim_left_matches(|chr: char| chr == '-' || chr.is_whitespace()) .into(), ), - Some('`') if chars.last() == Some('`') => Self::ExampleCode( + Some('`') if chars.last() == Some('`') => LineType::ExampleCode( trimmed .trim_matches(|chr: char| chr == '`' || chr.is_whitespace()) .into(), ), - Some(_) => Self::Other(trimmed.into()), + _ => LineType::Other(trimmed.into()), } } } -/// The reason why a certain path (e.g. config path or cache dir) was chosen. -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -pub enum PathSource { - /// OS convention (e.g. XDG on Linux) - OsConvention, - /// Env variable (TEALDEER_*) - EnvVar, - /// Config file - ConfigFile, - /// CLI argument override - Cli, -} - -impl fmt::Display for PathSource { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "{}", - match self { - Self::OsConvention => "OS convention", - Self::EnvVar => "env variable", - Self::ConfigFile => "config file", - Self::Cli => "command line argument", - } - ) - } -} - #[cfg(test)] mod test { use super::LineType; diff --git a/src/utils.rs b/src/utils.rs deleted file mode 100644 index f4d825a..0000000 --- a/src/utils.rs +++ /dev/null @@ -1,21 +0,0 @@ -use yansi::{Color, Paint}; - -/// Print a warning to stderr. If `enable_styles` is true, then a yellow -/// message will be printed. -pub fn print_warning(enable_styles: bool, message: &str) { - print_msg(enable_styles, message, "Warning: ", Color::Yellow); -} - -/// Print an anyhow error to stderr. If `enable_styles` is true, then a red -/// message will be printed. -pub fn print_error(enable_styles: bool, error: &anyhow::Error) { - print_msg(enable_styles, &format!("{error:?}"), "Error: ", Color::Red); -} - -fn print_msg(enable_styles: bool, message: &str, prefix: &'static str, color: Color) { - if enable_styles { - eprintln!("{}{}", prefix.paint(color), message.paint(color)); - } else { - eprintln!("{message}"); - } -} diff --git a/tests/cache/pages.en/common/git-checkout.md b/tests/cache/pages.en/common/git-checkout.md deleted file mode 100644 index ca1bacc..0000000 --- a/tests/cache/pages.en/common/git-checkout.md +++ /dev/null @@ -1,36 +0,0 @@ -# git checkout - -> Checkout a branch or paths to the working tree. -> More information: . - -- Create and switch to a new branch: - -`git checkout -b {{branch_name}}` - -- Create and switch to a new branch based on a specific reference (branch, remote/branch, tag are examples of valid references): - -`git checkout -b {{branch_name}} {{reference}}` - -- Switch to an existing local branch: - -`git checkout {{branch_name}}` - -- Switch to the previously checked out branch: - -`git checkout -` - -- Switch to an existing remote branch: - -`git checkout --track {{remote_name}}/{{branch_name}}` - -- Discard all unstaged changes in the current directory (see `git reset` for more undo-like commands): - -`git checkout .` - -- Discard unstaged changes to a given file: - -`git checkout {{path/to/file}}` - -- Replace a file in the current directory with the version of it committed in a given branch: - -`git checkout {{branch_name}} -- {{path/to/file}}` diff --git a/tests/cache/pages.en/common/which.md b/tests/cache/pages.en/common/which.md deleted file mode 100644 index 36d3941..0000000 --- a/tests/cache/pages.en/common/which.md +++ /dev/null @@ -1,11 +0,0 @@ -# which - -> Locate a program in the user's path. - -- Search the PATH environment variable and display the location of any matching executables: - -`which {{executable}}` - -- If there are multiple executables which match, display all: - -`which -a {{executable}}` diff --git a/tests/cache/pages.ja/common/apt.md b/tests/cache/pages.ja/common/apt.md deleted file mode 100644 index fe16a3d..0000000 --- a/tests/cache/pages.ja/common/apt.md +++ /dev/null @@ -1,37 +0,0 @@ -# apt - -> Debian系ディストリビューションで使われるパッケージ管理システムです。 -> Ubuntuのバージョンが16.04か、それ以降で対話モードを使う場合`apt-get`の代わりとして使用します。 -> 詳しくはこちら: - -- 利用可能なパーケージとバージョンのリストの更新(他の`apt`コマンドの前での実行を推奨): - -`sudo apt update` - -- 指定されたパッケージの検索: - -`apt search {{パッケージ}}` - -- パッケージの情報を出力: - -`apt show {{パッケージ}}` - -- パッケージのインストール、または利用可能な最新バージョンに更新: - -`sudo apt install {{パッケージ}}` - -- パッケージの削除(`sudo apt remove --purge`の場合設定ファイルも削除): - -`sudo apt remove {{パッケージ}}` - -- インストールされている全てのパッケージを最新のバージョンにアップグレード: - -`sudo apt upgrade` - -- インストールできるすべてのパッケージを表示: - -`apt list` - -- インストールされた全てのパッケージを表示(依存関係も表示): - -`apt list --installed` diff --git a/tests/style-config.toml b/tests/config.toml similarity index 84% rename from tests/style-config.toml rename to tests/config.toml index c68f2cc..7440a82 100644 --- a/tests/style-config.toml +++ b/tests/config.toml @@ -3,9 +3,6 @@ foreground = "green" underline = false bold = false -[style.command_name] -bold = true - [style.description] underline = false bold = false @@ -18,4 +15,3 @@ underline = false [style.example_variable] underline = true bold = false -italic = true diff --git a/tests/custom-pages/inkscape-v2.patch.md b/tests/custom-pages/inkscape-v2.patch.md deleted file mode 100644 index 5cc5d22..0000000 --- a/tests/custom-pages/inkscape-v2.patch.md +++ /dev/null @@ -1,3 +0,0 @@ -Custom inkscape entry - - My Inkscape example diff --git a/tests/inkscape-default.expected b/tests/inkscape-default.expected new file mode 100644 index 0000000..c7061b3 --- /dev/null +++ b/tests/inkscape-default.expected @@ -0,0 +1,28 @@ + + An SVG (Scalable Vector Graphics) editing program. + Use -z to not open the GUI and only process files in the console. + + Open an SVG file in the Inkscape GUI: + + inkscape filename.svg + + Export an SVG file into a bitmap with the default format (PNG) and the default resolution (90 DPI): + + inkscape filename.svg -e filename.png + + Export an SVG file into a bitmap of 600x400 pixels (aspect ratio distortion may occur): + + inkscape filename.svg -e filename.png -w 600 -h 400 + + Export a single object, given its ID, into a bitmap: + + inkscape filename.svg -i id -e object.png + + Export an SVG document to PDF, converting all texts to paths: + + inkscape filename.svg | inkscape | inkscape --export-pdf=inkscape.pdf | inkscape | inkscape --export-text-to-path + + Duplicate the object with id="path123", rotate the duplicate 90 degrees, save the file, and quit Inkscape: + + inkscape filename.svg --select=path123 --verb=EditDuplicate --verb=ObjectRotate90 --verb=FileSave --verb=FileQuit + diff --git a/tests/cache/pages.en/common/inkscape-v1.md b/tests/inkscape-v1.md similarity index 89% rename from tests/cache/pages.en/common/inkscape-v1.md rename to tests/inkscape-v1.md index fa63b2b..4071196 100644 --- a/tests/cache/pages.en/common/inkscape-v1.md +++ b/tests/inkscape-v1.md @@ -26,7 +26,3 @@ - Duplicate the object with id="path123", rotate the duplicate 90 degrees, save the file, and quit Inkscape: `inkscape {{filename.svg}} --select=path123 --verb=EditDuplicate --verb=ObjectRotate90 --verb=FileSave --verb=FileQuit` - -- Some invalid command just to test the correct highlighting of the command name: - -`inkscape --use-inkscape=v3.0 file` diff --git a/tests/cache/pages.en/common/inkscape-v2.md b/tests/inkscape-v2.md similarity index 89% rename from tests/cache/pages.en/common/inkscape-v2.md rename to tests/inkscape-v2.md index 0a1b530..3051867 100644 --- a/tests/cache/pages.en/common/inkscape-v2.md +++ b/tests/inkscape-v2.md @@ -27,7 +27,3 @@ Export an SVG document to PDF, converting all texts to paths: Duplicate the object with id="path123", rotate the duplicate 90 degrees, save the file, and quit Inkscape: inkscape {{filename.svg}} --select=path123 --verb=EditDuplicate --verb=ObjectRotate90 --verb=FileSave --verb=FileQuit - -Some invalid command just to test the correct highlighting of the command name: - - inkscape --use-inkscape=v3.0 file diff --git a/tests/inkscape-with-config.expected b/tests/inkscape-with-config.expected new file mode 100644 index 0000000..313e140 --- /dev/null +++ b/tests/inkscape-with-config.expected @@ -0,0 +1,28 @@ + + An SVG (Scalable Vector Graphics) editing program. + Use -z to not open the GUI and only process files in the console. + + Open an SVG file in the Inkscape GUI: + + inkscape filename.svg + + Export an SVG file into a bitmap with the default format (PNG) and the default resolution (90 DPI): + + inkscape filename.svg -e filename.png + + Export an SVG file into a bitmap of 600x400 pixels (aspect ratio distortion may occur): + + inkscape filename.svg -e filename.png -w 600 -h 400 + + Export a single object, given its ID, into a bitmap: + + inkscape filename.svg -i id -e object.png + + Export an SVG document to PDF, converting all texts to paths: + + inkscape filename.svg | inkscape | inkscape --export-pdf=inkscape.pdf | inkscape | inkscape --export-text-to-path + + Duplicate the object with id="path123", rotate the duplicate 90 degrees, save the file, and quit Inkscape: + + inkscape filename.svg --select=path123 --verb=EditDuplicate --verb=ObjectRotate90 --verb=FileSave --verb=FileQuit + diff --git a/tests/lib.rs b/tests/lib.rs index d431b5f..91e80b5 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -1,145 +1,29 @@ //! Integration tests. -use std::{ - fs::{self, create_dir_all, File}, - io::{self, Write}, - path::{Path, PathBuf}, - process::Command, - time::{Duration, SystemTime}, -}; +use std::fs::File; +use std::io::Write; +use std::process::Command; use assert_cmd::prelude::*; -use predicates::{ - boolean::PredicateBooleanExt, - ord::eq, - prelude::predicate::str::{contains, diff, is_empty, is_match}, -}; -use tempfile::{Builder as TempfileBuilder, TempDir}; - -pub static TLDR_PAGES_DIR: &str = "tldr-pages"; -pub static TLDR_OLD_PAGES_DIR: &str = "tldr-master"; +use tempdir::TempDir; +use predicates::boolean::PredicateBooleanExt; +use predicates::prelude::predicate::str::{contains, is_empty, similar}; struct TestEnv { - _test_dir: TempDir, + pub cache_dir: TempDir, + pub config_dir: TempDir, + pub input_dir: TempDir, pub default_features: bool, - pub features: Vec, } impl TestEnv { fn new() -> Self { - let test_dir: TempDir = TempfileBuilder::new() - .prefix(".tldr.test") - .tempdir() - .unwrap(); - - let this = TestEnv { - _test_dir: test_dir, + TestEnv { + cache_dir: TempDir::new(".tldr.test.cache").unwrap(), + config_dir: TempDir::new(".tldr.test.config").unwrap(), + input_dir: TempDir::new(".tldr.test.input").unwrap(), default_features: true, - features: vec![], - }; - - create_dir_all(this.cache_dir()).unwrap(); - create_dir_all(this.config_dir()).unwrap(); - create_dir_all(this.custom_pages_dir()).unwrap(); - - this.init_config(); - - this - } - - fn cache_dir(&self) -> PathBuf { - self._test_dir.path().join(".cache") - } - - fn config_dir(&self) -> PathBuf { - self._test_dir.path().join(".config") - } - - fn custom_pages_dir(&self) -> PathBuf { - self._test_dir.path().join(".custom_pages") - } - - fn append_to_config(&self, content: impl AsRef) { - File::options() - .create(true) - .append(true) - .open(self.config_dir().join("config.toml")) - .expect("Failed to open config file") - .write_all(content.as_ref().as_bytes()) - .expect("Failed to append to config file."); - } - fn delete_config(&self) { - fs::remove_file(self.config_dir().join("config.toml")).unwrap(); - } - fn init_config(&self) { - self.append_to_config(format!( - "directories.cache_dir = '{}'\n", - self.cache_dir().to_str().unwrap(), - )); - } - - fn create_secondary_config(self) -> Self { - self.append_to_secondary_config(format!( - "directories.cache_dir = '{}'\n", - self.cache_dir().to_str().unwrap(), - )); - self - } - - fn append_to_secondary_config(&self, content: impl AsRef) { - File::options() - .create(true) - .append(true) - .open(self.config_dir().join("config-secondary.toml")) - .expect("Failed to open config file") - .write_all(content.as_ref().as_bytes()) - .expect("Failed to append to config file."); - } - - fn remove_initial_config(self) -> Self { - let _ = fs::remove_file(self.config_dir().join("config.toml")); - self - } - - /// Add entry for that environment to the "common" pages. - fn add_entry(&self, name: &str, contents: &str) { - self.add_os_entry("common", name, contents); - } - - /// Add entry for that environment to an OS-specific subfolder. - fn add_os_entry(&self, os: &str, name: &str, contents: &str) { - self.add_os_lang_entry(os, "en", name, contents); - } - - /// Add entry for that environment to a language-specific subfolder. - fn add_lang_entry(&self, lang: &str, name: &str, contents: &str) { - self.add_os_lang_entry("common", lang, name, contents); - } - - /// Add entry for that environment to an OS- and language specific subfolder. - fn add_os_lang_entry(&self, os: &str, lang: &str, name: &str, contents: &str) { - let dir = self - .cache_dir() - .join(TLDR_PAGES_DIR) - .join(format!("pages.{lang}")) - .join(os); - create_dir_all(&dir).unwrap(); - - fs::write(dir.join(format!("{name}.md")), contents.as_bytes()).unwrap(); - } - - /// Add custom patch entry to the custom_pages_dir - fn add_page_entry(&self, name: &str, contents: &str) { - let dir = &self.custom_pages_dir(); - create_dir_all(dir).unwrap(); - fs::write(dir.join(format!("{name}.page.md")), contents.as_bytes()).unwrap(); - } - - /// Add custom patch entry to the custom_pages_dir - fn add_patch_entry(&self, name: &str, contents: &str) { - let dir = &self.custom_pages_dir(); - create_dir_all(dir).unwrap(); - fs::write(dir.join(format!("{name}.patch.md")), contents.as_bytes()).unwrap(); + } } /// Disable default features. @@ -148,321 +32,92 @@ impl TestEnv { self } - /// Add the specified feature. - fn with_feature>(mut self, feature: S) -> Self { - self.features.push(feature.into()); - self - } - /// Return a new `Command` with env vars set. fn command(&self) -> Command { let mut build = escargot::CargoBuild::new() .bin("tldr") - .arg("--color=never") .current_release() .current_target(); if !self.default_features { - build = build.no_default_features(); + build = build + .arg("--no-default-features") + .target_dir("target/no-default-features"); } - if !self.features.is_empty() { - build = build.features(self.features.join(" ")) - } - let run = build.run().expect("Failed to build tealdeer for testing"); + let run = build.run().unwrap(); let mut cmd = run.command(); - - // Avoid inheriting those from the test process. We can't just use .env_clear() because - // this breaks tests on Windows in GitHub Actions. - let relevant_env_variables = [ - "LANG", - "LANGUAGE", - "TEALDEER_CACHE_DIR", - "EDITOR", - "NO_COLOR", - ]; - for variable_name in relevant_env_variables { - cmd.env_remove(variable_name); - } - cmd.env("TEALDEER_CONFIG_DIR", self.config_dir().to_str().unwrap()); + cmd.env("TEALDEER_CACHE_DIR", self.cache_dir.path().to_str().unwrap()); + cmd.env("TEALDEER_CONFIG_DIR", self.config_dir.path().to_str().unwrap()); cmd } - - fn install_default_cache(self) -> Self { - copy_recursively( - &PathBuf::from_iter([env!("CARGO_MANIFEST_DIR"), "tests", "cache"]), - &self.cache_dir().join(TLDR_PAGES_DIR), - ) - .expect("Failed to copy the cache to the test environment"); - - self - } - - fn install_default_custom_pages(self) -> Self { - copy_recursively( - &PathBuf::from_iter([env!("CARGO_MANIFEST_DIR"), "tests", "custom-pages"]), - self.custom_pages_dir().as_path(), - ) - .expect("Failed to copy the custom pages to the test environment"); - - self.write_custom_pages_config() - } - - fn write_custom_pages_config(self) -> Self { - self.append_to_config(format!( - "directories.custom_pages_dir = '{}'\n", - self.custom_pages_dir().to_str().unwrap() - )); - - self - } -} - -fn copy_recursively(source: &Path, destination: &Path) -> io::Result<()> { - if source.is_dir() { - fs::create_dir_all(destination)?; - for entry in fs::read_dir(source)? { - let entry = entry?; - copy_recursively(&entry.path(), &destination.join(entry.file_name()))?; - } - } else { - fs::copy(source, destination)?; - } - - Ok(()) -} - -#[test] -#[should_panic] -fn test_cannot_build_without_tls_feature() { - let _ = TestEnv::new().no_default_features().command(); -} - -#[test] -fn test_load_the_correct_config() { - let testenv = TestEnv::new() - .install_default_cache() - .create_secondary_config(); - testenv.append_to_secondary_config(include_str!("style-config.toml")); - - let expected_default = include_str!("rendered/inkscape-default.expected"); - let expected_with_config = include_str!("rendered/inkscape-with-config.expected"); - - testenv - .command() - .args(["--color", "always", "inkscape-v2"]) - .assert() - .success() - .stdout(diff(expected_default)); - - testenv - .command() - .args([ - "--color", - "always", - "--config-path", - testenv - .config_dir() - .join("config-secondary.toml") - .to_str() - .unwrap(), - "inkscape-v2", - ]) - .assert() - .success() - .stdout(diff(expected_with_config)); -} - -#[test] -fn test_fail_on_custom_config_path_is_directory() { - let testenv = TestEnv::new(); - let error = if cfg!(windows) { - "Access is denied" - } else { - "Is a directory" - }; - testenv - .command() - .args([ - "--config-path", - testenv.config_dir().to_str().unwrap(), - "sl", - ]) - .assert() - .failure() - .stderr(contains(error)); } #[test] fn test_missing_cache() { TestEnv::new() .command() - .args(["sl"]) + .args(&["sl"]) .assert() .failure() - .stderr(contains("Page cache not found. Please run `tldr --update`")); + .stderr(contains("Cache not found. Please run `tldr --update`.")); } #[test] -fn test_tealdeer_page_works_without_cache() { - TestEnv::new() - .command() - .args(["tealdeer"]) - .assert() - .success() - .stdout(contains("for your installed tealdeer version")); -} - -#[cfg_attr(feature = "ignore-online-tests", ignore = "online test")] -#[test] -fn test_update_cache_default_features() { +fn test_update_cache() { let testenv = TestEnv::new(); testenv .command() - .args(["sl"]) + .args(&["sl"]) .assert() .failure() - .stderr(contains("Page cache not found. Please run `tldr --update`")); + .stderr(contains("Cache not found. Please run `tldr --update`.")); testenv .command() - .args(["--update"]) + .args(&["--update"]) .assert() .success() - .stderr(contains("Successfully updated cache.")); + .stdout(contains("Successfully updated cache.")); - testenv.command().args(["sl"]).assert().success(); + testenv + .command() + .args(&["sl"]) + .assert() + .success(); } -#[cfg_attr(feature = "ignore-online-tests", ignore = "online test")] -#[test] -fn test_update_cache_rustls_webpki() { - let testenv = TestEnv::new() - .no_default_features() - .with_feature("rustls-with-webpki-roots"); - - testenv - .command() - .args(["sl"]) - .assert() - .failure() - .stderr(contains("Page cache not found. Please run `tldr --update`")); - - testenv - .command() - .args(["--update"]) - .assert() - .success() - .stderr(contains("Successfully updated cache.")); - - testenv.command().args(["sl"]).assert().success(); -} - -#[cfg_attr(feature = "ignore-online-tests", ignore = "online test")] -#[test] -fn test_update_cache_native_tls() { - let testenv = TestEnv::new() - .no_default_features() - .with_feature("rustls-with-native-roots"); - - testenv - .command() - .args(["sl"]) - .assert() - .failure() - .stderr(contains("Page cache not found. Please run `tldr --update`")); - - testenv - .command() - .args(["--update"]) - .assert() - .success() - .stderr(contains("Successfully updated cache.")); - - testenv.command().args(["sl"]).assert().success(); -} - -#[cfg_attr(feature = "ignore-online-tests", ignore = "online test")] #[test] fn test_quiet_cache() { let testenv = TestEnv::new(); testenv .command() - .args(["--update", "--quiet"]) + .args(&["--update", "--quiet"]) .assert() .success() .stdout(is_empty()); testenv .command() - .args(["--clear-cache", "--quiet"]) + .args(&["--clear-cache", "--quiet"]) .assert() .success() .stdout(is_empty()); } -#[test] -fn test_clear_only_pages_directory() { - let testenv = TestEnv::new().install_default_cache(); - testenv - .command() - .args(["--clear-cache"]) - .assert() - .success() - .stderr(contains(format!( - "Successfully cleared cache at `{}`.", - testenv.cache_dir().join(TLDR_PAGES_DIR).to_str().unwrap(), - ))); - - assert!(testenv.cache_dir().is_dir()); - assert!(!testenv.cache_dir().join(TLDR_PAGES_DIR).exists()); -} - -#[test] -fn test_always_delete_old_pages_directory() { - let testenv = TestEnv::new().install_default_cache(); - fs::rename( - testenv.cache_dir().join(TLDR_PAGES_DIR), - testenv.cache_dir().join(TLDR_OLD_PAGES_DIR), - ) - .unwrap(); - - testenv - .command() - .arg("--list") - .assert() - .failure() - .stderr(contains("Cleared pages from old cache location.")) - .stderr(contains("Page cache not found.")); - - assert!(testenv.cache_dir().is_dir()); - assert!(!testenv.cache_dir().join(TLDR_PAGES_DIR).exists()); - assert!(!testenv.cache_dir().join(TLDR_OLD_PAGES_DIR).exists()); -} - -#[test] -fn test_warn_invalid_tls_backend() { - let testenv = TestEnv::new() - .no_default_features() - .with_feature("rustls-with-webpki-roots") - .remove_initial_config(); - - testenv.append_to_config("updates.tls_backend = 'invalid-tls-backend'\n"); - - testenv - .command() - .args(["sl"]) - .assert() - .failure() - .stderr(contains("unknown variant `invalid-tls-backend`, expected one of `native-tls`, `rustls-with-webpki-roots`, `rustls-with-native-roots`")); -} - #[test] fn test_quiet_failures() { - let testenv = TestEnv::new().install_default_cache(); + let testenv = TestEnv::new(); testenv .command() - .args(["fakeprogram", "-q"]) + .args(&["--update", "-q"]) + .assert() + .success() + .stdout(is_empty()); + + testenv + .command() + .args(&["fakeprogram", "-q"]) .assert() .failure() .stdout(is_empty()); @@ -470,159 +125,30 @@ fn test_quiet_failures() { #[test] fn test_quiet_old_cache() { - let testenv = TestEnv::new().install_default_cache(); - - filetime::set_file_mtime( - testenv.cache_dir().join(TLDR_PAGES_DIR), - filetime::FileTime::from_unix_time(1, 0), - ) - .unwrap(); - - testenv - .command() - .args(["which"]) - .assert() - .success() - .stderr(contains("The cache hasn't been updated for ")); - - testenv - .command() - .args(["which", "--quiet"]) - .assert() - .success() - .stderr(contains("The cache hasn't been updated for ").not()); -} - -#[test] -fn test_warn_cache_age_never() { - let testenv = TestEnv::new().install_default_cache(); - - filetime::set_file_mtime( - testenv.cache_dir().join(TLDR_PAGES_DIR), - filetime::FileTime::from_unix_time(1, 0), - ) - .unwrap(); - - testenv.append_to_config("[updates]\nwarn_cache_age = \"never\"\n"); - - testenv - .command() - .args(["which"]) - .assert() - .success() - .stderr(contains("The cache hasn't been updated for ").not()); -} - -#[cfg_attr(feature = "ignore-online-tests", ignore = "online test")] -#[test] -fn test_create_cache_directory_path() { - let testenv = TestEnv::new().remove_initial_config(); - let cache_dir = &testenv.cache_dir(); - let internal_cache_dir = cache_dir.join("internal"); - testenv.append_to_config(format!( - "directories.cache_dir = '{}'\n", - internal_cache_dir.to_str().unwrap() - )); - - let mut command = testenv.command(); - - assert!(!internal_cache_dir.exists()); - - command - .arg("--update") - .assert() - .success() - .stderr(contains(format!( - "Successfully created cache directory `{}`.", - internal_cache_dir.join(TLDR_PAGES_DIR).to_str().unwrap() - ))) - .stderr(contains("Successfully updated cache.")); - - assert!(internal_cache_dir.is_dir()); -} - -#[test] -fn test_cache_location_not_a_directory() { let testenv = TestEnv::new(); - let cache_dir = &testenv.cache_dir(); - File::create(cache_dir.join(TLDR_PAGES_DIR)).unwrap(); testenv .command() - .arg("--list") + .args(&["--update", "-q"]) .assert() - .failure() - .stderr(contains(format!( - "Cache directory `{}` exists, but is not a directory.", - cache_dir.join(TLDR_PAGES_DIR).display(), - ))); -} + .success() + .stdout(is_empty()); -#[cfg(unix)] -#[test] -fn test_cache_location_permission_denied() { - use std::os::unix::fs::PermissionsExt; - - let testenv = TestEnv::new().install_default_cache(); + let _ = utime::set_file_times(testenv.cache_dir.path().join("tldr-master"), 1, 1).unwrap(); testenv .command() - .arg("--list") + .args(&["tldr"]) .assert() .success() - .stderr(contains("Permission denied").not()); - - // Make cache directory unreadable - let cache_dir = testenv.cache_dir(); - let mut permissions = cache_dir.metadata().unwrap().permissions(); - permissions.set_mode(0o0); - fs::set_permissions(cache_dir, permissions).unwrap(); + .stdout(contains("Cache wasn't updated for more than ")); testenv .command() - .arg("--list") - .assert() - .failure() - .stderr(contains("Permission denied")); -} - -#[test] -fn test_cache_location_source() { - let testenv = TestEnv::new().remove_initial_config(); - let default_cache_dir = &testenv.cache_dir(); - let tmp_cache_dir = TempfileBuilder::new() - .prefix(".tldr.test.cache_dir") - .tempdir() - .unwrap(); - - // Source: Default (OS convention) - let mut command = testenv.command(); - command - .arg("--show-paths") + .args(&["tldr", "--quiet"]) .assert() .success() - .stdout(is_match("\nCache dir: [^(]* \\(OS convention\\)\n").unwrap()); - - // Source: Config variable - let mut command = testenv.command(); - testenv.append_to_config(format!( - "directories.cache_dir = '{}'\n", - tmp_cache_dir.path().to_str().unwrap(), - )); - command - .arg("--show-paths") - .assert() - .success() - .stdout(is_match("\nCache dir: [^(]* \\(config file\\)\n").unwrap()); - - // Source: Env var - let mut command = testenv.command(); - command.env("TEALDEER_CACHE_DIR", default_cache_dir.to_str().unwrap()); - command - .arg("--show-paths") - .assert() - .success() - .stdout(is_match("\nCache dir: [^(]* \\(env variable\\)\n").unwrap()); + .stdout(contains("Cache wasn't updated for more than ").not()); } #[test] @@ -631,868 +157,142 @@ fn test_setup_seed_config() { testenv .command() - .args(["--seed-config"]) - .assert() - .failure() - .stderr(contains("A configuration file already exists")); - - assert!(testenv.config_dir().join("config.toml").is_file()); - - let testenv = testenv.remove_initial_config(); - testenv - .command() - .args(["--seed-config"]) + .args(&["--seed-config"]) .assert() .success() - .stderr(contains("Successfully created seed config file here")); - - assert!(testenv.config_dir().join("config.toml").is_file()); - - // Create parent directories as needed for the default config path. - fs::remove_dir_all(testenv.config_dir()).unwrap(); - testenv - .command() - .args(["--seed-config"]) - .assert() - .success() - .stderr(contains("Successfully created seed config file here")); - - assert!(testenv.config_dir().join("config.toml").is_file()); - - // Write the default config to --config-path if specified by the user - // at the same time. - let custom_config_path = testenv.config_dir().join("config_custom.toml"); - testenv - .command() - .args([ - "--seed-config", - "--config-path", - custom_config_path.to_str().unwrap(), - ]) - .assert() - .success() - .stderr(contains("Successfully created seed config file here")); - - assert!(custom_config_path.is_file()); - - // DON'T create parent directories for a custom config path. - fs::remove_dir_all(testenv.config_dir()).unwrap(); - testenv - .command() - .args([ - "--seed-config", - "--config-path", - custom_config_path.to_str().unwrap(), - ]) - .assert() - .failure() - .stderr(contains("Could not create config file")); - - assert!(!custom_config_path.is_file()); + .stdout(contains("Successfully created seed config file")); } #[test] -fn test_show_paths() { +fn test_show_config_path() { let testenv = TestEnv::new(); - // Show general commands testenv .command() - .args(["--show-paths"]) + .args(&["--config-path"]) .assert() .success() .stdout(contains(format!( - "Config dir: {}", - testenv.config_dir().to_str().unwrap(), - ))) - .stdout(contains(format!( - "Config path: {}", - testenv.config_dir().join("config.toml").to_str().unwrap(), - ))) - .stdout(contains(format!( - "Cache dir: {}", - testenv.cache_dir().to_str().unwrap(), - ))) - .stdout(contains(format!( - "Pages dir: {}", - testenv.cache_dir().join(TLDR_PAGES_DIR).to_str().unwrap(), - ))); - - let testenv = testenv.write_custom_pages_config(); - - // Now ensure that this path is contained in the output - testenv - .command() - .args(["--show-paths"]) - .assert() - .success() - .stdout(contains(format!( - "Custom pages dir: {}", - testenv.custom_pages_dir().to_str().unwrap(), + "Config path is: {}/config.toml", + testenv.config_dir.path().to_str().unwrap(), ))); } -#[test] -fn test_os_specific_page() { +fn _test_correct_rendering(input_file: &str, filename: &str) { let testenv = TestEnv::new(); - testenv.add_os_entry("sunos", "truss", "contents"); + // Create input file + let file_path = testenv.input_dir.path().join(filename); + println!("Testfile path: {:?}", &file_path); + let mut file = File::create(&file_path).unwrap(); + file.write_all(input_file.as_bytes()).unwrap(); + + // Load expected output + let expected = include_str!("inkscape-default.expected"); testenv .command() - .args(["--platform", "sunos", "truss"]) - .assert() - .success(); -} - -#[test] -fn test_config_platforms() { - let testenv = TestEnv::new(); - testenv.add_os_entry("sunos", "sunos-command", ""); - - let set_config_platforms = |platforms| { - testenv.delete_config(); - testenv.init_config(); - testenv.append_to_config(format!("search.platforms = {platforms}")); - }; - - // By default all platforms are searched - testenv.command().arg("sunos-command").assert().success(); - - set_config_platforms("[]"); - testenv.command().arg("sunos-command").assert().failure(); - - set_config_platforms("['linux']"); - testenv.command().arg("sunos-command").assert().failure(); - - set_config_platforms("['sunos']"); - testenv.command().arg("sunos-command").assert().success(); - - set_config_platforms("['linux', 'all']"); - testenv.command().arg("sunos-command").assert().success(); - - set_config_platforms("['current', 'all']"); - testenv.command().arg("sunos-command").assert().success(); -} - -#[test] -fn test_markdown_rendering() { - let testenv = TestEnv::new().install_default_cache(); - - let expected = include_str!("cache/pages.en/common/which.md"); - testenv - .command() - .args(["--raw", "which"]) + .args(&["-f", &file_path.to_str().unwrap()]) .assert() .success() - .stdout(diff(expected)); -} - -fn _test_correct_rendering(page: &str, expected: &'static str, additional_args: &[&str]) { - let testenv = TestEnv::new().install_default_cache(); - - testenv - .command() - .args(additional_args) - .arg(page) - .assert() - .success() - .stdout(diff(expected)); + .stdout(similar(expected)); } /// An end-to-end integration test for direct file rendering (v1 syntax). #[test] fn test_correct_rendering_v1() { - _test_correct_rendering( - "inkscape-v1", - include_str!("rendered/inkscape-default.expected"), - &["--color", "always"], - ); + _test_correct_rendering(include_str!("inkscape-v1.md"), "inkscape-v1.md"); } /// An end-to-end integration test for direct file rendering (v2 syntax). #[test] fn test_correct_rendering_v2() { - _test_correct_rendering( - "inkscape-v2", - include_str!("rendered/inkscape-default.expected"), - &["--color", "always"], - ); + _test_correct_rendering(include_str!("inkscape-v2.md"), "inkscape-v2.md"); } -#[test] -/// An end-to-end integration test for direct file rendering with the `--color auto` option. This -/// will not use styling since output is not stdout. -fn test_rendering_color_auto() { - _test_correct_rendering( - "inkscape-v2", - include_str!("rendered/inkscape-default-no-color.expected"), - &["--color", "auto"], - ); -} - -#[test] -/// An end-to-end integration test for direct file rendering with the `--color never` option. -fn test_rendering_color_never() { - _test_correct_rendering( - "inkscape-v2", - include_str!("rendered/inkscape-default-no-color.expected"), - &["--color", "never"], - ); -} - -/// An end-to-end integration test for the indent config option -#[test] -fn test_rendering_with_indentation() { - let testenv = TestEnv::new().install_default_cache(); - let expected_custom_indentation = include_str!("rendered/inkscape-compact-no-color.expected"); - - // Configure to set base and command indents - testenv.append_to_config("display.indent.base = 3\n"); - testenv.append_to_config("display.indent.command = 1\n"); - - testenv - .command() - .args(["--color", "never", "inkscape-v2"]) - .assert() - .success() - .stdout(diff(expected_custom_indentation)); -} - -#[test] -fn test_rendering_i18n() { - _test_correct_rendering( - "apt", - include_str!("rendered/apt.ja.expected"), - &["--color", "always", "--language", "ja"], - ); -} - -/// An end-to-end integration test for rendering with custom syntax config. +/// An end-to-end integration test for rendering with constom syntax config. #[test] fn test_correct_rendering_with_config() { - let testenv = TestEnv::new().install_default_cache(); - - testenv.append_to_config(include_str!("style-config.toml")); - - let expected = include_str!("rendered/inkscape-with-config.expected"); - - testenv - .command() - .args(["--color", "always", "inkscape-v2"]) - .assert() - .success() - .stdout(diff(expected)); -} - -/// An end-to-end integration test for rendering with show_title config option enabled. -#[test] -fn test_show_title_config() { - // Test that default behavior without show_title shows no title - let testenv = TestEnv::new().install_default_cache(); - let expected_no_title = include_str!("rendered/inkscape-default.expected"); - - testenv - .command() - .args(["--color", "always", "inkscape-v2"]) - .assert() - .success() - .stdout(diff(expected_no_title)); - - // Configure to enable show_title - testenv.append_to_config("display.show_title = true\n"); - - let expected_no_color = include_str!("rendered/inkscape-with-title-no-color.expected"); - - testenv - .command() - .args(["inkscape-v2"]) - .assert() - .success() - .stdout(diff(expected_no_color)); - - let expected = include_str!("rendered/inkscape-with-title.expected"); - - testenv - .command() - .args(["--color", "always", "inkscape-v2"]) - .assert() - .success() - .stdout(diff(expected)); -} - -#[test] -fn test_spaces_find_command() { - let testenv = TestEnv::new().install_default_cache(); - - testenv - .command() - .args(["git", "checkout"]) - .assert() - .success(); -} - -#[test] -fn test_pager_flag_enable() { - let testenv = TestEnv::new().install_default_cache(); - - testenv - .command() - .args(["--pager", "which"]) - .assert() - .success(); -} - -#[test] -fn test_multiple_platform_command_search() { let testenv = TestEnv::new(); - testenv.add_os_entry("linux", "linux-only", "this command only exists for linux"); - testenv.add_os_entry( - "linux", - "windows-and-linux", - "# windows-and-linux \n\n > linux version", - ); - testenv.add_os_entry( - "windows", - "windows-and-linux", - "# windows-and-linux \n\n > windows version", - ); + + // Setup config file + // TODO should be config::CONFIG_FILE_NAME + let config_file_path = testenv.config_dir.path().join("config.toml"); + println!("Config path: {:?}", &config_file_path); + + let mut config_file = File::create(&config_file_path).unwrap(); + config_file + .write(include_str!("config.toml").as_bytes()) + .unwrap(); + + // Create input file + let file_path = testenv.input_dir.path().join("inkscape-v2.md"); + println!("Testfile path: {:?}", &file_path); + + let mut file = File::create(&file_path).unwrap(); + file.write_all(include_str!("inkscape-v2.md").as_bytes()).unwrap(); + + // Load expected output + let expected = include_str!("inkscape-with-config.expected"); testenv .command() - .args(["--platform", "windows", "--platform", "linux", "linux-only"]) - .assert() - .success(); - - // test order of platforms supplied if preserved - testenv - .command() - .args([ - "--platform", - "windows", - "--platform", - "linux", - "windows-and-linux", - ]) + .args(&["-f", &file_path.to_str().unwrap()]) .assert() .success() - .stdout(contains("windows version")); - - testenv - .command() - .args([ - "--platform", - "linux", - "--platform", - "windows", - "windows-and-linux", - ]) - .assert() - .success() - .stdout(contains("linux version")); + .stdout(similar(expected)); } +/// Updating from a network URL should not be possible when networking support +/// is not enabled. #[test] -fn test_multiple_platform_command_search_not_found() { - let testenv = TestEnv::new(); - testenv.add_os_entry( - "windows", - "windows-only", - "this command only exists for Windows", - ); - - testenv - .command() - .args(["--platform", "macos", "--platform", "linux", "windows-only"]) - .assert() - .stderr(contains("Page `windows-only` not found in cache.")); -} - -#[test] -fn test_macos_is_alias_for_osx() { - let testenv = TestEnv::new(); - testenv.add_os_entry("osx", "maconly", "this command only exists on mac"); - - testenv - .command() - .args(["--platform", "macos", "maconly"]) - .assert() - .success(); - testenv - .command() - .args(["--platform", "osx", "maconly"]) - .assert() - .success(); - - testenv - .command() - .args(["--platform", "macos", "--list"]) - .assert() - .stdout("maconly\n"); - testenv - .command() - .args(["--platform", "osx", "--list"]) - .assert() - .stdout("maconly\n"); - - testenv.append_to_config("search.platforms = ['osx']\n"); - testenv.command().arg("--list").assert().stdout("maconly\n"); - - testenv.delete_config(); - testenv.init_config(); - testenv.append_to_config("search.platforms = ['macos']\n"); - testenv.command().arg("--list").assert().stdout("maconly\n"); -} - -#[test] -fn test_common_platform_is_used_as_fallback() { - let testenv = TestEnv::new(); - testenv.add_entry("in-common", "this command comes from common"); - - // No platform specified - testenv.command().args(["in-common"]).assert().success(); - - // Platform specified - testenv - .command() - .args(["--platform", "linux", "in-common"]) - .assert() - .success(); -} - -#[test] -fn test_search_language_precedence() { - let testenv = TestEnv::new(); - for lang in ["en", "de", "it", "fr", "pl", "nl"] { - testenv.add_lang_entry(lang, lang, ""); - } - - #[expect(clippy::type_complexity)] - let run = |cases: &[(Vec<(&str, &str)>, Vec<&str>, &str)]| { - for (extra_env, extra_args, expected) in cases { - let mut cmd = testenv.command(); - for (key, value) in extra_env { - cmd.env(key, value); - } - cmd.args(extra_args); - cmd.arg("--list"); - cmd.assert().success().stdout(eq(*expected)); - } - }; - - let env_cases = &[ - (vec![], vec![], "en\n"), - (vec![("LANGUAGE", "de:it")], vec![], "en\n"), - ( - vec![("LANG", "fr"), ("LANGUAGE", "de:it")], - vec![], - "de\nen\nfr\nit\n", - ), - ( - vec![("LANG", "fr"), ("LANGUAGE", "de:it")], - vec!["--language", "pl"], - "pl\n", - ), - ]; - run(env_cases); - - // Environment is only used when config setting is not set - testenv.append_to_config("search.languages = ['nl']\n"); - let config_cases = &[ - (vec![], vec![], "nl\n"), - (vec![("LANGUAGE", "de:it")], vec![], "nl\n"), - (vec![("LANG", "fr"), ("LANGUAGE", "de:it")], vec![], "nl\n"), - ( - vec![("LANG", "fr"), ("LANGUAGE", "de:it")], - vec!["--language", "pl"], - "pl\n", - ), - ]; - run(config_cases); - - // The above update setting does not change anything - testenv.append_to_config("updates.download_languages = ['cz']"); - run(config_cases); - testenv.delete_config(); - testenv.init_config(); - testenv.append_to_config("updates.download_languages = ['cz']"); - run(env_cases); -} - -#[cfg_attr(feature = "ignore-online-tests", ignore = "online test")] -#[test] -fn test_update_language_arg() { +fn test_update_from_no_networking() { let testenv = TestEnv::new(); testenv + .no_default_features() // Disable networking .command() - .env("LANG", "it") - .arg("--update") - .assert() - .success() - .stderr(contains("it")) - .stderr(contains("en")); - - testenv - .command() - .env("LANG", "en") - .args(["--language", "it"]) - .arg("--update") - .assert() - .success() - .stderr(contains("it")) - .stderr(contains("en").not()); -} - -#[test] -fn test_list_flag_rendering() { - let testenv = TestEnv::new().write_custom_pages_config(); - - testenv - .command() - .args(["--list"]) + .args(&["--update-from", "https://github.com/tldr-pages/tldr/archive/master.tar.gz"]) .assert() .failure() - .stderr(contains("Page cache not found. Please run `tldr --update`")); - - testenv.add_entry("foo", ""); - - testenv - .command() - .args(["--list"]) - .assert() - .success() - .stdout("foo\n"); - - testenv.add_entry("bar", ""); - testenv.add_entry("baz", ""); - testenv.add_entry("qux", ""); - testenv.add_page_entry("faz", ""); - testenv.add_page_entry("bar", ""); - testenv.add_page_entry("fiz", ""); - testenv.add_patch_entry("buz", ""); - - testenv - .command() - .args(["--list"]) - .assert() - .success() - .stdout("bar\nbaz\nfaz\nfiz\nfoo\nqux\n"); + .stderr(contains("compiled without networking support")) + .stderr(contains("cannot update the cache from a network URL")); } +/// Updating from an invalid URL should result in an error message. #[test] -fn test_multi_platform_list_flag_rendering() { - let testenv = TestEnv::new().write_custom_pages_config(); - - testenv.add_entry("common", ""); - - testenv - .command() - .args(["--list"]) - .assert() - .success() - .stdout("common\n"); - - testenv - .command() - .args(["--platform", "linux", "--list"]) - .assert() - .success() - .stdout("common\n"); - - testenv - .command() - .args(["--platform", "windows", "--list"]) - .assert() - .success() - .stdout("common\n"); - - testenv.add_os_entry("linux", "rm", ""); - testenv.add_os_entry("linux", "ls", ""); - testenv.add_os_entry("windows", "del", ""); - testenv.add_os_entry("windows", "dir", ""); - testenv.add_os_entry("linux", "winux", ""); - testenv.add_os_entry("windows", "winux", ""); - - // test `--list` for `--platform linux` by itself - testenv - .command() - .args(["--platform", "linux", "--list"]) - .assert() - .success() - .stdout("common\nls\nrm\nwinux\n"); - - // test `--list` for `--platform windows` by itself - testenv - .command() - .args(["--platform", "windows", "--list"]) - .assert() - .success() - .stdout("common\ndel\ndir\nwinux\n"); - - // test `--list` for `--platform linux --platform windows` - testenv - .command() - .args(["--platform", "linux", "--platform", "windows", "--list"]) - .assert() - .success() - .stdout("common\ndel\ndir\nls\nrm\nwinux\n"); - - // test `--list` for `--platform windows --platform linux` - testenv - .command() - .args(["--platform", "linux", "--platform", "windows", "--list"]) - .assert() - .success() - .stdout("common\ndel\ndir\nls\nrm\nwinux\n"); -} - -#[cfg_attr(feature = "ignore-online-tests", ignore = "online test")] -#[test] -fn test_autoupdate_cache() { +fn test_update_from_invalid_url() { let testenv = TestEnv::new(); - - // The first time, if automatic updates are disabled, the cache should not be found testenv .command() - .args(["--list"]) + .args(&["--update-from", "httpsss:github.com/tldr-pages/tldr/archive/master.tar.gz"]) .assert() .failure() - .stderr(contains("Page cache not found. Please run `tldr --update`")); - - let cache_file_path = testenv.cache_dir().join(TLDR_PAGES_DIR); - - testenv - .append_to_config("updates.auto_update = true\nupdates.auto_update_interval_hours = 24\n"); - - // Helper function that runs `tldr --list` and asserts that the cache is automatically updated - // or not, depending on the value of `expected`. - let check_cache_updated = |expected| { - let assert = testenv.command().args(["--list"]).assert().success(); - let pred = contains("Successfully updated cache"); - if expected { - assert.stderr(pred) - } else { - assert.stderr(pred.not()) - }; - }; - - // The cache is updated the first time we run `tldr --list` - check_cache_updated(true); - - // The cache is not updated with a subsequent call - check_cache_updated(false); - - // We update the modification and access times such that they are about 23 hours from now. - // auto-update interval is 24 hours, the cache should not be updated - let new_mtime = SystemTime::now() - Duration::from_secs(82_800); - filetime::set_file_mtime(&cache_file_path, new_mtime.into()).unwrap(); - check_cache_updated(false); - - // We update the modification and access times such that they are about 25 hours from now. - // auto-update interval is 24 hours, the cache should be updated - let new_mtime = SystemTime::now() - Duration::from_secs(90_000); - filetime::set_file_mtime(&cache_file_path, new_mtime.into()).unwrap(); - check_cache_updated(true); - - // The cache is not updated with a subsequent call - check_cache_updated(false); + .stderr(contains("Could not update cache: HTTP error")) + .stderr(contains("URL scheme is not allowed")); } -/// End-end test to ensure .page.md files overwrite pages in cache_dir +/// Updating from the wrong (non-gzip-archive) URL should result in an error. #[test] -fn test_custom_page_overwrites() { - let testenv = TestEnv::new().write_custom_pages_config(); - - // Add file that should be ignored to the cache dir - testenv.add_entry("inkscape-v2", ""); - // Add .page.md file to custom_pages_dir - testenv.add_page_entry( - "inkscape-v2", - include_str!("cache/pages.en/common/inkscape-v2.md"), - ); - - // Load expected output - let expected = include_str!("rendered/inkscape-default-no-color.expected"); - - testenv - .command() - .args(["inkscape-v2", "--color", "never"]) - .assert() - .success() - .stdout(diff(expected)); -} - -/// End-End test to ensure that .patch.md files are appended to pages in the cache_dir -#[test] -fn test_custom_patch_appends_to_common() { - let testenv = TestEnv::new() - .install_default_cache() - .install_default_custom_pages(); - - // Load expected output - let expected = include_str!("rendered/inkscape-patched-no-color.expected"); - - testenv - .command() - .args(["inkscape-v2", "--color", "never"]) - .assert() - .success() - .stdout(diff(expected)); -} - -/// End-End test to ensure that .patch.md files are not appended to .page.md files in the custom_pages_dir -/// Maybe this interaction should change but I put this test here for the coverage -#[test] -fn test_custom_patch_does_not_append_to_custom() { - let testenv = TestEnv::new() - .install_default_cache() - .install_default_custom_pages(); - - // In addition to the page in the cache, add the same page as a custom page. - testenv.add_page_entry( - "inkscape-v2", - include_str!("cache/pages.en/common/inkscape-v2.md"), - ); - - // Load expected output - let expected = include_str!("rendered/inkscape-default-no-color.expected"); - - testenv - .command() - .args(["inkscape-v2", "--color", "never"]) - .assert() - .success() - .stdout(diff(expected)); -} - -#[test] -#[cfg(target_os = "windows")] -fn test_pager_warning() { - let testenv = TestEnv::new().install_default_cache(); - - // Regular call should not show a "pager flag not available on windows" warning - testenv - .command() - .args(["which"]) - .assert() - .success() - .stderr(contains("pager flag not available on Windows").not()); - - // But it should be shown if the pager flag is true - testenv - .command() - .args(["--pager", "which"]) - .assert() - .success() - .stderr(contains("pager flag not available on Windows")); -} - -/// Ensure that page lookup is case insensitive, so a page lookup for `eyed3` -/// and `eyeD3` should return the same page. -#[test] -fn test_lowercased_page_lookup() { +fn test_update_from_wrong_url() { let testenv = TestEnv::new(); - - // Lookup `eyed3`, initially fails - testenv.command().args(["eyed3"]).assert().failure(); - - // Add entry - testenv.add_entry("eyed3", "contents"); - - // Lookup `eyed3` again - testenv.command().args(["eyed3"]).assert().success(); - - // Lookup `eyeD3`, should succeed as well - testenv.command().args(["eyeD3"]).assert().success(); -} - -/// Regression test for #219: It should be possible to combine `--raw` and `-f`. -#[test] -fn test_raw_render_file() { - let testenv = TestEnv::new().install_default_cache(); - - let path = testenv - .cache_dir() - .join(TLDR_PAGES_DIR) - .join("pages.en/common/inkscape-v1.md"); - let mut args = vec!["--color", "never", "-f", &path.to_str().unwrap()]; - - // Default render testenv .command() - .args(&args) + .args(&["--update-from", "https://tldr.sh/"]) .assert() - .success() - .stdout(diff(include_str!( - "rendered/inkscape-default-no-color.expected" - ))); - - // Raw render - args.push("--raw"); - testenv - .command() - .args(&args) - .assert() - .success() - .stdout(diff(include_str!("cache/pages.en/common/inkscape-v1.md"))); -} - -fn touch_custom_page(testenv: &TestEnv) { - let args = vec!["--edit-page", "foo"]; - - testenv - .command() - .args(&args) - .env("EDITOR", "touch") - .assert() - .success(); - assert!(testenv.custom_pages_dir().join("foo.page.md").exists()); -} - -fn touch_custom_patch(testenv: &TestEnv) { - let args = vec!["--edit-patch", "foo"]; - - testenv - .command() - .args(&args) - .env("EDITOR", "touch") - .assert() - .success(); - assert!(testenv.custom_pages_dir().join("foo.patch.md").exists()); + .failure() + .stderr(contains("Could not update cache: Could not unpack compressed data")); } +/// When a path is specified that does not exist, an error message should be shown. #[test] -fn test_edit_page() { - let testenv = TestEnv::new().write_custom_pages_config(); - touch_custom_page(&testenv); -} - -#[test] -fn test_edit_patch() { - let testenv = TestEnv::new().write_custom_pages_config(); - touch_custom_patch(&testenv); -} - -#[test] -fn test_recreate_dir() { - let testenv = TestEnv::new().write_custom_pages_config(); - touch_custom_patch(&testenv); - touch_custom_page(&testenv); -} - -#[test] -fn test_custom_pages_dir_is_not_dir() { - let testenv = TestEnv::new().write_custom_pages_config(); - let _ = std::fs::remove_dir_all(testenv.custom_pages_dir()); - let _ = File::create(testenv.custom_pages_dir()).unwrap(); - assert!(testenv.custom_pages_dir().is_file()); - - let args = vec!["--edit-patch", "foo"]; - +fn test_update_from_missing_path() { + let testenv = TestEnv::new(); testenv + .no_default_features() .command() - .args(&args) - .env("EDITOR", "touch") + .args(&["--update-from", "ajsdfasjdkfljasdf"]) // Invalid path .assert() - .failure(); + .failure() + .stderr(contains("Could not update cache: Could not open file:")) + .stderr(contains("No such file or directory")); } diff --git a/tests/rendered/apt.ja.expected b/tests/rendered/apt.ja.expected deleted file mode 100644 index efdd35d..0000000 --- a/tests/rendered/apt.ja.expected +++ /dev/null @@ -1,37 +0,0 @@ - - Debian系ディストリビューションで使われるパッケージ管理システムです。 - Ubuntuのバージョンが16.04か、それ以降で対話モードを使う場合`apt-get`の代わりとして使用します。 - 詳しくはこちら: - - 利用可能なパーケージとバージョンのリストの更新(他の`apt`コマンドの前での実行を推奨): - - sudo apt update - - 指定されたパッケージの検索: - - apt search パッケージ - - パッケージの情報を出力: - - apt show パッケージ - - パッケージのインストール、または利用可能な最新バージョンに更新: - - sudo apt install パッケージ - - パッケージの削除(`sudo apt remove --purge`の場合設定ファイルも削除): - - sudo apt remove パッケージ - - インストールされている全てのパッケージを最新のバージョンにアップグレード: - - sudo apt upgrade - - インストールできるすべてのパッケージを表示: - - apt list - - インストールされた全てのパッケージを表示(依存関係も表示): - - apt list --installed - diff --git a/tests/rendered/inkscape-compact-no-color.expected b/tests/rendered/inkscape-compact-no-color.expected deleted file mode 100644 index 473bbe9..0000000 --- a/tests/rendered/inkscape-compact-no-color.expected +++ /dev/null @@ -1,32 +0,0 @@ - - An SVG (Scalable Vector Graphics) editing program. - Use -z to not open the GUI and only process files in the console. - - Open an SVG file in the Inkscape GUI: - - inkscape filename.svg - - Export an SVG file into a bitmap with the default format (PNG) and the default resolution (90 DPI): - - inkscape filename.svg -e filename.png - - Export an SVG file into a bitmap of 600x400 pixels (aspect ratio distortion may occur): - - inkscape filename.svg -e filename.png -w 600 -h 400 - - Export a single object, given its ID, into a bitmap: - - inkscape filename.svg -i id -e object.png - - Export an SVG document to PDF, converting all texts to paths: - - inkscape filename.svg | inkscape | inkscape --export-pdf=inkscape.pdf | inkscape | inkscape --export-text-to-path - - Duplicate the object with id="path123", rotate the duplicate 90 degrees, save the file, and quit Inkscape: - - inkscape filename.svg --select=path123 --verb=EditDuplicate --verb=ObjectRotate90 --verb=FileSave --verb=FileQuit - - Some invalid command just to test the correct highlighting of the command name: - - inkscape --use-inkscape=v3.0 file - diff --git a/tests/rendered/inkscape-default-no-color.expected b/tests/rendered/inkscape-default-no-color.expected deleted file mode 100644 index f34c1ce..0000000 --- a/tests/rendered/inkscape-default-no-color.expected +++ /dev/null @@ -1,32 +0,0 @@ - - An SVG (Scalable Vector Graphics) editing program. - Use -z to not open the GUI and only process files in the console. - - Open an SVG file in the Inkscape GUI: - - inkscape filename.svg - - Export an SVG file into a bitmap with the default format (PNG) and the default resolution (90 DPI): - - inkscape filename.svg -e filename.png - - Export an SVG file into a bitmap of 600x400 pixels (aspect ratio distortion may occur): - - inkscape filename.svg -e filename.png -w 600 -h 400 - - Export a single object, given its ID, into a bitmap: - - inkscape filename.svg -i id -e object.png - - Export an SVG document to PDF, converting all texts to paths: - - inkscape filename.svg | inkscape | inkscape --export-pdf=inkscape.pdf | inkscape | inkscape --export-text-to-path - - Duplicate the object with id="path123", rotate the duplicate 90 degrees, save the file, and quit Inkscape: - - inkscape filename.svg --select=path123 --verb=EditDuplicate --verb=ObjectRotate90 --verb=FileSave --verb=FileQuit - - Some invalid command just to test the correct highlighting of the command name: - - inkscape --use-inkscape=v3.0 file - diff --git a/tests/rendered/inkscape-default.expected b/tests/rendered/inkscape-default.expected deleted file mode 100644 index 3b37f0e..0000000 --- a/tests/rendered/inkscape-default.expected +++ /dev/null @@ -1,32 +0,0 @@ - - An SVG (Scalable Vector Graphics) editing program. - Use -z to not open the GUI and only process files in the console. - - Open an SVG file in the Inkscape GUI: - - inkscape filename.svg - - Export an SVG file into a bitmap with the default format (PNG) and the default resolution (90 DPI): - - inkscape filename.svg -e filename.png - - Export an SVG file into a bitmap of 600x400 pixels (aspect ratio distortion may occur): - - inkscape filename.svg -e filename.png -w 600 -h 400 - - Export a single object, given its ID, into a bitmap: - - inkscape filename.svg -i id -e object.png - - Export an SVG document to PDF, converting all texts to paths: - - inkscape filename.svg | inkscape | inkscape --export-pdf=inkscape.pdf | inkscape | inkscape --export-text-to-path - - Duplicate the object with id="path123", rotate the duplicate 90 degrees, save the file, and quit Inkscape: - - inkscape filename.svg --select=path123 --verb=EditDuplicate --verb=ObjectRotate90 --verb=FileSave --verb=FileQuit - - Some invalid command just to test the correct highlighting of the command name: - - inkscape --use-inkscape=v3.0 file - diff --git a/tests/rendered/inkscape-patched-no-color.expected b/tests/rendered/inkscape-patched-no-color.expected deleted file mode 100644 index de1db15..0000000 --- a/tests/rendered/inkscape-patched-no-color.expected +++ /dev/null @@ -1,36 +0,0 @@ - - An SVG (Scalable Vector Graphics) editing program. - Use -z to not open the GUI and only process files in the console. - - Open an SVG file in the Inkscape GUI: - - inkscape filename.svg - - Export an SVG file into a bitmap with the default format (PNG) and the default resolution (90 DPI): - - inkscape filename.svg -e filename.png - - Export an SVG file into a bitmap of 600x400 pixels (aspect ratio distortion may occur): - - inkscape filename.svg -e filename.png -w 600 -h 400 - - Export a single object, given its ID, into a bitmap: - - inkscape filename.svg -i id -e object.png - - Export an SVG document to PDF, converting all texts to paths: - - inkscape filename.svg | inkscape | inkscape --export-pdf=inkscape.pdf | inkscape | inkscape --export-text-to-path - - Duplicate the object with id="path123", rotate the duplicate 90 degrees, save the file, and quit Inkscape: - - inkscape filename.svg --select=path123 --verb=EditDuplicate --verb=ObjectRotate90 --verb=FileSave --verb=FileQuit - - Some invalid command just to test the correct highlighting of the command name: - - inkscape --use-inkscape=v3.0 file - - Custom inkscape entry - - My Inkscape example - diff --git a/tests/rendered/inkscape-with-config.expected b/tests/rendered/inkscape-with-config.expected deleted file mode 100644 index 33540a3..0000000 --- a/tests/rendered/inkscape-with-config.expected +++ /dev/null @@ -1,32 +0,0 @@ - - An SVG (Scalable Vector Graphics) editing program. - Use -z to not open the GUI and only process files in the console. - - Open an SVG file in the Inkscape GUI: - - inkscape filename.svg - - Export an SVG file into a bitmap with the default format (PNG) and the default resolution (90 DPI): - - inkscape filename.svg -e filename.png - - Export an SVG file into a bitmap of 600x400 pixels (aspect ratio distortion may occur): - - inkscape filename.svg -e filename.png -w 600 -h 400 - - Export a single object, given its ID, into a bitmap: - - inkscape filename.svg -i id -e object.png - - Export an SVG document to PDF, converting all texts to paths: - - inkscape filename.svg | inkscape | inkscape --export-pdf=inkscape.pdf | inkscape | inkscape --export-text-to-path - - Duplicate the object with id="path123", rotate the duplicate 90 degrees, save the file, and quit Inkscape: - - inkscape filename.svg --select=path123 --verb=EditDuplicate --verb=ObjectRotate90 --verb=FileSave --verb=FileQuit - - Some invalid command just to test the correct highlighting of the command name: - - inkscape --use-inkscape=v3.0 file - diff --git a/tests/rendered/inkscape-with-title-no-color.expected b/tests/rendered/inkscape-with-title-no-color.expected deleted file mode 100644 index b6a4572..0000000 --- a/tests/rendered/inkscape-with-title-no-color.expected +++ /dev/null @@ -1,34 +0,0 @@ - - inkscape - - An SVG (Scalable Vector Graphics) editing program. - Use -z to not open the GUI and only process files in the console. - - Open an SVG file in the Inkscape GUI: - - inkscape filename.svg - - Export an SVG file into a bitmap with the default format (PNG) and the default resolution (90 DPI): - - inkscape filename.svg -e filename.png - - Export an SVG file into a bitmap of 600x400 pixels (aspect ratio distortion may occur): - - inkscape filename.svg -e filename.png -w 600 -h 400 - - Export a single object, given its ID, into a bitmap: - - inkscape filename.svg -i id -e object.png - - Export an SVG document to PDF, converting all texts to paths: - - inkscape filename.svg | inkscape | inkscape --export-pdf=inkscape.pdf | inkscape | inkscape --export-text-to-path - - Duplicate the object with id="path123", rotate the duplicate 90 degrees, save the file, and quit Inkscape: - - inkscape filename.svg --select=path123 --verb=EditDuplicate --verb=ObjectRotate90 --verb=FileSave --verb=FileQuit - - Some invalid command just to test the correct highlighting of the command name: - - inkscape --use-inkscape=v3.0 file - diff --git a/tests/rendered/inkscape-with-title.expected b/tests/rendered/inkscape-with-title.expected deleted file mode 100644 index ff2de4a..0000000 --- a/tests/rendered/inkscape-with-title.expected +++ /dev/null @@ -1,34 +0,0 @@ - - inkscape - - An SVG (Scalable Vector Graphics) editing program. - Use -z to not open the GUI and only process files in the console. - - Open an SVG file in the Inkscape GUI: - - inkscape filename.svg - - Export an SVG file into a bitmap with the default format (PNG) and the default resolution (90 DPI): - - inkscape filename.svg -e filename.png - - Export an SVG file into a bitmap of 600x400 pixels (aspect ratio distortion may occur): - - inkscape filename.svg -e filename.png -w 600 -h 400 - - Export a single object, given its ID, into a bitmap: - - inkscape filename.svg -i id -e object.png - - Export an SVG document to PDF, converting all texts to paths: - - inkscape filename.svg | inkscape | inkscape --export-pdf=inkscape.pdf | inkscape | inkscape --export-text-to-path - - Duplicate the object with id="path123", rotate the duplicate 90 degrees, save the file, and quit Inkscape: - - inkscape filename.svg --select=path123 --verb=EditDuplicate --verb=ObjectRotate90 --verb=FileSave --verb=FileQuit - - Some invalid command just to test the correct highlighting of the command name: - - inkscape --use-inkscape=v3.0 file -