版本火车适配 升级到0.10.2

Signed-off-by: ljy9810 <longjianyin@h-partners.com>
This commit is contained in:
ljy9810
2025-08-29 11:00:53 +08:00
parent 6e417b7b60
commit f13c23e88a
49 changed files with 2603 additions and 2136 deletions
+10
View File
@@ -0,0 +1,10 @@
msrv = "1.60.0" # MSRV
warn-on-all-wildcard-imports = true
disallowed-methods = [
{ path = "std::option::Option::map_or", reason = "prefer `map(..).unwrap_or(..)` for legibility" },
{ path = "std::option::Option::map_or_else", reason = "prefer `map(..).unwrap_or_else(..)` for legibility" },
{ path = "std::result::Result::map_or", reason = "prefer `map(..).unwrap_or(..)` for legibility" },
{ path = "std::result::Result::map_or_else", reason = "prefer `map(..).unwrap_or_else(..)` for legibility" },
{ path = "std::iter::Iterator::for_each", reason = "prefer `for` for side-effects" },
{ path = "std::iter::Iterator::try_for_each", reason = "prefer `for` for side-effects" },
]
-7
View File
@@ -1,7 +0,0 @@
version: 2
updates:
- package-ecosystem: cargo
directory: "/"
schedule:
interval: daily
open-pull-requests-limit: 10
+107
View File
@@ -0,0 +1,107 @@
{
schedule: [
'before 5am on the first day of the month',
],
semanticCommits: 'enabled',
configMigration: true,
dependencyDashboard: true,
customManagers: [
{
customType: 'regex',
fileMatch: [
'^rust-toolchain\\.toml$',
'Cargo.toml$',
'clippy.toml$',
'\\.clippy.toml$',
'^\\.github/workflows/ci.yml$',
'^\\.github/workflows/rust-next.yml$',
],
matchStrings: [
'MSRV.*?(?<currentValue>\\d+\\.\\d+(\\.\\d+)?)',
'(?<currentValue>\\d+\\.\\d+(\\.\\d+)?).*?MSRV',
],
depNameTemplate: 'rust',
packageNameTemplate: 'rust-lang/rust',
datasourceTemplate: 'github-releases',
},
],
packageRules: [
{
commitMessageTopic: 'MSRV',
matchManagers: [
'custom.regex',
],
matchPackageNames: [
'rust',
],
minimumReleaseAge: '840 days', // 20 releases * 6 weeks per release * 7 days per week
internalChecksFilter: 'strict',
extractVersion: '^(?<version>\\d+\\.\\d+)', // Drop the patch version
schedule: [
'* * * * *',
],
},
// Goals:
// - Keep version reqs low, ignoring compatible normal/build dependencies
// - Take advantage of latest dev-dependencies
// - Rollup safe upgrades to reduce CI runner load
// - Help keep number of versions down by always using latest breaking change
// - Have lockfile and manifest in-sync
{
matchManagers: [
'cargo',
],
matchDepTypes: [
'build-dependencies',
'dependencies',
],
matchCurrentVersion: '>=0.1.0',
matchUpdateTypes: [
'patch',
],
enabled: false,
},
{
matchManagers: [
'cargo',
],
matchDepTypes: [
'build-dependencies',
'dependencies',
],
matchCurrentVersion: '>=1.0.0',
matchUpdateTypes: [
'minor',
],
enabled: false,
},
{
matchManagers: [
'cargo',
],
matchDepTypes: [
'dev-dependencies',
],
matchCurrentVersion: '>=0.1.0',
matchUpdateTypes: [
'patch',
],
automerge: true,
groupName: 'compatible (dev)',
},
{
matchManagers: [
'cargo',
],
matchDepTypes: [
'dev-dependencies',
],
matchCurrentVersion: '>=1.0.0',
matchUpdateTypes: [
'minor',
],
automerge: true,
groupName: 'compatible (dev)',
},
],
}
+37
View File
@@ -0,0 +1,37 @@
# These settings are synced to GitHub by https://probot.github.io/apps/settings/
repository:
description: "A logging implementation for `log` which is configured via an environment variable."
homepage: "https://docs.rs/env_logger"
topics: "rust logging"
has_issues: true
has_projects: false
has_wiki: false
has_downloads: false
default_branch: main
# Preference: people do clean commits
allow_merge_commit: true
# Backup in case we need to clean up commits
allow_squash_merge: true
# Not really needed
allow_rebase_merge: false
allow_auto_merge: true
delete_branch_on_merge: true
squash_merge_commit_title: "PR_TITLE"
squash_merge_commit_message: "PR_BODY"
merge_commit_message: "PR_BODY"
branches:
- name: main
protection:
required_pull_request_reviews: null
required_conversation_resolution: true
required_status_checks:
# Required. Require branches to be up to date before merging.
strict: false
contexts: ["CI", "Lint Commits", "Spell Check with Typos"]
enforce_admins: false
restrictions: null
+49
View File
@@ -0,0 +1,49 @@
name: Security audit
permissions:
contents: read
on:
pull_request:
paths:
- '**/Cargo.toml'
- '**/Cargo.lock'
push:
branches:
- main
env:
RUST_BACKTRACE: 1
CARGO_TERM_COLOR: always
CLICOLOR: 1
jobs:
security_audit:
permissions:
issues: write # to create issues (actions-rs/audit-check)
checks: write # to create check (actions-rs/audit-check)
runs-on: ubuntu-latest
# Prevent sudden announcement of a new advisory from failing ci:
continue-on-error: true
steps:
- name: Checkout repository
uses: actions/checkout@v4
- uses: actions-rs/audit-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
cargo_deny:
permissions:
issues: write # to create issues (actions-rs/audit-check)
checks: write # to create check (actions-rs/audit-check)
runs-on: ubuntu-latest
strategy:
matrix:
checks:
- bans licenses sources
steps:
- uses: actions/checkout@v4
- uses: EmbarkStudios/cargo-deny-action@v1
with:
command: check ${{ matrix.checks }}
rust-version: stable
+138 -114
View File
@@ -1,114 +1,138 @@
name: Continuous Integration
on:
pull_request:
paths:
- "**.rs"
- "Cargo.toml"
- "Cargo.lock"
jobs:
fmt:
name: Source formatting check
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly
override: true
components: rustfmt
- name: Check formatting
uses: actions-rs/cargo@v1
with:
command: fmt
args: -- --check
check:
name: Compilation check
runs-on: ubuntu-latest
strategy:
fail-fast: true
matrix:
rust:
- stable
- beta
- nightly
- 1.41.0
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ matrix.rust }}
override: true
- name: Run cargo check
uses: actions-rs/cargo@v1
with:
command: check
clippy:
name: Lint check
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
components: clippy
- name: Run lints
uses: actions-rs/cargo@v1
with:
command: clippy
args: -- -D warnings
ci-crate:
name: CI crate check
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
- name: Run ci crate
uses: actions-rs/cargo@v1
with:
command: run
args: -p ci
crate-example:
name: Crate example check
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
- name: Run crate example
uses: actions-rs/cargo@v1
with:
command: run
args: --example default
name: CI
permissions:
contents: read
on:
pull_request:
push:
branches:
- main
env:
RUST_BACKTRACE: 1
CARGO_TERM_COLOR: always
CLICOLOR: 1
jobs:
ci:
permissions:
contents: none
name: CI
needs: [test, msrv, docs, rustfmt, clippy]
runs-on: ubuntu-latest
steps:
- name: Done
run: exit 0
test:
name: Test
strategy:
matrix:
os: ["ubuntu-latest", "windows-latest", "macos-latest"]
rust: ["stable"]
continue-on-error: ${{ matrix.rust != 'stable' }}
runs-on: ${{ matrix.os }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ matrix.rust }}
- uses: Swatinem/rust-cache@v2
- uses: taiki-e/install-action@cargo-hack
- name: Build
run: cargo test --no-run --workspace --all-features
- name: Test
run: cargo hack test --workspace --feature-powerset
- name: Run crate example
run: cargo run --example default
msrv:
name: "Check MSRV: 1.60.0"
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
toolchain: "1.60" # MSRV
- uses: Swatinem/rust-cache@v2
- uses: taiki-e/install-action@cargo-hack
- name: Check
run: cargo hack check --workspace --all-targets --feature-powerset
lockfile:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
- uses: Swatinem/rust-cache@v2
- name: "Is lockfile updated?"
run: cargo fetch --locked
docs:
name: Docs
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
- uses: Swatinem/rust-cache@v2
- name: Check documentation
env:
RUSTDOCFLAGS: -D warnings
run: cargo doc --workspace --all-features --no-deps --document-private-items
rustfmt:
name: rustfmt
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
# Not MSRV because its harder to jump between versions and people are
# more likely to have stable
toolchain: stable
components: rustfmt
- uses: Swatinem/rust-cache@v2
- name: Check formatting
run: cargo fmt --all -- --check
clippy:
name: clippy
runs-on: ubuntu-latest
permissions:
security-events: write # to upload sarif results
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
toolchain: "1.60" # MSRV
components: clippy
- uses: Swatinem/rust-cache@v2
- name: Install SARIF tools
run: cargo install clippy-sarif --version 0.3.4 --locked # Held back due to msrv
- name: Install SARIF tools
run: cargo install sarif-fmt --version 0.3.4 --locked # Held back due to msrv
- name: Check
run: >
cargo clippy --workspace --all-features --all-targets --message-format=json -- -D warnings --allow deprecated
| clippy-sarif
| tee clippy-results.sarif
| sarif-fmt
continue-on-error: true
- name: Upload
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: clippy-results.sarif
wait-for-processing: true
- name: Report status
run: cargo clippy --workspace --all-features --all-targets -- -D warnings --allow deprecated
+24
View File
@@ -0,0 +1,24 @@
# Not run as part of pre-commit checks because they don't handle sending the correct commit
# range to `committed`
name: Lint Commits
on: [pull_request]
permissions:
contents: read
env:
RUST_BACKTRACE: 1
CARGO_TERM_COLOR: always
CLICOLOR: 1
jobs:
committed:
name: Lint Commits
runs-on: ubuntu-latest
steps:
- name: Checkout Actions Repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Lint Commits
uses: crate-ci/committed@master
-40
View File
@@ -1,40 +0,0 @@
name: Continuous Integration - Docs
on:
push:
branches:
- main
paths:
- "**.rs"
- "Cargo.toml"
- "Cargo.lock"
workflow_dispatch:
jobs:
docs:
name: Generate crate documentation
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v2
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly
override: true
- name: Generate documentation
uses: actions-rs/cargo@v1
env:
RUSTDOCFLAGS: "--enable-index-page -Zunstable-options"
with:
command: doc
args: --no-deps
- name: Deploy documentation
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./target/doc
+23
View File
@@ -0,0 +1,23 @@
name: pre-commit
permissions: {} # none
on:
pull_request:
push:
branches: [main]
env:
RUST_BACKTRACE: 1
CARGO_TERM_COLOR: always
CLICOLOR: 1
jobs:
pre-commit:
permissions:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: pre-commit/action@v3.0.0
+61
View File
@@ -0,0 +1,61 @@
name: rust-next
permissions:
contents: read
on:
schedule:
- cron: '5 5 5 * *'
env:
RUST_BACKTRACE: 1
CARGO_TERM_COLOR: always
CLICOLOR: 1
jobs:
test:
name: Test
strategy:
matrix:
os: ["ubuntu-latest", "windows-latest", "macos-latest"]
rust: ["stable", "beta"]
include:
- os: ubuntu-latest
rust: "nightly"
continue-on-error: ${{ matrix.rust != 'stable' }}
runs-on: ${{ matrix.os }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ matrix.rust }}
- uses: Swatinem/rust-cache@v2
- uses: taiki-e/install-action@cargo-hack
- name: Build
run: cargo test --no-run --workspace --all-features
- name: Test
run: cargo hack test --workspace --feature-powerset
- name: Run crate example
run: cargo run --example default
latest:
name: "Check latest dependencies"
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
- uses: Swatinem/rust-cache@v2
- uses: taiki-e/install-action@cargo-hack
- name: Update dependencues
run: cargo update
- name: Build
run: cargo test --no-run --workspace --all-features
- name: Test
run: cargo hack test --workspace --feature-powerset
- name: Run crate example
run: cargo run --example default
+21
View File
@@ -0,0 +1,21 @@
name: Spelling
permissions:
contents: read
on: [pull_request]
env:
RUST_BACKTRACE: 1
CARGO_TERM_COLOR: always
CLICOLOR: 1
jobs:
spelling:
name: Spell Check with Typos
runs-on: ubuntu-latest
steps:
- name: Checkout Actions Repository
uses: actions/checkout@v4
- name: Spell Check Repo
uses: crate-ci/typos@master
+1 -4
View File
@@ -1,4 +1 @@
target/
Cargo.lock
.emacs*
*~
target
+26
View File
@@ -0,0 +1,26 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: check-yaml
stages: [commit]
- id: check-json
stages: [commit]
- id: check-toml
stages: [commit]
- id: check-merge-conflict
stages: [commit]
- id: check-case-conflict
stages: [commit]
- id: detect-private-key
stages: [commit]
- repo: https://github.com/crate-ci/typos
rev: v1.16.20
hooks:
- id: typos
stages: [commit]
- repo: https://github.com/crate-ci/committed
rev: v1.0.20
hooks:
- id: committed
stages: [commit-msg]
+4 -5
View File
@@ -19,20 +19,19 @@ ohos_cargo_crate("lib") {
crate_root = "src/lib.rs"
sources = ["src/lib.rs"]
edition = "2018"
cargo_pkg_version = "0.9.3"
edition = "2021"
cargo_pkg_version = "0.10.2"
cargo_pkg_name = "env_logger"
deps = [
"//third_party/rust/crates/atty:lib",
"//third_party/rust/crates/is-terminal:lib",
"//third_party/rust/crates/humantime:lib",
"//third_party/rust/crates/log:lib",
"//third_party/rust/crates/regex:lib",
"//third_party/rust/crates/termcolor:lib",
]
features = [
"atty",
"auto-color",
"humantime",
"regex",
"termcolor",
]
}
+69 -2
View File
@@ -1,3 +1,70 @@
Changes to this crate are tracked via [GitHub Releases][releases].
# Change Log
All notable changes to this project will be documented in this file.
[releases]: https://github.com/env-logger-rs/env_logger/releases
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
<!-- next-header -->
## [Unreleased] - ReleaseDate
## [0.10.2] - 2024-01-18
### Performance
- Avoid extra UTF-8 validation performed in some cases
### Fixes
- Ensure custom pipes/stdout get flushed
- Don't panic on broken pipes when `color` is disabled
## [0.10.1] - 2023-11-10
### Performance
- Avoid hashing directives and accessing RNG on startup
### Documentation
- Tweak `RUST_LOG` documentation
## [0.10.0] - 2022-11-24
MSRV changed to 1.60 to hide optional dependencies
### Fixes
- Resolved soundness issue by switching from `atty` to `is-terminal`
### Breaking Changes
To open room for changing dependencies:
- Renamed `termcolor` feature to `color`
- Renamed `atty` feature to `auto-color`
## [0.9.3] - 2022-11-07
- Fix a regression from v0.9.2 where env_logger would fail to compile with the termcolor feature turned off.
## [0.9.2] - 2022-11-07
- Fix and un-deprecate Target::Pipe, which was basically not working at all before and deprecated in 0.9.1.
## [0.9.0] -- 2022-07-14
### Breaking Changes
- Default message format now prints the target instead of the module
### Improvements
- Added a method to print the module instead of the target
<!-- next-url -->
[Unreleased]: https://github.com/rust-cli/env_logger/compare/v0.10.2...HEAD
[0.10.2]: https://github.com/rust-cli/env_logger/compare/v0.10.1...v0.10.2
[0.10.1]: https://github.com/rust-cli/env_logger/compare/v0.10.0...v0.10.1
[0.10.0]: https://github.com/rust-cli/env_logger/compare/v0.9.3...v0.10.0
[0.9.3]: https://github.com/rust-cli/env_logger/compare/v0.9.2...v0.9.3
[0.9.2]: https://github.com/rust-cli/env_logger/compare/v0.9.0...v0.9.2
[0.9.0]: https://github.com/rust-cli/env_logger/compare/v0.8.4...v0.9.0
+68
View File
@@ -0,0 +1,68 @@
# Contributing to env_logger
Thanks for wanting to contribute! There are many ways to contribute and we
appreciate any level you're willing to do.
## Feature Requests
Need some new functionality to help? You can let us know by opening an
[issue][new issue]. It's helpful to look through [all issues][all issues] in
case its already being talked about.
## Bug Reports
Please let us know about what problems you run into, whether in behavior or
ergonomics of API. You can do this by opening an [issue][new issue]. It's
helpful to look through [all issues][all issues] in case its already being
talked about.
## Pull Requests
Looking for an idea? Check our [issues][issues]. If it's look more open ended,
it is probably best to post on the issue how you are thinking of resolving the
issue so you can get feedback early in the process. We want you to be
successful and it can be discouraging to find out a lot of re-work is needed.
Already have an idea? It might be good to first [create an issue][new issue]
to propose it so we can make sure we are aligned and lower the risk of having
to re-work some of it and the discouragement that goes along with that.
### Process
When you first post a PR, we request that the the commit history get cleaned
up. We recommend avoiding this during the PR to make it easier to review how
feedback was handled. Once the commit is ready, we'll ask you to clean up the
commit history. Once you let us know this is done, we can move forward with
merging! If you are uncomfortable with these parts of git, let us know and we
can help.
For commit messages, we use [Conventional](https://www.conventionalcommits.org)
style. If you already wrote your commits and don't feel comfortable changing
them, don't worry and go ahead and create your PR. We'll work with you on the
best route forward. You can check your branch locally with
[`committed`](https://github.com/crate-ci/committed).
As a heads up, we'll be running your PR through the following gauntlet:
- warnings turned to compile errors
- `cargo test`
- `rustfmt`
- `clippy`
- `rustdoc`
- [`committed`](https://github.com/crate-ci/committed)
## Releasing
Pre-requisites
- Running `cargo login`
- A member of `rust-cli:Maintainers`
- Push permission to the repo
- [`cargo-release`](https://github.com/crate-ci/cargo-release/)
When we're ready to release, a project owner should do the following
1. Update the changelog (see `cargo release changes` for ideas)
2. Determine what the next version is, according to semver
3. Run [`cargo release -x <level>`](https://github.com/crate-ci/cargo-release)
[issues]: https://github.com/rust-cli/env_logger/issues
[new issue]: https://github.com/rust-cli/env_logger/issues/new
[all issues]: https://github.com/rust-cli/env_logger/issues?utf8=%E2%9C%93&q=is%3Aissue
+36 -15
View File
@@ -1,30 +1,54 @@
[package]
name = "env_logger"
edition = "2018"
version = "0.9.3"
license = "MIT OR Apache-2.0"
readme = "README.md"
repository = "https://github.com/env-logger-rs/env_logger/"
documentation = "https://docs.rs/env_logger"
version = "0.10.2"
description = """
A logging implementation for `log` which is configured via an environment
variable.
"""
repository = "https://github.com/rust-cli/env_logger"
categories = ["development-tools::debugging"]
keywords = ["logging", "log", "logger"]
include = ["src/**/*", "tests", "LICENSE-*", "README.md", "CHANGELOG.md"]
[workspace]
members = [
"ci"
license = "MIT OR Apache-2.0"
edition = "2021"
rust-version = "1.60.0" # MSRV
include = [
"build.rs",
"src/**/*",
"Cargo.toml",
"Cargo.lock",
"LICENSE*",
"README.md",
"benches/**/*",
"examples/**/*",
"tests/**/*",
]
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
[package.metadata.release]
pre-release-replacements = [
{file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1},
{file="CHANGELOG.md", search="\\.\\.\\.HEAD", replace="...{{tag_name}}", exactly=1},
{file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1},
{file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## [Unreleased] - ReleaseDate\n", exactly=1},
{file="CHANGELOG.md", search="<!-- next-url -->", replace="<!-- next-url -->\n[Unreleased]: https://github.com/rust-cli/env_logger/compare/{{tag_name}}...HEAD", exactly=1},
]
[features]
default = ["auto-color", "humantime", "regex"]
color = ["dep:termcolor"]
auto-color = ["dep:is-terminal", "color"]
humantime = ["dep:humantime"]
regex = ["dep:regex"]
[dependencies]
log = { version = "0.4.8", features = ["std"] }
regex = { version = "1.0.3", optional = true, default-features=false, features=["std", "perf"] }
termcolor = { version = "1.1.1", optional = true }
humantime = { version = "2.0.0", optional = true }
atty = { version = "0.2.5", optional = true }
is-terminal = { version = "0.4.0", optional = true }
[[test]]
name = "regexp_filter"
@@ -41,6 +65,3 @@ harness = false
[[test]]
name = "init-twice-retains-filter"
harness = false
[features]
default = ["termcolor", "atty", "humantime", "regex"]
+170 -169
View File
@@ -1,201 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+17 -21
View File
@@ -1,23 +1,19 @@
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
Copyright (c) Individual contributors
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+1 -1
View File
@@ -3,7 +3,7 @@
"Name": "env_logger",
"License": "Apache License V2.0, MIT",
"License File": "LICENSE-APACHE, LICENSE-MIT",
"Version Number": "0.9.3",
"Version Number": "v0.10.2",
"Owner": "fangting12@huawei.com",
"Upstream URL": "https://github.com/rust-cli/env_logger",
"Description": "A Rust library that provides a flexible and customizable logging system."
+6 -12
View File
@@ -2,7 +2,6 @@
[![crates.io](https://img.shields.io/crates/v/env_logger.svg)](https://crates.io/crates/env_logger)
[![Documentation](https://docs.rs/env_logger/badge.svg)](https://docs.rs/env_logger)
[![Documentation](https://img.shields.io/badge/docs-main-blue.svg)](https://env-logger-rs.github.io/env_logger/env_logger/index.html)
Implements a logger that can be configured via environment variables.
@@ -16,17 +15,14 @@ Implements a logger that can be configured via environment variables.
It must be added along with `log` to the project dependencies:
```toml
[dependencies]
log = "0.4.0"
env_logger = "0.9.0"
```console
$ cargo add log env_logger
```
`env_logger` must be initialized as early as possible in the project. After it's initialized, you can use the `log` macros to do actual logging.
```rust
#[macro_use]
extern crate log;
use log::info;
fn main() {
env_logger::init();
@@ -75,7 +71,7 @@ There is also a pseudo logging level, `off`, which may be specified to disable
all logging for a given module or for the entire application. As with the
logging levels, the letter case is not significant.
`env_logger` can be configured in other ways besides an environment variable. See [the examples](https://github.com/env-logger-rs/env_logger/tree/main/examples) for more approaches.
`env_logger` can be configured in other ways besides an environment variable. See [the examples](https://github.com/rust-cli/env_logger/tree/main/examples) for more approaches.
### In tests
@@ -86,13 +82,10 @@ Tests can use the `env_logger` crate to see log messages generated during that t
log = "0.4.0"
[dev-dependencies]
env_logger = "0.9.0"
env_logger = "0.10.0"
```
```rust
#[macro_use]
extern crate log;
fn add_one(num: i32) -> i32 {
info!("add_one called with {}", num);
num + 1
@@ -101,6 +94,7 @@ fn add_one(num: i32) -> i32 {
#[cfg(test)]
mod tests {
use super::*;
use log::info;
fn init() {
let _ = env_logger::builder().is_test(true).try_init();
-7
View File
@@ -1,7 +0,0 @@
[package]
name = "ci"
edition = "2018"
version = "0.0.0"
publish = false
[dependencies]
-41
View File
@@ -1,41 +0,0 @@
mod permute;
mod task;
fn main() {
let features = ["termcolor", "humantime", "atty", "regex"];
// Run a default build
if !task::test(Default::default()) {
panic!("default test execution failed");
}
// Run a build with no features
if !task::test(task::TestArgs {
default_features: false,
..Default::default()
}) {
panic!("default test execution failed");
}
// Run a set of permutations
let failed = permute::all(&features)
.into_iter()
.filter(|features| {
!task::test(task::TestArgs {
features: features.clone(),
default_features: false,
lib_only: true,
})
})
.collect::<Vec<_>>();
if !failed.is_empty() {
for failed in failed {
eprintln!("FAIL: {:?}", failed);
}
panic!("test execution failed");
} else {
println!("test execution succeeded");
}
}
-30
View File
@@ -1,30 +0,0 @@
use std::collections::BTreeSet;
pub fn all<T>(input: &[T]) -> BTreeSet<BTreeSet<T>>
where
T: Ord + Eq + Clone,
{
let mut permutations = BTreeSet::new();
if input.is_empty() {
return permutations;
}
permutations.insert(input.iter().cloned().collect());
if input.len() > 1 {
for t in input {
let p = input
.iter()
.filter(|pt| *pt != t)
.cloned()
.collect::<Vec<_>>();
for pt in all(&p) {
permutations.insert(pt);
}
}
}
permutations
}
-77
View File
@@ -1,77 +0,0 @@
use std::collections::BTreeSet;
use std::process::{Command, Stdio};
pub type Feature = &'static str;
pub struct TestArgs {
pub features: BTreeSet<Feature>,
pub default_features: bool,
pub lib_only: bool,
}
impl Default for TestArgs {
fn default() -> Self {
TestArgs {
features: BTreeSet::new(),
default_features: true,
lib_only: false,
}
}
}
impl TestArgs {
fn features_string(&self) -> Option<String> {
if self.features.is_empty() {
return None;
}
let s = self.features.iter().fold(String::new(), |mut s, f| {
if !s.is_empty() {
s.push(' ');
}
s.push_str(f);
s
});
Some(s)
}
}
pub fn test(args: TestArgs) -> bool {
let features = args.features_string();
let mut command = Command::new("cargo");
command
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.arg("test")
.arg("--verbose");
if !args.default_features {
command.arg("--no-default-features");
}
if args.lib_only {
command.arg("--lib");
}
if let Some(features) = &features {
command.args(&["--features", features]);
}
println!("running {:?}", command);
let status = command.status().expect("Failed to execute command");
if !status.success() {
eprintln!(
"test execution failed for features: {}",
features.as_ref().map(AsRef::as_ref).unwrap_or("")
);
false
} else {
true
}
}
+3
View File
@@ -0,0 +1,3 @@
style="conventional"
ignore_author_re="(dependabot|renovate)"
merge_commit = false
+140
View File
@@ -0,0 +1,140 @@
# Note that all fields that take a lint level have these possible values:
# * deny - An error will be produced and the check will fail
# * warn - A warning will be produced, but the check will not fail
# * allow - No warning or error will be produced, though in some cases a note
# will be
# This section is considered when running `cargo deny check advisories`
# More documentation for the advisories section can be found here:
# https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html
[advisories]
# The lint level for security vulnerabilities
vulnerability = "deny"
# The lint level for unmaintained crates
unmaintained = "warn"
# The lint level for crates that have been yanked from their source registry
yanked = "warn"
# The lint level for crates with security notices. Note that as of
# 2019-12-17 there are no security notice advisories in
# https://github.com/rustsec/advisory-db
notice = "warn"
# A list of advisory IDs to ignore. Note that ignored advisories will still
# output a note when they are encountered.
#
# e.g. "RUSTSEC-0000-0000",
ignore = [
]
# This section is considered when running `cargo deny check licenses`
# More documentation for the licenses section can be found here:
# https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html
[licenses]
unlicensed = "deny"
# List of explicitly allowed licenses
# See https://spdx.org/licenses/ for list of possible licenses
# [possible values: any SPDX 3.11 short identifier (+ optional exception)].
allow = [
"MIT",
"MIT-0",
"Apache-2.0",
"BSD-3-Clause",
"MPL-2.0",
"Unicode-DFS-2016",
"CC0-1.0",
"ISC",
]
# List of explicitly disallowed licenses
# See https://spdx.org/licenses/ for list of possible licenses
# [possible values: any SPDX 3.11 short identifier (+ optional exception)].
deny = [
]
# Lint level for licenses considered copyleft
copyleft = "deny"
# Blanket approval or denial for OSI-approved or FSF Free/Libre licenses
# * both - The license will be approved if it is both OSI-approved *AND* FSF
# * either - The license will be approved if it is either OSI-approved *OR* FSF
# * osi-only - The license will be approved if is OSI-approved *AND NOT* FSF
# * fsf-only - The license will be approved if is FSF *AND NOT* OSI-approved
# * neither - This predicate is ignored and the default lint level is used
allow-osi-fsf-free = "neither"
# Lint level used when no other predicates are matched
# 1. License isn't in the allow or deny lists
# 2. License isn't copyleft
# 3. License isn't OSI/FSF, or allow-osi-fsf-free = "neither"
default = "deny"
# The confidence threshold for detecting a license from license text.
# The higher the value, the more closely the license text must be to the
# canonical license text of a valid SPDX license file.
# [possible values: any between 0.0 and 1.0].
confidence-threshold = 0.8
# Allow 1 or more licenses on a per-crate basis, so that particular licenses
# aren't accepted for every possible crate as with the normal allow list
exceptions = [
# Each entry is the crate and version constraint, and its specific allow
# list
#{ allow = ["Zlib"], name = "adler32", version = "*" },
]
[licenses.private]
# If true, ignores workspace crates that aren't published, or are only
# published to private registries.
# To see how to mark a crate as unpublished (to the official registry),
# visit https://doc.rust-lang.org/cargo/reference/manifest.html#the-publish-field.
ignore = true
# This section is considered when running `cargo deny check bans`.
# More documentation about the 'bans' section can be found here:
# https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html
[bans]
# Lint level for when multiple versions of the same crate are detected
multiple-versions = "warn"
# Lint level for when a crate version requirement is `*`
wildcards = "warn"
# The graph highlighting used when creating dotgraphs for crates
# with multiple versions
# * lowest-version - The path to the lowest versioned duplicate is highlighted
# * simplest-path - The path to the version with the fewest edges is highlighted
# * all - Both lowest-version and simplest-path are used
highlight = "all"
# The default lint level for `default` features for crates that are members of
# the workspace that is being checked. This can be overridden by allowing/denying
# `default` on a crate-by-crate basis if desired.
workspace-default-features = "allow"
# The default lint level for `default` features for external crates that are not
# members of the workspace. This can be overridden by allowing/denying `default`
# on a crate-by-crate basis if desired.
external-default-features = "allow"
# List of crates that are allowed. Use with care!
allow = [
#{ name = "ansi_term", version = "=0.11.0" },
]
# List of crates to deny
deny = [
# Each entry the name of a crate and a version range. If version is
# not specified, all versions will be matched.
#{ name = "ansi_term", version = "=0.11.0" },
#
# Wrapper crates can optionally be specified to allow the crate when it
# is a direct dependency of the otherwise banned crate
#{ name = "ansi_term", version = "=0.11.0", wrappers = [] },
]
# This section is considered when running `cargo deny check sources`.
# More documentation about the 'sources' section can be found here:
# https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html
[sources]
# Lint level for what to happen when a crate from a crate registry that is not
# in the allow list is encountered
unknown-registry = "deny"
# Lint level for what to happen when a crate from a git repository that is not
# in the allow list is encountered
unknown-git = "deny"
# List of URLs for allowed crate registries. Defaults to the crates.io index
# if not specified. If it is specified but empty, no registries are allowed.
allow-registry = ["https://github.com/rust-lang/crates.io-index"]
# List of URLs for allowed Git repositories
allow-git = []
[sources.allow-org]
# 1 or more github.com organizations to allow git sources for
github = []
+1 -2
View File
@@ -17,8 +17,7 @@ $ export MY_LOG_STYLE=never
If you want to control the logging output completely, see the `custom_logger` example.
*/
#[macro_use]
extern crate log;
use log::info;
use env_logger::{Builder, Env};
+2 -2
View File
@@ -17,7 +17,7 @@ $ export MY_LOG_STYLE=never
If you want to control the logging output completely, see the `custom_logger` example.
*/
#[cfg(all(feature = "termcolor", feature = "humantime"))]
#[cfg(all(feature = "color", feature = "humantime"))]
fn main() {
use env_logger::{fmt::Color, Builder, Env};
@@ -50,5 +50,5 @@ fn main() {
log::info!("a log from `MyLogger`");
}
#[cfg(not(all(feature = "termcolor", feature = "humantime")))]
#[cfg(not(all(feature = "color", feature = "humantime")))]
fn main() {}
+1 -4
View File
@@ -10,12 +10,9 @@ $ export MY_LOG_LEVEL='info'
If you only want to change the way logs are formatted, look at the `custom_format` example.
*/
#[macro_use]
extern crate log;
use env_logger::filter::{Builder, Filter};
use log::{Log, Metadata, Record, SetLoggerError};
use log::{info, Log, Metadata, Record, SetLoggerError};
const FILTER_ENV: &str = "MY_LOG_LEVEL";
+1 -2
View File
@@ -15,8 +15,7 @@ $ export MY_LOG_STYLE=never
```
*/
#[macro_use]
extern crate log;
use log::{debug, error, info, trace, warn};
use env_logger::Env;
+1 -4
View File
@@ -2,12 +2,9 @@
Specify logging filters in code instead of using an environment variable.
*/
#[macro_use]
extern crate log;
use env_logger::Builder;
use log::LevelFilter;
use log::{debug, error, info, trace, warn, LevelFilter};
fn main() {
Builder::new().filter_level(LevelFilter::max()).init();
+2 -3
View File
@@ -11,13 +11,12 @@ cargo test --example in_tests
You should see the `it_does_not_work` test fail and include its log output.
*/
#[cfg_attr(test, macro_use)]
extern crate log;
fn main() {}
#[cfg(test)]
mod tests {
use log::debug;
fn init_logger() {
let _ = env_logger::builder()
// Include all events in tests
+3
View File
@@ -0,0 +1,3 @@
owners = ["github:rust-cli:Maintainers"]
dependent-version = "fix"
allow-branch = ["main"]
-1
View File
@@ -1 +0,0 @@
1.41.0
+138 -127
View File
@@ -59,7 +59,6 @@
//! [`Filter::matches`]: struct.Filter.html#method.matches
use log::{Level, LevelFilter, Metadata, Record};
use std::collections::HashMap;
use std::env;
use std::fmt;
use std::mem;
@@ -72,18 +71,6 @@ mod inner;
#[path = "string.rs"]
mod inner;
/// A log filter.
///
/// This struct can be used to determine whether or not a log record
/// should be written to the output.
/// Use the [`Builder`] type to parse and construct a `Filter`.
///
/// [`Builder`]: struct.Builder.html
pub struct Filter {
directives: Vec<Directive>,
filter: Option<inner::Filter>,
}
/// A builder for a log filter.
///
/// It can be used to parse a set of directives from a string before building
@@ -108,17 +95,151 @@ pub struct Filter {
///
/// [`Filter`]: struct.Filter.html
pub struct Builder {
directives: HashMap<Option<String>, LevelFilter>,
directives: Vec<Directive>,
filter: Option<inner::Filter>,
built: bool,
}
impl Builder {
/// Initializes the filter builder with defaults.
pub fn new() -> Builder {
Builder {
directives: Vec::new(),
filter: None,
built: false,
}
}
/// Initializes the filter builder from an environment.
pub fn from_env(env: &str) -> Builder {
let mut builder = Builder::new();
if let Ok(s) = env::var(env) {
builder.parse(&s);
}
builder
}
/// Insert the directive replacing any directive with the same name.
fn insert_directive(&mut self, mut directive: Directive) {
if let Some(pos) = self
.directives
.iter()
.position(|d| d.name == directive.name)
{
mem::swap(&mut self.directives[pos], &mut directive);
} else {
self.directives.push(directive);
}
}
/// Adds a directive to the filter for a specific module.
pub fn filter_module(&mut self, module: &str, level: LevelFilter) -> &mut Self {
self.filter(Some(module), level)
}
/// Adds a directive to the filter for all modules.
pub fn filter_level(&mut self, level: LevelFilter) -> &mut Self {
self.filter(None, level)
}
/// Adds a directive to the filter.
///
/// The given module (if any) will log at most the specified level provided.
/// If no module is provided then the filter will apply to all log messages.
pub fn filter(&mut self, module: Option<&str>, level: LevelFilter) -> &mut Self {
self.insert_directive(Directive {
name: module.map(|s| s.to_string()),
level,
});
self
}
/// Parses the directives string.
///
/// See the [Enabling Logging] section for more details.
///
/// [Enabling Logging]: ../index.html#enabling-logging
pub fn parse(&mut self, filters: &str) -> &mut Self {
let (directives, filter) = parse_spec(filters);
self.filter = filter;
for directive in directives {
self.insert_directive(directive);
}
self
}
/// Build a log filter.
pub fn build(&mut self) -> Filter {
assert!(!self.built, "attempt to re-use consumed builder");
self.built = true;
let mut directives = Vec::new();
if self.directives.is_empty() {
// Adds the default filter if none exist
directives.push(Directive {
name: None,
level: LevelFilter::Error,
});
} else {
// Consume directives.
directives = mem::take(&mut self.directives);
// Sort the directives by length of their name, this allows a
// little more efficient lookup at runtime.
directives.sort_by(|a, b| {
let alen = a.name.as_ref().map(|a| a.len()).unwrap_or(0);
let blen = b.name.as_ref().map(|b| b.len()).unwrap_or(0);
alen.cmp(&blen)
});
}
Filter {
directives: mem::take(&mut directives),
filter: mem::take(&mut self.filter),
}
}
}
impl Default for Builder {
fn default() -> Self {
Builder::new()
}
}
impl fmt::Debug for Builder {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.built {
f.debug_struct("Filter").field("built", &true).finish()
} else {
f.debug_struct("Filter")
.field("filter", &self.filter)
.field("directives", &self.directives)
.finish()
}
}
}
#[derive(Debug)]
struct Directive {
name: Option<String>,
level: LevelFilter,
}
/// A log filter.
///
/// This struct can be used to determine whether or not a log record
/// should be written to the output.
/// Use the [`Builder`] type to parse and construct a `Filter`.
///
/// [`Builder`]: struct.Builder.html
pub struct Filter {
directives: Vec<Directive>,
filter: Option<inner::Filter>,
}
impl Filter {
/// Returns the maximum `LevelFilter` that this filter instance is
/// configured to output.
@@ -168,103 +289,6 @@ impl Filter {
}
}
impl Builder {
/// Initializes the filter builder with defaults.
pub fn new() -> Builder {
Builder {
directives: HashMap::new(),
filter: None,
built: false,
}
}
/// Initializes the filter builder from an environment.
pub fn from_env(env: &str) -> Builder {
let mut builder = Builder::new();
if let Ok(s) = env::var(env) {
builder.parse(&s);
}
builder
}
/// Adds a directive to the filter for a specific module.
pub fn filter_module(&mut self, module: &str, level: LevelFilter) -> &mut Self {
self.filter(Some(module), level)
}
/// Adds a directive to the filter for all modules.
pub fn filter_level(&mut self, level: LevelFilter) -> &mut Self {
self.filter(None, level)
}
/// Adds a directive to the filter.
///
/// The given module (if any) will log at most the specified level provided.
/// If no module is provided then the filter will apply to all log messages.
pub fn filter(&mut self, module: Option<&str>, level: LevelFilter) -> &mut Self {
self.directives.insert(module.map(|s| s.to_string()), level);
self
}
/// Parses the directives string.
///
/// See the [Enabling Logging] section for more details.
///
/// [Enabling Logging]: ../index.html#enabling-logging
pub fn parse(&mut self, filters: &str) -> &mut Self {
let (directives, filter) = parse_spec(filters);
self.filter = filter;
for directive in directives {
self.directives.insert(directive.name, directive.level);
}
self
}
/// Build a log filter.
pub fn build(&mut self) -> Filter {
assert!(!self.built, "attempt to re-use consumed builder");
self.built = true;
let mut directives = Vec::new();
if self.directives.is_empty() {
// Adds the default filter if none exist
directives.push(Directive {
name: None,
level: LevelFilter::Error,
});
} else {
// Consume map of directives.
let directives_map = mem::take(&mut self.directives);
directives = directives_map
.into_iter()
.map(|(name, level)| Directive { name, level })
.collect();
// Sort the directives by length of their name, this allows a
// little more efficient lookup at runtime.
directives.sort_by(|a, b| {
let alen = a.name.as_ref().map(|a| a.len()).unwrap_or(0);
let blen = b.name.as_ref().map(|b| b.len()).unwrap_or(0);
alen.cmp(&blen)
});
}
Filter {
directives: mem::take(&mut directives),
filter: mem::replace(&mut self.filter, None),
}
}
}
impl Default for Builder {
fn default() -> Self {
Builder::new()
}
}
impl fmt::Debug for Filter {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Filter")
@@ -274,19 +298,6 @@ impl fmt::Debug for Filter {
}
}
impl fmt::Debug for Builder {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.built {
f.debug_struct("Filter").field("built", &true).finish()
} else {
f.debug_struct("Filter")
.field("filter", &self.filter)
.field("directives", &self.directives)
.finish()
}
}
}
/// Parse a logging specification string (e.g: "crate1,crate2::mod3,crate3::x=error/foo")
/// and return a vector with log directives.
fn parse_spec(spec: &str) -> (Vec<Directive>, Option<inner::Filter>) {
@@ -762,7 +773,7 @@ mod tests {
#[test]
fn parse_spec_blank_level_isolated_comma_only() {
// The spec should contain zero or more comma-separated string slices,
// so a comma-only string should be interpretted as two empty strings
// so a comma-only string should be interpreted as two empty strings
// (which should both be treated as invalid, so ignored).
let (dirs, filter) = parse_spec(","); // should be ignored
assert_eq!(dirs.len(), 0);
@@ -772,7 +783,7 @@ mod tests {
#[test]
fn parse_spec_blank_level_isolated_comma_blank() {
// The spec should contain zero or more comma-separated string slices,
// so this bogus spec should be interpretted as containing one empty
// so this bogus spec should be interpreted as containing one empty
// string and one blank string. Both should both be treated as
// invalid, so ignored.
let (dirs, filter) = parse_spec(", "); // should be ignored
@@ -783,7 +794,7 @@ mod tests {
#[test]
fn parse_spec_blank_level_isolated_blank_comma() {
// The spec should contain zero or more comma-separated string slices,
// so this bogus spec should be interpretted as containing one blank
// so this bogus spec should be interpreted as containing one blank
// string and one empty string. Both should both be treated as
// invalid, so ignored.
let (dirs, filter) = parse_spec(" ,"); // should be ignored
@@ -7,10 +7,6 @@ use humantime::{
use crate::fmt::{Formatter, TimestampPrecision};
pub(in crate::fmt) mod glob {
pub use super::*;
}
impl Formatter {
/// Get a [`Timestamp`] for the current date and time in UTC.
///
-11
View File
@@ -1,11 +0,0 @@
/*
This internal module contains the timestamp implementation.
Its public API is available when the `humantime` crate is available.
*/
#[cfg_attr(feature = "humantime", path = "extern_impl.rs")]
#[cfg_attr(not(feature = "humantime"), path = "shim_impl.rs")]
mod imp;
pub(in crate::fmt) use self::imp::*;
-5
View File
@@ -1,5 +0,0 @@
/*
Timestamps aren't available when we don't have a `humantime` dependency.
*/
pub(in crate::fmt) mod glob {}
+88 -23
View File
@@ -35,12 +35,21 @@ use std::io::prelude::*;
use std::rc::Rc;
use std::{fmt, io, mem};
#[cfg(feature = "color")]
use log::Level;
use log::Record;
#[cfg(feature = "humantime")]
mod humantime;
pub(crate) mod writer;
pub use self::humantime::glob::*;
#[cfg(feature = "color")]
mod style;
#[cfg(feature = "color")]
pub use style::{Color, Style, StyledValue};
#[cfg(feature = "humantime")]
pub use self::humantime::Timestamp;
pub use self::writer::glob::*;
use self::writer::{Buffer, Writer};
@@ -120,6 +129,62 @@ impl Formatter {
}
}
#[cfg(feature = "color")]
impl Formatter {
/// Begin a new [`Style`].
///
/// # Examples
///
/// Create a bold, red colored style and use it to print the log level:
///
/// ```
/// use std::io::Write;
/// use env_logger::fmt::Color;
///
/// let mut builder = env_logger::Builder::new();
///
/// builder.format(|buf, record| {
/// let mut level_style = buf.style();
///
/// level_style.set_color(Color::Red).set_bold(true);
///
/// writeln!(buf, "{}: {}",
/// level_style.value(record.level()),
/// record.args())
/// });
/// ```
///
/// [`Style`]: struct.Style.html
pub fn style(&self) -> Style {
Style {
buf: self.buf.clone(),
spec: termcolor::ColorSpec::new(),
}
}
/// Get the default [`Style`] for the given level.
///
/// The style can be used to print other values besides the level.
pub fn default_level_style(&self, level: Level) -> Style {
let mut level_style = self.style();
match level {
Level::Trace => level_style.set_color(Color::Cyan),
Level::Debug => level_style.set_color(Color::Blue),
Level::Info => level_style.set_color(Color::Green),
Level::Warn => level_style.set_color(Color::Yellow),
Level::Error => level_style.set_color(Color::Red).set_bold(true),
};
level_style
}
/// Get a printable [`Style`] for the given level.
///
/// The style can only be used to print the level.
pub fn default_styled_level(&self, level: Level) -> StyledValue<'static, Level> {
self.default_level_style(level).into_value(level)
}
}
impl Write for Formatter {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.buf.borrow_mut().write(buf)
@@ -149,21 +214,6 @@ pub(crate) struct Builder {
built: bool,
}
impl Default for Builder {
fn default() -> Self {
Builder {
format_timestamp: Some(Default::default()),
format_module_path: false,
format_target: true,
format_level: true,
format_indent: Some(4),
custom_format: None,
format_suffix: "\n",
built: false,
}
}
}
impl Builder {
/// Convert the format into a callable function.
///
@@ -202,9 +252,24 @@ impl Builder {
}
}
#[cfg(feature = "termcolor")]
impl Default for Builder {
fn default() -> Self {
Builder {
format_timestamp: Some(Default::default()),
format_module_path: false,
format_target: true,
format_level: true,
format_indent: Some(4),
custom_format: None,
format_suffix: "\n",
built: false,
}
}
}
#[cfg(feature = "color")]
type SubtleStyle = StyledValue<'static, &'static str>;
#[cfg(not(feature = "termcolor"))]
#[cfg(not(feature = "color"))]
type SubtleStyle = &'static str;
/// The default format.
@@ -233,7 +298,7 @@ impl<'a> DefaultFormat<'a> {
}
fn subtle_style(&self, text: &'static str) -> SubtleStyle {
#[cfg(feature = "termcolor")]
#[cfg(feature = "color")]
{
self.buf
.style()
@@ -242,7 +307,7 @@ impl<'a> DefaultFormat<'a> {
.clone()
.into_value(text)
}
#[cfg(not(feature = "termcolor"))]
#[cfg(not(feature = "color"))]
{
text
}
@@ -268,11 +333,11 @@ impl<'a> DefaultFormat<'a> {
}
let level = {
#[cfg(feature = "termcolor")]
#[cfg(feature = "color")]
{
self.buf.default_styled_level(record.level())
}
#[cfg(not(feature = "termcolor"))]
#[cfg(not(feature = "color"))]
{
record.level()
}
@@ -403,7 +468,7 @@ mod tests {
fmt.write(&record).expect("failed to write record");
let buf = buf.borrow();
String::from_utf8(buf.bytes().to_vec()).expect("failed to read record")
String::from_utf8(buf.as_bytes().to_vec()).expect("failed to read record")
}
fn write_target(target: &str, fmt: DefaultFormat) -> String {
@@ -1,190 +1,9 @@
use std::borrow::Cow;
use std::cell::RefCell;
use std::fmt;
use std::io::{self, Write};
use std::rc::Rc;
use std::sync::Mutex;
use log::Level;
use termcolor::{self, ColorChoice, ColorSpec, WriteColor};
use crate::fmt::{Formatter, WritableTarget, WriteStyle};
pub(in crate::fmt::writer) mod glob {
pub use super::*;
}
impl Formatter {
/// Begin a new [`Style`].
///
/// # Examples
///
/// Create a bold, red colored style and use it to print the log level:
///
/// ```
/// use std::io::Write;
/// use env_logger::fmt::Color;
///
/// let mut builder = env_logger::Builder::new();
///
/// builder.format(|buf, record| {
/// let mut level_style = buf.style();
///
/// level_style.set_color(Color::Red).set_bold(true);
///
/// writeln!(buf, "{}: {}",
/// level_style.value(record.level()),
/// record.args())
/// });
/// ```
///
/// [`Style`]: struct.Style.html
pub fn style(&self) -> Style {
Style {
buf: self.buf.clone(),
spec: ColorSpec::new(),
}
}
/// Get the default [`Style`] for the given level.
///
/// The style can be used to print other values besides the level.
pub fn default_level_style(&self, level: Level) -> Style {
let mut level_style = self.style();
match level {
Level::Trace => level_style.set_color(Color::Cyan),
Level::Debug => level_style.set_color(Color::Blue),
Level::Info => level_style.set_color(Color::Green),
Level::Warn => level_style.set_color(Color::Yellow),
Level::Error => level_style.set_color(Color::Red).set_bold(true),
};
level_style
}
/// Get a printable [`Style`] for the given level.
///
/// The style can only be used to print the level.
pub fn default_styled_level(&self, level: Level) -> StyledValue<'static, Level> {
self.default_level_style(level).into_value(level)
}
}
pub(in crate::fmt::writer) struct BufferWriter {
inner: termcolor::BufferWriter,
uncolored_target: Option<WritableTarget>,
}
pub(in crate::fmt) struct Buffer {
inner: termcolor::Buffer,
has_uncolored_target: bool,
}
impl BufferWriter {
pub(in crate::fmt::writer) fn stderr(is_test: bool, write_style: WriteStyle) -> Self {
BufferWriter {
inner: termcolor::BufferWriter::stderr(write_style.into_color_choice()),
uncolored_target: if is_test {
Some(WritableTarget::Stderr)
} else {
None
},
}
}
pub(in crate::fmt::writer) fn stdout(is_test: bool, write_style: WriteStyle) -> Self {
BufferWriter {
inner: termcolor::BufferWriter::stdout(write_style.into_color_choice()),
uncolored_target: if is_test {
Some(WritableTarget::Stdout)
} else {
None
},
}
}
pub(in crate::fmt::writer) fn pipe(
write_style: WriteStyle,
pipe: Box<Mutex<dyn io::Write + Send + 'static>>,
) -> Self {
BufferWriter {
// The inner Buffer is never printed from, but it is still needed to handle coloring and other formating
inner: termcolor::BufferWriter::stderr(write_style.into_color_choice()),
uncolored_target: Some(WritableTarget::Pipe(pipe)),
}
}
pub(in crate::fmt::writer) fn buffer(&self) -> Buffer {
Buffer {
inner: self.inner.buffer(),
has_uncolored_target: self.uncolored_target.is_some(),
}
}
pub(in crate::fmt::writer) fn print(&self, buf: &Buffer) -> io::Result<()> {
if let Some(target) = &self.uncolored_target {
// This impl uses the `eprint` and `print` macros
// instead of `termcolor`'s buffer.
// This is so their output can be captured by `cargo test`
let log = String::from_utf8_lossy(buf.bytes());
match target {
WritableTarget::Stderr => eprint!("{}", log),
WritableTarget::Stdout => print!("{}", log),
WritableTarget::Pipe(pipe) => write!(pipe.lock().unwrap(), "{}", log)?,
}
Ok(())
} else {
self.inner.print(&buf.inner)
}
}
}
impl Buffer {
pub(in crate::fmt) fn clear(&mut self) {
self.inner.clear()
}
pub(in crate::fmt) fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.inner.write(buf)
}
pub(in crate::fmt) fn flush(&mut self) -> io::Result<()> {
self.inner.flush()
}
pub(in crate::fmt) fn bytes(&self) -> &[u8] {
self.inner.as_slice()
}
fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
// Ignore styles for test captured logs because they can't be printed
if !self.has_uncolored_target {
self.inner.set_color(spec)
} else {
Ok(())
}
}
fn reset(&mut self) -> io::Result<()> {
// Ignore styles for test captured logs because they can't be printed
if !self.has_uncolored_target {
self.inner.reset()
} else {
Ok(())
}
}
}
impl WriteStyle {
fn into_color_choice(self) -> ColorChoice {
match self {
WriteStyle::Always => ColorChoice::Always,
WriteStyle::Auto => ColorChoice::Auto,
WriteStyle::Never => ColorChoice::Never,
}
}
}
use super::Buffer;
/// A set of styles to apply to the terminal output.
///
@@ -240,18 +59,8 @@ impl WriteStyle {
/// [`value`]: #method.value
#[derive(Clone)]
pub struct Style {
buf: Rc<RefCell<Buffer>>,
spec: ColorSpec,
}
/// A value that can be printed using the given styles.
///
/// It is the result of calling [`Style::value`].
///
/// [`Style::value`]: struct.Style.html#method.value
pub struct StyledValue<'a, T> {
style: Cow<'a, Style>,
value: T,
pub(in crate::fmt) buf: Rc<RefCell<Buffer>>,
pub(in crate::fmt) spec: termcolor::ColorSpec,
}
impl Style {
@@ -426,6 +235,22 @@ impl Style {
}
}
impl fmt::Debug for Style {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Style").field("spec", &self.spec).finish()
}
}
/// A value that can be printed using the given styles.
///
/// It is the result of calling [`Style::value`].
///
/// [`Style::value`]: struct.Style.html#method.value
pub struct StyledValue<'a, T> {
style: Cow<'a, Style>,
value: T,
}
impl<'a, T> StyledValue<'a, T> {
fn write_fmt<F>(&self, f: F) -> fmt::Result
where
@@ -445,12 +270,6 @@ impl<'a, T> StyledValue<'a, T> {
}
}
impl fmt::Debug for Style {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Style").field("spec", &self.spec).finish()
}
}
macro_rules! impl_styled_value_fmt {
($($fmt_trait:path),*) => {
$(
+9 -8
View File
@@ -1,24 +1,25 @@
/*
This internal module contains the terminal detection implementation.
If the `atty` crate is available then we use it to detect whether we're
attached to a particular TTY. If the `atty` crate is not available we
assume we're not attached to anything. This effectively prevents styles
from being printed.
If the `auto-color` feature is enabled then we detect whether we're attached to a particular TTY.
Otherwise, assume we're not attached to anything. This effectively prevents styles from being
printed.
*/
#[cfg(feature = "atty")]
#[cfg(feature = "auto-color")]
mod imp {
use is_terminal::IsTerminal;
pub(in crate::fmt) fn is_stdout() -> bool {
atty::is(atty::Stream::Stdout)
std::io::stdout().is_terminal()
}
pub(in crate::fmt) fn is_stderr() -> bool {
atty::is(atty::Stream::Stderr)
std::io::stderr().is_terminal()
}
}
#[cfg(not(feature = "atty"))]
#[cfg(not(feature = "auto-color"))]
mod imp {
pub(in crate::fmt) fn is_stdout() -> bool {
false
@@ -5,8 +5,11 @@ Its public API is available when the `termcolor` crate is available.
The terminal printing is shimmed when the `termcolor` crate is not available.
*/
#[cfg_attr(feature = "termcolor", path = "extern_impl.rs")]
#[cfg_attr(not(feature = "termcolor"), path = "shim_impl.rs")]
mod imp;
pub(in crate::fmt) use self::imp::*;
#[cfg(feature = "color")]
mod termcolor;
#[cfg(feature = "color")]
pub(in crate::fmt) use self::termcolor::*;
#[cfg(not(feature = "color"))]
mod plain;
#[cfg(not(feature = "color"))]
pub(in crate::fmt) use plain::*;
+68
View File
@@ -0,0 +1,68 @@
use std::{io, sync::Mutex};
use crate::fmt::{WritableTarget, WriteStyle};
pub(in crate::fmt::writer) struct BufferWriter {
target: WritableTarget,
}
impl BufferWriter {
pub(in crate::fmt::writer) fn stderr(is_test: bool, _write_style: WriteStyle) -> Self {
BufferWriter {
target: if is_test {
WritableTarget::PrintStderr
} else {
WritableTarget::WriteStderr
},
}
}
pub(in crate::fmt::writer) fn stdout(is_test: bool, _write_style: WriteStyle) -> Self {
BufferWriter {
target: if is_test {
WritableTarget::PrintStdout
} else {
WritableTarget::WriteStdout
},
}
}
pub(in crate::fmt::writer) fn pipe(pipe: Box<Mutex<dyn io::Write + Send + 'static>>) -> Self {
BufferWriter {
target: WritableTarget::Pipe(pipe),
}
}
pub(in crate::fmt::writer) fn write_style(&self) -> WriteStyle {
WriteStyle::Never
}
pub(in crate::fmt::writer) fn buffer(&self) -> Buffer {
Buffer(Vec::new())
}
pub(in crate::fmt::writer) fn print(&self, buf: &Buffer) -> io::Result<()> {
self.target.print(buf)
}
}
pub(in crate::fmt) struct Buffer(Vec<u8>);
impl Buffer {
pub(in crate::fmt) fn clear(&mut self) {
self.0.clear();
}
pub(in crate::fmt) fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.0.extend(buf);
Ok(buf.len())
}
pub(in crate::fmt) fn flush(&mut self) -> io::Result<()> {
Ok(())
}
pub(in crate::fmt) fn as_bytes(&self) -> &[u8] {
&self.0
}
}
+108
View File
@@ -0,0 +1,108 @@
use std::io::{self, Write};
use std::sync::Mutex;
use termcolor::{self, ColorSpec, WriteColor};
use crate::fmt::{WritableTarget, WriteStyle};
pub(in crate::fmt::writer) struct BufferWriter {
inner: termcolor::BufferWriter,
uncolored_target: Option<WritableTarget>,
write_style: WriteStyle,
}
impl BufferWriter {
pub(in crate::fmt::writer) fn stderr(is_test: bool, write_style: WriteStyle) -> Self {
BufferWriter {
inner: termcolor::BufferWriter::stderr(write_style.into_color_choice()),
uncolored_target: if is_test {
Some(WritableTarget::PrintStderr)
} else {
None
},
write_style,
}
}
pub(in crate::fmt::writer) fn stdout(is_test: bool, write_style: WriteStyle) -> Self {
BufferWriter {
inner: termcolor::BufferWriter::stdout(write_style.into_color_choice()),
uncolored_target: if is_test {
Some(WritableTarget::PrintStdout)
} else {
None
},
write_style,
}
}
pub(in crate::fmt::writer) fn pipe(pipe: Box<Mutex<dyn io::Write + Send + 'static>>) -> Self {
let write_style = WriteStyle::Never;
BufferWriter {
// The inner Buffer is never printed from, but it is still needed to handle coloring and other formatting
inner: termcolor::BufferWriter::stderr(write_style.into_color_choice()),
uncolored_target: Some(WritableTarget::Pipe(pipe)),
write_style,
}
}
pub(in crate::fmt::writer) fn write_style(&self) -> WriteStyle {
self.write_style
}
pub(in crate::fmt::writer) fn buffer(&self) -> Buffer {
Buffer {
inner: self.inner.buffer(),
has_uncolored_target: self.uncolored_target.is_some(),
}
}
pub(in crate::fmt::writer) fn print(&self, buf: &Buffer) -> io::Result<()> {
if let Some(target) = &self.uncolored_target {
target.print(buf)
} else {
self.inner.print(&buf.inner)
}
}
}
pub(in crate::fmt) struct Buffer {
inner: termcolor::Buffer,
has_uncolored_target: bool,
}
impl Buffer {
pub(in crate::fmt) fn clear(&mut self) {
self.inner.clear()
}
pub(in crate::fmt) fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.inner.write(buf)
}
pub(in crate::fmt) fn flush(&mut self) -> io::Result<()> {
self.inner.flush()
}
pub(in crate::fmt) fn as_bytes(&self) -> &[u8] {
self.inner.as_slice()
}
pub(in crate::fmt) fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
// Ignore styles for test captured logs because they can't be printed
if !self.has_uncolored_target {
self.inner.set_color(spec)
} else {
Ok(())
}
}
pub(in crate::fmt) fn reset(&mut self) -> io::Result<()> {
// Ignore styles for test captured logs because they can't be printed
if !self.has_uncolored_target {
self.inner.reset()
} else {
Ok(())
}
}
}
+80 -44
View File
@@ -1,16 +1,15 @@
mod atty;
mod termcolor;
mod buffer;
use self::atty::{is_stderr, is_stdout};
use self::termcolor::BufferWriter;
use self::buffer::BufferWriter;
use std::{fmt, io, mem, sync::Mutex};
pub(super) mod glob {
pub use super::termcolor::glob::*;
pub use super::*;
}
pub(super) use self::termcolor::Buffer;
pub(super) use self::buffer::Buffer;
/// Log target, either `stdout`, `stderr` or a custom pipe.
#[non_exhaustive]
@@ -47,27 +46,49 @@ impl fmt::Debug for Target {
///
/// Same as `Target`, except the pipe is wrapped in a mutex for interior mutability.
pub(super) enum WritableTarget {
/// Logs will be sent to standard output.
Stdout,
/// Logs will be sent to standard error.
Stderr,
/// Logs will be written to standard output.
#[allow(dead_code)]
WriteStdout,
/// Logs will be printed to standard output.
PrintStdout,
/// Logs will be written to standard error.
#[allow(dead_code)]
WriteStderr,
/// Logs will be printed to standard error.
PrintStderr,
/// Logs will be sent to a custom pipe.
Pipe(Box<Mutex<dyn io::Write + Send + 'static>>),
}
impl From<Target> for WritableTarget {
fn from(target: Target) -> Self {
match target {
Target::Stdout => Self::Stdout,
Target::Stderr => Self::Stderr,
Target::Pipe(pipe) => Self::Pipe(Box::new(Mutex::new(pipe))),
}
}
}
impl WritableTarget {
fn print(&self, buf: &Buffer) -> io::Result<()> {
use std::io::Write as _;
impl Default for WritableTarget {
fn default() -> Self {
Self::from(Target::default())
let buf = buf.as_bytes();
match self {
WritableTarget::WriteStdout => {
let stream = std::io::stdout();
let mut stream = stream.lock();
stream.write_all(buf)?;
stream.flush()?;
}
WritableTarget::PrintStdout => print!("{}", String::from_utf8_lossy(buf)),
WritableTarget::WriteStderr => {
let stream = std::io::stderr();
let mut stream = stream.lock();
stream.write_all(buf)?;
stream.flush()?;
}
WritableTarget::PrintStderr => eprint!("{}", String::from_utf8_lossy(buf)),
// Safety: If the target type is `Pipe`, `target_pipe` will always be non-empty.
WritableTarget::Pipe(pipe) => {
let mut stream = pipe.lock().unwrap();
stream.write_all(buf)?;
stream.flush()?;
}
}
Ok(())
}
}
@@ -77,8 +98,10 @@ impl fmt::Debug for WritableTarget {
f,
"{}",
match self {
Self::Stdout => "stdout",
Self::Stderr => "stderr",
Self::WriteStdout => "stdout",
Self::PrintStdout => "stdout",
Self::WriteStderr => "stderr",
Self::PrintStderr => "stderr",
Self::Pipe(_) => "pipe",
}
)
@@ -101,15 +124,25 @@ impl Default for WriteStyle {
}
}
#[cfg(feature = "color")]
impl WriteStyle {
fn into_color_choice(self) -> ::termcolor::ColorChoice {
match self {
WriteStyle::Always => ::termcolor::ColorChoice::Always,
WriteStyle::Auto => ::termcolor::ColorChoice::Auto,
WriteStyle::Never => ::termcolor::ColorChoice::Never,
}
}
}
/// A terminal target with color awareness.
pub(crate) struct Writer {
inner: BufferWriter,
write_style: WriteStyle,
}
impl Writer {
pub fn write_style(&self) -> WriteStyle {
self.write_style
self.inner.write_style()
}
pub(super) fn buffer(&self) -> Buffer {
@@ -121,12 +154,18 @@ impl Writer {
}
}
impl fmt::Debug for Writer {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Writer").finish()
}
}
/// A builder for a terminal writer.
///
/// The target and style choice can be configured before building.
#[derive(Debug)]
pub(crate) struct Builder {
target: WritableTarget,
target: Target,
write_style: WriteStyle,
is_test: bool,
built: bool,
@@ -145,7 +184,7 @@ impl Builder {
/// Set the target to write to.
pub(crate) fn target(&mut self, target: Target) -> &mut Self {
self.target = target.into();
self.target = target;
self
}
@@ -165,6 +204,7 @@ impl Builder {
}
/// Whether or not to capture logs for `cargo test`.
#[allow(clippy::wrong_self_convention)]
pub(crate) fn is_test(&mut self, is_test: bool) -> &mut Self {
self.is_test = is_test;
self
@@ -178,9 +218,9 @@ impl Builder {
let color_choice = match self.write_style {
WriteStyle::Auto => {
if match &self.target {
WritableTarget::Stderr => is_stderr(),
WritableTarget::Stdout => is_stdout(),
WritableTarget::Pipe(_) => false,
Target::Stderr => is_stderr(),
Target::Stdout => is_stdout(),
Target::Pipe(_) => false,
} {
WriteStyle::Auto
} else {
@@ -189,17 +229,19 @@ impl Builder {
}
color_choice => color_choice,
};
let writer = match mem::take(&mut self.target) {
WritableTarget::Stderr => BufferWriter::stderr(self.is_test, color_choice),
WritableTarget::Stdout => BufferWriter::stdout(self.is_test, color_choice),
WritableTarget::Pipe(pipe) => BufferWriter::pipe(color_choice, pipe),
let color_choice = if self.is_test {
WriteStyle::Never
} else {
color_choice
};
Writer {
inner: writer,
write_style: self.write_style,
}
let writer = match mem::take(&mut self.target) {
Target::Stderr => BufferWriter::stderr(self.is_test, color_choice),
Target::Stdout => BufferWriter::stdout(self.is_test, color_choice),
Target::Pipe(pipe) => BufferWriter::pipe(Box::new(Mutex::new(pipe))),
};
Writer { inner: writer }
}
}
@@ -209,12 +251,6 @@ impl Default for Builder {
}
}
impl fmt::Debug for Writer {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Writer").finish()
}
}
fn parse_write_style(spec: &str) -> WriteStyle {
match spec {
"auto" => WriteStyle::Auto,
-72
View File
@@ -1,72 +0,0 @@
use std::{io, sync::Mutex};
use crate::fmt::{WritableTarget, WriteStyle};
pub(in crate::fmt::writer) mod glob {}
pub(in crate::fmt::writer) struct BufferWriter {
target: WritableTarget,
}
pub(in crate::fmt) struct Buffer(Vec<u8>);
impl BufferWriter {
pub(in crate::fmt::writer) fn stderr(_is_test: bool, _write_style: WriteStyle) -> Self {
BufferWriter {
target: WritableTarget::Stderr,
}
}
pub(in crate::fmt::writer) fn stdout(_is_test: bool, _write_style: WriteStyle) -> Self {
BufferWriter {
target: WritableTarget::Stdout,
}
}
pub(in crate::fmt::writer) fn pipe(
_write_style: WriteStyle,
pipe: Box<Mutex<dyn io::Write + Send + 'static>>,
) -> Self {
BufferWriter {
target: WritableTarget::Pipe(pipe),
}
}
pub(in crate::fmt::writer) fn buffer(&self) -> Buffer {
Buffer(Vec::new())
}
pub(in crate::fmt::writer) fn print(&self, buf: &Buffer) -> io::Result<()> {
// This impl uses the `eprint` and `print` macros
// instead of using the streams directly.
// This is so their output can be captured by `cargo test`.
match &self.target {
// Safety: If the target type is `Pipe`, `target_pipe` will always be non-empty.
WritableTarget::Pipe(pipe) => pipe.lock().unwrap().write_all(&buf.0)?,
WritableTarget::Stdout => print!("{}", String::from_utf8_lossy(&buf.0)),
WritableTarget::Stderr => eprint!("{}", String::from_utf8_lossy(&buf.0)),
}
Ok(())
}
}
impl Buffer {
pub(in crate::fmt) fn clear(&mut self) {
self.0.clear();
}
pub(in crate::fmt) fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.0.extend(buf);
Ok(buf.len())
}
pub(in crate::fmt) fn flush(&mut self) -> io::Result<()> {
Ok(())
}
#[cfg(test)]
pub(in crate::fmt) fn bytes(&self) -> &[u8] {
&self.0
}
}
+47 -1074
View File
File diff suppressed because it is too large Load Diff
+1016
View File
File diff suppressed because it is too large Load Diff