├── .cargo └── config.toml ├── .clippy.toml ├── .github ├── renovate.json5 ├── settings.yml └── workflows │ ├── audit.yml │ ├── ci.yml │ ├── committed.yml │ ├── pre-commit.yml │ ├── rust-next.yml │ └── spelling.yml ├── .gitignore ├── .pre-commit-config.yaml ├── CONTRIBUTING.md ├── Cargo.lock ├── Cargo.toml ├── README.md ├── committed.toml ├── crates ├── anstream │ ├── CHANGELOG.md │ ├── Cargo.toml │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ ├── README.md │ ├── benches │ │ ├── stream.rs │ │ ├── strip.rs │ │ └── wincon.rs │ ├── examples │ │ ├── dump-stream.rs │ │ └── query-stream.rs │ ├── src │ │ ├── _macros.rs │ │ ├── adapter │ │ │ ├── mod.rs │ │ │ ├── strip.rs │ │ │ └── wincon.rs │ │ ├── auto.rs │ │ ├── buffer.rs │ │ ├── fmt.rs │ │ ├── lib.rs │ │ ├── stream.rs │ │ ├── strip.rs │ │ └── wincon.rs │ └── tests │ │ ├── demo.vte │ │ ├── macros.rs │ │ ├── rg_help.vte │ │ └── rg_linus.vte ├── anstyle-ansi-term │ ├── CHANGELOG.md │ ├── Cargo.toml │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ ├── README.md │ └── src │ │ └── lib.rs ├── anstyle-crossterm │ ├── CHANGELOG.md │ ├── Cargo.toml │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ ├── README.md │ └── src │ │ └── lib.rs ├── anstyle-git │ ├── CHANGELOG.md │ ├── Cargo.toml │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ ├── README.md │ └── src │ │ └── lib.rs ├── anstyle-lossy │ ├── CHANGELOG.md │ ├── Cargo.toml │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ ├── README.md │ └── src │ │ ├── lib.rs │ │ └── palette.rs ├── anstyle-ls │ ├── CHANGELOG.md │ ├── Cargo.toml │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ ├── README.md │ └── src │ │ └── lib.rs ├── anstyle-owo-colors │ ├── CHANGELOG.md │ ├── Cargo.toml │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ ├── README.md │ └── src │ │ └── lib.rs ├── anstyle-parse │ ├── CHANGELOG.md │ ├── Cargo.toml │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ ├── README.md │ ├── benches │ │ └── parse.rs │ ├── examples │ │ └── parselog.rs │ ├── src │ │ ├── lib.rs │ │ ├── params.rs │ │ └── state │ │ │ ├── codegen.rs │ │ │ ├── definitions.rs │ │ │ ├── mod.rs │ │ │ └── table.rs │ └── tests │ │ ├── demo.vte │ │ ├── rg_help.vte │ │ ├── rg_linus.vte │ │ └── testsuite.rs ├── anstyle-query │ ├── CHANGELOG.md │ ├── Cargo.toml │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ ├── README.md │ ├── examples │ │ └── query.rs │ └── src │ │ ├── lib.rs │ │ └── windows.rs ├── anstyle-roff │ ├── CHANGELOG.md │ ├── Cargo.toml │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ ├── README.md │ ├── src │ │ ├── lib.rs │ │ └── styled_str.rs │ └── tests │ │ ├── roffs │ │ ├── ansi_color.roff │ │ ├── bold.roff │ │ ├── bright_ansi_colors.roff │ │ └── italic.roff │ │ └── test_roff.rs ├── anstyle-svg │ ├── CHANGELOG.md │ ├── Cargo.toml │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ ├── README.md │ ├── examples │ │ └── convert.rs │ ├── rainbow.sh │ ├── src │ │ ├── adapter.rs │ │ └── lib.rs │ └── tests │ │ ├── hyperlink-demo.svg │ │ ├── hyperlink-demo.vte │ │ ├── rainbow.svg │ │ ├── rainbow.vte │ │ ├── rg_linus.svg │ │ ├── rg_linus.vte │ │ └── term.rs ├── anstyle-syntect │ ├── CHANGELOG.md │ ├── Cargo.toml │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ ├── README.md │ └── src │ │ └── lib.rs ├── anstyle-termcolor │ ├── CHANGELOG.md │ ├── Cargo.toml │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ ├── README.md │ └── src │ │ └── lib.rs ├── anstyle-wincon │ ├── CHANGELOG.md │ ├── Cargo.toml │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ ├── README.md │ ├── examples │ │ ├── dump-wincon.rs │ │ └── set-wincon.rs │ └── src │ │ ├── ansi.rs │ │ ├── lib.rs │ │ ├── stream.rs │ │ └── windows.rs ├── anstyle-yansi │ ├── CHANGELOG.md │ ├── Cargo.toml │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ ├── README.md │ └── src │ │ └── lib.rs ├── anstyle │ ├── CHANGELOG.md │ ├── Cargo.toml │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ ├── README.md │ ├── examples │ │ └── dump-style.rs │ └── src │ │ ├── color.rs │ │ ├── effect.rs │ │ ├── lib.rs │ │ ├── macros.rs │ │ ├── reset.rs │ │ └── style.rs ├── colorchoice-clap │ ├── CHANGELOG.md │ ├── Cargo.toml │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ ├── README.md │ └── src │ │ └── lib.rs └── colorchoice │ ├── CHANGELOG.md │ ├── Cargo.toml │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ ├── README.md │ └── src │ └── lib.rs ├── deny.toml ├── release.toml └── typos.toml /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [resolver] 2 | incompatible-rust-versions = "fallback" 3 | -------------------------------------------------------------------------------- /.clippy.toml: -------------------------------------------------------------------------------- 1 | allow-print-in-tests = true 2 | allow-expect-in-tests = true 3 | allow-unwrap-in-tests = true 4 | allow-dbg-in-tests = true 5 | disallowed-methods = [ 6 | { path = "std::option::Option::map_or", reason = "prefer `map(..).unwrap_or(..)` for legibility" }, 7 | { path = "std::option::Option::map_or_else", reason = "prefer `map(..).unwrap_or_else(..)` for legibility" }, 8 | { path = "std::result::Result::map_or", reason = "prefer `map(..).unwrap_or(..)` for legibility" }, 9 | { path = "std::result::Result::map_or_else", reason = "prefer `map(..).unwrap_or_else(..)` for legibility" }, 10 | { path = "std::iter::Iterator::for_each", reason = "prefer `for` for side-effects" }, 11 | { path = "std::iter::Iterator::try_for_each", reason = "prefer `for` for side-effects" }, 12 | ] 13 | -------------------------------------------------------------------------------- /.github/renovate.json5: -------------------------------------------------------------------------------- 1 | { 2 | schedule: [ 3 | 'before 5am on the first day of the month', 4 | ], 5 | semanticCommits: 'enabled', 6 | commitMessageLowerCase: 'never', 7 | configMigration: true, 8 | dependencyDashboard: true, 9 | customManagers: [ 10 | { 11 | customType: 'regex', 12 | managerFilePatterns: [ 13 | '/^rust-toolchain\\.toml$/', 14 | '/Cargo.toml$/', 15 | '/clippy.toml$/', 16 | '/\\.clippy.toml$/', 17 | '/^\\.github/workflows/ci.yml$/', 18 | '/^\\.github/workflows/rust-next.yml$/', 19 | ], 20 | matchStrings: [ 21 | 'STABLE.*?(?\\d+\\.\\d+(\\.\\d+)?)', 22 | '(?\\d+\\.\\d+(\\.\\d+)?).*?STABLE', 23 | ], 24 | depNameTemplate: 'STABLE', 25 | packageNameTemplate: 'rust-lang/rust', 26 | datasourceTemplate: 'github-releases', 27 | }, 28 | ], 29 | packageRules: [ 30 | { 31 | commitMessageTopic: 'Rust Stable', 32 | matchManagers: [ 33 | 'custom.regex', 34 | ], 35 | matchDepNames: [ 36 | 'STABLE', 37 | ], 38 | extractVersion: '^(?\\d+\\.\\d+)', // Drop the patch version 39 | schedule: [ 40 | '* * * * *', 41 | ], 42 | automerge: true, 43 | }, 44 | // Goals: 45 | // - Keep version reqs low, ignoring compatible normal/build dependencies 46 | // - Take advantage of latest dev-dependencies 47 | // - Rollup safe upgrades to reduce CI runner load 48 | // - Help keep number of versions down by always using latest breaking change 49 | // - Have lockfile and manifest in-sync 50 | { 51 | matchManagers: [ 52 | 'cargo', 53 | ], 54 | matchDepTypes: [ 55 | 'build-dependencies', 56 | 'dependencies', 57 | ], 58 | matchCurrentVersion: '>=0.1.0', 59 | matchUpdateTypes: [ 60 | 'patch', 61 | ], 62 | enabled: false, 63 | }, 64 | { 65 | matchManagers: [ 66 | 'cargo', 67 | ], 68 | matchDepTypes: [ 69 | 'build-dependencies', 70 | 'dependencies', 71 | ], 72 | matchCurrentVersion: '>=1.0.0', 73 | matchUpdateTypes: [ 74 | 'minor', 75 | 'patch', 76 | ], 77 | enabled: false, 78 | }, 79 | { 80 | matchManagers: [ 81 | 'cargo', 82 | ], 83 | matchDepTypes: [ 84 | 'dev-dependencies', 85 | ], 86 | matchCurrentVersion: '>=0.1.0', 87 | matchUpdateTypes: [ 88 | 'patch', 89 | ], 90 | automerge: true, 91 | groupName: 'compatible (dev)', 92 | }, 93 | { 94 | matchManagers: [ 95 | 'cargo', 96 | ], 97 | matchDepTypes: [ 98 | 'dev-dependencies', 99 | ], 100 | matchCurrentVersion: '>=1.0.0', 101 | matchUpdateTypes: [ 102 | 'minor', 103 | 'patch', 104 | ], 105 | automerge: true, 106 | groupName: 'compatible (dev)', 107 | }, 108 | ], 109 | } 110 | -------------------------------------------------------------------------------- /.github/settings.yml: -------------------------------------------------------------------------------- 1 | # These settings are synced to GitHub by https://probot.github.io/apps/settings/ 2 | 3 | repository: 4 | description: ANSI text styling 5 | homepage: "https://docs.rs/anstyle" 6 | topics: "rust cli color" 7 | has_issues: true 8 | has_projects: false 9 | has_wiki: false 10 | has_downloads: true 11 | default_branch: main 12 | 13 | # Preference: people do clean commits 14 | allow_merge_commit: true 15 | # Backup in case we need to clean up commits 16 | allow_squash_merge: true 17 | # Not really needed 18 | allow_rebase_merge: false 19 | 20 | allow_auto_merge: true 21 | delete_branch_on_merge: true 22 | 23 | squash_merge_commit_title: "PR_TITLE" 24 | squash_merge_commit_message: "PR_BODY" 25 | merge_commit_message: "PR_BODY" 26 | 27 | labels: 28 | - name: "A-style" 29 | description: "Area: anstyle" 30 | color: '#f7e101' 31 | - name: "A-stream" 32 | description: "Area: anstream" 33 | color: '#f7e101' 34 | # Type 35 | - name: "C-bug" 36 | color: '#f5f1fd' 37 | description: "Category: Things not working as expected" 38 | - name: "C-enhancement" 39 | color: '#f5f1fd' 40 | description: "Category: Raise on the bar on expectations" 41 | # Status 42 | - name: "S-triage" 43 | description: "Status: New; needs maintainer attention." 44 | color: '#D3DDDD' 45 | - name: "S-blocked" 46 | description: "Status: Blocked on something else such as an RFC or other implementation work." 47 | color: '#D3DDDD' 48 | # Meta 49 | - name: "breaking-change" 50 | color: '#E10C02' 51 | - name: "E-help-wanted" 52 | color: '#02E10C' 53 | description: "Help wanted!" 54 | 55 | # This serves more as documentation. 56 | # Branch protection API was replaced by rulesets but settings isn't updated. 57 | # See https://github.com/repository-settings/app/issues/825 58 | # 59 | # branches: 60 | # - name: main 61 | # protection: 62 | # required_pull_request_reviews: null 63 | # required_conversation_resolution: true 64 | # required_status_checks: 65 | # # Required. Require branches to be up to date before merging. 66 | # strict: false 67 | # contexts: ["CI", "Spell Check with Typos"] 68 | # enforce_admins: false 69 | # restrictions: null 70 | -------------------------------------------------------------------------------- /.github/workflows/audit.yml: -------------------------------------------------------------------------------- 1 | name: Security audit 2 | 3 | permissions: 4 | contents: read 5 | 6 | on: 7 | pull_request: 8 | paths: 9 | - '**/Cargo.toml' 10 | - '**/Cargo.lock' 11 | push: 12 | branches: 13 | - main 14 | 15 | env: 16 | RUST_BACKTRACE: 1 17 | CARGO_TERM_COLOR: always 18 | CLICOLOR: 1 19 | 20 | concurrency: 21 | group: "${{ github.workflow }}-${{ github.ref }}" 22 | cancel-in-progress: true 23 | 24 | jobs: 25 | security_audit: 26 | permissions: 27 | issues: write # to create issues (actions-rs/audit-check) 28 | checks: write # to create check (actions-rs/audit-check) 29 | runs-on: ubuntu-latest 30 | # Prevent sudden announcement of a new advisory from failing ci: 31 | continue-on-error: true 32 | steps: 33 | - name: Checkout repository 34 | uses: actions/checkout@v4 35 | - uses: actions-rs/audit-check@v1 36 | with: 37 | token: ${{ secrets.GITHUB_TOKEN }} 38 | 39 | cargo_deny: 40 | permissions: 41 | issues: write # to create issues (actions-rs/audit-check) 42 | checks: write # to create check (actions-rs/audit-check) 43 | runs-on: ubuntu-latest 44 | strategy: 45 | matrix: 46 | checks: 47 | - bans licenses sources 48 | steps: 49 | - uses: actions/checkout@v4 50 | - uses: EmbarkStudios/cargo-deny-action@v2 51 | with: 52 | command: check ${{ matrix.checks }} 53 | rust-version: stable 54 | -------------------------------------------------------------------------------- /.github/workflows/committed.yml: -------------------------------------------------------------------------------- 1 | # Not run as part of pre-commit checks because they don't handle sending the correct commit 2 | # range to `committed` 3 | name: Lint Commits 4 | on: [pull_request] 5 | 6 | permissions: 7 | contents: read 8 | 9 | env: 10 | RUST_BACKTRACE: 1 11 | CARGO_TERM_COLOR: always 12 | CLICOLOR: 1 13 | 14 | concurrency: 15 | group: "${{ github.workflow }}-${{ github.ref }}" 16 | cancel-in-progress: true 17 | 18 | jobs: 19 | committed: 20 | name: Lint Commits 21 | runs-on: ubuntu-latest 22 | steps: 23 | - name: Checkout Actions Repository 24 | uses: actions/checkout@v4 25 | with: 26 | fetch-depth: 0 27 | - name: Lint Commits 28 | uses: crate-ci/committed@master 29 | -------------------------------------------------------------------------------- /.github/workflows/pre-commit.yml: -------------------------------------------------------------------------------- 1 | name: pre-commit 2 | 3 | permissions: {} # none 4 | 5 | on: 6 | pull_request: 7 | push: 8 | branches: [main] 9 | 10 | env: 11 | RUST_BACKTRACE: 1 12 | CARGO_TERM_COLOR: always 13 | CLICOLOR: 1 14 | 15 | concurrency: 16 | group: "${{ github.workflow }}-${{ github.ref }}" 17 | cancel-in-progress: true 18 | 19 | jobs: 20 | pre-commit: 21 | permissions: 22 | contents: read 23 | runs-on: ubuntu-latest 24 | steps: 25 | - uses: actions/checkout@v4 26 | - uses: actions/setup-python@v5 27 | with: 28 | python-version: '3.x' 29 | - uses: pre-commit/action@v3.0.1 30 | -------------------------------------------------------------------------------- /.github/workflows/rust-next.yml: -------------------------------------------------------------------------------- 1 | name: rust-next 2 | 3 | permissions: 4 | contents: read 5 | 6 | on: 7 | schedule: 8 | - cron: '1 1 1 * *' 9 | 10 | env: 11 | RUST_BACKTRACE: 1 12 | CARGO_TERM_COLOR: always 13 | CLICOLOR: 1 14 | 15 | concurrency: 16 | group: "${{ github.workflow }}-${{ github.ref }}" 17 | cancel-in-progress: true 18 | 19 | jobs: 20 | test: 21 | name: Test 22 | strategy: 23 | matrix: 24 | os: ["ubuntu-latest", "windows-latest", "macos-latest"] 25 | rust: ["stable", "beta"] 26 | include: 27 | - os: ubuntu-latest 28 | rust: "nightly" 29 | continue-on-error: ${{ matrix.rust != 'stable' }} 30 | runs-on: ${{ matrix.os }} 31 | steps: 32 | - name: Checkout repository 33 | uses: actions/checkout@v4 34 | - name: Install Rust 35 | uses: dtolnay/rust-toolchain@stable 36 | with: 37 | toolchain: ${{ matrix.rust }} 38 | components: rustfmt 39 | - uses: Swatinem/rust-cache@v2 40 | - uses: taiki-e/install-action@cargo-hack 41 | - name: Build 42 | run: cargo test --workspace --no-run 43 | - name: Test 44 | run: cargo hack test --each-feature --workspace 45 | latest: 46 | name: "Check latest dependencies" 47 | runs-on: ubuntu-latest 48 | steps: 49 | - name: Checkout repository 50 | uses: actions/checkout@v4 51 | - name: Install Rust 52 | uses: dtolnay/rust-toolchain@stable 53 | with: 54 | toolchain: stable 55 | components: rustfmt 56 | - uses: Swatinem/rust-cache@v2 57 | - uses: taiki-e/install-action@cargo-hack 58 | - name: Update dependencies 59 | run: cargo update 60 | - name: Build 61 | run: cargo test --workspace --no-run 62 | - name: Test 63 | run: cargo hack test --each-feature --workspace 64 | -------------------------------------------------------------------------------- /.github/workflows/spelling.yml: -------------------------------------------------------------------------------- 1 | name: Spelling 2 | 3 | permissions: 4 | contents: read 5 | 6 | on: [pull_request] 7 | 8 | env: 9 | RUST_BACKTRACE: 1 10 | CARGO_TERM_COLOR: always 11 | CLICOLOR: 1 12 | 13 | concurrency: 14 | group: "${{ github.workflow }}-${{ github.ref }}" 15 | cancel-in-progress: true 16 | 17 | jobs: 18 | spelling: 19 | name: Spell Check with Typos 20 | runs-on: ubuntu-latest 21 | steps: 22 | - name: Checkout Actions Repository 23 | uses: actions/checkout@v4 24 | - name: Spell Check Repo 25 | uses: crate-ci/typos@master 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | default_install_hook_types: ["pre-commit", "commit-msg"] 2 | repos: 3 | - repo: https://github.com/pre-commit/pre-commit-hooks 4 | rev: v5.0.0 5 | hooks: 6 | - id: check-yaml 7 | - id: check-json 8 | - id: check-toml 9 | - id: check-merge-conflict 10 | - id: check-case-conflict 11 | - id: detect-private-key 12 | - repo: https://github.com/crate-ci/typos 13 | rev: v1.32.0 14 | hooks: 15 | - id: typos 16 | - repo: https://github.com/crate-ci/committed 17 | rev: v1.1.7 18 | hooks: 19 | - id: committed 20 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to anstyle 2 | 3 | Thanks for wanting to contribute! There are many ways to contribute and we 4 | appreciate any level you're willing to do. 5 | 6 | ## Feature Requests 7 | 8 | Need some new functionality to help? You can let us know by opening an 9 | [issue][new issue]. It's helpful to look through [all issues][all issues] in 10 | case it's already being talked about. 11 | 12 | ## Bug Reports 13 | 14 | Please let us know about what problems you run into, whether in behavior or 15 | ergonomics of API. You can do this by opening an [issue][new issue]. It's 16 | helpful to look through [all issues][all issues] in case it's already being 17 | talked about. 18 | 19 | ## Pull Requests 20 | 21 | Looking for an idea? Check our [issues][issues]. If the issue looks open ended, 22 | it is probably best to post on the issue how you are thinking of resolving the 23 | issue so you can get feedback early in the process. We want you to be 24 | successful and it can be discouraging to find out a lot of re-work is needed. 25 | 26 | Already have an idea? It might be good to first [create an issue][new issue] 27 | to propose it so we can make sure we are aligned and lower the risk of having 28 | to re-work some of it and the discouragement that goes along with that. 29 | 30 | ### Process 31 | 32 | As a heads up, we'll be running your PR through the following gauntlet: 33 | - warnings turned to compile errors 34 | - `cargo test` 35 | - `rustfmt` 36 | - `clippy` 37 | - `rustdoc` 38 | - [`committed`](https://github.com/crate-ci/committed) as we use [Conventional](https://www.conventionalcommits.org) commit style 39 | - [`typos`](https://github.com/crate-ci/typos) to check spelling 40 | 41 | Not everything can be checked automatically though. 42 | 43 | We request that the commit history gets cleaned up. 44 | 45 | We ask that commits are atomic, meaning they are complete and have a single responsibility. 46 | A complete commit should build, pass tests, update documentation and tests, and not have dead code. 47 | 48 | PRs should tell a cohesive story, with refactor and test commits that keep the 49 | fix or feature commits simple and clear. 50 | 51 | Specifically, we would encourage 52 | - File renames be isolated into their own commit 53 | - Add tests in a commit before their feature or fix, showing the current behavior (i.e. they should pass). 54 | The diff for the feature/fix commit will then show how the behavior changed, 55 | making the commit's intent clearer to reviewers and the community, and showing people that the 56 | test is verifying the expected state. 57 | - e.g. [clap#5520](https://github.com/clap-rs/clap/pull/5520) 58 | 59 | Note that we are talking about ideals. 60 | We understand having a clean history requires more advanced git skills; 61 | feel free to ask us for help! 62 | We might even suggest where it would work to be lax. 63 | We also understand that editing some early commits may cause a lot of churn 64 | with merge conflicts which can make it not worth editing all of the history. 65 | 66 | For code organization, we recommend 67 | - Grouping `impl` blocks next to their type (or trait) 68 | - Grouping private items after the `pub` item that uses them. 69 | - The intent is to help people quickly find the "relevant" details, allowing them to "dig deeper" as needed. Or put another way, the `pub` items serve as a table-of-contents. 70 | - The exact order is fuzzy; do what makes sense 71 | 72 | ## Releasing 73 | 74 | Pre-requisites 75 | - Running `cargo login` 76 | - A member of `rust-cli:Maintainers` 77 | - Push permission to the repo 78 | - [`cargo-release`](https://github.com/crate-ci/cargo-release/) 79 | 80 | When we're ready to release, a project owner should do the following 81 | 1. Update the changelog (see `cargo release changes` for ideas) 82 | 2. Determine what the next version is, according to semver 83 | 3. Run [`cargo release -x `](https://github.com/crate-ci/cargo-release) 84 | 85 | ## Resources 86 | 87 | - [HN post on syntax history](https://news.ycombinator.com/item?id=35137153#35157058) 88 | 89 | [issues]: https://github.com/rust-cli/anstyle/issues 90 | [new issue]: https://github.com/rust-cli/anstyle/issues/new 91 | [all issues]: https://github.com/rust-cli/anstyle/issues?utf8=%E2%9C%93&q=is%3Aissue 92 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["crates/*"] 3 | resolver = "2" 4 | 5 | [workspace.package] 6 | repository = "https://github.com/rust-cli/anstyle.git" 7 | license = "MIT OR Apache-2.0" 8 | edition = "2021" 9 | rust-version = "1.66.0" # MSRV 10 | include = [ 11 | "build.rs", 12 | "src/**/*", 13 | "Cargo.toml", 14 | "Cargo.lock", 15 | "LICENSE*", 16 | "README.md", 17 | "examples/**/*" 18 | ] 19 | 20 | [workspace.lints.rust] 21 | rust_2018_idioms = { level = "warn", priority = -1 } 22 | unnameable_types = "warn" 23 | unreachable_pub = "warn" 24 | unsafe_op_in_unsafe_fn = "warn" 25 | unused_lifetimes = "warn" 26 | unused_macro_rules = "warn" 27 | unused_qualifications = "warn" 28 | 29 | [workspace.lints.clippy] 30 | bool_assert_comparison = "allow" 31 | branches_sharing_code = "allow" 32 | checked_conversions = "warn" 33 | collapsible_else_if = "allow" 34 | create_dir = "warn" 35 | dbg_macro = "warn" 36 | debug_assert_with_mut_call = "warn" 37 | doc_markdown = "warn" 38 | empty_enum = "warn" 39 | enum_glob_use = "warn" 40 | expl_impl_clone_on_copy = "warn" 41 | explicit_deref_methods = "warn" 42 | explicit_into_iter_loop = "warn" 43 | fallible_impl_from = "warn" 44 | filter_map_next = "warn" 45 | flat_map_option = "warn" 46 | float_cmp_const = "warn" 47 | fn_params_excessive_bools = "warn" 48 | from_iter_instead_of_collect = "warn" 49 | if_same_then_else = "allow" 50 | implicit_clone = "warn" 51 | imprecise_flops = "warn" 52 | inconsistent_struct_constructor = "warn" 53 | inefficient_to_string = "warn" 54 | infinite_loop = "warn" 55 | invalid_upcast_comparisons = "warn" 56 | large_digit_groups = "warn" 57 | large_stack_arrays = "warn" 58 | large_types_passed_by_value = "warn" 59 | let_and_return = "allow" # sometimes good to name what you are returning 60 | linkedlist = "warn" 61 | lossy_float_literal = "warn" 62 | macro_use_imports = "warn" 63 | mem_forget = "warn" 64 | mutex_integer = "warn" 65 | needless_continue = "allow" 66 | needless_for_each = "warn" 67 | negative_feature_names = "warn" 68 | path_buf_push_overwrite = "warn" 69 | ptr_as_ptr = "warn" 70 | rc_mutex = "warn" 71 | redundant_feature_names = "warn" 72 | ref_option_ref = "warn" 73 | rest_pat_in_fully_bound_structs = "warn" 74 | result_large_err = "allow" 75 | same_functions_in_if_condition = "warn" 76 | self_named_module_files = "warn" 77 | semicolon_if_nothing_returned = "warn" 78 | str_to_string = "warn" 79 | string_add = "warn" 80 | string_add_assign = "warn" 81 | string_lit_as_bytes = "warn" 82 | string_to_string = "warn" 83 | todo = "warn" 84 | trait_duplication_in_bounds = "warn" 85 | uninlined_format_args = "warn" 86 | verbose_file_reads = "warn" 87 | wildcard_imports = "warn" 88 | zero_sized_map_values = "warn" 89 | 90 | [profile.dev] 91 | panic = "abort" 92 | 93 | [profile.release] 94 | panic = "abort" 95 | codegen-units = 1 96 | lto = true 97 | # debug = "line-tables-only" # requires Cargo 1.71 98 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This repo contains: 2 | - [`anstream`](./crates/anstream) for a simple cross platform library for writing colored text to a terminal 3 | - [`anstyle`](./crates/anstyle) for style definitions 4 | - User-styling parsers 5 | - [`anstyle-git`](./crates/anstyle-git) for parsing `git` style descriptions 6 | - [`anstyle-ls`](./crates/anstyle-ls) for parsing `LS_COLORS` style descriptions 7 | - Convert to other formats 8 | - [`anstyle-roff`](./crates/anstyle-roff) for converting ANSI codes to `ROFF` 9 | - [`anstyle-svg`](./crates/anstyle-svg) for converting ANSI codes to `SVG` 10 | - Styling integration 11 | - [`anstyle-ansi-term`](./crates/anstyle-ansi-term) for adapting `anstyle` to `ansi_term` 12 | - [`anstyle-crossterm`](./crates/anstyle-crossterm) for adapting `anstyle` to `crossterm` 13 | - [`anstyle-owo-colors`](./crates/anstyle-owo-colors) for adapting `anstyle` to `owo-colors` 14 | - [`anstyle-syntect`](./crates/anstyle-syntect) for adapting `anstyle` to `syntect` 15 | - [`anstyle-termcolor`](./crates/anstyle-termcolor) for adapting `anstyle` to `termcolor` 16 | - [`anstyle-yansi`](./crates/anstyle-yansi) for adapting `anstyle` to `yansi` 17 | - Utilities 18 | - [`anstyle-lossy`](./crates/anstyle-lossy) for converting between color types 19 | - [`anstyle-parse`](./crates/anstyle-parse) for parsing ANSI Style Escapes 20 | - [`anstyle-wincon`](./crates/anstyle-wincon) for styling legacy Microsoft terminals 21 | - [`colorchoice-clap`](./crates/colorchoice-clap) for using `color` flag in `clap` 22 | -------------------------------------------------------------------------------- /committed.toml: -------------------------------------------------------------------------------- 1 | style="conventional" 2 | ignore_author_re="(dependabot|renovate)" 3 | merge_commit = false 4 | -------------------------------------------------------------------------------- /crates/anstream/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "anstream" 3 | version = "0.6.19" 4 | description = "A simple cross platform library for writing colored text to a terminal." 5 | categories = ["command-line-interface"] 6 | keywords = ["ansi", "terminal", "color", "strip", "wincon"] 7 | repository.workspace = true 8 | license.workspace = true 9 | edition.workspace = true 10 | rust-version.workspace = true 11 | include.workspace = true 12 | 13 | [package.metadata.docs.rs] 14 | all-features = true 15 | rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"] 16 | 17 | [package.metadata.release] 18 | pre-release-replacements = [ 19 | {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, 20 | {file="CHANGELOG.md", search="\\.\\.\\.HEAD", replace="...{{tag_name}}", exactly=1}, 21 | {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, 22 | {file="CHANGELOG.md", search="", replace="\n## [Unreleased] - ReleaseDate\n", exactly=1}, 23 | {file="CHANGELOG.md", search="", replace="\n[Unreleased]: https://github.com/rust-cli/anstyle/compare/{{tag_name}}...HEAD", exactly=1}, 24 | ] 25 | 26 | [features] 27 | default = ["auto", "wincon"] 28 | auto = ["dep:anstyle-query"] 29 | wincon = ["dep:anstyle-wincon"] 30 | # Enable in `dev-dependencies` to make sure output is captured for tests 31 | test = [] 32 | 33 | [dependencies] 34 | anstyle = { version = "1.0.0", path = "../anstyle" } 35 | anstyle-parse = { version = "0.2.0", path = "../anstyle-parse" } 36 | colorchoice = { version = "1.0.0", path = "../colorchoice" } 37 | anstyle-query = { version = "1.0.0", path = "../anstyle-query", optional = true } 38 | utf8parse = "0.2.1" 39 | is_terminal_polyfill = "1.48" 40 | 41 | [target.'cfg(windows)'.dependencies] 42 | anstyle-wincon = { version = "3.0.5", path = "../anstyle-wincon", optional = true } 43 | 44 | [dev-dependencies] 45 | divan = "0.1.11" 46 | lexopt = "0.3.0" 47 | owo-colors = "4.0.0" 48 | proptest = "1.4.0" 49 | strip-ansi-escapes = "0.2.0" 50 | 51 | [[example]] 52 | name = "dump-stream" 53 | required-features = ["auto"] 54 | 55 | [[example]] 56 | name = "query-stream" 57 | required-features = ["auto"] 58 | 59 | [[bench]] 60 | name = "strip" 61 | harness = false 62 | 63 | [[bench]] 64 | name = "wincon" 65 | harness = false 66 | 67 | [[bench]] 68 | name = "stream" 69 | harness = false 70 | 71 | [lints] 72 | workspace = true 73 | -------------------------------------------------------------------------------- /crates/anstream/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) Individual contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /crates/anstream/README.md: -------------------------------------------------------------------------------- 1 | # anstream 2 | 3 | > A simple cross platform library for writing colored text to a terminal. 4 | 5 | *A portmanteau of "ansi stream"* 6 | 7 | [![Documentation](https://img.shields.io/badge/docs-master-blue.svg)][Documentation] 8 | ![License](https://img.shields.io/crates/l/anstream.svg) 9 | [![Crates Status](https://img.shields.io/crates/v/anstream.svg)](https://crates.io/crates/anstream) 10 | 11 | Specialized `stdout` and `stderr` that accept ANSI escape codes and adapt them 12 | based on the terminal's capabilities. 13 | 14 | `anstream::adapter::strip_str` may also be of interest on its own for low 15 | overhead stripping of ANSI escape codes. 16 | 17 | ## License 18 | 19 | Licensed under either of 20 | 21 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or ) 22 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or ) 23 | 24 | at your option. 25 | 26 | ## [Contribute](../../CONTRIBUTING.md) 27 | 28 | Unless you explicitly state otherwise, any contribution intentionally 29 | submitted for inclusion in the work by you, as defined in the Apache-2.0 30 | license, shall be dual-licensed as above, without any additional terms or 31 | conditions. 32 | 33 | [Crates.io]: https://crates.io/crates/anstream 34 | [Documentation]: https://docs.rs/anstream 35 | -------------------------------------------------------------------------------- /crates/anstream/benches/stream.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::unwrap_used)] 2 | #![allow(clippy::incompatible_msrv)] 3 | 4 | use std::io::Write as _; 5 | 6 | #[divan::bench(args = DATA)] 7 | fn nop(data: &Data) -> Vec { 8 | let buffer = Vec::with_capacity(data.content().len()); 9 | let mut stream = buffer; 10 | 11 | stream.write_all(data.content()).unwrap(); 12 | 13 | stream 14 | } 15 | 16 | #[divan::bench(args = DATA)] 17 | fn strip_stream(data: &Data) -> Vec { 18 | let buffer = Vec::with_capacity(data.content().len()); 19 | let mut stream = anstream::StripStream::new(buffer); 20 | 21 | stream.write_all(data.content()).unwrap(); 22 | 23 | stream.into_inner() 24 | } 25 | 26 | #[divan::bench(args = DATA)] 27 | #[cfg(all(windows, feature = "wincon"))] 28 | fn wincon_stream(data: &Data) -> Vec { 29 | let buffer = Vec::with_capacity(data.content().len()); 30 | let mut stream = anstream::WinconStream::new(buffer); 31 | 32 | stream.write_all(data.content()).unwrap(); 33 | 34 | stream.into_inner() 35 | } 36 | 37 | #[divan::bench(args = DATA)] 38 | fn auto_stream_always_ansi(data: &Data) -> Vec { 39 | let buffer = Vec::with_capacity(data.content().len()); 40 | let mut stream = anstream::AutoStream::always_ansi(buffer); 41 | 42 | stream.write_all(data.content()).unwrap(); 43 | 44 | stream.into_inner() 45 | } 46 | 47 | #[divan::bench(args = DATA)] 48 | fn auto_stream_always(data: &Data) -> Vec { 49 | let buffer = Vec::with_capacity(data.content().len()); 50 | let mut stream = anstream::AutoStream::always(buffer); 51 | 52 | stream.write_all(data.content()).unwrap(); 53 | 54 | stream.into_inner() 55 | } 56 | 57 | #[divan::bench(args = DATA)] 58 | fn auto_stream_never(data: &Data) -> Vec { 59 | let buffer = Vec::with_capacity(data.content().len()); 60 | let mut stream = anstream::AutoStream::never(buffer); 61 | 62 | stream.write_all(data.content()).unwrap(); 63 | 64 | stream.into_inner() 65 | } 66 | 67 | const DATA: &[Data] = &[ 68 | Data( 69 | "0-state_changes", 70 | b"\x1b]2;X\x1b\\ \x1b[0m \x1bP0@\x1b\\".as_slice(), 71 | ), 72 | Data("1-demo.vte", include_bytes!("../tests/demo.vte").as_slice()), 73 | Data( 74 | "2-rg_help.vte", 75 | include_bytes!("../tests/rg_help.vte").as_slice(), 76 | ), 77 | Data( 78 | "3-rg_linus.vte", 79 | include_bytes!("../tests/rg_linus.vte").as_slice(), 80 | ), 81 | ]; 82 | 83 | #[derive(Debug)] 84 | struct Data(&'static str, &'static [u8]); 85 | 86 | impl Data { 87 | const fn name(&self) -> &'static str { 88 | self.0 89 | } 90 | 91 | const fn content(&self) -> &'static [u8] { 92 | self.1 93 | } 94 | } 95 | 96 | impl std::fmt::Display for Data { 97 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 98 | self.name().fmt(f) 99 | } 100 | } 101 | 102 | fn main() { 103 | divan::main(); 104 | } 105 | -------------------------------------------------------------------------------- /crates/anstream/benches/strip.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::unwrap_used)] 2 | #![allow(clippy::incompatible_msrv)] 3 | 4 | #[derive(Default)] 5 | struct Strip(String); 6 | impl Strip { 7 | fn with_capacity(capacity: usize) -> Self { 8 | Self(String::with_capacity(capacity)) 9 | } 10 | } 11 | impl anstyle_parse::Perform for Strip { 12 | fn print(&mut self, c: char) { 13 | self.0.push(c); 14 | } 15 | 16 | fn execute(&mut self, byte: u8) { 17 | if byte.is_ascii_whitespace() { 18 | self.0.push(byte as char); 19 | } 20 | } 21 | } 22 | 23 | #[divan::bench(args = DATA)] 24 | fn advance_strip(data: &Data) -> String { 25 | let mut stripped = Strip::with_capacity(data.content().len()); 26 | let mut parser = anstyle_parse::Parser::::new(); 27 | 28 | for byte in data.content() { 29 | parser.advance(&mut stripped, *byte); 30 | } 31 | 32 | stripped.0 33 | } 34 | 35 | #[divan::bench(args = DATA)] 36 | fn strip_ansi_escapes(data: &Data) -> Vec { 37 | let stripped = strip_ansi_escapes::strip(data.content()); 38 | 39 | stripped 40 | } 41 | 42 | #[divan::bench(args = DATA)] 43 | fn strip_str(data: &Data) -> String { 44 | if let Ok(content) = std::str::from_utf8(data.content()) { 45 | let stripped = anstream::adapter::strip_str(content).to_string(); 46 | 47 | stripped 48 | } else { 49 | "".to_owned() 50 | } 51 | } 52 | 53 | #[divan::bench(args = DATA)] 54 | fn strip_str_strip_next(data: &Data) -> String { 55 | if let Ok(content) = std::str::from_utf8(data.content()) { 56 | let mut stripped = String::with_capacity(data.content().len()); 57 | let mut state = anstream::adapter::StripStr::new(); 58 | for printable in state.strip_next(content) { 59 | stripped.push_str(printable); 60 | } 61 | 62 | stripped 63 | } else { 64 | "".to_owned() 65 | } 66 | } 67 | 68 | #[divan::bench(args = DATA)] 69 | fn strip_bytes(data: &Data) -> Vec { 70 | let stripped = anstream::adapter::strip_bytes(data.content()).into_vec(); 71 | 72 | stripped 73 | } 74 | 75 | const DATA: &[Data] = &[ 76 | Data( 77 | "0-state_changes", 78 | b"\x1b]2;X\x1b\\ \x1b[0m \x1bP0@\x1b\\".as_slice(), 79 | ), 80 | Data("1-demo.vte", include_bytes!("../tests/demo.vte").as_slice()), 81 | Data( 82 | "2-rg_help.vte", 83 | include_bytes!("../tests/rg_help.vte").as_slice(), 84 | ), 85 | Data( 86 | "3-rg_linus.vte", 87 | include_bytes!("../tests/rg_linus.vte").as_slice(), 88 | ), 89 | ]; 90 | 91 | #[derive(Debug)] 92 | struct Data(&'static str, &'static [u8]); 93 | 94 | impl Data { 95 | const fn name(&self) -> &'static str { 96 | self.0 97 | } 98 | 99 | const fn content(&self) -> &'static [u8] { 100 | self.1 101 | } 102 | } 103 | 104 | impl std::fmt::Display for Data { 105 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 106 | self.name().fmt(f) 107 | } 108 | } 109 | 110 | #[test] 111 | fn verify_data() { 112 | for data in DATA { 113 | // Make sure the comparison is fair 114 | if let Ok(content) = std::str::from_utf8(data.content()) { 115 | let mut stripped = Strip::with_capacity(content.len()); 116 | let mut parser = anstyle_parse::Parser::::new(); 117 | for byte in content.as_bytes() { 118 | parser.advance(&mut stripped, *byte); 119 | } 120 | assert_eq!( 121 | stripped.0, 122 | anstream::adapter::strip_str(content).to_string() 123 | ); 124 | assert_eq!( 125 | stripped.0, 126 | String::from_utf8(anstream::adapter::strip_bytes(content.as_bytes()).into_vec()) 127 | .unwrap() 128 | ); 129 | } 130 | } 131 | } 132 | 133 | fn main() { 134 | divan::main(); 135 | } 136 | -------------------------------------------------------------------------------- /crates/anstream/benches/wincon.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::incompatible_msrv)] 2 | 3 | #[divan::bench(args = DATA)] 4 | fn nop(data: &Data) -> Vec<(anstyle::Style, String)> { 5 | let mut state = anstream::adapter::WinconBytes::new(); 6 | let stripped = state.extract_next(data.content()).collect::>(); 7 | 8 | stripped 9 | } 10 | 11 | const DATA: &[Data] = &[ 12 | Data( 13 | "0-state_changes", 14 | b"\x1b]2;X\x1b\\ \x1b[0m \x1bP0@\x1b\\".as_slice(), 15 | ), 16 | Data("1-demo.vte", include_bytes!("../tests/demo.vte").as_slice()), 17 | Data( 18 | "2-rg_help.vte", 19 | include_bytes!("../tests/rg_help.vte").as_slice(), 20 | ), 21 | Data( 22 | "3-rg_linus.vte", 23 | include_bytes!("../tests/rg_linus.vte").as_slice(), 24 | ), 25 | ]; 26 | 27 | #[derive(Debug)] 28 | struct Data(&'static str, &'static [u8]); 29 | 30 | impl Data { 31 | const fn name(&self) -> &'static str { 32 | self.0 33 | } 34 | 35 | const fn content(&self) -> &'static [u8] { 36 | self.1 37 | } 38 | } 39 | 40 | impl std::fmt::Display for Data { 41 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 42 | self.name().fmt(f) 43 | } 44 | } 45 | 46 | fn main() { 47 | divan::main(); 48 | } 49 | -------------------------------------------------------------------------------- /crates/anstream/examples/dump-stream.rs: -------------------------------------------------------------------------------- 1 | //! Write colored text, adapting to the terminals capabilities 2 | 3 | use std::io::Write; 4 | 5 | fn main() -> Result<(), lexopt::Error> { 6 | let args = Args::parse()?; 7 | let stdout = anstream::stdout(); 8 | let mut stdout = stdout.lock(); 9 | 10 | for fixed in 0..16 { 11 | let color = anstyle::Ansi256Color(fixed) 12 | .into_ansi() 13 | .expect("within 4-bit color range"); 14 | let style = style(color, args.layer, args.effects); 15 | let _ = print_number(&mut stdout, fixed, style); 16 | if fixed == 7 || fixed == 15 { 17 | let _ = writeln!(&mut stdout); 18 | } 19 | } 20 | 21 | for fixed in 16..232 { 22 | let col = (fixed - 16) % 36; 23 | if col == 0 { 24 | let _ = writeln!(stdout); 25 | } 26 | let color = anstyle::Ansi256Color(fixed); 27 | let style = style(color, args.layer, args.effects); 28 | let _ = print_number(&mut stdout, fixed, style); 29 | } 30 | 31 | let _ = writeln!(stdout); 32 | let _ = writeln!(stdout); 33 | for fixed in 232..=255 { 34 | let color = anstyle::Ansi256Color(fixed); 35 | let style = style(color, args.layer, args.effects); 36 | let _ = print_number(&mut stdout, fixed, style); 37 | } 38 | 39 | let _ = writeln!(stdout); 40 | 41 | Ok(()) 42 | } 43 | 44 | fn style( 45 | color: impl Into, 46 | layer: Layer, 47 | effects: anstyle::Effects, 48 | ) -> anstyle::Style { 49 | let color = color.into(); 50 | (match layer { 51 | Layer::Fg => anstyle::Style::new().fg_color(Some(color)), 52 | Layer::Bg => anstyle::Style::new().bg_color(Some(color)), 53 | Layer::Underline => anstyle::Style::new().underline_color(Some(color)), 54 | }) | effects 55 | } 56 | 57 | fn print_number(stdout: &mut impl Write, fixed: u8, style: anstyle::Style) -> std::io::Result<()> { 58 | write!(stdout, "{style}{fixed:>3X}{style:#}",) 59 | } 60 | 61 | #[derive(Default)] 62 | struct Args { 63 | effects: anstyle::Effects, 64 | layer: Layer, 65 | } 66 | 67 | #[derive(Copy, Clone, Default)] 68 | enum Layer { 69 | #[default] 70 | Fg, 71 | Bg, 72 | Underline, 73 | } 74 | 75 | impl Args { 76 | fn parse() -> Result { 77 | use lexopt::prelude::*; 78 | 79 | let mut res = Args::default(); 80 | 81 | let mut args = lexopt::Parser::from_env(); 82 | while let Some(arg) = args.next()? { 83 | match arg { 84 | Long("layer") => { 85 | res.layer = args.value()?.parse_with(|s| match s { 86 | "fg" => Ok(Layer::Fg), 87 | "bg" => Ok(Layer::Bg), 88 | "underline" => Ok(Layer::Underline), 89 | _ => Err("expected values fg, bg, underline"), 90 | })?; 91 | } 92 | Long("effect") => { 93 | const EFFECTS: [(&str, anstyle::Effects); 12] = [ 94 | ("bold", anstyle::Effects::BOLD), 95 | ("dimmed", anstyle::Effects::DIMMED), 96 | ("italic", anstyle::Effects::ITALIC), 97 | ("underline", anstyle::Effects::UNDERLINE), 98 | ("double_underline", anstyle::Effects::DOUBLE_UNDERLINE), 99 | ("curly_underline", anstyle::Effects::CURLY_UNDERLINE), 100 | ("dotted_underline", anstyle::Effects::DOTTED_UNDERLINE), 101 | ("dashed_underline", anstyle::Effects::DASHED_UNDERLINE), 102 | ("blink", anstyle::Effects::BLINK), 103 | ("invert", anstyle::Effects::INVERT), 104 | ("hidden", anstyle::Effects::HIDDEN), 105 | ("strikethrough", anstyle::Effects::STRIKETHROUGH), 106 | ]; 107 | let effect = args.value()?.parse_with(|s| { 108 | EFFECTS 109 | .into_iter() 110 | .find(|(name, _)| *name == s) 111 | .map(|(_, effect)| effect) 112 | .ok_or_else(|| { 113 | format!( 114 | "expected one of {}", 115 | EFFECTS 116 | .into_iter() 117 | .map(|(n, _)| n) 118 | .collect::>() 119 | .join(", ") 120 | ) 121 | }) 122 | })?; 123 | res.effects = res.effects.insert(effect); 124 | } 125 | _ => return Err(arg.unexpected()), 126 | } 127 | } 128 | Ok(res) 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /crates/anstream/examples/query-stream.rs: -------------------------------------------------------------------------------- 1 | //! Report a terminal's capabilities 2 | 3 | fn main() { 4 | println!("stdout:"); 5 | println!( 6 | " choice: {:?}", 7 | anstream::AutoStream::choice(&std::io::stdout()) 8 | ); 9 | println!( 10 | " choice: {:?}", 11 | anstream::AutoStream::auto(std::io::stdout()).current_choice() 12 | ); 13 | println!("stderr:"); 14 | println!( 15 | " choice: {:?}", 16 | anstream::AutoStream::choice(&std::io::stderr()) 17 | ); 18 | println!( 19 | " choice: {:?}", 20 | anstream::AutoStream::auto(std::io::stderr()).current_choice() 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /crates/anstream/src/adapter/mod.rs: -------------------------------------------------------------------------------- 1 | //! Gracefully degrade styled output 2 | 3 | mod strip; 4 | mod wincon; 5 | 6 | pub use strip::strip_bytes; 7 | pub use strip::strip_str; 8 | pub use strip::StripBytes; 9 | pub use strip::StripBytesIter; 10 | pub use strip::StripStr; 11 | pub use strip::StripStrIter; 12 | pub use strip::StrippedBytes; 13 | pub use strip::StrippedStr; 14 | pub use wincon::WinconBytes; 15 | pub use wincon::WinconBytesIter; 16 | -------------------------------------------------------------------------------- /crates/anstream/src/buffer.rs: -------------------------------------------------------------------------------- 1 | #![allow(deprecated)] 2 | 3 | /// In-memory [`RawStream`][crate::stream::RawStream] 4 | #[derive(Clone, Default, Debug, PartialEq, Eq)] 5 | #[deprecated(since = "0.6.2", note = "Use Vec")] 6 | #[doc(hidden)] 7 | pub struct Buffer(Vec); 8 | 9 | impl Buffer { 10 | #[inline] 11 | pub fn new() -> Self { 12 | Default::default() 13 | } 14 | 15 | #[inline] 16 | pub fn with_capacity(capacity: usize) -> Self { 17 | Self(Vec::with_capacity(capacity)) 18 | } 19 | 20 | #[inline] 21 | pub fn as_bytes(&self) -> &[u8] { 22 | &self.0 23 | } 24 | } 25 | 26 | impl AsRef<[u8]> for Buffer { 27 | #[inline] 28 | fn as_ref(&self) -> &[u8] { 29 | self.as_bytes() 30 | } 31 | } 32 | 33 | impl std::io::Write for Buffer { 34 | #[inline] 35 | fn write(&mut self, buf: &[u8]) -> std::io::Result { 36 | self.0.extend(buf); 37 | Ok(buf.len()) 38 | } 39 | 40 | #[inline] 41 | fn flush(&mut self) -> std::io::Result<()> { 42 | Ok(()) 43 | } 44 | } 45 | 46 | #[cfg(all(windows, feature = "wincon"))] 47 | impl anstyle_wincon::WinconStream for Buffer { 48 | fn write_colored( 49 | &mut self, 50 | fg: Option, 51 | bg: Option, 52 | data: &[u8], 53 | ) -> std::io::Result { 54 | self.0.write_colored(fg, bg, data) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /crates/anstream/src/fmt.rs: -------------------------------------------------------------------------------- 1 | /// A shim which allows a [`std::io::Write`] to be implemented in terms of a [`std::fmt::Write`] 2 | /// 3 | /// This saves off I/O errors. instead of discarding them 4 | pub(crate) struct Adapter 5 | where 6 | W: FnMut(&[u8]) -> std::io::Result<()>, 7 | { 8 | writer: W, 9 | error: std::io::Result<()>, 10 | } 11 | 12 | impl Adapter 13 | where 14 | W: FnMut(&[u8]) -> std::io::Result<()>, 15 | { 16 | pub(crate) fn new(writer: W) -> Self { 17 | Adapter { 18 | writer, 19 | error: Ok(()), 20 | } 21 | } 22 | 23 | pub(crate) fn write_fmt(mut self, fmt: std::fmt::Arguments<'_>) -> std::io::Result<()> { 24 | match std::fmt::write(&mut self, fmt) { 25 | Ok(()) => Ok(()), 26 | Err(..) => { 27 | // check if the error came from the underlying `Write` or not 28 | if self.error.is_err() { 29 | self.error 30 | } else { 31 | Err(std::io::Error::new( 32 | std::io::ErrorKind::Other, 33 | "formatter error", 34 | )) 35 | } 36 | } 37 | } 38 | } 39 | } 40 | 41 | impl std::fmt::Write for Adapter 42 | where 43 | W: FnMut(&[u8]) -> std::io::Result<()>, 44 | { 45 | fn write_str(&mut self, s: &str) -> std::fmt::Result { 46 | match (self.writer)(s.as_bytes()) { 47 | Ok(()) => Ok(()), 48 | Err(e) => { 49 | self.error = Err(e); 50 | Err(std::fmt::Error) 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /crates/anstream/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! **Auto-adapting [`stdout`] / [`stderr`] streams** 2 | //! 3 | //! *A portmanteau of "ansi stream"* 4 | //! 5 | //! [`AutoStream`] always accepts [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code), 6 | //! [adapting to the user's terminal's capabilities][AutoStream]. 7 | //! 8 | //! Benefits 9 | //! - Allows the caller to not be concerned with the terminal's capabilities 10 | //! - Semver safe way of passing styled text between crates as ANSI escape codes offer more 11 | //! compatibility than most crate APIs. 12 | //! 13 | //! Available styling crates: 14 | //! - [anstyle](https://docs.rs/anstyle) for minimal runtime styling, designed to go in public APIs 15 | //! - [owo-colors](https://docs.rs/owo-colors) for feature-rich runtime styling 16 | //! - [color-print](https://docs.rs/color-print) for feature-rich compile-time styling 17 | //! 18 | //! # Example 19 | //! 20 | //! ``` 21 | //! # #[cfg(feature = "auto")] { 22 | //! use anstream::println; 23 | //! use owo_colors::OwoColorize as _; 24 | //! 25 | //! // Foreground colors 26 | //! println!("My number is {:#x}!", 10.green()); 27 | //! // Background colors 28 | //! println!("My number is not {}!", 4.on_red()); 29 | //! # } 30 | //! ``` 31 | //! 32 | //! And this will correctly handle piping to a file, etc 33 | 34 | #![cfg_attr(docsrs, feature(doc_auto_cfg))] 35 | #![warn(missing_docs)] 36 | #![warn(clippy::print_stderr)] 37 | #![warn(clippy::print_stdout)] 38 | 39 | pub mod adapter; 40 | pub mod stream; 41 | #[doc(hidden)] 42 | #[macro_use] 43 | pub mod _macros; 44 | 45 | mod auto; 46 | mod buffer; 47 | mod fmt; 48 | mod strip; 49 | #[cfg(all(windows, feature = "wincon"))] 50 | mod wincon; 51 | 52 | pub use auto::AutoStream; 53 | pub use strip::StripStream; 54 | #[cfg(all(windows, feature = "wincon"))] 55 | pub use wincon::WinconStream; 56 | 57 | #[allow(deprecated)] 58 | pub use buffer::Buffer; 59 | 60 | /// An adaptive wrapper around the global standard output stream of the current process 61 | pub type Stdout = AutoStream; 62 | /// An adaptive wrapper around the global standard error stream of the current process 63 | pub type Stderr = AutoStream; 64 | 65 | /// Create an ANSI escape code compatible stdout 66 | /// 67 | /// **Note:** Call [`AutoStream::lock`] in loops to avoid the performance hit of acquiring/releasing 68 | /// from the implicit locking in each [`std::io::Write`] call 69 | #[cfg(feature = "auto")] 70 | pub fn stdout() -> Stdout { 71 | let stdout = std::io::stdout(); 72 | AutoStream::auto(stdout) 73 | } 74 | 75 | /// Create an ANSI escape code compatible stderr 76 | /// 77 | /// **Note:** Call [`AutoStream::lock`] in loops to avoid the performance hit of acquiring/releasing 78 | /// from the implicit locking in each [`std::io::Write`] call 79 | #[cfg(feature = "auto")] 80 | pub fn stderr() -> Stderr { 81 | let stderr = std::io::stderr(); 82 | AutoStream::auto(stderr) 83 | } 84 | 85 | /// Selection for overriding color output 86 | pub use colorchoice::ColorChoice; 87 | 88 | #[doc = include_str!("../README.md")] 89 | #[cfg(doctest)] 90 | pub struct ReadmeDoctests; 91 | -------------------------------------------------------------------------------- /crates/anstream/tests/demo.vte: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-cli/anstyle/94847b3ab52097fc950c97503172ad4575492b9a/crates/anstream/tests/demo.vte -------------------------------------------------------------------------------- /crates/anstream/tests/macros.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | #[cfg(feature = "auto")] 3 | fn print() { 4 | anstream::print!( 5 | "{}This should be captured{}", 6 | anstyle::AnsiColor::Red.on_default().render(), 7 | anstyle::Reset.render() 8 | ); 9 | } 10 | 11 | #[test] 12 | #[cfg(feature = "auto")] 13 | fn println() { 14 | anstream::println!( 15 | "{}This should be captured{}", 16 | anstyle::AnsiColor::Red.on_default().render(), 17 | anstyle::Reset.render() 18 | ); 19 | } 20 | 21 | #[test] 22 | #[cfg(feature = "auto")] 23 | fn eprint() { 24 | anstream::eprint!( 25 | "{}This should be captured{}", 26 | anstyle::AnsiColor::Red.on_default().render(), 27 | anstyle::Reset.render() 28 | ); 29 | } 30 | 31 | #[test] 32 | #[cfg(feature = "auto")] 33 | fn eprintln() { 34 | anstream::eprintln!( 35 | "{}This should be captured{}", 36 | anstyle::AnsiColor::Red.on_default().render(), 37 | anstyle::Reset.render() 38 | ); 39 | } 40 | 41 | #[test] 42 | #[cfg(feature = "auto")] 43 | #[should_panic] 44 | fn panic() { 45 | anstream::panic!( 46 | "{}This should be captured{}", 47 | anstyle::AnsiColor::Red.on_default().render(), 48 | anstyle::Reset.render() 49 | ); 50 | } 51 | -------------------------------------------------------------------------------- /crates/anstyle-ansi-term/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/). 6 | 7 | 8 | ## [Unreleased] - ReleaseDate 9 | 10 | ## [1.0.4] - 2025-06-04 11 | 12 | ## [1.0.3] - 2024-10-24 13 | 14 | ### Compatibility 15 | 16 | - Update MSRV to 1.66 17 | 18 | ## [1.0.2] - 2024-07-25 19 | 20 | ## [1.0.1] - 2024-05-02 21 | 22 | ### Fixes 23 | 24 | - Drop MSRV to 1.65 25 | 26 | ## [1.0.0] - 2023-04-13 27 | 28 | ### Breaking Change 29 | 30 | - Updated `anstyle` 31 | 32 | ## [0.2.0] - 2023-03-08 33 | 34 | ### Breaking Change 35 | 36 | - `anstyle` upgraded 37 | 38 | ## [0.1.2] - 2022-10-07 39 | 40 | ## [0.1.1] - 2022-08-17 41 | 42 | 43 | [Unreleased]: https://github.com/rust-cli/anstyle/compare/anstyle-ansi-term-v1.0.4...HEAD 44 | [1.0.4]: https://github.com/rust-cli/anstyle/compare/anstyle-ansi-term-v1.0.3...anstyle-ansi-term-v1.0.4 45 | [1.0.3]: https://github.com/rust-cli/anstyle/compare/anstyle-ansi-term-v1.0.2...anstyle-ansi-term-v1.0.3 46 | [1.0.2]: https://github.com/rust-cli/anstyle/compare/anstyle-ansi-term-v1.0.1...anstyle-ansi-term-v1.0.2 47 | [1.0.1]: https://github.com/rust-cli/anstyle/compare/anstyle-ansi-term-v1.0.0...anstyle-ansi-term-v1.0.1 48 | [1.0.0]: https://github.com/rust-cli/anstyle/compare/anstyle-ansi-term-v0.2.0...anstyle-ansi-term-v1.0.0 49 | [0.2.0]: https://github.com/rust-cli/anstyle/compare/anstyle-ansi-term-v0.1.2...anstyle-ansi-term-v0.2.0 50 | [0.1.2]: https://github.com/rust-cli/anstyle/compare/anstyle-ansi-term-v0.1.1...anstyle-ansi-term-v0.1.2 51 | [0.1.1]: https://github.com/rust-cli/anstyle/compare/b85aa4d265faa9ed632887415cc14125ae37f4db...anstyle-ansi-term-v0.1.1 52 | -------------------------------------------------------------------------------- /crates/anstyle-ansi-term/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "anstyle-ansi-term" 3 | version = "1.0.4" 4 | description = "Adapt between ansi_term and anstyle" 5 | categories = ["command-line-interface"] 6 | keywords = ["ansi", "terminal", "color", "ansi_term"] 7 | repository.workspace = true 8 | license.workspace = true 9 | edition.workspace = true 10 | rust-version.workspace = true 11 | include.workspace = true 12 | 13 | [package.metadata.docs.rs] 14 | all-features = true 15 | rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"] 16 | 17 | [package.metadata.release] 18 | pre-release-replacements = [ 19 | {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, 20 | {file="CHANGELOG.md", search="\\.\\.\\.HEAD", replace="...{{tag_name}}", exactly=1}, 21 | {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, 22 | {file="CHANGELOG.md", search="", replace="\n## [Unreleased] - ReleaseDate\n", exactly=1}, 23 | {file="CHANGELOG.md", search="", replace="\n[Unreleased]: https://github.com/rust-cli/anstyle/compare/{{tag_name}}...HEAD", exactly=1}, 24 | ] 25 | 26 | [dependencies] 27 | anstyle = { version = "1.0.0", path = "../anstyle" } 28 | ansi_term = "0.12.1" 29 | 30 | [lints] 31 | workspace = true 32 | -------------------------------------------------------------------------------- /crates/anstyle-ansi-term/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) Individual contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /crates/anstyle-ansi-term/README.md: -------------------------------------------------------------------------------- 1 | # anstyle-ansi-term 2 | 3 | > Convert between [`ansi_term`](https://lib.rs/ansi_term) and generic styling types 4 | 5 | [![Documentation](https://img.shields.io/badge/docs-master-blue.svg)][Documentation] 6 | ![License](https://img.shields.io/crates/l/anstyle-ansi-term.svg) 7 | [![Crates Status](https://img.shields.io/crates/v/anstyle-ansi-term.svg)](https://crates.io/crates/anstyle-ansi-term) 8 | 9 | ## License 10 | 11 | Licensed under either of 12 | 13 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or ) 14 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or ) 15 | 16 | at your option. 17 | 18 | ## [Contribute](../../CONTRIBUTING.md) 19 | 20 | Unless you explicitly state otherwise, any contribution intentionally 21 | submitted for inclusion in the work by you, as defined in the Apache-2.0 22 | license, shall be dual-licensed as above, without any additional terms or 23 | conditions. 24 | 25 | [Crates.io]: https://crates.io/crates/anstyle-ansi-term 26 | [Documentation]: https://docs.rs/anstyle-ansi-term 27 | -------------------------------------------------------------------------------- /crates/anstyle-ansi-term/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Convert between [`ansi_term`](https://lib.rs/ansi_term) and generic styling types 2 | 3 | #![cfg_attr(docsrs, feature(doc_auto_cfg))] 4 | #![warn(missing_docs)] 5 | #![warn(clippy::print_stderr)] 6 | #![warn(clippy::print_stdout)] 7 | 8 | /// Adapt generic styling to [`ansi_term`] 9 | pub fn to_ansi_term(astyle: anstyle::Style) -> ansi_term::Style { 10 | let mut style = ansi_term::Style::new(); 11 | 12 | if let Some((fg, fg_bold)) = astyle.get_fg_color().map(to_ansi_color) { 13 | style = style.fg(fg); 14 | if fg_bold { 15 | style = style.bold(); 16 | } 17 | } 18 | if let Some((bg, _)) = astyle.get_bg_color().map(to_ansi_color) { 19 | style = style.on(bg); 20 | } 21 | 22 | let effects = astyle.get_effects(); 23 | if effects.contains(anstyle::Effects::BOLD) { 24 | style = style.bold(); 25 | } 26 | if effects.contains(anstyle::Effects::DIMMED) { 27 | style = style.dimmed(); 28 | } 29 | if effects.contains(anstyle::Effects::ITALIC) { 30 | style = style.italic(); 31 | } 32 | if effects.contains(anstyle::Effects::UNDERLINE) { 33 | style = style.underline(); 34 | } 35 | if effects.contains(anstyle::Effects::BLINK) { 36 | style = style.blink(); 37 | } 38 | if effects.contains(anstyle::Effects::INVERT) { 39 | style = style.reverse(); 40 | } 41 | if effects.contains(anstyle::Effects::HIDDEN) { 42 | style = style.hidden(); 43 | } 44 | if effects.contains(anstyle::Effects::STRIKETHROUGH) { 45 | style = style.strikethrough(); 46 | } 47 | style 48 | } 49 | 50 | fn to_ansi_color(color: anstyle::Color) -> (ansi_term::Color, bool) { 51 | match color { 52 | anstyle::Color::Ansi(ansi) => ansi_to_ansi_color(ansi), 53 | anstyle::Color::Ansi256(xterm) => (xterm_to_ansi_color(xterm), false), 54 | anstyle::Color::Rgb(rgb) => (rgb_to_ansi_color(rgb), false), 55 | } 56 | } 57 | 58 | fn ansi_to_ansi_color(color: anstyle::AnsiColor) -> (ansi_term::Color, bool) { 59 | match color { 60 | anstyle::AnsiColor::Black => (ansi_term::Color::Black, false), 61 | anstyle::AnsiColor::Red => (ansi_term::Color::Red, false), 62 | anstyle::AnsiColor::Green => (ansi_term::Color::Green, false), 63 | anstyle::AnsiColor::Yellow => (ansi_term::Color::Yellow, false), 64 | anstyle::AnsiColor::Blue => (ansi_term::Color::Blue, false), 65 | anstyle::AnsiColor::Magenta => (ansi_term::Color::Purple, false), 66 | anstyle::AnsiColor::Cyan => (ansi_term::Color::Cyan, false), 67 | anstyle::AnsiColor::White => (ansi_term::Color::White, false), 68 | anstyle::AnsiColor::BrightBlack => (ansi_term::Color::Black, true), 69 | anstyle::AnsiColor::BrightRed => (ansi_term::Color::Red, true), 70 | anstyle::AnsiColor::BrightGreen => (ansi_term::Color::Green, true), 71 | anstyle::AnsiColor::BrightYellow => (ansi_term::Color::Yellow, true), 72 | anstyle::AnsiColor::BrightBlue => (ansi_term::Color::Black, true), 73 | anstyle::AnsiColor::BrightMagenta => (ansi_term::Color::Purple, true), 74 | anstyle::AnsiColor::BrightCyan => (ansi_term::Color::Cyan, true), 75 | anstyle::AnsiColor::BrightWhite => (ansi_term::Color::White, true), 76 | } 77 | } 78 | 79 | fn xterm_to_ansi_color(color: anstyle::Ansi256Color) -> ansi_term::Color { 80 | ansi_term::Color::Fixed(color.0) 81 | } 82 | 83 | fn rgb_to_ansi_color(color: anstyle::RgbColor) -> ansi_term::Color { 84 | ansi_term::Color::RGB(color.0, color.1, color.2) 85 | } 86 | 87 | #[doc = include_str!("../README.md")] 88 | #[cfg(doctest)] 89 | pub struct ReadmeDoctests; 90 | -------------------------------------------------------------------------------- /crates/anstyle-crossterm/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/). 6 | 7 | 8 | ## [Unreleased] - ReleaseDate 9 | 10 | ## [4.0.1] - 2025-06-04 11 | 12 | ## [4.0.0] - 2025-05-01 13 | 14 | ### Breaking Change 15 | 16 | - Update crossterm to 0.29 17 | 18 | ## [3.0.1] - 2024-10-24 19 | 20 | ### Compatibility 21 | 22 | - Update MSRV to 1.66 23 | 24 | ## [3.0.0] - 2024-08-01 25 | 26 | ### Breaking Change 27 | 28 | - Update crossterm to 0.28 29 | 30 | ## [2.0.2] - 2024-07-25 31 | 32 | ## [2.0.1] - 2024-05-02 33 | 34 | ### Fixes 35 | 36 | - Drop MSRV to 1.65 37 | 38 | ## [2.0.0] - 2023-09-28 39 | 40 | ### Breaking Change 41 | 42 | - Update crossterm to 0.27 43 | 44 | ### Compatibility 45 | 46 | - Update MSRV to 1.70.0 47 | 48 | ## [1.0.0] - 2023-04-13 49 | 50 | ### Breaking Change 51 | 52 | - Updated `anstyle` 53 | 54 | ## [0.2.0] - 2023-03-08 55 | 56 | ### Breaking Change 57 | 58 | - `anstyle` upgraded 59 | 60 | ## [0.1.1] - 2022-10-07 61 | 62 | 63 | [Unreleased]: https://github.com/rust-cli/anstyle/compare/anstyle-crossterm-v4.0.1...HEAD 64 | [4.0.1]: https://github.com/rust-cli/anstyle/compare/anstyle-crossterm-v4.0.0...anstyle-crossterm-v4.0.1 65 | [4.0.0]: https://github.com/rust-cli/anstyle/compare/anstyle-crossterm-v3.0.1...anstyle-crossterm-v4.0.0 66 | [3.0.1]: https://github.com/rust-cli/anstyle/compare/anstyle-crossterm-v3.0.0...anstyle-crossterm-v3.0.1 67 | [3.0.0]: https://github.com/rust-cli/anstyle/compare/anstyle-crossterm-v2.0.2...anstyle-crossterm-v3.0.0 68 | [2.0.2]: https://github.com/rust-cli/anstyle/compare/anstyle-crossterm-v2.0.1...anstyle-crossterm-v2.0.2 69 | [2.0.1]: https://github.com/rust-cli/anstyle/compare/anstyle-crossterm-v2.0.0...anstyle-crossterm-v2.0.1 70 | [2.0.0]: https://github.com/rust-cli/anstyle/compare/anstyle-crossterm-v1.0.0...anstyle-crossterm-v2.0.0 71 | [1.0.0]: https://github.com/rust-cli/anstyle/compare/anstyle-crossterm-v0.2.0...anstyle-crossterm-v1.0.0 72 | [0.2.0]: https://github.com/rust-cli/anstyle/compare/anstyle-crossterm-v0.1.1...anstyle-crossterm-v0.2.0 73 | [0.1.1]: https://github.com/rust-cli/anstyle/compare/08f1895103116a5c4bd25e3514463467f997fd71...anstyle-crossterm-v0.1.1 74 | -------------------------------------------------------------------------------- /crates/anstyle-crossterm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "anstyle-crossterm" 3 | version = "4.0.1" 4 | description = "Adapt between crossterm and anstyle" 5 | categories = ["command-line-interface"] 6 | keywords = ["ansi", "terminal", "color", "crossterm"] 7 | repository.workspace = true 8 | license.workspace = true 9 | edition.workspace = true 10 | rust-version.workspace = true 11 | include.workspace = true 12 | 13 | [package.metadata.docs.rs] 14 | all-features = true 15 | rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"] 16 | 17 | [package.metadata.release] 18 | pre-release-replacements = [ 19 | {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, 20 | {file="CHANGELOG.md", search="\\.\\.\\.HEAD", replace="...{{tag_name}}", exactly=1}, 21 | {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, 22 | {file="CHANGELOG.md", search="", replace="\n## [Unreleased] - ReleaseDate\n", exactly=1}, 23 | {file="CHANGELOG.md", search="", replace="\n[Unreleased]: https://github.com/rust-cli/anstyle/compare/{{tag_name}}...HEAD", exactly=1}, 24 | ] 25 | 26 | [dependencies] 27 | anstyle = { version = "1.0.0", path = "../anstyle" } 28 | crossterm = { version = "0.29.0", default-features = false, features = ["windows"] } 29 | 30 | [lints] 31 | workspace = true 32 | -------------------------------------------------------------------------------- /crates/anstyle-crossterm/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) Individual contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /crates/anstyle-crossterm/README.md: -------------------------------------------------------------------------------- 1 | # anstyle-crossterm 2 | 3 | > Convert between [`crossterm`](https://lib.rs/crossterm) and generic styling types 4 | 5 | [![Documentation](https://img.shields.io/badge/docs-master-blue.svg)][Documentation] 6 | ![License](https://img.shields.io/crates/l/anstyle-crossterm.svg) 7 | [![Crates Status](https://img.shields.io/crates/v/anstyle-crossterm.svg)](https://crates.io/crates/anstyle-crossterm) 8 | 9 | ## License 10 | 11 | Licensed under either of 12 | 13 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or ) 14 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or ) 15 | 16 | at your option. 17 | 18 | ## [Contribute](../../CONTRIBUTING.md) 19 | 20 | Unless you explicitly state otherwise, any contribution intentionally 21 | submitted for inclusion in the work by you, as defined in the Apache-2.0 22 | license, shall be dual-licensed as above, without any additional terms or 23 | conditions. 24 | 25 | [Crates.io]: https://crates.io/crates/anstyle-crossterm 26 | [Documentation]: https://docs.rs/anstyle-crossterm 27 | -------------------------------------------------------------------------------- /crates/anstyle-crossterm/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Convert between [`crossterm`](https://lib.rs/crossterm) and [generic styling types][anstyle] 2 | 3 | #![cfg_attr(docsrs, feature(doc_auto_cfg))] 4 | #![warn(missing_docs)] 5 | #![warn(clippy::print_stderr)] 6 | #![warn(clippy::print_stdout)] 7 | 8 | /// Adapt generic styling to [`crossterm`] 9 | pub fn to_crossterm(astyle: anstyle::Style) -> crossterm::style::ContentStyle { 10 | let foreground_color = astyle.get_fg_color().map(to_ansi_color); 11 | let background_color = astyle.get_bg_color().map(to_ansi_color); 12 | let underline_color = astyle.get_underline_color().map(to_ansi_color); 13 | 14 | let mut attributes = crossterm::style::Attributes::default(); 15 | 16 | let effects = astyle.get_effects(); 17 | if effects.contains(anstyle::Effects::BOLD) { 18 | attributes.set(crossterm::style::Attribute::Bold); 19 | } 20 | if effects.contains(anstyle::Effects::DIMMED) { 21 | attributes.set(crossterm::style::Attribute::Dim); 22 | } 23 | if effects.contains(anstyle::Effects::ITALIC) { 24 | attributes.set(crossterm::style::Attribute::Italic); 25 | } 26 | if effects.contains(anstyle::Effects::UNDERLINE) { 27 | attributes.set(crossterm::style::Attribute::Underlined); 28 | } 29 | if effects.contains(anstyle::Effects::BLINK) { 30 | attributes.set(crossterm::style::Attribute::SlowBlink); 31 | } 32 | if effects.contains(anstyle::Effects::INVERT) { 33 | attributes.set(crossterm::style::Attribute::Reverse); 34 | } 35 | if effects.contains(anstyle::Effects::HIDDEN) { 36 | attributes.set(crossterm::style::Attribute::Hidden); 37 | } 38 | if effects.contains(anstyle::Effects::STRIKETHROUGH) { 39 | attributes.set(crossterm::style::Attribute::OverLined); 40 | } 41 | 42 | crossterm::style::ContentStyle { 43 | foreground_color, 44 | background_color, 45 | underline_color, 46 | attributes, 47 | } 48 | } 49 | 50 | fn to_ansi_color(color: anstyle::Color) -> crossterm::style::Color { 51 | match color { 52 | anstyle::Color::Ansi(ansi) => ansi_to_ansi_color(ansi), 53 | anstyle::Color::Ansi256(xterm) => xterm_to_ansi_color(xterm), 54 | anstyle::Color::Rgb(rgb) => rgb_to_ansi_color(rgb), 55 | } 56 | } 57 | 58 | fn ansi_to_ansi_color(color: anstyle::AnsiColor) -> crossterm::style::Color { 59 | match color { 60 | anstyle::AnsiColor::Black => crossterm::style::Color::Black, 61 | anstyle::AnsiColor::Red => crossterm::style::Color::DarkRed, 62 | anstyle::AnsiColor::Green => crossterm::style::Color::DarkGreen, 63 | anstyle::AnsiColor::Yellow => crossterm::style::Color::DarkYellow, 64 | anstyle::AnsiColor::Blue => crossterm::style::Color::DarkBlue, 65 | anstyle::AnsiColor::Magenta => crossterm::style::Color::DarkMagenta, 66 | anstyle::AnsiColor::Cyan => crossterm::style::Color::DarkCyan, 67 | anstyle::AnsiColor::White => crossterm::style::Color::Grey, 68 | anstyle::AnsiColor::BrightBlack => crossterm::style::Color::DarkGrey, 69 | anstyle::AnsiColor::BrightRed => crossterm::style::Color::Red, 70 | anstyle::AnsiColor::BrightGreen => crossterm::style::Color::Green, 71 | anstyle::AnsiColor::BrightYellow => crossterm::style::Color::Yellow, 72 | anstyle::AnsiColor::BrightBlue => crossterm::style::Color::Black, 73 | anstyle::AnsiColor::BrightMagenta => crossterm::style::Color::Magenta, 74 | anstyle::AnsiColor::BrightCyan => crossterm::style::Color::Cyan, 75 | anstyle::AnsiColor::BrightWhite => crossterm::style::Color::White, 76 | } 77 | } 78 | 79 | fn xterm_to_ansi_color(color: anstyle::Ansi256Color) -> crossterm::style::Color { 80 | crossterm::style::Color::AnsiValue(color.0) 81 | } 82 | 83 | fn rgb_to_ansi_color(color: anstyle::RgbColor) -> crossterm::style::Color { 84 | crossterm::style::Color::Rgb { 85 | r: color.0, 86 | g: color.1, 87 | b: color.2, 88 | } 89 | } 90 | 91 | #[doc = include_str!("../README.md")] 92 | #[cfg(doctest)] 93 | pub struct ReadmeDoctests; 94 | -------------------------------------------------------------------------------- /crates/anstyle-git/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/). 6 | 7 | 8 | ## [Unreleased] - ReleaseDate 9 | 10 | ## [1.1.3] - 2025-06-04 11 | 12 | ## [1.1.2] - 2024-10-24 13 | 14 | ### Compatibility 15 | 16 | - Update MSRV to 1.66 17 | 18 | ## [1.1.1] - 2024-07-25 19 | 20 | ## [1.1.0] - 2024-07-14 21 | 22 | ### Features 23 | 24 | - Add support for `#RGB` 12-bit colors 25 | 26 | ## [1.0.1] - 2024-05-02 27 | 28 | ### Fixes 29 | 30 | - Drop MSRV to 1.65 31 | 32 | ### Compatibility 33 | 34 | - Update MSRV to 1.70.0 35 | 36 | ## [1.0.0] - 2023-04-13 37 | 38 | ### Breaking Change 39 | 40 | - Updated `anstyle` 41 | 42 | ## [0.3.0] - 2023-03-08 43 | 44 | ### Breaking Change 45 | 46 | - `anstyle` upgraded 47 | 48 | ## [0.2.3] - 2022-10-07 49 | 50 | ## [0.2.2] - 2022-08-17 51 | 52 | ## [0.2.1] - 2022-05-19 53 | 54 | 55 | [Unreleased]: https://github.com/rust-cli/anstyle/compare/anstyle-git-v1.1.3...HEAD 56 | [1.1.3]: https://github.com/rust-cli/anstyle/compare/anstyle-git-v1.1.2...anstyle-git-v1.1.3 57 | [1.1.2]: https://github.com/rust-cli/anstyle/compare/anstyle-git-v1.1.1...anstyle-git-v1.1.2 58 | [1.1.1]: https://github.com/rust-cli/anstyle/compare/anstyle-git-v1.1.0...anstyle-git-v1.1.1 59 | [1.1.0]: https://github.com/rust-cli/anstyle/compare/anstyle-git-v1.0.1...anstyle-git-v1.1.0 60 | [1.0.1]: https://github.com/rust-cli/anstyle/compare/anstyle-git-v1.0.0...anstyle-git-v1.0.1 61 | [1.0.0]: https://github.com/rust-cli/anstyle/compare/anstyle-git-v0.3.0...anstyle-git-v1.0.0 62 | [0.3.0]: https://github.com/rust-cli/anstyle/compare/anstyle-git-v0.2.3...anstyle-git-v0.3.0 63 | [0.2.3]: https://github.com/rust-cli/anstyle/compare/anstyle-git-v0.2.2...anstyle-git-v0.2.3 64 | [0.2.2]: https://github.com/rust-cli/anstyle/compare/anstyle-git-v0.2.1...anstyle-git-v0.2.2 65 | [0.2.1]: https://github.com/rust-cli/anstyle/compare/28b441e...anstyle-git-v0.2.1 66 | -------------------------------------------------------------------------------- /crates/anstyle-git/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "anstyle-git" 3 | version = "1.1.3" 4 | description = "Parse Git Style Descriptions" 5 | categories = ["command-line-interface"] 6 | keywords = ["ansi", "terminal", "color", "git", "colorparse"] 7 | repository.workspace = true 8 | license.workspace = true 9 | edition.workspace = true 10 | rust-version.workspace = true 11 | include.workspace = true 12 | 13 | [package.metadata.docs.rs] 14 | all-features = true 15 | rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"] 16 | 17 | [package.metadata.release] 18 | pre-release-replacements = [ 19 | {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, 20 | {file="CHANGELOG.md", search="\\.\\.\\.HEAD", replace="...{{tag_name}}", exactly=1}, 21 | {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, 22 | {file="CHANGELOG.md", search="", replace="\n## [Unreleased] - ReleaseDate\n", exactly=1}, 23 | {file="CHANGELOG.md", search="", replace="\n[Unreleased]: https://github.com/rust-cli/anstyle/compare/{{tag_name}}...HEAD", exactly=1}, 24 | ] 25 | 26 | [dependencies] 27 | anstyle = { version = "1.0.0", path = "../anstyle" } 28 | 29 | [lints] 30 | workspace = true 31 | -------------------------------------------------------------------------------- /crates/anstyle-git/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) Individual contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /crates/anstyle-git/README.md: -------------------------------------------------------------------------------- 1 | # anstyle-git 2 | 3 | > Parse [Git Style Descriptions](https://git-scm.com/book/en/v2/Customizing-Git-Git-Configuration) 4 | 5 | [![Documentation](https://img.shields.io/badge/docs-master-blue.svg)][Documentation] 6 | ![License](https://img.shields.io/crates/l/anstyle-git.svg) 7 | [![Crates Status](https://img.shields.io/crates/v/anstyle-git.svg)](https://crates.io/crates/anstyle-git) 8 | 9 | ## License 10 | 11 | Licensed under either of 12 | 13 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or ) 14 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or ) 15 | 16 | at your option. 17 | 18 | ## [Contribute](../../CONTRIBUTING.md) 19 | 20 | Unless you explicitly state otherwise, any contribution intentionally 21 | submitted for inclusion in the work by you, as defined in the Apache-2.0 22 | license, shall be dual-licensed as above, without any additional terms or 23 | conditions. 24 | 25 | ### Special Thanks 26 | 27 | [joshtriplett](https://github.com/joshtriplett) for [colorparse](https://github.com/joshtriplett/colorparse) which this was forked from 28 | 29 | [Crates.io]: https://crates.io/crates/anstyle-git 30 | [Documentation]: https://docs.rs/anstyle-git 31 | -------------------------------------------------------------------------------- /crates/anstyle-lossy/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/). 6 | 7 | 8 | ## [Unreleased] - ReleaseDate 9 | 10 | ## [1.1.4] - 2025-06-04 11 | 12 | ## [1.1.3] - 2024-10-24 13 | 14 | ### Compatibility 15 | 16 | - Update MSRV to 1.66 17 | 18 | ## [1.1.2] - 2024-07-25 19 | 20 | ## [1.1.1] - 2024-05-02 21 | 22 | ### Fixes 23 | 24 | - Drop MSRV to 1.65 25 | 26 | ## [1.1.0] - 2024-02-18 27 | 28 | ### Compatibility 29 | 30 | - Update MSRV to 1.70.0 31 | 32 | ### Feaures 33 | 34 | - Expose palette API 35 | 36 | ### Fixes 37 | 38 | - Fixed panic 39 | 40 | ## [1.0.0] - 2023-04-13 41 | 42 | ### Breaking Change 43 | 44 | - Updated `anstyle` 45 | 46 | ## [0.2.0] - 2023-03-08 47 | 48 | ### Breaking Change 49 | 50 | - `anstyle` upgraded 51 | 52 | ## [0.1.1] - 2022-10-07 53 | 54 | 55 | [Unreleased]: https://github.com/rust-cli/anstyle/compare/anstyle-lossy-v1.1.4...HEAD 56 | [1.1.4]: https://github.com/rust-cli/anstyle/compare/anstyle-lossy-v1.1.3...anstyle-lossy-v1.1.4 57 | [1.1.3]: https://github.com/rust-cli/anstyle/compare/anstyle-lossy-v1.1.2...anstyle-lossy-v1.1.3 58 | [1.1.2]: https://github.com/rust-cli/anstyle/compare/anstyle-lossy-v1.1.1...anstyle-lossy-v1.1.2 59 | [1.1.1]: https://github.com/rust-cli/anstyle/compare/anstyle-lossy-v1.1.0...anstyle-lossy-v1.1.1 60 | [1.1.0]: https://github.com/rust-cli/anstyle/compare/anstyle-lossy-v1.0.0...anstyle-lossy-v1.1.0 61 | [1.0.0]: https://github.com/rust-cli/anstyle/compare/anstyle-lossy-v0.2.0...anstyle-lossy-v1.0.0 62 | [0.2.0]: https://github.com/rust-cli/anstyle/compare/anstyle-lossy-v0.1.1...anstyle-lossy-v0.2.0 63 | [0.1.1]: https://github.com/rust-cli/anstyle/compare/b31a85851f2104407bbbced9e24e0ca71021e3f8...anstyle-lossy-v0.1.1 64 | -------------------------------------------------------------------------------- /crates/anstyle-lossy/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "anstyle-lossy" 3 | version = "1.1.4" 4 | description = "Lossy conversion between ANSI Color Codes" 5 | categories = ["command-line-interface"] 6 | keywords = ["ansi", "terminal", "color"] 7 | repository.workspace = true 8 | license.workspace = true 9 | edition.workspace = true 10 | rust-version.workspace = true 11 | include.workspace = true 12 | 13 | [package.metadata.docs.rs] 14 | all-features = true 15 | rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"] 16 | 17 | [package.metadata.release] 18 | pre-release-replacements = [ 19 | {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, 20 | {file="CHANGELOG.md", search="\\.\\.\\.HEAD", replace="...{{tag_name}}", exactly=1}, 21 | {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, 22 | {file="CHANGELOG.md", search="", replace="\n## [Unreleased] - ReleaseDate\n", exactly=1}, 23 | {file="CHANGELOG.md", search="", replace="\n[Unreleased]: https://github.com/rust-cli/anstyle/compare/{{tag_name}}...HEAD", exactly=1}, 24 | ] 25 | 26 | [dependencies] 27 | anstyle = { version = "1.0.0", path = "../anstyle" } 28 | 29 | [lints] 30 | workspace = true 31 | -------------------------------------------------------------------------------- /crates/anstyle-lossy/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) Individual contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /crates/anstyle-lossy/README.md: -------------------------------------------------------------------------------- 1 | # anstyle-lossy 2 | 3 | > Lossy conversion between ANSI Color Codes 4 | 5 | [![Documentation](https://img.shields.io/badge/docs-master-blue.svg)][Documentation] 6 | ![License](https://img.shields.io/crates/l/anstyle-lossy.svg) 7 | [![Crates Status](https://img.shields.io/crates/v/anstyle-lossy.svg)](https://crates.io/crates/anstyle-lossy) 8 | 9 | ## License 10 | 11 | Licensed under either of 12 | 13 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or ) 14 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or ) 15 | 16 | at your option. 17 | 18 | ## [Contribute](../../CONTRIBUTING.md) 19 | 20 | Unless you explicitly state otherwise, any contribution intentionally 21 | submitted for inclusion in the work by you, as defined in the Apache-2.0 22 | license, shall be dual-licensed as above, without any additional terms or 23 | conditions. 24 | 25 | [Crates.io]: https://crates.io/crates/anstyle-lossy 26 | [Documentation]: https://docs.rs/anstyle-lossy 27 | -------------------------------------------------------------------------------- /crates/anstyle-lossy/src/palette.rs: -------------------------------------------------------------------------------- 1 | //! Popular color palettes for [`anstyle::AnsiColor`] 2 | //! 3 | //! Based on [wikipedia](https://en.wikipedia.org/wiki/ANSI_escape_code#3-bit_and_4-bit) 4 | use anstyle::RgbColor as Rgb; 5 | 6 | /// A color palette for rendering 4-bit [`anstyle::AnsiColor`] 7 | #[allow(clippy::exhaustive_structs)] 8 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] 9 | pub struct Palette(pub RawPalette); 10 | type RawPalette = [Rgb; 16]; 11 | 12 | impl Palette { 13 | /// Look up the [`anstyle::RgbColor`] in the palette 14 | pub const fn get(&self, color: anstyle::AnsiColor) -> Rgb { 15 | let color = anstyle::Ansi256Color::from_ansi(color); 16 | *self.get_ansi256_ref(color) 17 | } 18 | const fn get_ansi256_ref(&self, color: anstyle::Ansi256Color) -> &Rgb { 19 | let index = color.index() as usize; 20 | &self.0[index] 21 | } 22 | 23 | pub(crate) const fn rgb_from_ansi(&self, color: anstyle::AnsiColor) -> anstyle::RgbColor { 24 | self.get(color) 25 | } 26 | 27 | pub(crate) const fn rgb_from_index(&self, index: u8) -> Option { 28 | let index = index as usize; 29 | if index < self.0.len() { 30 | Some(self.0[index]) 31 | } else { 32 | None 33 | } 34 | } 35 | 36 | pub(crate) const fn find_match(&self, color: anstyle::RgbColor) -> anstyle::AnsiColor { 37 | let mut best_index = 0; 38 | let mut best_distance = crate::distance(color, self.0[best_index]); 39 | 40 | let mut index = best_index + 1; 41 | while index < self.0.len() { 42 | let distance = crate::distance(color, self.0[index]); 43 | if distance < best_distance { 44 | best_index = index; 45 | best_distance = distance; 46 | } 47 | 48 | index += 1; 49 | } 50 | 51 | if let Some(color) = anstyle::Ansi256Color(best_index as u8).into_ansi() { 52 | color 53 | } else { 54 | // Panic 55 | #[allow(clippy::no_effect)] 56 | ["best_index is out of bounds"][best_index]; 57 | // Make compiler happy 58 | anstyle::AnsiColor::Black 59 | } 60 | } 61 | } 62 | 63 | impl Default for Palette { 64 | fn default() -> Self { 65 | DEFAULT 66 | } 67 | } 68 | 69 | impl std::ops::Index for Palette { 70 | type Output = Rgb; 71 | 72 | #[inline] 73 | fn index(&self, color: anstyle::AnsiColor) -> &Rgb { 74 | let color = anstyle::Ansi256Color::from_ansi(color); 75 | self.get_ansi256_ref(color) 76 | } 77 | } 78 | 79 | impl From for Palette { 80 | fn from(raw: RawPalette) -> Self { 81 | Self(raw) 82 | } 83 | } 84 | 85 | /// Platform-specific default 86 | #[cfg(not(windows))] 87 | pub use VGA as DEFAULT; 88 | 89 | /// Platform-specific default 90 | #[cfg(windows)] 91 | pub use WIN10_CONSOLE as DEFAULT; 92 | 93 | /// Typical colors that are used when booting PCs and leaving them in text mode 94 | pub const VGA: Palette = Palette([ 95 | Rgb(0, 0, 0), 96 | Rgb(170, 0, 0), 97 | Rgb(0, 170, 0), 98 | Rgb(170, 85, 0), 99 | Rgb(0, 0, 170), 100 | Rgb(170, 0, 170), 101 | Rgb(0, 170, 170), 102 | Rgb(170, 170, 170), 103 | Rgb(85, 85, 85), 104 | Rgb(255, 85, 85), 105 | Rgb(85, 255, 85), 106 | Rgb(255, 255, 85), 107 | Rgb(85, 85, 255), 108 | Rgb(255, 85, 255), 109 | Rgb(85, 255, 255), 110 | Rgb(255, 255, 255), 111 | ]); 112 | 113 | /// Campbell theme, used as of Windows 10 version 1709. 114 | pub const WIN10_CONSOLE: Palette = Palette([ 115 | Rgb(12, 12, 12), 116 | Rgb(197, 15, 31), 117 | Rgb(19, 161, 14), 118 | Rgb(193, 156, 0), 119 | Rgb(0, 55, 218), 120 | Rgb(136, 23, 152), 121 | Rgb(58, 150, 221), 122 | Rgb(204, 204, 204), 123 | Rgb(118, 118, 118), 124 | Rgb(231, 72, 86), 125 | Rgb(22, 198, 12), 126 | Rgb(249, 241, 165), 127 | Rgb(59, 120, 255), 128 | Rgb(180, 0, 158), 129 | Rgb(97, 214, 214), 130 | Rgb(242, 242, 242), 131 | ]); 132 | -------------------------------------------------------------------------------- /crates/anstyle-ls/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/). 6 | 7 | 8 | ## [Unreleased] - ReleaseDate 9 | 10 | ## [1.0.5] - 2025-06-04 11 | 12 | ## [1.0.4] - 2024-10-24 13 | 14 | ### Compatibility 15 | 16 | - Update MSRV to 1.66 17 | 18 | ## [1.0.3] - 2024-07-25 19 | 20 | ## [1.0.2] - 2024-05-02 21 | 22 | ### Fixes 23 | 24 | - Drop MSRV to 1.65 25 | 26 | ## [1.0.1] - 2023-06-20 27 | 28 | ## [1.0.0] - 2023-04-13 29 | 30 | ### Breaking Change 31 | 32 | - Updated `anstyle` 33 | 34 | ## [0.2.0] - 2023-03-08 35 | 36 | ### Breaking Change 37 | 38 | - `anstyle` upgraded 39 | 40 | ## [0.1.1] - 2022-10-07 41 | 42 | 43 | [Unreleased]: https://github.com/rust-cli/anstyle/compare/anstyle-ls-v1.0.5...HEAD 44 | [1.0.5]: https://github.com/rust-cli/anstyle/compare/anstyle-ls-v1.0.4...anstyle-ls-v1.0.5 45 | [1.0.4]: https://github.com/rust-cli/anstyle/compare/anstyle-ls-v1.0.3...anstyle-ls-v1.0.4 46 | [1.0.3]: https://github.com/rust-cli/anstyle/compare/anstyle-ls-v1.0.2...anstyle-ls-v1.0.3 47 | [1.0.2]: https://github.com/rust-cli/anstyle/compare/anstyle-ls-v1.0.1...anstyle-ls-v1.0.2 48 | [1.0.1]: https://github.com/rust-cli/anstyle/compare/anstyle-ls-v1.0.0...anstyle-ls-v1.0.1 49 | [1.0.0]: https://github.com/rust-cli/anstyle/compare/anstyle-ls-v0.2.0...anstyle-ls-v1.0.0 50 | [0.2.0]: https://github.com/rust-cli/anstyle/compare/anstyle-ls-v0.1.1...anstyle-ls-v0.2.0 51 | [0.1.1]: https://github.com/rust-cli/anstyle/compare/8a8922b4adb784d07bf04680bcb3cc7afdc030e5...anstyle-ls-v0.1.1 52 | -------------------------------------------------------------------------------- /crates/anstyle-ls/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "anstyle-ls" 3 | version = "1.0.5" 4 | description = "Parse LS_COLORS Style Descriptions" 5 | categories = ["command-line-interface"] 6 | keywords = ["ansi", "terminal", "color", "ls", "colorparse"] 7 | repository.workspace = true 8 | license.workspace = true 9 | edition.workspace = true 10 | rust-version.workspace = true 11 | include.workspace = true 12 | 13 | [package.metadata.docs.rs] 14 | all-features = true 15 | rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"] 16 | 17 | [package.metadata.release] 18 | pre-release-replacements = [ 19 | {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, 20 | {file="CHANGELOG.md", search="\\.\\.\\.HEAD", replace="...{{tag_name}}", exactly=1}, 21 | {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, 22 | {file="CHANGELOG.md", search="", replace="\n## [Unreleased] - ReleaseDate\n", exactly=1}, 23 | {file="CHANGELOG.md", search="", replace="\n[Unreleased]: https://github.com/rust-cli/anstyle/compare/{{tag_name}}...HEAD", exactly=1}, 24 | ] 25 | 26 | [dependencies] 27 | anstyle = { version = "1.0.0", path = "../anstyle" } 28 | 29 | [lints] 30 | workspace = true 31 | -------------------------------------------------------------------------------- /crates/anstyle-ls/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) Individual contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /crates/anstyle-ls/README.md: -------------------------------------------------------------------------------- 1 | # anstyle-ls 2 | 3 | > Parse LS_COLORS Style Descriptions 4 | 5 | [![Documentation](https://img.shields.io/badge/docs-master-blue.svg)][Documentation] 6 | ![License](https://img.shields.io/crates/l/anstyle-ls.svg) 7 | [![Crates Status](https://img.shields.io/crates/v/anstyle-ls.svg)](https://crates.io/crates/anstyle-ls) 8 | 9 | ## References 10 | 11 | Information about the `LS_COLORS` environment variable is sparse. Here is a short list of useful references: 12 | 13 | - [`LS_COLORS` implementation in the GNU coreutils version of `ls`](https://github.com/coreutils/coreutils/blob/17983b2cb3bccbb4fa69691178caddd99269bda9/src/ls.c#L2507-L2647) (the reference implementation) 14 | - [`LS_COLORS` implementation in `bfs`](https://github.com/tavianator/bfs/blob/2d3b03183c9f1cdb685977f349bf4bbc74a2038d/color.c#L308) by [**@tavianator**](https://github.com/tavianator) 15 | - [The `DIR_COLORS(5)` man page](https://linux.die.net/man/5/dir_colors) 16 | 17 | ## License 18 | 19 | Licensed under either of 20 | 21 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or ) 22 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or ) 23 | 24 | at your option. 25 | 26 | ## [Contribute](../../CONTRIBUTING.md) 27 | 28 | Unless you explicitly state otherwise, any contribution intentionally 29 | submitted for inclusion in the work by you, as defined in the Apache-2.0 30 | license, shall be dual-licensed as above, without any additional terms or 31 | conditions. 32 | 33 | ### Special Thanks 34 | 35 | sharkdp for [ls_colors](https://github.com/sharkdp/lscolors) 36 | 37 | [Crates.io]: https://crates.io/crates/anstyle-ls 38 | [Documentation]: https://docs.rs/anstyle-ls 39 | -------------------------------------------------------------------------------- /crates/anstyle-owo-colors/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/). 6 | 7 | 8 | ## [Unreleased] - ReleaseDate 9 | 10 | ## [2.0.4] - 2025-06-04 11 | 12 | ## [2.0.3] - 2024-10-24 13 | 14 | ### Compatibility 15 | 16 | - Update MSRV to 1.66 17 | 18 | ## [2.0.2] - 2024-07-25 19 | 20 | ## [2.0.1] - 2024-05-02 21 | 22 | ### Fixes 23 | 24 | - Drop MSRV to 1.65 25 | 26 | ## [2.0.0] - 2024-02-18 27 | 28 | ### Compatibility 29 | 30 | - Update to owo-colors v4 31 | - Update MSRV to 1.70.0 32 | 33 | ## [1.0.1] - 2023-07-06 34 | 35 | ### Fixes 36 | 37 | - Correctly convert colors 38 | 39 | ## [1.0.0] - 2023-04-13 40 | 41 | ### Breaking Change 42 | 43 | - Updated `anstyle` 44 | 45 | ## [0.3.0] - 2023-03-08 46 | 47 | ### Breaking Change 48 | 49 | - `anstyle` upgraded 50 | 51 | ## [0.2.2] - 2022-10-07 52 | 53 | ## [0.2.1] - 2022-08-17 54 | 55 | ## [0.2.0] - 2022-05-19 56 | 57 | ## [0.1.1] - 2022-05-18 58 | 59 | 60 | [Unreleased]: https://github.com/rust-cli/anstyle/compare/anstyle-owo-colors-v2.0.4...HEAD 61 | [2.0.4]: https://github.com/rust-cli/anstyle/compare/anstyle-owo-colors-v2.0.3...anstyle-owo-colors-v2.0.4 62 | [2.0.3]: https://github.com/rust-cli/anstyle/compare/anstyle-owo-colors-v2.0.2...anstyle-owo-colors-v2.0.3 63 | [2.0.2]: https://github.com/rust-cli/anstyle/compare/anstyle-owo-colors-v2.0.1...anstyle-owo-colors-v2.0.2 64 | [2.0.1]: https://github.com/rust-cli/anstyle/compare/anstyle-owo-colors-v2.0.0...anstyle-owo-colors-v2.0.1 65 | [2.0.0]: https://github.com/rust-cli/anstyle/compare/anstyle-owo-colors-v1.0.1...anstyle-owo-colors-v2.0.0 66 | [1.0.1]: https://github.com/rust-cli/anstyle/compare/anstyle-owo-colors-v1.0.0...anstyle-owo-colors-v1.0.1 67 | [1.0.0]: https://github.com/rust-cli/anstyle/compare/anstyle-owo-colors-v0.3.0...anstyle-owo-colors-v1.0.0 68 | [0.3.0]: https://github.com/rust-cli/anstyle/compare/anstyle-owo-colors-v0.2.2...anstyle-owo-colors-v0.3.0 69 | [0.2.2]: https://github.com/rust-cli/anstyle/compare/anstyle-owo-colors-v0.2.1...anstyle-owo-colors-v0.2.2 70 | [0.2.1]: https://github.com/rust-cli/anstyle/compare/anstyle-owo-colors-v0.2.0...anstyle-owo-colors-v0.2.1 71 | [0.2.0]: https://github.com/rust-cli/anstyle/compare/anstyle-owo-colors-v0.1.1...anstyle-owo-colors-v0.2.0 72 | [0.1.1]: https://github.com/rust-cli/anstyle/compare/eac8804...anstyle-owo-colors-v0.1.1 73 | -------------------------------------------------------------------------------- /crates/anstyle-owo-colors/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "anstyle-owo-colors" 3 | version = "2.0.4" 4 | description = "Adapt between owo-colors and anstyle" 5 | categories = ["command-line-interface"] 6 | keywords = ["ansi", "terminal", "color", "owo-colors"] 7 | repository.workspace = true 8 | license.workspace = true 9 | edition.workspace = true 10 | rust-version.workspace = true 11 | include.workspace = true 12 | 13 | [package.metadata.docs.rs] 14 | all-features = true 15 | rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"] 16 | 17 | [package.metadata.release] 18 | pre-release-replacements = [ 19 | {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, 20 | {file="CHANGELOG.md", search="\\.\\.\\.HEAD", replace="...{{tag_name}}", exactly=1}, 21 | {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, 22 | {file="CHANGELOG.md", search="", replace="\n## [Unreleased] - ReleaseDate\n", exactly=1}, 23 | {file="CHANGELOG.md", search="", replace="\n[Unreleased]: https://github.com/rust-cli/anstyle/compare/{{tag_name}}...HEAD", exactly=1}, 24 | ] 25 | 26 | [dependencies] 27 | anstyle = { version = "1.0.0", path = "../anstyle" } 28 | owo-colors = "4.0.0" 29 | 30 | [lints] 31 | workspace = true 32 | -------------------------------------------------------------------------------- /crates/anstyle-owo-colors/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) Individual contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /crates/anstyle-owo-colors/README.md: -------------------------------------------------------------------------------- 1 | # anstyle-owo-colors 2 | 3 | > Convert between [owo-colors](https://lib.rs/owo-colors) and generic styling types 4 | 5 | [![Documentation](https://img.shields.io/badge/docs-master-blue.svg)][Documentation] 6 | ![License](https://img.shields.io/crates/l/anstyle-owo-colors.svg) 7 | [![Crates Status](https://img.shields.io/crates/v/anstyle-owo-colors.svg)](https://crates.io/crates/anstyle-owo-colors) 8 | 9 | ## License 10 | 11 | Licensed under either of 12 | 13 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or ) 14 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or ) 15 | 16 | at your option. 17 | 18 | ## [Contribute](../../CONTRIBUTING.md) 19 | 20 | Unless you explicitly state otherwise, any contribution intentionally 21 | submitted for inclusion in the work by you, as defined in the Apache-2.0 22 | license, shall be dual-licensed as above, without any additional terms or 23 | conditions. 24 | 25 | [Crates.io]: https://crates.io/crates/anstyle-owo-colors 26 | [Documentation]: https://docs.rs/anstyle-owo-colors 27 | -------------------------------------------------------------------------------- /crates/anstyle-owo-colors/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Convert between [owo-colors](https://lib.rs/owo-colors) and generic styling types 2 | 3 | #![cfg_attr(docsrs, feature(doc_auto_cfg))] 4 | #![warn(missing_docs)] 5 | #![warn(clippy::print_stderr)] 6 | #![warn(clippy::print_stdout)] 7 | 8 | /// Adapt generic styling to [`owo_colors`] 9 | pub fn to_owo_style(style: anstyle::Style) -> owo_colors::Style { 10 | let fg = style.get_fg_color().map(to_owo_colors); 11 | let bg = style.get_bg_color().map(to_owo_colors); 12 | let effects = style.get_effects(); 13 | 14 | let mut style = owo_colors::Style::new(); 15 | if let Some(fg) = fg { 16 | style = style.color(fg); 17 | } 18 | if let Some(bg) = bg { 19 | style = style.on_color(bg); 20 | } 21 | if effects.contains(anstyle::Effects::BOLD) { 22 | style = style.bold(); 23 | } 24 | if effects.contains(anstyle::Effects::DIMMED) { 25 | style = style.dimmed(); 26 | } 27 | if effects.contains(anstyle::Effects::ITALIC) { 28 | style = style.italic(); 29 | } 30 | if effects.contains(anstyle::Effects::UNDERLINE) { 31 | style = style.underline(); 32 | } 33 | if effects.contains(anstyle::Effects::BLINK) { 34 | style = style.blink(); 35 | } 36 | if effects.contains(anstyle::Effects::INVERT) { 37 | style = style.reversed(); 38 | } 39 | if effects.contains(anstyle::Effects::HIDDEN) { 40 | style = style.hidden(); 41 | } 42 | if effects.contains(anstyle::Effects::STRIKETHROUGH) { 43 | style = style.strikethrough(); 44 | } 45 | style 46 | } 47 | 48 | /// Adapt generic colors to [`owo_colors`] 49 | pub fn to_owo_colors(color: anstyle::Color) -> owo_colors::DynColors { 50 | match color { 51 | anstyle::Color::Ansi(ansi) => owo_colors::DynColors::Ansi(ansi_to_owo_colors_color(ansi)), 52 | anstyle::Color::Ansi256(xterm) => { 53 | owo_colors::DynColors::Xterm(xterm_to_owo_colors_color(xterm)) 54 | } 55 | anstyle::Color::Rgb(rgb) => { 56 | let (r, g, b) = rgb_to_owo_colors_color(rgb); 57 | owo_colors::DynColors::Rgb(r, g, b) 58 | } 59 | } 60 | } 61 | 62 | fn ansi_to_owo_colors_color(color: anstyle::AnsiColor) -> owo_colors::colored::Color { 63 | match color { 64 | anstyle::AnsiColor::Black => owo_colors::colored::Color::Black, 65 | anstyle::AnsiColor::Red => owo_colors::colored::Color::Red, 66 | anstyle::AnsiColor::Green => owo_colors::colored::Color::Green, 67 | anstyle::AnsiColor::Yellow => owo_colors::colored::Color::Yellow, 68 | anstyle::AnsiColor::Blue => owo_colors::colored::Color::Blue, 69 | anstyle::AnsiColor::Magenta => owo_colors::colored::Color::Magenta, 70 | anstyle::AnsiColor::Cyan => owo_colors::colored::Color::Cyan, 71 | anstyle::AnsiColor::White => owo_colors::colored::Color::White, 72 | anstyle::AnsiColor::BrightBlack => owo_colors::colored::Color::BrightBlack, 73 | anstyle::AnsiColor::BrightRed => owo_colors::colored::Color::BrightRed, 74 | anstyle::AnsiColor::BrightGreen => owo_colors::colored::Color::BrightGreen, 75 | anstyle::AnsiColor::BrightYellow => owo_colors::colored::Color::BrightYellow, 76 | anstyle::AnsiColor::BrightBlue => owo_colors::colored::Color::BrightBlue, 77 | anstyle::AnsiColor::BrightMagenta => owo_colors::colored::Color::BrightMagenta, 78 | anstyle::AnsiColor::BrightCyan => owo_colors::colored::Color::BrightCyan, 79 | anstyle::AnsiColor::BrightWhite => owo_colors::colored::Color::BrightWhite, 80 | } 81 | } 82 | 83 | fn xterm_to_owo_colors_color(color: anstyle::Ansi256Color) -> owo_colors::XtermColors { 84 | owo_colors::XtermColors::from(color.0) 85 | } 86 | 87 | fn rgb_to_owo_colors_color(color: anstyle::RgbColor) -> (u8, u8, u8) { 88 | (color.0, color.1, color.2) 89 | } 90 | 91 | #[doc = include_str!("../README.md")] 92 | #[cfg(doctest)] 93 | pub struct ReadmeDoctests; 94 | -------------------------------------------------------------------------------- /crates/anstyle-parse/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/). 6 | 7 | 8 | ## [Unreleased] - ReleaseDate 9 | 10 | ## [0.2.7] - 2025-06-04 11 | 12 | ## [0.2.6] - 2024-10-24 13 | 14 | ### Compatibility 15 | 16 | - Update MSRV to 1.66 17 | 18 | ## [0.2.5] - 2024-07-25 19 | 20 | ## [0.2.4] - 2024-05-02 21 | 22 | ### Fixes 23 | 24 | - Drop MSRV to 1.65 25 | 26 | ## [0.2.3] - 2023-12-04 27 | 28 | ## [0.2.2] - 2023-09-28 29 | 30 | ### Compatibility 31 | 32 | - Update MSRV to 1.70.0 33 | 34 | ## [0.2.1] - 2023-06-20 35 | 36 | ## [0.2.0] - 2023-04-13 37 | 38 | ## [0.1.1] - 2023-03-13 39 | 40 | ### Performance 41 | 42 | - Reduce binary size overhead 43 | 44 | ## [0.1.0] - 2023-03-08 45 | 46 | ## [0.0.1] - 2023-03-07 47 | 48 | 49 | [Unreleased]: https://github.com/rust-cli/anstyle/compare/anstyle-parse-v0.2.7...HEAD 50 | [0.2.7]: https://github.com/rust-cli/anstyle/compare/anstyle-parse-v0.2.6...anstyle-parse-v0.2.7 51 | [0.2.6]: https://github.com/rust-cli/anstyle/compare/anstyle-parse-v0.2.5...anstyle-parse-v0.2.6 52 | [0.2.5]: https://github.com/rust-cli/anstyle/compare/anstyle-parse-v0.2.4...anstyle-parse-v0.2.5 53 | [0.2.4]: https://github.com/rust-cli/anstyle/compare/anstyle-parse-v0.2.3...anstyle-parse-v0.2.4 54 | [0.2.3]: https://github.com/rust-cli/anstyle/compare/anstyle-parse-v0.2.2...anstyle-parse-v0.2.3 55 | [0.2.2]: https://github.com/rust-cli/anstyle/compare/anstyle-parse-v0.2.1...anstyle-parse-v0.2.2 56 | [0.2.1]: https://github.com/rust-cli/anstyle/compare/anstyle-parse-v0.2.0...anstyle-parse-v0.2.1 57 | [0.2.0]: https://github.com/rust-cli/anstyle/compare/anstyle-parse-v0.1.1...anstyle-parse-v0.2.0 58 | [0.1.1]: https://github.com/rust-cli/anstyle/compare/anstyle-parse-v0.1.0...anstyle-parse-v0.1.1 59 | [0.1.0]: https://github.com/rust-cli/anstyle/compare/anstyle-parse-v0.0.1...anstyle-parse-v0.1.0 60 | [0.0.1]: https://github.com/rust-cli/anstyle/compare/3279127...anstyle-parse-v0.0.1 61 | -------------------------------------------------------------------------------- /crates/anstyle-parse/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "anstyle-parse" 3 | version = "0.2.7" 4 | description = "Parse ANSI Style Escapes" 5 | categories = ["command-line-interface"] 6 | keywords = ["ansi", "terminal", "color", "vte"] 7 | repository.workspace = true 8 | license.workspace = true 9 | edition.workspace = true 10 | rust-version.workspace = true 11 | include.workspace = true 12 | 13 | [package.metadata.docs.rs] 14 | all-features = true 15 | rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"] 16 | 17 | [package.metadata.release] 18 | pre-release-replacements = [ 19 | {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, 20 | {file="CHANGELOG.md", search="\\.\\.\\.HEAD", replace="...{{tag_name}}", exactly=1}, 21 | {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, 22 | {file="CHANGELOG.md", search="", replace="\n## [Unreleased] - ReleaseDate\n", exactly=1}, 23 | {file="CHANGELOG.md", search="", replace="\n[Unreleased]: https://github.com/rust-cli/anstyle/compare/{{tag_name}}...HEAD", exactly=1}, 24 | ] 25 | 26 | [dependencies] 27 | arrayvec = { version = "0.7.2", default-features = false, optional = true } 28 | utf8parse = { version = "0.2.1", optional = true } 29 | 30 | [features] 31 | default = ["utf8"] 32 | core = ["dep:arrayvec"] 33 | utf8 = ["dep:utf8parse"] 34 | 35 | [dev-dependencies] 36 | codegenrs = { version = "3.0.1", default-features = false } 37 | divan = "0.1.14" 38 | proptest = "1.4.0" 39 | snapbox = "0.6.5" 40 | vte_generate_state_changes = { version = "0.1.1" } 41 | 42 | [[bench]] 43 | name = "parse" 44 | harness = false 45 | required-features = ["utf8"] 46 | 47 | [lints] 48 | workspace = true 49 | -------------------------------------------------------------------------------- /crates/anstyle-parse/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) Individual contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /crates/anstyle-parse/README.md: -------------------------------------------------------------------------------- 1 | # anstyle-parse 2 | 3 | > Parse [Parse ANSI Style Escapes](https://vt100.net/emu/dec_ansi_parser) 4 | 5 | [![Documentation](https://img.shields.io/badge/docs-master-blue.svg)][Documentation] 6 | ![License](https://img.shields.io/crates/l/anstyle-parse.svg) 7 | [![Crates Status](https://img.shields.io/crates/v/anstyle-parse.svg)](https://crates.io/crates/anstyle-parse) 8 | 9 | ## License 10 | 11 | Licensed under either of 12 | 13 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or ) 14 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or ) 15 | 16 | at your option. 17 | 18 | ## [Contribute](../../CONTRIBUTING.md) 19 | 20 | Unless you explicitly state otherwise, any contribution intentionally 21 | submitted for inclusion in the work by you, as defined in the Apache-2.0 22 | license, shall be dual-licensed as above, without any additional terms or 23 | conditions. 24 | 25 | ### Special Thanks 26 | 27 | [chrisduerr](https://github.com/alacritty/vte/commits?author=chrisduerr) and the 28 | [alacritty project](https://github.com/alacritty/alacritty) for 29 | [vte](https://crates.io/crates/vte) which 30 | [this was forked from](https://github.com/alacritty/vte/issues/82) 31 | 32 | [Crates.io]: https://crates.io/crates/anstyle-parse 33 | [Documentation]: https://docs.rs/anstyle-parse 34 | -------------------------------------------------------------------------------- /crates/anstyle-parse/examples/parselog.rs: -------------------------------------------------------------------------------- 1 | //! Parse input from stdin and log actions on stdout 2 | use std::io::{self, Read}; 3 | 4 | use anstyle_parse::{DefaultCharAccumulator, Params, Parser, Perform}; 5 | 6 | /// A type implementing Perform that just logs actions 7 | struct Log; 8 | 9 | impl Perform for Log { 10 | fn print(&mut self, c: char) { 11 | println!("[print] {c:?}"); 12 | } 13 | 14 | fn execute(&mut self, byte: u8) { 15 | println!("[execute] {byte:02x}"); 16 | } 17 | 18 | fn hook(&mut self, params: &Params, intermediates: &[u8], ignore: bool, c: u8) { 19 | println!( 20 | "[hook] params={params:?}, intermediates={intermediates:?}, ignore={ignore:?}, char={c:?}" 21 | ); 22 | } 23 | 24 | fn put(&mut self, byte: u8) { 25 | println!("[put] {byte:02x}"); 26 | } 27 | 28 | fn unhook(&mut self) { 29 | println!("[unhook]"); 30 | } 31 | 32 | fn osc_dispatch(&mut self, params: &[&[u8]], bell_terminated: bool) { 33 | println!("[osc_dispatch] params={params:?} bell_terminated={bell_terminated}"); 34 | } 35 | 36 | fn csi_dispatch(&mut self, params: &Params, intermediates: &[u8], ignore: bool, c: u8) { 37 | println!( 38 | "[csi_dispatch] params={params:#?}, intermediates={intermediates:?}, ignore={ignore:?}, char={c:?}" 39 | ); 40 | } 41 | 42 | fn esc_dispatch(&mut self, intermediates: &[u8], ignore: bool, byte: u8) { 43 | println!( 44 | "[esc_dispatch] intermediates={intermediates:?}, ignore={ignore:?}, byte={byte:02x}" 45 | ); 46 | } 47 | } 48 | 49 | fn main() { 50 | let input = io::stdin(); 51 | let mut handle = input.lock(); 52 | 53 | let mut statemachine = Parser::::new(); 54 | let mut performer = Log; 55 | 56 | let mut buf = [0; 2048]; 57 | 58 | loop { 59 | match handle.read(&mut buf) { 60 | Ok(0) => break, 61 | Ok(n) => { 62 | for byte in &buf[..n] { 63 | statemachine.advance(&mut performer, *byte); 64 | } 65 | } 66 | Err(err) => { 67 | println!("err: {err}"); 68 | break; 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /crates/anstyle-parse/src/params.rs: -------------------------------------------------------------------------------- 1 | //! Fixed size parameters list with optional subparameters. 2 | 3 | use core::fmt::{self, Debug, Formatter}; 4 | 5 | pub(crate) const MAX_PARAMS: usize = 32; 6 | 7 | #[derive(Default, Clone, PartialEq, Eq)] 8 | pub struct Params { 9 | /// Number of subparameters for each parameter. 10 | /// 11 | /// For each entry in the `params` slice, this stores the length of the param as number of 12 | /// subparams at the same index as the param in the `params` slice. 13 | /// 14 | /// At the subparam positions the length will always be `0`. 15 | subparams: [u8; MAX_PARAMS], 16 | 17 | /// All parameters and subparameters. 18 | params: [u16; MAX_PARAMS], 19 | 20 | /// Number of suparameters in the current parameter. 21 | current_subparams: u8, 22 | 23 | /// Total number of parameters and subparameters. 24 | len: usize, 25 | } 26 | 27 | impl Params { 28 | /// Returns the number of parameters. 29 | #[inline] 30 | pub fn len(&self) -> usize { 31 | self.len 32 | } 33 | 34 | /// Returns `true` if there are no parameters present. 35 | #[inline] 36 | pub fn is_empty(&self) -> bool { 37 | self.len == 0 38 | } 39 | 40 | /// Returns an iterator over all parameters and subparameters. 41 | #[inline] 42 | pub fn iter(&self) -> ParamsIter<'_> { 43 | ParamsIter::new(self) 44 | } 45 | 46 | /// Returns `true` if there is no more space for additional parameters. 47 | #[inline] 48 | pub(crate) fn is_full(&self) -> bool { 49 | self.len == MAX_PARAMS 50 | } 51 | 52 | /// Clear all parameters. 53 | #[inline] 54 | pub(crate) fn clear(&mut self) { 55 | self.current_subparams = 0; 56 | self.len = 0; 57 | } 58 | 59 | /// Add an additional parameter. 60 | #[inline] 61 | pub(crate) fn push(&mut self, item: u16) { 62 | self.subparams[self.len - self.current_subparams as usize] = self.current_subparams + 1; 63 | self.params[self.len] = item; 64 | self.current_subparams = 0; 65 | self.len += 1; 66 | } 67 | 68 | /// Add an additional subparameter to the current parameter. 69 | #[inline] 70 | pub(crate) fn extend(&mut self, item: u16) { 71 | self.subparams[self.len - self.current_subparams as usize] = self.current_subparams + 1; 72 | self.params[self.len] = item; 73 | self.current_subparams += 1; 74 | self.len += 1; 75 | } 76 | } 77 | 78 | impl<'a> IntoIterator for &'a Params { 79 | type IntoIter = ParamsIter<'a>; 80 | type Item = &'a [u16]; 81 | 82 | fn into_iter(self) -> Self::IntoIter { 83 | self.iter() 84 | } 85 | } 86 | 87 | /// Immutable subparameter iterator. 88 | pub struct ParamsIter<'a> { 89 | params: &'a Params, 90 | index: usize, 91 | } 92 | 93 | impl<'a> ParamsIter<'a> { 94 | fn new(params: &'a Params) -> Self { 95 | Self { params, index: 0 } 96 | } 97 | } 98 | 99 | impl<'a> Iterator for ParamsIter<'a> { 100 | type Item = &'a [u16]; 101 | 102 | fn next(&mut self) -> Option { 103 | if self.index >= self.params.len() { 104 | return None; 105 | } 106 | 107 | // Get all subparameters for the current parameter. 108 | let num_subparams = self.params.subparams[self.index]; 109 | let param = &self.params.params[self.index..self.index + num_subparams as usize]; 110 | 111 | // Jump to the next parameter. 112 | self.index += num_subparams as usize; 113 | 114 | Some(param) 115 | } 116 | 117 | fn size_hint(&self) -> (usize, Option) { 118 | let remaining = self.params.len() - self.index; 119 | (remaining, Some(remaining)) 120 | } 121 | } 122 | 123 | impl Debug for Params { 124 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 125 | write!(f, "[")?; 126 | 127 | for (i, param) in self.iter().enumerate() { 128 | if i != 0 { 129 | write!(f, ";")?; 130 | } 131 | 132 | for (i, subparam) in param.iter().enumerate() { 133 | if i != 0 { 134 | write!(f, ":")?; 135 | } 136 | 137 | subparam.fmt(f)?; 138 | } 139 | } 140 | 141 | write!(f, "]") 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /crates/anstyle-parse/src/state/definitions.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::exhaustive_enums)] 2 | 3 | use core::mem; 4 | 5 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 6 | #[repr(u8)] 7 | #[derive(Default)] 8 | pub enum State { 9 | Anywhere = 0, 10 | CsiEntry = 1, 11 | CsiIgnore = 2, 12 | CsiIntermediate = 3, 13 | CsiParam = 4, 14 | DcsEntry = 5, 15 | DcsIgnore = 6, 16 | DcsIntermediate = 7, 17 | DcsParam = 8, 18 | DcsPassthrough = 9, 19 | Escape = 10, 20 | EscapeIntermediate = 11, 21 | #[default] 22 | Ground = 12, 23 | OscString = 13, 24 | SosPmApcString = 14, 25 | Utf8 = 15, 26 | } 27 | 28 | impl TryFrom for State { 29 | type Error = u8; 30 | 31 | #[inline(always)] 32 | fn try_from(raw: u8) -> Result { 33 | STATES.get(raw as usize).ok_or(raw).copied() 34 | } 35 | } 36 | 37 | const STATES: [State; 16] = [ 38 | State::Anywhere, 39 | State::CsiEntry, 40 | State::CsiIgnore, 41 | State::CsiIntermediate, 42 | State::CsiParam, 43 | State::DcsEntry, 44 | State::DcsIgnore, 45 | State::DcsIntermediate, 46 | State::DcsParam, 47 | State::DcsPassthrough, 48 | State::Escape, 49 | State::EscapeIntermediate, 50 | State::Ground, 51 | State::OscString, 52 | State::SosPmApcString, 53 | State::Utf8, 54 | ]; 55 | 56 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 57 | #[repr(u8)] 58 | #[derive(Default)] 59 | pub enum Action { 60 | #[default] 61 | Nop = 0, 62 | Clear = 1, 63 | Collect = 2, 64 | CsiDispatch = 3, 65 | EscDispatch = 4, 66 | Execute = 5, 67 | Hook = 6, 68 | Ignore = 7, 69 | OscEnd = 8, 70 | OscPut = 9, 71 | OscStart = 10, 72 | Param = 11, 73 | Print = 12, 74 | Put = 13, 75 | Unhook = 14, 76 | BeginUtf8 = 15, 77 | } 78 | 79 | impl TryFrom for Action { 80 | type Error = u8; 81 | 82 | #[inline(always)] 83 | fn try_from(raw: u8) -> Result { 84 | ACTIONS.get(raw as usize).ok_or(raw).copied() 85 | } 86 | } 87 | 88 | const ACTIONS: [Action; 16] = [ 89 | Action::Nop, 90 | Action::Clear, 91 | Action::Collect, 92 | Action::CsiDispatch, 93 | Action::EscDispatch, 94 | Action::Execute, 95 | Action::Hook, 96 | Action::Ignore, 97 | Action::OscEnd, 98 | Action::OscPut, 99 | Action::OscStart, 100 | Action::Param, 101 | Action::Print, 102 | Action::Put, 103 | Action::Unhook, 104 | Action::BeginUtf8, 105 | ]; 106 | 107 | /// Unpack a u8 into a State and Action 108 | /// 109 | /// The implementation of this assumes that there are *precisely* 16 variants for both Action and 110 | /// State. Furthermore, it assumes that the enums are tag-only; that is, there is no data in any 111 | /// variant. 112 | /// 113 | /// Bad things will happen if those invariants are violated. 114 | #[inline(always)] 115 | pub(crate) const fn unpack(delta: u8) -> (State, Action) { 116 | unsafe { 117 | ( 118 | // State is stored in bottom 4 bits 119 | mem::transmute::(delta & 0x0f), 120 | // Action is stored in top 4 bits 121 | mem::transmute::(delta >> 4), 122 | ) 123 | } 124 | } 125 | 126 | #[inline(always)] 127 | #[cfg(test)] 128 | pub(crate) const fn pack(state: State, action: Action) -> u8 { 129 | ((action as u8) << 4) | state as u8 130 | } 131 | 132 | #[cfg(test)] 133 | mod tests { 134 | use super::*; 135 | 136 | #[test] 137 | fn unpack_state_action() { 138 | match unpack(0xee) { 139 | (State::SosPmApcString, Action::Unhook) => (), 140 | _ => panic!("unpack failed"), 141 | } 142 | 143 | match unpack(0x0f) { 144 | (State::Utf8, Action::Nop) => (), 145 | _ => panic!("unpack failed"), 146 | } 147 | 148 | match unpack(0xff) { 149 | (State::Utf8, Action::BeginUtf8) => (), 150 | _ => panic!("unpack failed"), 151 | } 152 | } 153 | 154 | #[test] 155 | fn pack_state_action() { 156 | match unpack(0xee) { 157 | (State::SosPmApcString, Action::Unhook) => (), 158 | _ => panic!("unpack failed"), 159 | } 160 | 161 | match unpack(0x0f) { 162 | (State::Utf8, Action::Nop) => (), 163 | _ => panic!("unpack failed"), 164 | } 165 | 166 | match unpack(0xff) { 167 | (State::Utf8, Action::BeginUtf8) => (), 168 | _ => panic!("unpack failed"), 169 | } 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /crates/anstyle-parse/src/state/mod.rs: -------------------------------------------------------------------------------- 1 | //! ANSI escape code parsing state machine 2 | 3 | #[cfg(test)] 4 | mod codegen; 5 | mod definitions; 6 | mod table; 7 | 8 | #[cfg(test)] 9 | pub(crate) use definitions::pack; 10 | pub(crate) use definitions::unpack; 11 | pub use definitions::Action; 12 | pub use definitions::State; 13 | 14 | /// Transition to next [`State`] 15 | /// 16 | /// Note: This does not directly support UTF-8. 17 | /// - If the data is validated as UTF-8 (e.g. `str`) or single-byte C1 control codes are 18 | /// unsupported, then treat [`Action::BeginUtf8`] and [`Action::Execute`] for UTF-8 continuations 19 | /// as [`Action::Print`]. 20 | /// - If the data is not validated, then a UTF-8 state machine will need to be implemented on top, 21 | /// starting with [`Action::BeginUtf8`]. 22 | /// 23 | /// Note: When [`State::Anywhere`] is returned, revert back to the prior state. 24 | #[inline] 25 | pub const fn state_change(state: State, byte: u8) -> (State, Action) { 26 | // Handle state changes in the anywhere state before evaluating changes 27 | // for current state. 28 | let mut change = state_change_(State::Anywhere, byte); 29 | if change == 0 { 30 | change = state_change_(state, byte); 31 | } 32 | 33 | // Unpack into a state and action 34 | unpack(change) 35 | } 36 | 37 | #[inline] 38 | const fn state_change_(state: State, byte: u8) -> u8 { 39 | let state_idx = state as usize; 40 | let byte_idx = byte as usize; 41 | 42 | table::STATE_CHANGES[state_idx][byte_idx] 43 | } 44 | -------------------------------------------------------------------------------- /crates/anstyle-parse/tests/demo.vte: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-cli/anstyle/94847b3ab52097fc950c97503172ad4575492b9a/crates/anstyle-parse/tests/demo.vte -------------------------------------------------------------------------------- /crates/anstyle-query/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/). 6 | 7 | 8 | ## [Unreleased] - ReleaseDate 9 | 10 | ## [1.1.3] - 2025-06-04 11 | 12 | ## [1.1.2] - 2024-10-24 13 | 14 | ### Compatibility 15 | 16 | - Update MSRV to 1.66 17 | 18 | ## [1.1.1] - 2024-07-25 19 | 20 | ## [1.1.0] - 2024-06-04 21 | 22 | ### Fixes 23 | 24 | - `CLICOLOR_FORCE` now only checks for non-empty strings, rather than non-`"0"` strings 25 | 26 | ## [1.0.3] - 2024-05-02 27 | 28 | ### Fixes 29 | 30 | - Drop MSRV to 1.65 31 | 32 | ## [1.0.2] - 2023-12-08 33 | 34 | ### Features 35 | 36 | - Expose `windows::enable_virtual_terminal_processing` 37 | 38 | ## [1.0.1] - 2023-12-04 39 | 40 | ### Compatibility 41 | 42 | - Update MSRV to 1.70.0 43 | 44 | ## [1.0.0] - 2023-04-13 45 | 46 | 47 | [Unreleased]: https://github.com/rust-cli/anstyle/compare/anstyle-query-v1.1.3...HEAD 48 | [1.1.3]: https://github.com/rust-cli/anstyle/compare/anstyle-query-v1.1.2...anstyle-query-v1.1.3 49 | [1.1.2]: https://github.com/rust-cli/anstyle/compare/anstyle-query-v1.1.1...anstyle-query-v1.1.2 50 | [1.1.1]: https://github.com/rust-cli/anstyle/compare/anstyle-query-v1.1.0...anstyle-query-v1.1.1 51 | [1.1.0]: https://github.com/rust-cli/anstyle/compare/anstyle-query-v1.0.3...anstyle-query-v1.1.0 52 | [1.0.3]: https://github.com/rust-cli/anstyle/compare/anstyle-query-v1.0.2...anstyle-query-v1.0.3 53 | [1.0.2]: https://github.com/rust-cli/anstyle/compare/anstyle-query-v1.0.1...anstyle-query-v1.0.2 54 | [1.0.1]: https://github.com/rust-cli/anstyle/compare/anstyle-query-v1.0.0...anstyle-query-v1.0.1 55 | [1.0.0]: https://github.com/rust-cli/anstyle/compare/c4423c1...anstyle-query-v1.0.0 56 | -------------------------------------------------------------------------------- /crates/anstyle-query/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "anstyle-query" 3 | version = "1.1.3" 4 | description = "Look up colored console capabilities" 5 | categories = ["command-line-interface"] 6 | keywords = ["cli", "color", "no-std", "terminal", "ansi"] 7 | repository.workspace = true 8 | license.workspace = true 9 | edition.workspace = true 10 | rust-version.workspace = true 11 | include.workspace = true 12 | 13 | [package.metadata.docs.rs] 14 | all-features = true 15 | rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"] 16 | 17 | [package.metadata.release] 18 | pre-release-replacements = [ 19 | {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, 20 | {file="CHANGELOG.md", search="\\.\\.\\.HEAD", replace="...{{tag_name}}", exactly=1}, 21 | {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, 22 | {file="CHANGELOG.md", search="", replace="\n## [Unreleased] - ReleaseDate\n", exactly=1}, 23 | {file="CHANGELOG.md", search="", replace="\n[Unreleased]: https://github.com/rust-cli/anstyle/compare/{{tag_name}}...HEAD", exactly=1}, 24 | ] 25 | 26 | [target.'cfg(windows)'.dependencies] 27 | windows-sys = { version = "0.59.0", features = ["Win32_System_Console", "Win32_Foundation"] } 28 | 29 | [lints] 30 | workspace = true 31 | -------------------------------------------------------------------------------- /crates/anstyle-query/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) Individual contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /crates/anstyle-query/README.md: -------------------------------------------------------------------------------- 1 | # anstyle-query 2 | 3 | > **Low level terminal capability lookups** 4 | 5 | [![Documentation](https://img.shields.io/badge/docs-master-blue.svg)][Documentation] 6 | ![License](https://img.shields.io/crates/l/anstyle-query.svg) 7 | [![Crates Status](https://img.shields.io/crates/v/anstyle-query.svg)](https://crates.io/crates/anstyle-query) 8 | 9 | ## License 10 | 11 | Licensed under either of 12 | 13 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or ) 14 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or ) 15 | 16 | at your option. 17 | 18 | ## [Contribute](../../CONTRIBUTING.md) 19 | 20 | Unless you explicitly state otherwise, any contribution intentionally 21 | submitted for inclusion in the work by you, as defined in the Apache-2.0 22 | license, shall be dual-licensed as above, without any additional terms or 23 | conditions. 24 | 25 | [Documentation]: https://docs.rs/anstyle-query 26 | -------------------------------------------------------------------------------- /crates/anstyle-query/examples/query.rs: -------------------------------------------------------------------------------- 1 | //! Report a terminal's capabilities 2 | 3 | fn main() { 4 | println!("clicolor: {:?}", anstyle_query::clicolor()); 5 | println!("clicolor_force: {}", anstyle_query::clicolor_force()); 6 | println!("no_color: {}", anstyle_query::no_color()); 7 | println!( 8 | "term_supports_ansi_color: {}", 9 | anstyle_query::term_supports_ansi_color() 10 | ); 11 | println!( 12 | "term_supports_color: {}", 13 | anstyle_query::term_supports_color() 14 | ); 15 | println!("truecolor: {}", anstyle_query::truecolor()); 16 | println!( 17 | "enable_ansi_colors: {:?}", 18 | anstyle_query::windows::enable_ansi_colors() 19 | ); 20 | #[cfg(windows)] 21 | println!( 22 | " enable_virtual_terminal_processing: {:?}", 23 | anstyle_query::windows::enable_virtual_terminal_processing() 24 | ); 25 | println!("is_ci: {:?}", anstyle_query::is_ci()); 26 | } 27 | -------------------------------------------------------------------------------- /crates/anstyle-query/src/windows.rs: -------------------------------------------------------------------------------- 1 | //! Windows-specific style queries 2 | 3 | #[cfg(windows)] 4 | mod windows_console { 5 | use std::os::windows::io::AsRawHandle; 6 | use std::os::windows::io::RawHandle; 7 | 8 | use windows_sys::Win32::Foundation::HANDLE; 9 | use windows_sys::Win32::System::Console::CONSOLE_MODE; 10 | use windows_sys::Win32::System::Console::ENABLE_VIRTUAL_TERMINAL_PROCESSING; 11 | 12 | fn enable_vt(handle: RawHandle) -> std::io::Result<()> { 13 | unsafe { 14 | let handle: HANDLE = handle as HANDLE; 15 | if handle.is_null() { 16 | return Err(std::io::Error::new( 17 | std::io::ErrorKind::BrokenPipe, 18 | "console is detached", 19 | )); 20 | } 21 | 22 | let mut dwmode: CONSOLE_MODE = 0; 23 | if windows_sys::Win32::System::Console::GetConsoleMode(handle, &mut dwmode) == 0 { 24 | return Err(std::io::Error::last_os_error()); 25 | } 26 | 27 | dwmode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; 28 | if windows_sys::Win32::System::Console::SetConsoleMode(handle, dwmode) == 0 { 29 | return Err(std::io::Error::last_os_error()); 30 | } 31 | 32 | Ok(()) 33 | } 34 | } 35 | 36 | pub(crate) fn enable_virtual_terminal_processing() -> std::io::Result<()> { 37 | let stdout = std::io::stdout(); 38 | let stdout_handle = stdout.as_raw_handle(); 39 | let stderr = std::io::stderr(); 40 | let stderr_handle = stderr.as_raw_handle(); 41 | 42 | enable_vt(stdout_handle)?; 43 | if stdout_handle != stderr_handle { 44 | enable_vt(stderr_handle)?; 45 | } 46 | 47 | Ok(()) 48 | } 49 | 50 | #[inline] 51 | pub(crate) fn enable_ansi_colors() -> Option { 52 | Some( 53 | enable_virtual_terminal_processing() 54 | .map(|_| true) 55 | .unwrap_or(false), 56 | ) 57 | } 58 | } 59 | 60 | #[cfg(not(windows))] 61 | mod windows_console { 62 | #[inline] 63 | pub(crate) fn enable_ansi_colors() -> Option { 64 | None 65 | } 66 | } 67 | 68 | /// Enable ANSI escape codes ([`ENABLE_VIRTUAL_TERMINAL_PROCESSING`](https://learn.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#output-sequences)) 69 | /// 70 | /// For non-windows systems, returns `None` 71 | pub fn enable_ansi_colors() -> Option { 72 | windows_console::enable_ansi_colors() 73 | } 74 | 75 | /// Raw `ENABLE_VIRTUAL_TERMINAL_PROCESSING` on stdout/stderr 76 | #[cfg(windows)] 77 | pub fn enable_virtual_terminal_processing() -> std::io::Result<()> { 78 | windows_console::enable_virtual_terminal_processing() 79 | } 80 | -------------------------------------------------------------------------------- /crates/anstyle-roff/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/). 6 | 7 | 8 | ## [Unreleased] - ReleaseDate 9 | 10 | ## [0.3.6] - 2025-06-04 11 | 12 | ## [0.3.5] - 2024-10-24 13 | 14 | ### Compatibility 15 | 16 | - Update MSRV to 1.66 17 | 18 | ## [0.3.4] - 2024-07-25 19 | 20 | ## [0.3.3] - 2024-05-02 21 | 22 | ### Fixes 23 | 24 | - Drop MSRV to 1.65 25 | 26 | ## [0.3.2] - 2023-09-28 27 | 28 | ### Compatibility 29 | 30 | - Update MSRV to 1.70.0 31 | 32 | ## [0.3.1] - 2023-06-20 33 | 34 | ## [0.3.0] - 2023-04-13 35 | 36 | ### Breaking Change 37 | 38 | - Updated `anstyle` 39 | 40 | ## [0.2.0] - 2023-03-08 41 | 42 | ### Features 43 | 44 | - Implement basic traits (e.g. `Clone`) 45 | 46 | ## [0.1.0] - 2023-02-10 47 | 48 | 49 | [Unreleased]: https://github.com/rust-cli/anstyle/compare/anstyle-roff-v0.3.6...HEAD 50 | [0.3.6]: https://github.com/rust-cli/anstyle/compare/anstyle-roff-v0.3.5...anstyle-roff-v0.3.6 51 | [0.3.5]: https://github.com/rust-cli/anstyle/compare/anstyle-roff-v0.3.4...anstyle-roff-v0.3.5 52 | [0.3.4]: https://github.com/rust-cli/anstyle/compare/anstyle-roff-v0.3.3...anstyle-roff-v0.3.4 53 | [0.3.3]: https://github.com/rust-cli/anstyle/compare/anstyle-roff-v0.3.2...anstyle-roff-v0.3.3 54 | [0.3.2]: https://github.com/rust-cli/anstyle/compare/anstyle-roff-v0.3.1...anstyle-roff-v0.3.2 55 | [0.3.1]: https://github.com/rust-cli/anstyle/compare/anstyle-roff-v0.3.0...anstyle-roff-v0.3.1 56 | [0.3.0]: https://github.com/rust-cli/anstyle/compare/anstyle-roff-v0.2.0...anstyle-roff-v0.3.0 57 | [0.2.0]: https://github.com/rust-cli/anstyle/compare/anstyle-roff-v0.1.0...anstyle-roff-v0.2.0 58 | [0.1.0]: https://github.com/rust-cli/anstyle/compare/6d15580ecb12a97fb7fffa015d7cae88f1ade671...anstyle-roff-v0.1.0 59 | -------------------------------------------------------------------------------- /crates/anstyle-roff/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "anstyle-roff" 3 | version = "0.3.6" 4 | description = "Adapt between anstyle and roff" 5 | categories = ["command-line-interface"] 6 | keywords = ["ansi", "terminal", "roff"] 7 | repository.workspace = true 8 | license.workspace = true 9 | edition.workspace = true 10 | rust-version.workspace = true 11 | include.workspace = true 12 | 13 | [package.metadata.docs.rs] 14 | all-features = true 15 | rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"] 16 | 17 | [package.metadata.release] 18 | pre-release-replacements = [ 19 | {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, 20 | {file="CHANGELOG.md", search="\\.\\.\\.HEAD", replace="...{{tag_name}}", exactly=1}, 21 | {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, 22 | {file="CHANGELOG.md", search="", replace="\n## [Unreleased] - ReleaseDate\n", exactly=1}, 23 | {file="CHANGELOG.md", search="", replace="\n[Unreleased]: https://github.com/rust-cli/anstyle/compare/{{tag_name}}...HEAD", exactly=1}, 24 | ] 25 | 26 | [dependencies] 27 | anstyle = { version = "1.0.0", path = "../anstyle" } 28 | anstyle-lossy = { version = "1.0.0", path = "../anstyle-lossy" } 29 | roff = "0.2.1" 30 | cansi = "2.2.1" 31 | 32 | [dev-dependencies] 33 | snapbox = "0.6.5" 34 | 35 | [lints] 36 | workspace = true 37 | -------------------------------------------------------------------------------- /crates/anstyle-roff/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) Individual contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /crates/anstyle-roff/README.md: -------------------------------------------------------------------------------- 1 | # anstyle-roff 2 | 3 | > Convert from ANSI styling escape codes to [roff](https://manpages.debian.org/bullseye/groff/groff.7.en.html) document. 4 | 5 | [![Documentation](https://img.shields.io/badge/docs-master-blue.svg)][Documentation] 6 | ![License](https://img.shields.io/crates/l/anstyle-roff.svg) 7 | [![Crates Status](https://img.shields.io/crates/v/anstyle-roff.svg)](https://crates.io/crates/anstyle-roff) 8 | 9 | ## License 10 | 11 | Licensed under either of 12 | 13 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or ) 14 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or ) 15 | 16 | at your option. 17 | 18 | ## [Contribute](../../CONTRIBUTING.md) 19 | 20 | Unless you explicitly state otherwise, any contribution intentionally 21 | submitted for inclusion in the work by you, as defined in the Apache-2.0 22 | license, shall be dual-licensed as above, without any additional terms or 23 | conditions. 24 | 25 | [Crates.io]: https://crates.io/crates/anstyle-roff 26 | [Documentation]: https://docs.rs/anstyle-roff 27 | -------------------------------------------------------------------------------- /crates/anstyle-roff/tests/roffs/ansi_color.roff: -------------------------------------------------------------------------------- 1 | .gcolor red 2 | .fcolor blue 3 | test 4 | -------------------------------------------------------------------------------- /crates/anstyle-roff/tests/roffs/bold.roff: -------------------------------------------------------------------------------- 1 | .gcolor default 2 | .fcolor default 3 | \fBtest\fR 4 | -------------------------------------------------------------------------------- /crates/anstyle-roff/tests/roffs/bright_ansi_colors.roff: -------------------------------------------------------------------------------- 1 | .gcolor red 2 | .fcolor blue 3 | \fBtest\fR 4 | -------------------------------------------------------------------------------- /crates/anstyle-roff/tests/roffs/italic.roff: -------------------------------------------------------------------------------- 1 | .gcolor default 2 | .fcolor default 3 | \fItest\fR 4 | -------------------------------------------------------------------------------- /crates/anstyle-roff/tests/test_roff.rs: -------------------------------------------------------------------------------- 1 | use anstyle::Style; 2 | use snapbox::assert_data_eq; 3 | use snapbox::file; 4 | 5 | #[test] 6 | fn test_ansi_color_output() { 7 | // HACK: cansi doesn't properly merge separate sequences 8 | // let style = AnsiColor::Red.on(AnsiColor::Blue); 9 | // let text = format!("{}{}", style.render(), "test"); 10 | let text = "\u{1b}[31;44mtest".to_owned(); 11 | let roff_doc = anstyle_roff::to_roff(&text); 12 | assert_data_eq!(roff_doc.to_roff(), file!["roffs/ansi_color.roff"].raw()); 13 | } 14 | 15 | #[test] 16 | fn test_bold_output() { 17 | let style = Style::new().bold(); 18 | let text = format!("{}{}", style.render(), "test"); 19 | dbg!(&text); 20 | let roff_doc = anstyle_roff::to_roff(&text); 21 | 22 | assert_data_eq!(roff_doc.to_roff(), file!["roffs/bold.roff"].raw()); 23 | } 24 | 25 | #[test] 26 | fn test_italic_output() { 27 | let style = Style::new().italic(); 28 | let text = format!("{}{}", style.render(), "test"); 29 | dbg!(&text); 30 | 31 | let roff_doc = anstyle_roff::to_roff(&text); 32 | assert_data_eq!(roff_doc.to_roff(), file!["roffs/italic.roff"].raw()); 33 | } 34 | 35 | #[test] 36 | fn test_bright_color_output_as_bold() { 37 | // HACK: cansi doesn't properly merge separate sequences 38 | // let style = AnsiColor::BrightRed.on(AnsiColor::Blue); 39 | // let text = format!("{}{}", style.render(), "test"); 40 | let text = "\u{1b}[91;44mtest".to_owned(); 41 | dbg!(&text); 42 | let roff_doc = anstyle_roff::to_roff(&text); 43 | assert_data_eq!( 44 | roff_doc.to_roff(), 45 | file!["roffs/bright_ansi_colors.roff"].raw() 46 | ); 47 | } 48 | -------------------------------------------------------------------------------- /crates/anstyle-svg/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/). 6 | 7 | 8 | ## [Unreleased] - ReleaseDate 9 | 10 | ## [0.1.8] - 2025-06-04 11 | 12 | ### Features 13 | 14 | - Respect hyperlink ANSI escape codes and render them as `` tags 15 | 16 | ## [0.1.7] - 2024-10-24 17 | 18 | ## [0.1.6] - 2024-10-10 19 | 20 | ### Internal 21 | 22 | - Update `unicode-width` to 0.2 23 | 24 | ### Compatibility 25 | 26 | - Update MSRV to 1.66 27 | 28 | ## [0.1.5] - 2024-07-25 29 | 30 | ## [0.1.4] - 2024-05-02 31 | 32 | ### Fixes 33 | 34 | - Drop MSRV to 1.65 35 | 36 | ## [0.1.3] - 2024-02-22 37 | 38 | ### Fixes 39 | 40 | - Reduce noise in SVG 41 | 42 | ## [0.1.2] - 2024-02-20 43 | 44 | ### Fixes 45 | 46 | - Ensure each section has a blank line inbetween 47 | - Add configurable padding 48 | - Allow setting min width 49 | - Use round numbers for width 50 | 51 | ## [0.1.1] - 2024-02-18 52 | 53 | ### Documentation 54 | 55 | - Specify what the API does 56 | 57 | ## [0.1.0] - 2024-02-18 58 | 59 | 60 | [Unreleased]: https://github.com/rust-cli/anstyle/compare/anstyle-svg-v0.1.8...HEAD 61 | [0.1.8]: https://github.com/rust-cli/anstyle/compare/anstyle-svg-v0.1.7...anstyle-svg-v0.1.8 62 | [0.1.7]: https://github.com/rust-cli/anstyle/compare/anstyle-svg-v0.1.6...anstyle-svg-v0.1.7 63 | [0.1.6]: https://github.com/rust-cli/anstyle/compare/anstyle-svg-v0.1.5...anstyle-svg-v0.1.6 64 | [0.1.5]: https://github.com/rust-cli/anstyle/compare/anstyle-svg-v0.1.4...anstyle-svg-v0.1.5 65 | [0.1.4]: https://github.com/rust-cli/anstyle/compare/anstyle-svg-v0.1.3...anstyle-svg-v0.1.4 66 | [0.1.3]: https://github.com/rust-cli/anstyle/compare/anstyle-svg-v0.1.2...anstyle-svg-v0.1.3 67 | [0.1.2]: https://github.com/rust-cli/anstyle/compare/anstyle-svg-v0.1.1...anstyle-svg-v0.1.2 68 | [0.1.1]: https://github.com/rust-cli/anstyle/compare/anstyle-svg-v0.1.0...anstyle-svg-v0.1.1 69 | [0.1.0]: https://github.com/rust-cli/anstyle/compare/f6784b0002e4fbd4c3532dc66aad8b86fc3c7748...anstyle-svg-v0.1.0 70 | 71 | -------------------------------------------------------------------------------- /crates/anstyle-svg/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "anstyle-svg" 3 | version = "0.1.8" 4 | description = "Convert ANSI escape codes to SVG" 5 | categories = ["command-line-interface"] 6 | keywords = ["ansi", "terminal", "color", "svg"] 7 | repository.workspace = true 8 | license.workspace = true 9 | edition.workspace = true 10 | rust-version.workspace = true 11 | include.workspace = true 12 | 13 | [package.metadata.docs.rs] 14 | all-features = true 15 | rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"] 16 | 17 | [package.metadata.release] 18 | pre-release-replacements = [ 19 | {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, 20 | {file="CHANGELOG.md", search="\\.\\.\\.HEAD", replace="...{{tag_name}}", exactly=1}, 21 | {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, 22 | {file="CHANGELOG.md", search="", replace="\n## [Unreleased] - ReleaseDate\n", exactly=1}, 23 | {file="CHANGELOG.md", search="", replace="\n[Unreleased]: https://github.com/rust-cli/anstyle/compare/{{tag_name}}...HEAD", exactly=1}, 24 | ] 25 | 26 | [dependencies] 27 | anstyle = { version = "1.0.0", path = "../anstyle" } 28 | anstyle-parse = { version = "0.2.6", path = "../anstyle-parse" } 29 | anstyle-lossy = { version = "1.0.0", path = "../anstyle-lossy" } 30 | html-escape = "0.2.13" 31 | unicode-width = "0.2.0" 32 | 33 | [dev-dependencies] 34 | proptest = "1.5.0" 35 | snapbox = "0.6.5" 36 | 37 | [lints] 38 | workspace = true 39 | -------------------------------------------------------------------------------- /crates/anstyle-svg/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) Individual contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /crates/anstyle-svg/README.md: -------------------------------------------------------------------------------- 1 | # anstyle-svg 2 | 3 | > Convert ANSI escape codes to SVG 4 | 5 | [![Documentation](https://img.shields.io/badge/docs-master-blue.svg)][Documentation] 6 | ![License](https://img.shields.io/crates/l/anstyle-svg.svg) 7 | [![Crates Status](https://img.shields.io/crates/v/anstyle-svg.svg)](https://crates.io/crates/anstyle-svg) 8 | 9 | ## License 10 | 11 | Licensed under either of 12 | 13 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or ) 14 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or ) 15 | 16 | at your option. 17 | 18 | ## [Contribute](../../CONTRIBUTING.md) 19 | 20 | Unless you explicitly state otherwise, any contribution intentionally 21 | submitted for inclusion in the work by you, as defined in the Apache-2.0 22 | license, shall be dual-licensed as above, without any additional terms or 23 | conditions. 24 | 25 | [Crates.io]: https://crates.io/crates/anstyle-svg 26 | [Documentation]: https://docs.rs/anstyle-svg 27 | -------------------------------------------------------------------------------- /crates/anstyle-svg/examples/convert.rs: -------------------------------------------------------------------------------- 1 | use std::io::Read as _; 2 | 3 | fn main() { 4 | let mut stdin = String::new(); 5 | std::io::stdin().read_to_string(&mut stdin).unwrap(); 6 | 7 | let term = anstyle_svg::Term::new(); 8 | let stdout = term.render_svg(&stdin); 9 | 10 | print!("{stdout}"); 11 | } 12 | -------------------------------------------------------------------------------- /crates/anstyle-svg/rainbow.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | echo "color: foreground" 4 | cargo run -p anstyle --example dump-style -- --layer fg 5 | echo 6 | echo "color: background" 7 | cargo run -p anstyle --example dump-style -- --layer bg 8 | echo 9 | echo "color: underline" 10 | cargo run -p anstyle --example dump-style -- --layer underline 11 | echo 12 | echo "italic" 13 | cargo run -p anstyle --example dump-style -- --effect italic 14 | echo 15 | echo "bold" 16 | cargo run -p anstyle --example dump-style -- --effect bold 17 | echo 18 | echo "dimmed" 19 | cargo run -p anstyle --example dump-style -- --effect dimmed 20 | echo 21 | echo "underline" 22 | cargo run -p anstyle --example dump-style -- --effect underline 23 | echo 24 | echo "double_underline" 25 | cargo run -p anstyle --example dump-style -- --effect double_underline 26 | echo 27 | echo "curly_underline" 28 | cargo run -p anstyle --example dump-style -- --effect curly_underline 29 | echo 30 | echo "dotted_underline" 31 | cargo run -p anstyle --example dump-style -- --effect dotted_underline 32 | echo 33 | echo "dashed_underline" 34 | cargo run -p anstyle --example dump-style -- --effect dashed_underline 35 | echo 36 | echo "blink" 37 | cargo run -p anstyle --example dump-style -- --effect blink 38 | echo 39 | echo "invert" 40 | cargo run -p anstyle --example dump-style -- --effect invert 41 | echo 42 | echo "hidden" 43 | cargo run -p anstyle --example dump-style -- --effect hidden 44 | echo 45 | echo "strikethrough" 46 | cargo run -p anstyle --example dump-style -- --effect strikethrough 47 | -------------------------------------------------------------------------------- /crates/anstyle-svg/tests/term.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn rainbow() { 3 | let input = std::fs::read_to_string("tests/rainbow.vte").unwrap(); 4 | let actual = anstyle_svg::Term::new().render_svg(&input); 5 | snapbox::assert_data_eq!(actual, snapbox::file!["rainbow.svg": Text].raw()); 6 | } 7 | 8 | #[test] 9 | fn rg_linus() { 10 | let input = std::fs::read_to_string("tests/rg_linus.vte").unwrap(); 11 | let actual = anstyle_svg::Term::new().render_svg(&input); 12 | snapbox::assert_data_eq!(actual, snapbox::file!["rg_linus.svg": Text].raw()); 13 | } 14 | 15 | #[test] 16 | fn hyperlink_demo() { 17 | let bytes = std::fs::read("tests/hyperlink-demo.vte").unwrap(); 18 | String::from_utf8(bytes).unwrap(); 19 | let input = std::fs::read_to_string("tests/hyperlink-demo.vte").unwrap(); 20 | let actual = anstyle_svg::Term::new().render_svg(&input); 21 | snapbox::assert_data_eq!(actual, snapbox::file!["hyperlink-demo.svg": Text].raw()); 22 | } 23 | -------------------------------------------------------------------------------- /crates/anstyle-syntect/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/). 6 | 7 | 8 | ## [Unreleased] - ReleaseDate 9 | 10 | ## [1.0.4] - 2025-06-04 11 | 12 | ## [1.0.3] - 2024-10-24 13 | 14 | ### Compatibility 15 | 16 | - Update MSRV to 1.66 17 | 18 | ## [1.0.2] - 2024-07-25 19 | 20 | ## [1.0.1] - 2024-05-02 21 | 22 | ### Fixes 23 | 24 | - Drop MSRV to 1.65 25 | 26 | ## [1.0.0] - 2023-04-13 27 | 28 | ### Breaking Change 29 | 30 | - Updated `anstyle` 31 | 32 | ## [0.2.0] - 2023-03-08 33 | 34 | ### Breaking Change 35 | 36 | - `anstyle` upgraded 37 | 38 | ## [0.1.2] - 2022-10-13 39 | 40 | ## [0.1.1] - 2022-10-13 41 | 42 | 43 | [Unreleased]: https://github.com/rust-cli/anstyle/compare/anstyle-syntect-v1.0.4...HEAD 44 | [1.0.4]: https://github.com/rust-cli/anstyle/compare/anstyle-syntect-v1.0.3...anstyle-syntect-v1.0.4 45 | [1.0.3]: https://github.com/rust-cli/anstyle/compare/anstyle-syntect-v1.0.2...anstyle-syntect-v1.0.3 46 | [1.0.2]: https://github.com/rust-cli/anstyle/compare/anstyle-syntect-v1.0.1...anstyle-syntect-v1.0.2 47 | [1.0.1]: https://github.com/rust-cli/anstyle/compare/anstyle-syntect-v1.0.0...anstyle-syntect-v1.0.1 48 | [1.0.0]: https://github.com/rust-cli/anstyle/compare/anstyle-syntect-v0.2.0...anstyle-syntect-v1.0.0 49 | [0.2.0]: https://github.com/rust-cli/anstyle/compare/anstyle-syntect-v0.1.2...anstyle-syntect-v0.2.0 50 | [0.1.2]: https://github.com/rust-cli/anstyle/compare/anstyle-syntect-v0.1.1...anstyle-syntect-v0.1.2 51 | [0.1.1]: https://github.com/rust-cli/anstyle/compare/eedb6bfa9b9dc932e9046d122eb0ef314184e013...anstyle-syntect-v0.1.1 52 | -------------------------------------------------------------------------------- /crates/anstyle-syntect/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "anstyle-syntect" 3 | version = "1.0.4" 4 | description = "Adapt between syntect and anstyle" 5 | categories = ["command-line-interface"] 6 | keywords = ["ansi", "terminal", "color", "syntect"] 7 | repository.workspace = true 8 | license.workspace = true 9 | edition.workspace = true 10 | rust-version.workspace = true 11 | include.workspace = true 12 | 13 | [package.metadata.docs.rs] 14 | all-features = true 15 | rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"] 16 | 17 | [package.metadata.release] 18 | pre-release-replacements = [ 19 | {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, 20 | {file="CHANGELOG.md", search="\\.\\.\\.HEAD", replace="...{{tag_name}}", exactly=1}, 21 | {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, 22 | {file="CHANGELOG.md", search="", replace="\n## [Unreleased] - ReleaseDate\n", exactly=1}, 23 | {file="CHANGELOG.md", search="", replace="\n[Unreleased]: https://github.com/rust-cli/anstyle/compare/{{tag_name}}...HEAD", exactly=1}, 24 | ] 25 | 26 | [dependencies] 27 | anstyle = { version = "1.0.0", path = "../anstyle" } 28 | syntect = { version = "5.0.0", default-features = false } 29 | 30 | [target.'cfg(any())'.dependencies] 31 | thiserror = "1.0.2" # HACK: bad minimal dep in syntect 32 | 33 | [lints] 34 | workspace = true 35 | -------------------------------------------------------------------------------- /crates/anstyle-syntect/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) Individual contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /crates/anstyle-syntect/README.md: -------------------------------------------------------------------------------- 1 | # anstyle-syntect 2 | 3 | > Convert between [`syntect`](https://lib.rs/syntect) highlighting and generic styling types 4 | 5 | [![Documentation](https://img.shields.io/badge/docs-master-blue.svg)][Documentation] 6 | ![License](https://img.shields.io/crates/l/anstyle-syntect.svg) 7 | [![Crates Status](https://img.shields.io/crates/v/anstyle-syntect.svg)](https://crates.io/crates/anstyle-syntect) 8 | 9 | ## License 10 | 11 | Licensed under either of 12 | 13 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or ) 14 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or ) 15 | 16 | at your option. 17 | 18 | ## [Contribute](../../CONTRIBUTING.md) 19 | 20 | Unless you explicitly state otherwise, any contribution intentionally 21 | submitted for inclusion in the work by you, as defined in the Apache-2.0 22 | license, shall be dual-licensed as above, without any additional terms or 23 | conditions. 24 | 25 | [Crates.io]: https://crates.io/crates/anstyle-syntect 26 | [Documentation]: https://docs.rs/anstyle-syntect 27 | -------------------------------------------------------------------------------- /crates/anstyle-syntect/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Convert between [`syntect`](https://lib.rs/syntect) highlighting and 2 | //! [generic styling types][anstyle::Style] 3 | 4 | #![cfg_attr(docsrs, feature(doc_auto_cfg))] 5 | #![warn(missing_docs)] 6 | #![warn(clippy::print_stderr)] 7 | #![warn(clippy::print_stdout)] 8 | 9 | /// Convert highlighting style to general style 10 | pub fn to_anstyle(style: syntect::highlighting::Style) -> anstyle::Style { 11 | anstyle::Style::new() 12 | .fg_color(Some(to_anstyle_color(style.foreground))) 13 | .bg_color(Some(to_anstyle_color(style.background))) 14 | .effects(to_anstyle_effects(style.font_style)) 15 | } 16 | 17 | /// Convert highlighting color to general color 18 | pub fn to_anstyle_color(color: syntect::highlighting::Color) -> anstyle::Color { 19 | anstyle::RgbColor(color.r, color.g, color.b).into() 20 | } 21 | 22 | /// Convert highlighting style to general effects 23 | pub fn to_anstyle_effects(style: syntect::highlighting::FontStyle) -> anstyle::Effects { 24 | let mut effects = anstyle::Effects::new(); 25 | 26 | if style.contains(syntect::highlighting::FontStyle::BOLD) { 27 | effects |= anstyle::Effects::BOLD; 28 | } 29 | if style.contains(syntect::highlighting::FontStyle::ITALIC) { 30 | effects |= anstyle::Effects::ITALIC; 31 | } 32 | if style.contains(syntect::highlighting::FontStyle::UNDERLINE) { 33 | effects |= anstyle::Effects::UNDERLINE; 34 | } 35 | 36 | effects 37 | } 38 | 39 | #[doc = include_str!("../README.md")] 40 | #[cfg(doctest)] 41 | pub struct ReadmeDoctests; 42 | -------------------------------------------------------------------------------- /crates/anstyle-termcolor/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/). 6 | 7 | 8 | ## [Unreleased] - ReleaseDate 9 | 10 | ## [1.1.4] - 2025-06-04 11 | 12 | ## [1.1.3] - 2024-10-24 13 | 14 | ### Compatibility 15 | 16 | - Update MSRV to 1.66 17 | 18 | ## [1.1.2] - 2024-07-25 19 | 20 | ## [1.1.1] - 2024-05-02 21 | 22 | ### Fixes 23 | 24 | - Drop MSRV to 1.65 25 | 26 | ## [1.1.0] - 2023-09-12 27 | 28 | ### Compatibility 29 | 30 | - Update MSRV to 1.70.0 31 | 32 | ### Fixes 33 | 34 | - Correctly apply text styles 35 | 36 | ## [1.0.0] - 2023-04-13 37 | 38 | ### Breaking Change 39 | 40 | - Updated `anstyle` 41 | 42 | ## [0.3.0] - 2023-03-08 43 | 44 | ### Breaking Change 45 | 46 | - `anstyle` upgraded 47 | 48 | ## [0.2.2] - 2022-10-07 49 | 50 | ## [0.2.1] - 2022-08-17 51 | 52 | ## [0.2.0] - 2022-05-19 53 | 54 | ## [0.1.1] - 2022-05-18 55 | 56 | 57 | [Unreleased]: https://github.com/rust-cli/anstyle/compare/anstyle-termcolor-v1.1.4...HEAD 58 | [1.1.4]: https://github.com/rust-cli/anstyle/compare/anstyle-termcolor-v1.1.3...anstyle-termcolor-v1.1.4 59 | [1.1.3]: https://github.com/rust-cli/anstyle/compare/anstyle-termcolor-v1.1.2...anstyle-termcolor-v1.1.3 60 | [1.1.2]: https://github.com/rust-cli/anstyle/compare/anstyle-termcolor-v1.1.1...anstyle-termcolor-v1.1.2 61 | [1.1.1]: https://github.com/rust-cli/anstyle/compare/anstyle-termcolor-v1.1.0...anstyle-termcolor-v1.1.1 62 | [1.1.0]: https://github.com/rust-cli/anstyle/compare/anstyle-termcolor-v1.0.0...anstyle-termcolor-v1.1.0 63 | [1.0.0]: https://github.com/rust-cli/anstyle/compare/anstyle-termcolor-v0.3.0...anstyle-termcolor-v1.0.0 64 | [0.3.0]: https://github.com/rust-cli/anstyle/compare/anstyle-termcolor-v0.2.2...anstyle-termcolor-v0.3.0 65 | [0.2.2]: https://github.com/rust-cli/anstyle/compare/anstyle-termcolor-v0.2.1...anstyle-termcolor-v0.2.2 66 | [0.2.1]: https://github.com/rust-cli/anstyle/compare/anstyle-termcolor-v0.2.0...anstyle-termcolor-v0.2.1 67 | [0.2.0]: https://github.com/rust-cli/anstyle/compare/anstyle-termcolor-v0.1.1...anstyle-termcolor-v0.2.0 68 | [0.1.1]: https://github.com/rust-cli/anstyle/compare/2c1d02f...anstyle-termcolor-v0.1.1 69 | -------------------------------------------------------------------------------- /crates/anstyle-termcolor/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "anstyle-termcolor" 3 | version = "1.1.4" 4 | description = "Adapt between termcolor and anstyle" 5 | categories = ["command-line-interface"] 6 | keywords = ["ansi", "terminal", "color", "termcolor"] 7 | repository.workspace = true 8 | license.workspace = true 9 | edition.workspace = true 10 | rust-version.workspace = true 11 | include.workspace = true 12 | 13 | [package.metadata.docs.rs] 14 | all-features = true 15 | rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"] 16 | 17 | [package.metadata.release] 18 | pre-release-replacements = [ 19 | {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, 20 | {file="CHANGELOG.md", search="\\.\\.\\.HEAD", replace="...{{tag_name}}", exactly=1}, 21 | {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, 22 | {file="CHANGELOG.md", search="", replace="\n## [Unreleased] - ReleaseDate\n", exactly=1}, 23 | {file="CHANGELOG.md", search="", replace="\n[Unreleased]: https://github.com/rust-cli/anstyle/compare/{{tag_name}}...HEAD", exactly=1}, 24 | ] 25 | 26 | [dependencies] 27 | anstyle = { version = "1.0.0", path = "../anstyle" } 28 | termcolor = "1.1.3" 29 | 30 | [lints] 31 | workspace = true 32 | -------------------------------------------------------------------------------- /crates/anstyle-termcolor/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) Individual contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /crates/anstyle-termcolor/README.md: -------------------------------------------------------------------------------- 1 | # anstyle-termcolor 2 | 3 | > Convert between [termcolor](https://lib.rs/termcolor) and generic styling types 4 | 5 | [![Documentation](https://img.shields.io/badge/docs-master-blue.svg)][Documentation] 6 | ![License](https://img.shields.io/crates/l/anstyle-termcolor.svg) 7 | [![Crates Status](https://img.shields.io/crates/v/anstyle-termcolor.svg)](https://crates.io/crates/anstyle-termcolor) 8 | 9 | ## License 10 | 11 | Licensed under either of 12 | 13 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or ) 14 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or ) 15 | 16 | at your option. 17 | 18 | ## [Contribute](../../CONTRIBUTING.md) 19 | 20 | Unless you explicitly state otherwise, any contribution intentionally 21 | submitted for inclusion in the work by you, as defined in the Apache-2.0 22 | license, shall be dual-licensed as above, without any additional terms or 23 | conditions. 24 | 25 | [Crates.io]: https://crates.io/crates/anstyle-termcolor 26 | [Documentation]: https://docs.rs/anstyle-termcolor 27 | -------------------------------------------------------------------------------- /crates/anstyle-termcolor/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Convert between [termcolor](https://lib.rs/termcolor) and [generic styling types][anstyle] 2 | 3 | #![cfg_attr(docsrs, feature(doc_auto_cfg))] 4 | #![warn(missing_docs)] 5 | #![warn(clippy::print_stderr)] 6 | #![warn(clippy::print_stdout)] 7 | 8 | /// Adapt generic styling to [`termcolor`] 9 | pub fn to_termcolor_spec(style: anstyle::Style) -> termcolor::ColorSpec { 10 | let fg = style.get_fg_color().map(to_termcolor_color); 11 | let bg = style.get_bg_color().map(to_termcolor_color); 12 | let effects = style.get_effects(); 13 | 14 | let mut style = termcolor::ColorSpec::new(); 15 | style.set_fg(fg); 16 | style.set_bg(bg); 17 | style.set_bold(effects.contains(anstyle::Effects::BOLD)); 18 | style.set_dimmed(effects.contains(anstyle::Effects::DIMMED)); 19 | style.set_italic(effects.contains(anstyle::Effects::ITALIC)); 20 | style.set_underline(effects.contains(anstyle::Effects::UNDERLINE)); 21 | style 22 | } 23 | 24 | /// Adapt generic colors to [`termcolor`] 25 | pub fn to_termcolor_color(color: anstyle::Color) -> termcolor::Color { 26 | match color { 27 | anstyle::Color::Ansi(ansi) => ansi_to_termcolor_color(ansi), 28 | anstyle::Color::Ansi256(xterm) => xterm_to_termcolor_color(xterm), 29 | anstyle::Color::Rgb(rgb) => rgb_to_termcolor_color(rgb), 30 | } 31 | } 32 | 33 | fn ansi_to_termcolor_color(color: anstyle::AnsiColor) -> termcolor::Color { 34 | match color { 35 | anstyle::AnsiColor::Black => termcolor::Color::Black, 36 | anstyle::AnsiColor::Red => termcolor::Color::Red, 37 | anstyle::AnsiColor::Green => termcolor::Color::Green, 38 | anstyle::AnsiColor::Yellow => termcolor::Color::Yellow, 39 | anstyle::AnsiColor::Blue => termcolor::Color::Blue, 40 | anstyle::AnsiColor::Magenta => termcolor::Color::Magenta, 41 | anstyle::AnsiColor::Cyan => termcolor::Color::Cyan, 42 | anstyle::AnsiColor::White => termcolor::Color::White, 43 | anstyle::AnsiColor::BrightBlack => termcolor::Color::Black, 44 | anstyle::AnsiColor::BrightRed => termcolor::Color::Red, 45 | anstyle::AnsiColor::BrightGreen => termcolor::Color::Green, 46 | anstyle::AnsiColor::BrightYellow => termcolor::Color::Yellow, 47 | anstyle::AnsiColor::BrightBlue => termcolor::Color::Black, 48 | anstyle::AnsiColor::BrightMagenta => termcolor::Color::Magenta, 49 | anstyle::AnsiColor::BrightCyan => termcolor::Color::Cyan, 50 | anstyle::AnsiColor::BrightWhite => termcolor::Color::White, 51 | } 52 | } 53 | 54 | fn xterm_to_termcolor_color(color: anstyle::Ansi256Color) -> termcolor::Color { 55 | termcolor::Color::Ansi256(color.0) 56 | } 57 | 58 | fn rgb_to_termcolor_color(color: anstyle::RgbColor) -> termcolor::Color { 59 | termcolor::Color::Rgb(color.0, color.1, color.2) 60 | } 61 | 62 | #[doc = include_str!("../README.md")] 63 | #[cfg(doctest)] 64 | pub struct ReadmeDoctests; 65 | -------------------------------------------------------------------------------- /crates/anstyle-wincon/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/). 6 | 7 | 8 | ## [Unreleased] - ReleaseDate 9 | 10 | ## [3.0.9] - 2025-06-04 11 | 12 | ## [3.0.8] - 2025-05-22 13 | 14 | ## [3.0.7] - 2025-01-13 15 | 16 | - Restore MSRV for Windows 17 | 18 | ## [3.0.6] - 2024-10-24 19 | 20 | ## [3.0.5] - 2024-10-24 21 | 22 | ### Compatibility 23 | 24 | - Update MSRV to 1.66 25 | 26 | ### Features 27 | 28 | - Implement AutoStream for dyn Write + auto traits 29 | 30 | ## [3.0.4] - 2024-07-25 31 | 32 | ## [3.0.3] - 2024-05-02 33 | 34 | ### Fixes 35 | 36 | - Drop MSRV to 1.65 37 | 38 | ## [3.0.2] - 2023-12-04 39 | 40 | ## [3.0.1] - 2023-09-29 41 | 42 | ### Features 43 | 44 | - Impl `WinconStream` for `Box` 45 | 46 | ## [3.0.0] - 2023-09-28 47 | 48 | ### Breaking Change 49 | 50 | - API is rewritten from scratch, just being a `WinconStream::write_colored` 51 | 52 | ## [2.1.0] - 2023-08-24 53 | 54 | ### Compatibility 55 | 56 | - Update MSRV to 1.70.0 57 | 58 | ### Features 59 | 60 | - Allow querying `is_terminal` 61 | 62 | ## [2.0.0] - 2023-08-23 63 | 64 | ### Breaking Change 65 | 66 | - Removed support for non-static locked streams 67 | - Removed `Console::map` 68 | - Exposed lower level get/set color APIs 69 | 70 | ### Features 71 | 72 | ## [1.0.2] - 2023-08-09 73 | 74 | ## [1.0.1] - 2023-04-24 75 | 76 | ### Features 77 | 78 | - `std::fs::File` support (writes ANSI to it) 79 | 80 | ## [1.0.0] - 2023-04-13 81 | 82 | ### Breaking Change 83 | 84 | - Updated `anstyle` 85 | 86 | ## [0.2.0] - 2023-03-13 87 | 88 | ### Breaking Chaneg 89 | 90 | - Take two at `Console::new` reporting errors 91 | 92 | ## [0.1.1] - 2023-03-08 93 | 94 | ### Features 95 | 96 | - `Console` now implements `Debug` 97 | 98 | ## [0.1.0] - 2023-03-08 99 | 100 | ### Breaking Change 101 | 102 | - `anstyle` upgraded 103 | - `Console::new` no longer errors 104 | - `Stream::set_color` and `Stream::get_color` changed their signatures 105 | 106 | ### Features 107 | 108 | - `Console::lock` support 109 | - `Console::into_inner` support 110 | - `Console::map` support 111 | - `Console::flush` support 112 | - An ANSI implementation for windows 113 | 114 | 115 | ## [0.0.1] - 2023-03-07 116 | 117 | 118 | [Unreleased]: https://github.com/rust-cli/anstyle/compare/anstyle-wincon-v3.0.9...HEAD 119 | [3.0.9]: https://github.com/rust-cli/anstyle/compare/anstyle-wincon-v3.0.8...anstyle-wincon-v3.0.9 120 | [3.0.8]: https://github.com/rust-cli/anstyle/compare/anstyle-wincon-v3.0.7...anstyle-wincon-v3.0.8 121 | [3.0.7]: https://github.com/rust-cli/anstyle/compare/anstyle-wincon-v3.0.6...anstyle-wincon-v3.0.7 122 | [3.0.6]: https://github.com/rust-cli/anstyle/compare/anstyle-wincon-v3.0.5...anstyle-wincon-v3.0.6 123 | [3.0.5]: https://github.com/rust-cli/anstyle/compare/anstyle-wincon-v3.0.4...anstyle-wincon-v3.0.5 124 | [3.0.4]: https://github.com/rust-cli/anstyle/compare/anstyle-wincon-v3.0.3...anstyle-wincon-v3.0.4 125 | [3.0.3]: https://github.com/rust-cli/anstyle/compare/anstyle-wincon-v3.0.2...anstyle-wincon-v3.0.3 126 | [3.0.2]: https://github.com/rust-cli/anstyle/compare/anstyle-wincon-v3.0.1...anstyle-wincon-v3.0.2 127 | [3.0.1]: https://github.com/rust-cli/anstyle/compare/anstyle-wincon-v3.0.0...anstyle-wincon-v3.0.1 128 | [3.0.0]: https://github.com/rust-cli/anstyle/compare/anstyle-wincon-v2.1.0...anstyle-wincon-v3.0.0 129 | [2.1.0]: https://github.com/rust-cli/anstyle/compare/anstyle-wincon-v2.0.0...anstyle-wincon-v2.1.0 130 | [2.0.0]: https://github.com/rust-cli/anstyle/compare/anstyle-wincon-v1.0.2...anstyle-wincon-v2.0.0 131 | [1.0.2]: https://github.com/rust-cli/anstyle/compare/anstyle-wincon-v1.0.1...anstyle-wincon-v1.0.2 132 | [1.0.1]: https://github.com/rust-cli/anstyle/compare/anstyle-wincon-v1.0.0...anstyle-wincon-v1.0.1 133 | [1.0.0]: https://github.com/rust-cli/anstyle/compare/anstyle-wincon-v0.2.0...anstyle-wincon-v1.0.0 134 | [0.2.0]: https://github.com/rust-cli/anstyle/compare/anstyle-wincon-v0.1.1...anstyle-wincon-v0.2.0 135 | [0.1.1]: https://github.com/rust-cli/anstyle/compare/anstyle-wincon-v0.1.0...anstyle-wincon-v0.1.1 136 | [0.1.0]: https://github.com/rust-cli/anstyle/compare/anstyle-wincon-v0.0.1...anstyle-wincon-v0.1.0 137 | [0.0.1]: https://github.com/rust-cli/anstyle/compare/58e49814ccbdbd9cd30862e268a391cd61ce0f89...anstyle-wincon-v0.0.1 138 | -------------------------------------------------------------------------------- /crates/anstyle-wincon/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "anstyle-wincon" 3 | version = "3.0.9" 4 | description = "Styling legacy Windows terminals" 5 | categories = ["command-line-interface"] 6 | keywords = ["ansi", "terminal", "color", "windows"] 7 | repository.workspace = true 8 | license.workspace = true 9 | edition.workspace = true 10 | rust-version.workspace = true 11 | include.workspace = true 12 | 13 | [package.metadata.docs.rs] 14 | all-features = true 15 | rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"] 16 | targets = ["x86_64-pc-windows-msvc"] 17 | 18 | [package.metadata.release] 19 | pre-release-replacements = [ 20 | {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, 21 | {file="CHANGELOG.md", search="\\.\\.\\.HEAD", replace="...{{tag_name}}", exactly=1}, 22 | {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, 23 | {file="CHANGELOG.md", search="", replace="\n## [Unreleased] - ReleaseDate\n", exactly=1}, 24 | {file="CHANGELOG.md", search="", replace="\n[Unreleased]: https://github.com/rust-cli/anstyle/compare/{{tag_name}}...HEAD", exactly=1}, 25 | ] 26 | 27 | [dependencies] 28 | anstyle = { version = "1.0.0", path = "../anstyle" } 29 | 30 | [dev-dependencies] 31 | lexopt = "0.3.0" 32 | 33 | [target.'cfg(windows)'.dependencies] 34 | windows-sys = { version = "0.59.0", features = ["Win32_System_Console", "Win32_Foundation"] } 35 | once_cell_polyfill = "1.56.0" 36 | 37 | [lints] 38 | workspace = true 39 | -------------------------------------------------------------------------------- /crates/anstyle-wincon/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) Individual contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /crates/anstyle-wincon/README.md: -------------------------------------------------------------------------------- 1 | # anstyle-wincon 2 | 3 | > Styling legacy Windows terminals 4 | 5 | [![Documentation](https://img.shields.io/badge/docs-master-blue.svg)][Documentation] 6 | ![License](https://img.shields.io/crates/l/anstyle-wincon.svg) 7 | [![Crates Status](https://img.shields.io/crates/v/anstyle-wincon.svg)](https://crates.io/crates/anstyle-wincon) 8 | 9 | ## License 10 | 11 | Licensed under either of 12 | 13 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or ) 14 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or ) 15 | 16 | at your option. 17 | 18 | ## [Contribute](../../CONTRIBUTING.md) 19 | 20 | Unless you explicitly state otherwise, any contribution intentionally 21 | submitted for inclusion in the work by you, as defined in the Apache-2.0 22 | license, shall be dual-licensed as above, without any additional terms or 23 | conditions. 24 | 25 | ### Special Thanks 26 | 27 | [burntsushi](https://github.com/burntsushi) for [termcolor](https://github.com/burntsushi/termcolor) as I don't know Windows either :) 28 | 29 | [Crates.io]: https://crates.io/crates/anstyle-wincon 30 | [Documentation]: https://docs.rs/anstyle-wincon 31 | -------------------------------------------------------------------------------- /crates/anstyle-wincon/examples/set-wincon.rs: -------------------------------------------------------------------------------- 1 | //! Interactively manipulate wincon colors 2 | 3 | #![cfg_attr(not(windows), allow(dead_code))] 4 | 5 | #[cfg(not(windows))] 6 | fn main() { 7 | panic!("unsupported"); 8 | } 9 | 10 | #[cfg(windows)] 11 | fn main() -> Result<(), lexopt::Error> { 12 | use anstyle_wincon::WinconStream as _; 13 | 14 | let args = Args::parse()?; 15 | let stdout = std::io::stdout(); 16 | let mut stdout = stdout.lock(); 17 | 18 | let fg = args.fg.and_then(|c| c.into_ansi()); 19 | let bg = args.bg.and_then(|c| c.into_ansi()); 20 | 21 | let _ = stdout.write_colored(fg, bg, b""); 22 | 23 | #[allow(clippy::mem_forget)] 24 | std::mem::forget(stdout); 25 | 26 | Ok(()) 27 | } 28 | 29 | #[derive(Default)] 30 | struct Args { 31 | fg: Option, 32 | bg: Option, 33 | } 34 | 35 | impl Args { 36 | fn parse() -> Result { 37 | use lexopt::prelude::*; 38 | 39 | let mut res = Args::default(); 40 | 41 | let mut args = lexopt::Parser::from_env(); 42 | while let Some(arg) = args.next()? { 43 | match arg { 44 | Long("fg") => { 45 | res.fg = Some( 46 | args.value()? 47 | .parse_with(|s| s.parse::().map(anstyle::Ansi256Color))?, 48 | ); 49 | } 50 | Long("bg") => { 51 | res.fg = Some( 52 | args.value()? 53 | .parse_with(|s| s.parse::().map(anstyle::Ansi256Color))?, 54 | ); 55 | } 56 | _ => return Err(arg.unexpected()), 57 | } 58 | } 59 | Ok(res) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /crates/anstyle-wincon/src/ansi.rs: -------------------------------------------------------------------------------- 1 | //! Low-level ANSI-styling 2 | 3 | /// Write ANSI colored text to the stream 4 | pub fn write_colored( 5 | stream: &mut S, 6 | fg: Option, 7 | bg: Option, 8 | data: &[u8], 9 | ) -> std::io::Result { 10 | let non_default = fg.is_some() || bg.is_some(); 11 | 12 | if non_default { 13 | if let Some(fg) = fg { 14 | write!(stream, "{}", fg.render_fg())?; 15 | } 16 | if let Some(bg) = bg { 17 | write!(stream, "{}", bg.render_bg())?; 18 | } 19 | } 20 | let written = stream.write(data)?; 21 | if non_default { 22 | write!(stream, "{}", anstyle::Reset.render())?; 23 | } 24 | Ok(written) 25 | } 26 | -------------------------------------------------------------------------------- /crates/anstyle-wincon/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Styling legacy Windows terminals 2 | //! 3 | //! See [`WinconStream`] 4 | //! 5 | //! This fills a similar role as [`winapi-util`](https://crates.io/crates/winapi-util) does for 6 | //! [`termcolor`](https://crates.io/crates/termcolor) with the differences 7 | //! - Uses `windows-sys` rather than `winapi` 8 | //! - Uses [`anstyle`](https://crates.io/crates/termcolor) rather than defining its own types 9 | //! - More focused, smaller 10 | 11 | #![cfg_attr(docsrs, feature(doc_auto_cfg))] 12 | #![warn(missing_docs)] 13 | #![warn(clippy::print_stderr)] 14 | #![warn(clippy::print_stdout)] 15 | 16 | pub mod ansi; 17 | mod stream; 18 | #[cfg(windows)] 19 | pub mod windows; 20 | 21 | pub use stream::WinconStream; 22 | 23 | #[doc = include_str!("../README.md")] 24 | #[cfg(doctest)] 25 | pub struct ReadmeDoctests; 26 | -------------------------------------------------------------------------------- /crates/anstyle-wincon/src/stream.rs: -------------------------------------------------------------------------------- 1 | /// Extend `std::io::Write` with wincon styling 2 | pub trait WinconStream { 3 | /// Write colored text to the stream 4 | fn write_colored( 5 | &mut self, 6 | fg: Option, 7 | bg: Option, 8 | data: &[u8], 9 | ) -> std::io::Result; 10 | } 11 | 12 | impl WinconStream for &mut T { 13 | fn write_colored( 14 | &mut self, 15 | fg: Option, 16 | bg: Option, 17 | data: &[u8], 18 | ) -> std::io::Result { 19 | (**self).write_colored(fg, bg, data) 20 | } 21 | } 22 | 23 | impl WinconStream for Box { 24 | fn write_colored( 25 | &mut self, 26 | fg: Option, 27 | bg: Option, 28 | data: &[u8], 29 | ) -> std::io::Result { 30 | (**self).write_colored(fg, bg, data) 31 | } 32 | } 33 | 34 | impl WinconStream for dyn std::io::Write { 35 | fn write_colored( 36 | &mut self, 37 | fg: Option, 38 | bg: Option, 39 | data: &[u8], 40 | ) -> std::io::Result { 41 | crate::ansi::write_colored(self, fg, bg, data) 42 | } 43 | } 44 | 45 | impl WinconStream for dyn std::io::Write + Send { 46 | fn write_colored( 47 | &mut self, 48 | fg: Option, 49 | bg: Option, 50 | data: &[u8], 51 | ) -> std::io::Result { 52 | crate::ansi::write_colored(self, fg, bg, data) 53 | } 54 | } 55 | 56 | impl WinconStream for dyn std::io::Write + Send + Sync { 57 | fn write_colored( 58 | &mut self, 59 | fg: Option, 60 | bg: Option, 61 | data: &[u8], 62 | ) -> std::io::Result { 63 | crate::ansi::write_colored(self, fg, bg, data) 64 | } 65 | } 66 | 67 | impl WinconStream for std::fs::File { 68 | fn write_colored( 69 | &mut self, 70 | fg: Option, 71 | bg: Option, 72 | data: &[u8], 73 | ) -> std::io::Result { 74 | crate::ansi::write_colored(self, fg, bg, data) 75 | } 76 | } 77 | 78 | impl WinconStream for Vec { 79 | fn write_colored( 80 | &mut self, 81 | fg: Option, 82 | bg: Option, 83 | data: &[u8], 84 | ) -> std::io::Result { 85 | crate::ansi::write_colored(self, fg, bg, data) 86 | } 87 | } 88 | 89 | impl WinconStream for std::io::Stdout { 90 | fn write_colored( 91 | &mut self, 92 | fg: Option, 93 | bg: Option, 94 | data: &[u8], 95 | ) -> std::io::Result { 96 | // Ensure exclusive access 97 | self.lock().write_colored(fg, bg, data) 98 | } 99 | } 100 | 101 | impl WinconStream for std::io::Stderr { 102 | fn write_colored( 103 | &mut self, 104 | fg: Option, 105 | bg: Option, 106 | data: &[u8], 107 | ) -> std::io::Result { 108 | // Ensure exclusive access 109 | self.lock().write_colored(fg, bg, data) 110 | } 111 | } 112 | 113 | #[cfg(not(windows))] 114 | mod platform { 115 | impl super::WinconStream for std::io::StdoutLock<'_> { 116 | fn write_colored( 117 | &mut self, 118 | fg: Option, 119 | bg: Option, 120 | data: &[u8], 121 | ) -> std::io::Result { 122 | crate::ansi::write_colored(self, fg, bg, data) 123 | } 124 | } 125 | 126 | impl super::WinconStream for std::io::StderrLock<'_> { 127 | fn write_colored( 128 | &mut self, 129 | fg: Option, 130 | bg: Option, 131 | data: &[u8], 132 | ) -> std::io::Result { 133 | crate::ansi::write_colored(self, fg, bg, data) 134 | } 135 | } 136 | } 137 | 138 | #[cfg(windows)] 139 | mod platform { 140 | impl super::WinconStream for std::io::StdoutLock<'_> { 141 | fn write_colored( 142 | &mut self, 143 | fg: Option, 144 | bg: Option, 145 | data: &[u8], 146 | ) -> std::io::Result { 147 | let initial = crate::windows::stdout_initial_colors(); 148 | crate::windows::write_colored(self, fg, bg, data, initial) 149 | } 150 | } 151 | 152 | impl super::WinconStream for std::io::StderrLock<'_> { 153 | fn write_colored( 154 | &mut self, 155 | fg: Option, 156 | bg: Option, 157 | data: &[u8], 158 | ) -> std::io::Result { 159 | let initial = crate::windows::stderr_initial_colors(); 160 | crate::windows::write_colored(self, fg, bg, data, initial) 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /crates/anstyle-yansi/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/). 6 | 7 | 8 | ## [Unreleased] - ReleaseDate 9 | 10 | ## [2.0.3] - 2025-06-04 11 | 12 | ## [2.0.2] - 2024-10-24 13 | 14 | ### Compatibility 15 | 16 | - Update MSRV to 1.66 17 | 18 | ## [2.0.1] - 2024-07-25 19 | 20 | ## [2.0.0] - 2024-06-23 21 | 22 | - Update to yansi 1.0 23 | 24 | ## [1.0.1] - 2024-05-02 25 | 26 | ### Fixes 27 | 28 | - Drop MSRV to 1.65 29 | 30 | ## [1.0.0] - 2023-04-13 31 | 32 | ### Breaking Change 33 | 34 | - Updated `anstyle` 35 | 36 | ## [0.3.0] - 2023-03-08 37 | 38 | ### Breaking Change 39 | 40 | - `anstyle` upgraded 41 | 42 | ## [0.2.2] - 2022-10-07 43 | 44 | ## [0.2.1] - 2022-08-17 45 | 46 | ## [0.2.0] - 2022-05-19 47 | 48 | ## [0.1.1] - 2022-05-18 49 | 50 | 51 | [Unreleased]: https://github.com/rust-cli/anstyle/compare/anstyle-yansi-v2.0.3...HEAD 52 | [2.0.3]: https://github.com/rust-cli/anstyle/compare/anstyle-yansi-v2.0.2...anstyle-yansi-v2.0.3 53 | [2.0.2]: https://github.com/rust-cli/anstyle/compare/anstyle-yansi-v2.0.1...anstyle-yansi-v2.0.2 54 | [2.0.1]: https://github.com/rust-cli/anstyle/compare/anstyle-yansi-v2.0.0...anstyle-yansi-v2.0.1 55 | [2.0.0]: https://github.com/rust-cli/anstyle/compare/anstyle-yansi-v1.0.1...anstyle-yansi-v2.0.0 56 | [1.0.1]: https://github.com/rust-cli/anstyle/compare/anstyle-yansi-v1.0.0...anstyle-yansi-v1.0.1 57 | [1.0.0]: https://github.com/rust-cli/anstyle/compare/anstyle-yansi-v0.3.0...anstyle-yansi-v1.0.0 58 | [0.3.0]: https://github.com/rust-cli/anstyle/compare/anstyle-yansi-v0.2.2...anstyle-yansi-v0.3.0 59 | [0.2.2]: https://github.com/rust-cli/anstyle/compare/anstyle-yansi-v0.2.1...anstyle-yansi-v0.2.2 60 | [0.2.1]: https://github.com/rust-cli/anstyle/compare/anstyle-yansi-v0.2.0...anstyle-yansi-v0.2.1 61 | [0.2.0]: https://github.com/rust-cli/anstyle/compare/anstyle-yansi-v0.1.1...anstyle-yansi-v0.2.0 62 | [0.1.1]: https://github.com/rust-cli/anstyle/compare/1fd4f936999b5b3ecd5e3f0d8decb9e9c62b7f24...anstyle-yansi-v0.1.1 63 | -------------------------------------------------------------------------------- /crates/anstyle-yansi/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "anstyle-yansi" 3 | version = "2.0.3" 4 | description = "Adapt between yansi and anstyle" 5 | categories = ["command-line-interface"] 6 | keywords = ["ansi", "terminal", "color", "yansi"] 7 | repository.workspace = true 8 | license.workspace = true 9 | edition.workspace = true 10 | rust-version.workspace = true 11 | include.workspace = true 12 | 13 | [package.metadata.docs.rs] 14 | all-features = true 15 | rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"] 16 | 17 | [package.metadata.release] 18 | pre-release-replacements = [ 19 | {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, 20 | {file="CHANGELOG.md", search="\\.\\.\\.HEAD", replace="...{{tag_name}}", exactly=1}, 21 | {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, 22 | {file="CHANGELOG.md", search="", replace="\n## [Unreleased] - ReleaseDate\n", exactly=1}, 23 | {file="CHANGELOG.md", search="", replace="\n[Unreleased]: https://github.com/rust-cli/anstyle/compare/{{tag_name}}...HEAD", exactly=1}, 24 | ] 25 | 26 | [dependencies] 27 | anstyle = { version = "1.0.0", path = "../anstyle" } 28 | yansi = "1.0.0" 29 | 30 | [lints] 31 | workspace = true 32 | -------------------------------------------------------------------------------- /crates/anstyle-yansi/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) Individual contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /crates/anstyle-yansi/README.md: -------------------------------------------------------------------------------- 1 | # anstyle-yansi 2 | 3 | > Convert between [yansi](https://lib.rs/yansi) and generic styling types 4 | 5 | [![Documentation](https://img.shields.io/badge/docs-master-blue.svg)][Documentation] 6 | ![License](https://img.shields.io/crates/l/anstyle-yansi.svg) 7 | [![Crates Status](https://img.shields.io/crates/v/anstyle-yansi.svg)](https://crates.io/crates/anstyle-yansi) 8 | 9 | ## License 10 | 11 | Licensed under either of 12 | 13 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or ) 14 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or ) 15 | 16 | at your option. 17 | 18 | ## [Contribute](../../CONTRIBUTING.md) 19 | 20 | Unless you explicitly state otherwise, any contribution intentionally 21 | submitted for inclusion in the work by you, as defined in the Apache-2.0 22 | license, shall be dual-licensed as above, without any additional terms or 23 | conditions. 24 | 25 | [Crates.io]: https://crates.io/crates/anstyle-yansi 26 | [Documentation]: https://docs.rs/anstyle-yansi 27 | -------------------------------------------------------------------------------- /crates/anstyle-yansi/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Convert between [yansi](https://lib.rs/yansi) and generic styling types 2 | 3 | #![cfg_attr(docsrs, feature(doc_auto_cfg))] 4 | #![warn(missing_docs)] 5 | #![warn(clippy::print_stderr)] 6 | #![warn(clippy::print_stdout)] 7 | 8 | /// Adapt generic styling to [`yansi`] 9 | pub fn to_yansi_style(style: anstyle::Style) -> yansi::Style { 10 | let fg = style 11 | .get_fg_color() 12 | .map(to_yansi_color) 13 | .unwrap_or(yansi::Color::Primary); 14 | let bg = style 15 | .get_bg_color() 16 | .map(to_yansi_color) 17 | .unwrap_or(yansi::Color::Primary); 18 | let effects = style.get_effects(); 19 | 20 | let mut style = yansi::Style::new().fg(fg).bg(bg); 21 | if effects.contains(anstyle::Effects::BOLD) { 22 | style = style.bold(); 23 | } 24 | if effects.contains(anstyle::Effects::DIMMED) { 25 | style = style.dim(); 26 | } 27 | if effects.contains(anstyle::Effects::ITALIC) { 28 | style = style.italic(); 29 | } 30 | if effects.contains(anstyle::Effects::UNDERLINE) { 31 | style = style.underline(); 32 | } 33 | if effects.contains(anstyle::Effects::BLINK) { 34 | style = style.blink(); 35 | } 36 | if effects.contains(anstyle::Effects::INVERT) { 37 | style = style.invert(); 38 | } 39 | if effects.contains(anstyle::Effects::HIDDEN) { 40 | style = style.conceal(); 41 | } 42 | if effects.contains(anstyle::Effects::STRIKETHROUGH) { 43 | style = style.strike(); 44 | } 45 | style 46 | } 47 | 48 | /// Adapt generic color to [`yansi`] 49 | pub fn to_yansi_color(color: anstyle::Color) -> yansi::Color { 50 | match color { 51 | anstyle::Color::Ansi(ansi) => ansi_to_yansi_color(ansi), 52 | anstyle::Color::Ansi256(xterm) => xterm_to_yansi_color(xterm), 53 | anstyle::Color::Rgb(rgb) => rgb_to_yansi_color(rgb), 54 | } 55 | } 56 | 57 | fn ansi_to_yansi_color(color: anstyle::AnsiColor) -> yansi::Color { 58 | match color { 59 | anstyle::AnsiColor::Black => yansi::Color::Black, 60 | anstyle::AnsiColor::Red => yansi::Color::Red, 61 | anstyle::AnsiColor::Green => yansi::Color::Green, 62 | anstyle::AnsiColor::Yellow => yansi::Color::Yellow, 63 | anstyle::AnsiColor::Blue => yansi::Color::Blue, 64 | anstyle::AnsiColor::Magenta => yansi::Color::Magenta, 65 | anstyle::AnsiColor::Cyan => yansi::Color::Cyan, 66 | anstyle::AnsiColor::White => yansi::Color::White, 67 | anstyle::AnsiColor::BrightBlack => yansi::Color::BrightBlack, 68 | anstyle::AnsiColor::BrightRed => yansi::Color::BrightRed, 69 | anstyle::AnsiColor::BrightGreen => yansi::Color::BrightGreen, 70 | anstyle::AnsiColor::BrightYellow => yansi::Color::BrightYellow, 71 | anstyle::AnsiColor::BrightBlue => yansi::Color::BrightBlack, 72 | anstyle::AnsiColor::BrightMagenta => yansi::Color::BrightMagenta, 73 | anstyle::AnsiColor::BrightCyan => yansi::Color::BrightCyan, 74 | anstyle::AnsiColor::BrightWhite => yansi::Color::BrightWhite, 75 | } 76 | } 77 | 78 | fn xterm_to_yansi_color(color: anstyle::Ansi256Color) -> yansi::Color { 79 | yansi::Color::Fixed(color.0) 80 | } 81 | 82 | fn rgb_to_yansi_color(color: anstyle::RgbColor) -> yansi::Color { 83 | yansi::Color::Rgb(color.0, color.1, color.2) 84 | } 85 | 86 | #[doc = include_str!("../README.md")] 87 | #[cfg(doctest)] 88 | pub struct ReadmeDoctests; 89 | -------------------------------------------------------------------------------- /crates/anstyle/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "anstyle" 3 | version = "1.0.11" 4 | description = "ANSI text styling" 5 | categories = ["command-line-interface"] 6 | keywords = ["ansi", "terminal", "color", "no_std"] 7 | repository.workspace = true 8 | license.workspace = true 9 | edition.workspace = true 10 | rust-version.workspace = true 11 | include.workspace = true 12 | 13 | [package.metadata.docs.rs] 14 | all-features = true 15 | rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"] 16 | 17 | [package.metadata.release] 18 | tag-prefix = "" 19 | pre-release-replacements = [ 20 | {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, 21 | {file="CHANGELOG.md", search="\\.\\.\\.HEAD", replace="...{{tag_name}}", exactly=1}, 22 | {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, 23 | {file="CHANGELOG.md", search="", replace="\n## [Unreleased] - ReleaseDate\n", exactly=1}, 24 | {file="CHANGELOG.md", search="", replace="\n[Unreleased]: https://github.com/rust-cli/anstyle/compare/{{tag_name}}...HEAD", exactly=1}, 25 | ] 26 | 27 | [features] 28 | default = ["std"] 29 | std = [] 30 | 31 | [dependencies] 32 | 33 | [dev-dependencies] 34 | lexopt = "0.3.0" 35 | 36 | [lints] 37 | workspace = true 38 | -------------------------------------------------------------------------------- /crates/anstyle/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) Individual contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /crates/anstyle/README.md: -------------------------------------------------------------------------------- 1 | # anstyle 2 | 3 | > ANSI text styling 4 | 5 | *A portmanteau of "ansi style"* 6 | 7 | [![Documentation](https://img.shields.io/badge/docs-master-blue.svg)][Documentation] 8 | ![License](https://img.shields.io/crates/l/anstyle.svg) 9 | [![Crates Status](https://img.shields.io/crates/v/anstyle.svg)](https://crates.io/crates/anstyle) 10 | 11 | `anstyle` provides core types describing [ANSI styling escape 12 | codes](https://en.wikipedia.org/wiki/ANSI_escape_code) for interoperability 13 | between crates. For example, this would allow a crate to provide an API for 14 | customizing the colors used without putting the underlying text styling crate 15 | in the API. 16 | 17 | For integration with your text styling crate, see: 18 | - [anstyle-termcolor](crates/termcolor) 19 | - [anstyle-owo-colors](crates/owo) 20 | - [anstyle-yansi](crates/yansi) 21 | 22 | General utilities: 23 | - [anstyle-git](crates/git): Parse Git style descriptions 24 | 25 | ## License 26 | 27 | Licensed under either of 28 | 29 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or ) 30 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or ) 31 | 32 | at your option. 33 | 34 | ## [Contribute](../../CONTRIBUTING.md) 35 | 36 | Unless you explicitly state otherwise, any contribution intentionally 37 | submitted for inclusion in the work by you, as defined in the Apache-2.0 38 | license, shall be dual-licensed as above, without any additional terms or 39 | conditions. 40 | 41 | [Crates.io]: https://crates.io/crates/anstyle 42 | [Documentation]: https://docs.rs/anstyle 43 | -------------------------------------------------------------------------------- /crates/anstyle/examples/dump-style.rs: -------------------------------------------------------------------------------- 1 | //! Write ANSI escape code colored text 2 | 3 | use std::io::Write; 4 | 5 | fn main() -> Result<(), lexopt::Error> { 6 | let args = Args::parse()?; 7 | let stdout = std::io::stdout(); 8 | let mut stdout = stdout.lock(); 9 | 10 | for fixed in 0..16 { 11 | let color = anstyle::Ansi256Color(fixed) 12 | .into_ansi() 13 | .expect("4-bit range used"); 14 | let style = style(color, args.layer, args.effects); 15 | let _ = print_number(&mut stdout, fixed, style); 16 | if fixed == 7 || fixed == 15 { 17 | let _ = writeln!(&mut stdout); 18 | } 19 | } 20 | 21 | for fixed in 16..232 { 22 | let col = (fixed - 16) % 36; 23 | if col == 0 { 24 | let _ = writeln!(stdout); 25 | } 26 | let color = anstyle::Ansi256Color(fixed); 27 | let style = style(color, args.layer, args.effects); 28 | let _ = print_number(&mut stdout, fixed, style); 29 | } 30 | 31 | let _ = writeln!(stdout); 32 | let _ = writeln!(stdout); 33 | for fixed in 232..=255 { 34 | let color = anstyle::Ansi256Color(fixed); 35 | let style = style(color, args.layer, args.effects); 36 | let _ = print_number(&mut stdout, fixed, style); 37 | } 38 | 39 | let _ = writeln!(stdout); 40 | 41 | Ok(()) 42 | } 43 | 44 | fn style( 45 | color: impl Into, 46 | layer: Layer, 47 | effects: anstyle::Effects, 48 | ) -> anstyle::Style { 49 | let color = color.into(); 50 | (match layer { 51 | Layer::Fg => anstyle::Style::new().fg_color(Some(color)), 52 | Layer::Bg => anstyle::Style::new().bg_color(Some(color)), 53 | Layer::Underline => anstyle::Style::new().underline_color(Some(color)), 54 | }) | effects 55 | } 56 | 57 | fn print_number( 58 | stdout: &mut std::io::StdoutLock<'_>, 59 | fixed: u8, 60 | style: anstyle::Style, 61 | ) -> std::io::Result<()> { 62 | write!(stdout, "{style}{fixed:>3X}{style:#}",) 63 | } 64 | 65 | #[derive(Default)] 66 | struct Args { 67 | effects: anstyle::Effects, 68 | layer: Layer, 69 | } 70 | 71 | #[derive(Copy, Clone, Default)] 72 | enum Layer { 73 | #[default] 74 | Fg, 75 | Bg, 76 | Underline, 77 | } 78 | 79 | impl Args { 80 | fn parse() -> Result { 81 | use lexopt::prelude::*; 82 | 83 | let mut res = Args::default(); 84 | 85 | let mut args = lexopt::Parser::from_env(); 86 | while let Some(arg) = args.next()? { 87 | match arg { 88 | Long("layer") => { 89 | res.layer = args.value()?.parse_with(|s| match s { 90 | "fg" => Ok(Layer::Fg), 91 | "bg" => Ok(Layer::Bg), 92 | "underline" => Ok(Layer::Underline), 93 | _ => Err("expected values fg, bg, underline"), 94 | })?; 95 | } 96 | Long("effect") => { 97 | const EFFECTS: [(&str, anstyle::Effects); 12] = [ 98 | ("bold", anstyle::Effects::BOLD), 99 | ("dimmed", anstyle::Effects::DIMMED), 100 | ("italic", anstyle::Effects::ITALIC), 101 | ("underline", anstyle::Effects::UNDERLINE), 102 | ("double_underline", anstyle::Effects::DOUBLE_UNDERLINE), 103 | ("curly_underline", anstyle::Effects::CURLY_UNDERLINE), 104 | ("dotted_underline", anstyle::Effects::DOTTED_UNDERLINE), 105 | ("dashed_underline", anstyle::Effects::DASHED_UNDERLINE), 106 | ("blink", anstyle::Effects::BLINK), 107 | ("invert", anstyle::Effects::INVERT), 108 | ("hidden", anstyle::Effects::HIDDEN), 109 | ("strikethrough", anstyle::Effects::STRIKETHROUGH), 110 | ]; 111 | let effect = args.value()?.parse_with(|s| { 112 | EFFECTS 113 | .into_iter() 114 | .find(|(name, _)| *name == s) 115 | .map(|(_, effect)| effect) 116 | .ok_or_else(|| { 117 | format!( 118 | "expected one of {}", 119 | EFFECTS 120 | .into_iter() 121 | .map(|(n, _)| n) 122 | .collect::>() 123 | .join(", ") 124 | ) 125 | }) 126 | })?; 127 | res.effects = res.effects.insert(effect); 128 | } 129 | _ => return Err(arg.unexpected()), 130 | } 131 | } 132 | Ok(res) 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /crates/anstyle/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! ANSI Text Styling 2 | //! 3 | //! *A portmanteau of "ansi style"* 4 | //! 5 | //! `anstyle` provides core types describing [ANSI styling escape 6 | //! codes](https://en.wikipedia.org/wiki/ANSI_escape_code) for interoperability 7 | //! between crates. 8 | //! 9 | //! Example use cases: 10 | //! - An argument parser allowing callers to define the colors used in the help-output without 11 | //! putting the text formatting crate in the public API 12 | //! - A style description parser that can work with any text formatting crate 13 | //! 14 | //! Priorities: 15 | //! 1. API stability 16 | //! 2. Low compile-time and binary-size overhead 17 | //! 3. `const` friendly API for callers to statically define their stylesheet 18 | //! 19 | //! For integration with text styling crate, see: 20 | //! - [anstyle-ansi-term](https://docs.rs/anstyle-ansi-term) 21 | //! - [anstyle-crossterm](https://docs.rs/anstyle-crossterm) 22 | //! - [anstyle-owo-colors](https://docs.rs/anstyle-owo-colors) 23 | //! - [anstyle-termcolor](https://docs.rs/anstyle-termcolor) 24 | //! - [anstyle-yansi](https://docs.rs/anstyle-yansi) 25 | //! 26 | //! User-styling parsers: 27 | //! - [anstyle-git](https://docs.rs/anstyle-git): Parse Git style descriptions 28 | //! - [anstyle-ls](https://docs.rs/anstyle-ls): Parse `LS_COLORS` style descriptions 29 | //! 30 | //! Convert to other formats 31 | //! - [anstream](https://docs.rs/anstream): A simple cross platform library for writing colored text to a terminal 32 | //! - [anstyle-roff](https://docs.rs/anstyle-roff): For converting to ROFF 33 | //! - [anstyle-syntect](https://docs.rs/anstyle-syntect): For working with syntax highlighting 34 | //! 35 | //! Utilities 36 | //! - [anstyle-lossy](https://docs.rs/anstyle-lossy): Convert between `anstyle::Color` types 37 | //! - [anstyle-parse](https://docs.rs/anstyle-parse): Parsing ANSI Style Escapes 38 | //! - [anstyle-wincon](https://docs.rs/anstyle-wincon): Styling legacy Microsoft terminals 39 | //! 40 | //! # Examples 41 | //! 42 | //! The core type is [`Style`]: 43 | //! ```rust 44 | //! let style = anstyle::Style::new().bold(); 45 | //! ``` 46 | 47 | #![cfg_attr(not(feature = "std"), no_std)] 48 | #![cfg_attr(docsrs, feature(doc_auto_cfg))] 49 | #![warn(missing_docs)] 50 | #![warn(clippy::std_instead_of_core)] 51 | #![warn(clippy::std_instead_of_alloc)] 52 | #![warn(clippy::print_stderr)] 53 | #![warn(clippy::print_stdout)] 54 | 55 | #[macro_use] 56 | mod macros; 57 | 58 | mod color; 59 | mod effect; 60 | mod reset; 61 | mod style; 62 | 63 | pub use color::*; 64 | pub use effect::*; 65 | pub use reset::*; 66 | pub use style::*; 67 | 68 | #[doc = include_str!("../README.md")] 69 | #[cfg(doctest)] 70 | pub struct ReadmeDoctests; 71 | -------------------------------------------------------------------------------- /crates/anstyle/src/macros.rs: -------------------------------------------------------------------------------- 1 | macro_rules! escape { 2 | ($($inner:expr),*) => { 3 | concat!("\x1B[", $($inner),*, "m") 4 | }; 5 | } 6 | -------------------------------------------------------------------------------- /crates/anstyle/src/reset.rs: -------------------------------------------------------------------------------- 1 | /// Reset terminal formatting 2 | #[allow(clippy::exhaustive_structs)] 3 | #[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 4 | pub struct Reset; 5 | 6 | impl Reset { 7 | /// Render the ANSI code 8 | /// 9 | /// `Reset` also implements `Display` directly, so calling this method is optional. 10 | #[inline] 11 | pub fn render(self) -> impl core::fmt::Display + Copy { 12 | self 13 | } 14 | } 15 | 16 | impl core::fmt::Display for Reset { 17 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 18 | f.write_str(RESET) 19 | } 20 | } 21 | 22 | pub(crate) const RESET: &str = "\x1B[0m"; 23 | 24 | #[cfg(test)] 25 | #[cfg(feature = "std")] 26 | mod test { 27 | use super::*; 28 | 29 | #[test] 30 | fn print_size_of() { 31 | use core::mem::size_of; 32 | dbg!(size_of::()); 33 | } 34 | 35 | #[test] 36 | fn no_align() { 37 | #[track_caller] 38 | fn assert_no_align(d: impl core::fmt::Display) { 39 | let expected = format!("{d}"); 40 | let actual = format!("{d:<10}"); 41 | assert_eq!(expected, actual); 42 | } 43 | 44 | assert_no_align(Reset); 45 | assert_no_align(Reset.render()); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /crates/colorchoice-clap/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/). 6 | 7 | 8 | ## [Unreleased] - ReleaseDate 9 | 10 | ## [1.0.7] - 2025-06-04 11 | 12 | ## [1.0.6] - 2024-10-24 13 | 14 | ### Compatibility 15 | 16 | - Update MSRV to 1.66 17 | 18 | ## [1.0.5] - 2024-07-25 19 | 20 | ## [1.0.4] - 2024-05-02 21 | 22 | ### Fixes 23 | 24 | - Drop MSRV to 1.65 25 | 26 | ## [1.0.3] - 2023-09-28 27 | 28 | ### Compatibility 29 | 30 | - Update MSRV to 1.70.0 31 | 32 | ## [1.0.2] - 2023-08-23 33 | 34 | ### Documentation 35 | 36 | - Expanded example 37 | 38 | ## [1.0.1] - 2023-06-20 39 | 40 | ### Fixes 41 | 42 | - Better interop with clap by reusing `clap::ColorChoice` 43 | 44 | ## [1.0.0] - 2023-04-13 45 | 46 | 47 | [Unreleased]: https://github.com/rust-cli/anstyle/compare/colorchoice-clap-v1.0.7...HEAD 48 | [1.0.7]: https://github.com/rust-cli/anstyle/compare/colorchoice-clap-v1.0.6...colorchoice-clap-v1.0.7 49 | [1.0.6]: https://github.com/rust-cli/anstyle/compare/colorchoice-clap-v1.0.5...colorchoice-clap-v1.0.6 50 | [1.0.5]: https://github.com/rust-cli/anstyle/compare/colorchoice-clap-v1.0.4...colorchoice-clap-v1.0.5 51 | [1.0.4]: https://github.com/rust-cli/anstyle/compare/colorchoice-clap-v1.0.3...colorchoice-clap-v1.0.4 52 | [1.0.3]: https://github.com/rust-cli/anstyle/compare/colorchoice-clap-v1.0.2...colorchoice-clap-v1.0.3 53 | [1.0.2]: https://github.com/rust-cli/anstyle/compare/colorchoice-clap-v1.0.1...colorchoice-clap-v1.0.2 54 | [1.0.1]: https://github.com/rust-cli/anstyle/compare/colorchoice-clap-v1.0.0...colorchoice-clap-v1.0.1 55 | [1.0.0]: https://github.com/rust-cli/anstyle/compare/c4423c1...colorchoice-clap-v1.0.0 56 | -------------------------------------------------------------------------------- /crates/colorchoice-clap/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "colorchoice-clap" 3 | version = "1.0.7" 4 | description = "Clap mixin to override console colors" 5 | categories = ["command-line-interface"] 6 | keywords = ["clap", "cli", "color", "terminal", "ansi"] 7 | repository.workspace = true 8 | license.workspace = true 9 | edition.workspace = true 10 | rust-version.workspace = true 11 | include.workspace = true 12 | 13 | [package.metadata.docs.rs] 14 | all-features = true 15 | rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"] 16 | 17 | [package.metadata.release] 18 | pre-release-replacements = [ 19 | {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, 20 | {file="CHANGELOG.md", search="\\.\\.\\.HEAD", replace="...{{tag_name}}", exactly=1}, 21 | {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, 22 | {file="CHANGELOG.md", search="", replace="\n## [Unreleased] - ReleaseDate\n", exactly=1}, 23 | {file="CHANGELOG.md", search="", replace="\n[Unreleased]: https://github.com/rust-cli/anstyle/compare/{{tag_name}}...HEAD", exactly=1}, 24 | ] 25 | 26 | [dependencies] 27 | colorchoice = { version = "1.0.0", path = "../colorchoice", default-features = false } 28 | clap = { version = "4.3.5", default-features = false, features = ["std", "derive", "color"] } 29 | 30 | [dev-dependencies] 31 | anstream = { path = "../anstream" } 32 | clap = "4.3.5" 33 | owo-colors = "4.0.0" 34 | 35 | [lints] 36 | workspace = true 37 | -------------------------------------------------------------------------------- /crates/colorchoice-clap/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) Individual contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /crates/colorchoice-clap/README.md: -------------------------------------------------------------------------------- 1 | # colorchoice-clap 2 | 3 | > **Convenience helper for working with `clap` to override console colors** 4 | 5 | [![Documentation](https://img.shields.io/badge/docs-master-blue.svg)][Documentation] 6 | ![License](https://img.shields.io/crates/l/colorchoice-clap.svg) 7 | [![Crates Status](https://img.shields.io/crates/v/colorchoice-clap.svg)](https://crates.io/crates/colorchoice-clap) 8 | 9 | ## License 10 | 11 | Licensed under either of 12 | 13 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or ) 14 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or ) 15 | 16 | at your option. 17 | 18 | ## [Contribute](../../CONTRIBUTING.md) 19 | 20 | Unless you explicitly state otherwise, any contribution intentionally 21 | submitted for inclusion in the work by you, as defined in the Apache-2.0 22 | license, shall be dual-licensed as above, without any additional terms or 23 | conditions. 24 | 25 | [Documentation]: https://docs.rs/colorchoice-clap 26 | -------------------------------------------------------------------------------- /crates/colorchoice-clap/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Mixin a clap argument for colored output selection 2 | //! 3 | //! ## Examples 4 | //! 5 | //! To get `--color` through your entire program, just `flatten` [`Color`] 6 | //! and use it to configure your formatter: 7 | //! 8 | //! ```rust,no_run 9 | //! use clap::Parser; 10 | //! use owo_colors::OwoColorize as _; 11 | //! 12 | //! /// Le CLI 13 | //! #[derive(Debug, Parser)] 14 | //! struct Cli { 15 | //! #[command(flatten)] 16 | //! color: colorchoice_clap::Color, 17 | //! } 18 | //! 19 | //! let cli = Cli::parse(); 20 | //! 21 | //! cli.color.write_global(); 22 | //! 23 | //! anstream::println!("Hello, {}!", "world".red()); 24 | //! ``` 25 | 26 | #![cfg_attr(docsrs, feature(doc_auto_cfg))] 27 | #![warn(missing_docs)] 28 | #![warn(clippy::print_stderr)] 29 | #![warn(clippy::print_stdout)] 30 | 31 | pub use clap::ColorChoice; 32 | 33 | /// Mixin a clap argument for colored output selection 34 | #[allow(clippy::exhaustive_structs)] 35 | #[derive(Copy, Clone, Default, Debug, PartialEq, Eq, clap::Args)] 36 | #[command(about = None, long_about = None)] 37 | pub struct Color { 38 | /// Controls when to use color. 39 | #[arg(long, default_value_t = ColorChoice::Auto, value_name = "WHEN", value_enum, global = true)] 40 | pub color: ColorChoice, 41 | } 42 | 43 | impl Color { 44 | /// Set the user selection on `colorchoice` 45 | pub fn write_global(&self) { 46 | self.as_choice().write_global(); 47 | } 48 | 49 | /// Get the user's selection 50 | pub fn as_choice(&self) -> colorchoice::ColorChoice { 51 | match self.color { 52 | ColorChoice::Auto => colorchoice::ColorChoice::Auto, 53 | ColorChoice::Always => colorchoice::ColorChoice::Always, 54 | ColorChoice::Never => colorchoice::ColorChoice::Never, 55 | } 56 | } 57 | } 58 | 59 | #[cfg(test)] 60 | mod test { 61 | use super::*; 62 | 63 | #[test] 64 | fn verify_app() { 65 | #[derive(Debug, clap::Parser)] 66 | struct Cli { 67 | #[clap(flatten)] 68 | color: Color, 69 | } 70 | 71 | use clap::CommandFactory; 72 | Cli::command().debug_assert(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /crates/colorchoice/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/). 6 | 7 | 8 | ## [Unreleased] - ReleaseDate 9 | 10 | ## [1.0.4] - 2025-06-04 11 | 12 | ## [1.0.3] - 2024-10-24 13 | 14 | ### Compatibility 15 | 16 | - Update MSRV to 1.66 17 | 18 | ## [1.0.2] - 2024-07-25 19 | 20 | ## [1.0.1] - 2024-05-02 21 | 22 | ### Fixes 23 | 24 | - Drop MSRV to 1.65 25 | 26 | ## [1.0.0] - 2023-04-13 27 | 28 | 29 | [Unreleased]: https://github.com/rust-cli/anstyle/compare/colorchoice-v1.0.4...HEAD 30 | [1.0.4]: https://github.com/rust-cli/anstyle/compare/colorchoice-v1.0.3...colorchoice-v1.0.4 31 | [1.0.3]: https://github.com/rust-cli/anstyle/compare/colorchoice-v1.0.2...colorchoice-v1.0.3 32 | [1.0.2]: https://github.com/rust-cli/anstyle/compare/colorchoice-v1.0.1...colorchoice-v1.0.2 33 | [1.0.1]: https://github.com/rust-cli/anstyle/compare/colorchoice-v1.0.0...colorchoice-v1.0.1 34 | [1.0.0]: https://github.com/rust-cli/anstyle/compare/c4423c1...colorchoice-v1.0.0 35 | -------------------------------------------------------------------------------- /crates/colorchoice/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "colorchoice" 3 | version = "1.0.4" 4 | description = "Global override of color control" 5 | categories = ["command-line-interface"] 6 | keywords = ["cli", "color", "no-std", "terminal", "ansi"] 7 | repository.workspace = true 8 | license.workspace = true 9 | edition.workspace = true 10 | rust-version.workspace = true 11 | include.workspace = true 12 | 13 | [package.metadata.docs.rs] 14 | all-features = true 15 | rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"] 16 | 17 | [package.metadata.release] 18 | pre-release-replacements = [ 19 | {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, 20 | {file="CHANGELOG.md", search="\\.\\.\\.HEAD", replace="...{{tag_name}}", exactly=1}, 21 | {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, 22 | {file="CHANGELOG.md", search="", replace="\n## [Unreleased] - ReleaseDate\n", exactly=1}, 23 | {file="CHANGELOG.md", search="", replace="\n[Unreleased]: https://github.com/rust-cli/anstyle/compare/{{tag_name}}...HEAD", exactly=1}, 24 | ] 25 | 26 | [lints] 27 | workspace = true 28 | -------------------------------------------------------------------------------- /crates/colorchoice/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) Individual contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /crates/colorchoice/README.md: -------------------------------------------------------------------------------- 1 | # colorchoice 2 | 3 | > Global override of color control 4 | 5 | [![Documentation](https://img.shields.io/badge/docs-master-blue.svg)][Documentation] 6 | ![License](https://img.shields.io/crates/l/colorchoice.svg) 7 | [![Crates Status](https://img.shields.io/crates/v/colorchoice.svg)](https://crates.io/crates/colorchoice) 8 | 9 | ## License 10 | 11 | Licensed under either of 12 | 13 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or ) 14 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or ) 15 | 16 | at your option. 17 | 18 | ## [Contribute](../../CONTRIBUTING.md) 19 | 20 | Special note: to be successful, this crate **cannot** break compatibility or 21 | else different crates in the hierarchy will be reading different globals. 22 | While end users can work around this, it isn't ideal. If we need a new API, we can make 23 | the old API an adapter to the new logic. 24 | 25 | Similarly, we should strive to reduce **risk** of breaking compatibility by 26 | exposing as little as possible. Anything more should be broken out into a 27 | separate crate that this crate can call into. 28 | 29 | Unless you explicitly state otherwise, any contribution intentionally 30 | submitted for inclusion in the work by you, as defined in the Apache-2.0 31 | license, shall be dual-licensed as above, without any additional terms or 32 | conditions. 33 | 34 | [Documentation]: https://docs.rs/colorchoice 35 | -------------------------------------------------------------------------------- /crates/colorchoice/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Global override of color control 2 | 3 | #![cfg_attr(not(test), no_std)] 4 | #![cfg_attr(docsrs, feature(doc_auto_cfg))] 5 | #![warn(missing_docs)] 6 | #![warn(clippy::print_stderr)] 7 | #![warn(clippy::print_stdout)] 8 | 9 | use core::sync::atomic::{AtomicUsize, Ordering}; 10 | 11 | /// Selection for overriding color output 12 | #[allow(clippy::exhaustive_enums)] 13 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 14 | pub enum ColorChoice { 15 | /// Use colors if the output device appears to support them 16 | Auto, 17 | /// Like `Always`, except it never tries to use anything other than emitting ANSI 18 | /// color codes. 19 | AlwaysAnsi, 20 | /// Try very hard to emit colors. 21 | /// 22 | /// This includes emitting ANSI colors on Windows if the console API is unavailable. 23 | Always, 24 | /// Never emit colors. 25 | Never, 26 | } 27 | 28 | impl ColorChoice { 29 | /// Get the current [`ColorChoice`] state 30 | pub fn global() -> Self { 31 | USER.get() 32 | } 33 | 34 | /// Override the detected [`ColorChoice`] 35 | pub fn write_global(self) { 36 | USER.set(self); 37 | } 38 | } 39 | 40 | impl Default for ColorChoice { 41 | fn default() -> Self { 42 | Self::Auto 43 | } 44 | } 45 | 46 | static USER: AtomicChoice = AtomicChoice::new(); 47 | 48 | #[derive(Debug)] 49 | pub(crate) struct AtomicChoice(AtomicUsize); 50 | 51 | impl AtomicChoice { 52 | pub(crate) const fn new() -> Self { 53 | Self(AtomicUsize::new(Self::from_choice(ColorChoice::Auto))) 54 | } 55 | 56 | pub(crate) fn get(&self) -> ColorChoice { 57 | let choice = self.0.load(Ordering::SeqCst); 58 | Self::to_choice(choice).expect("Only `ColorChoice` values can be `set`") 59 | } 60 | 61 | pub(crate) fn set(&self, choice: ColorChoice) { 62 | let choice = Self::from_choice(choice); 63 | self.0.store(choice, Ordering::SeqCst); 64 | } 65 | 66 | const fn from_choice(choice: ColorChoice) -> usize { 67 | match choice { 68 | ColorChoice::Auto => 0, 69 | ColorChoice::AlwaysAnsi => 1, 70 | ColorChoice::Always => 2, 71 | ColorChoice::Never => 3, 72 | } 73 | } 74 | 75 | const fn to_choice(choice: usize) -> Option { 76 | match choice { 77 | 0 => Some(ColorChoice::Auto), 78 | 1 => Some(ColorChoice::AlwaysAnsi), 79 | 2 => Some(ColorChoice::Always), 80 | 3 => Some(ColorChoice::Never), 81 | _ => None, 82 | } 83 | } 84 | } 85 | 86 | impl Default for AtomicChoice { 87 | fn default() -> Self { 88 | Self::new() 89 | } 90 | } 91 | 92 | #[cfg(test)] 93 | mod test { 94 | use super::*; 95 | 96 | #[test] 97 | fn choice_serialization() { 98 | let expected = vec![ 99 | ColorChoice::Auto, 100 | ColorChoice::AlwaysAnsi, 101 | ColorChoice::Always, 102 | ColorChoice::Never, 103 | ]; 104 | let values: Vec<_> = expected 105 | .iter() 106 | .cloned() 107 | .map(AtomicChoice::from_choice) 108 | .collect(); 109 | let actual: Vec<_> = values 110 | .iter() 111 | .cloned() 112 | .filter_map(AtomicChoice::to_choice) 113 | .collect(); 114 | assert_eq!(expected, actual); 115 | } 116 | } 117 | 118 | #[doc = include_str!("../README.md")] 119 | #[cfg(doctest)] 120 | pub struct ReadmeDoctests; 121 | -------------------------------------------------------------------------------- /release.toml: -------------------------------------------------------------------------------- 1 | owners = ["github:rust-cli:Maintainers"] 2 | dependent-version = "fix" 3 | allow-branch = ["main"] 4 | -------------------------------------------------------------------------------- /typos.toml: -------------------------------------------------------------------------------- 1 | [type.vte] 2 | extend-glob = ["*.vte"] 3 | check-file = false 4 | 5 | [type.svg] 6 | check-file = false 7 | --------------------------------------------------------------------------------