mirror of
https://github.com/openharmony/third_party_rust_env_logger.git
synced 2026-06-30 21:27:58 -04:00
@@ -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" },
|
||||
]
|
||||
@@ -1,7 +0,0 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: cargo
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 10
|
||||
@@ -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)',
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -1,4 +1 @@
|
||||
target/
|
||||
Cargo.lock
|
||||
.emacs*
|
||||
*~
|
||||
target
|
||||
|
||||
@@ -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]
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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."
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
[](https://crates.io/crates/env_logger)
|
||||
[](https://docs.rs/env_logger)
|
||||
[](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();
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
[package]
|
||||
name = "ci"
|
||||
edition = "2018"
|
||||
version = "0.0.0"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
style="conventional"
|
||||
ignore_author_re="(dependabot|renovate)"
|
||||
merge_commit = false
|
||||
@@ -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 = []
|
||||
@@ -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};
|
||||
|
||||
|
||||
@@ -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() {}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
owners = ["github:rust-cli:Maintainers"]
|
||||
dependent-version = "fix"
|
||||
allow-branch = ["main"]
|
||||
@@ -1 +0,0 @@
|
||||
1.41.0
|
||||
+138
-127
@@ -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.
|
||||
///
|
||||
@@ -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::*;
|
||||
@@ -1,5 +0,0 @@
|
||||
/*
|
||||
Timestamps aren't available when we don't have a `humantime` dependency.
|
||||
*/
|
||||
|
||||
pub(in crate::fmt) mod glob {}
|
||||
+88
-23
@@ -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),*) => {
|
||||
$(
|
||||
@@ -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::*;
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
+1016
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user