mirror of
https://github.com/sharkdp/hexyl.git
synced 2026-10-30 09:05:54 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bfa2726e05 | ||
|
|
d6af232691 | ||
|
|
cc6987e270 | ||
|
|
ee56a3396d | ||
|
|
4833c2afe4 | ||
|
|
dfd7bdef49 | ||
|
|
fd4aee2b85 | ||
|
|
cf8e7ec079 | ||
|
|
faa9866d11 | ||
|
|
1df35eeb63 | ||
|
|
437a15f3d6 | ||
|
|
70fa092df1 | ||
|
|
b2f0e75ed2 | ||
|
|
4fc0c1f1b0 | ||
|
|
4ce6e9d799 | ||
|
|
5bd76fd11b | ||
|
|
c7e2235293 |
No files matched your search
+112
-120
@@ -1,8 +1,8 @@
|
|||||||
name: CICD
|
name: CICD
|
||||||
|
|
||||||
env:
|
env:
|
||||||
MIN_SUPPORTED_RUST_VERSION: "1.60.0"
|
|
||||||
CICD_INTERMEDIATES_DIR: "_cicd-intermediates"
|
CICD_INTERMEDIATES_DIR: "_cicd-intermediates"
|
||||||
|
MSRV_FEATURES: ""
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
@@ -14,84 +14,102 @@ on:
|
|||||||
- '*'
|
- '*'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
crate_metadata:
|
||||||
|
name: Extract crate metadata
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Extract crate information
|
||||||
|
id: crate_metadata
|
||||||
|
run: |
|
||||||
|
cargo metadata --no-deps --format-version 1 | jq -r '"name=" + .packages[0].name' | tee -a $GITHUB_OUTPUT
|
||||||
|
cargo metadata --no-deps --format-version 1 | jq -r '"version=" + .packages[0].version' | tee -a $GITHUB_OUTPUT
|
||||||
|
cargo metadata --no-deps --format-version 1 | jq -r '"maintainer=" + .packages[0].authors[0]' | tee -a $GITHUB_OUTPUT
|
||||||
|
cargo metadata --no-deps --format-version 1 | jq -r '"homepage=" + .packages[0].homepage' | tee -a $GITHUB_OUTPUT
|
||||||
|
cargo metadata --no-deps --format-version 1 | jq -r '"msrv=" + .packages[0].rust_version' | tee -a $GITHUB_OUTPUT
|
||||||
|
outputs:
|
||||||
|
name: ${{ steps.crate_metadata.outputs.name }}
|
||||||
|
version: ${{ steps.crate_metadata.outputs.version }}
|
||||||
|
maintainer: ${{ steps.crate_metadata.outputs.maintainer }}
|
||||||
|
homepage: ${{ steps.crate_metadata.outputs.homepage }}
|
||||||
|
msrv: ${{ steps.crate_metadata.outputs.msrv }}
|
||||||
|
|
||||||
|
ensure_cargo_fmt:
|
||||||
|
name: Ensure 'cargo fmt' has been run
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
components: rustfmt
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- run: cargo fmt -- --check
|
||||||
|
|
||||||
min_version:
|
min_version:
|
||||||
name: Minimum supported rust version
|
name: Minimum supported rust version
|
||||||
runs-on: ubuntu-18.04
|
runs-on: ubuntu-20.04
|
||||||
|
needs: crate_metadata
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout source code
|
- name: Checkout source code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Install rust toolchain (v${{ env.MIN_SUPPORTED_RUST_VERSION }})
|
- name: Install rust toolchain (v${{ needs.crate_metadata.outputs.msrv }})
|
||||||
uses: actions-rs/toolchain@v1
|
uses: dtolnay/rust-toolchain@master
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ env.MIN_SUPPORTED_RUST_VERSION }}
|
toolchain: ${{ needs.crate_metadata.outputs.msrv }}
|
||||||
default: true
|
components: clippy
|
||||||
profile: minimal # minimal component installation (ie, no documentation)
|
|
||||||
components: clippy, rustfmt
|
|
||||||
|
|
||||||
- name: Ensure `cargo fmt` has been run
|
|
||||||
uses: actions-rs/cargo@v1
|
|
||||||
with:
|
|
||||||
command: fmt
|
|
||||||
args: -- --check
|
|
||||||
|
|
||||||
- name: Run clippy (on minimum supported rust version to prevent warnings we can't fix)
|
- name: Run clippy (on minimum supported rust version to prevent warnings we can't fix)
|
||||||
uses: actions-rs/cargo@v1
|
run: cargo clippy --locked --all-targets ${{ env.MSRV_FEATURES }}
|
||||||
with:
|
|
||||||
command: clippy
|
|
||||||
args: --locked --all-targets
|
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
uses: actions-rs/cargo@v1
|
run: cargo test --locked ${{ env.MSRV_FEATURES }}
|
||||||
with:
|
|
||||||
command: test
|
|
||||||
args: --locked
|
|
||||||
|
|
||||||
build:
|
build:
|
||||||
name: ${{ matrix.job.os }} (${{ matrix.job.target }})
|
name: ${{ matrix.job.target }} (${{ matrix.job.os }})
|
||||||
runs-on: ${{ matrix.job.os }}
|
runs-on: ${{ matrix.job.os }}
|
||||||
|
needs: crate_metadata
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
job:
|
job:
|
||||||
- { os: ubuntu-18.04, target: arm-unknown-linux-gnueabihf , use-cross: true }
|
- { target: aarch64-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true }
|
||||||
- { os: ubuntu-18.04, target: aarch64-unknown-linux-gnu , use-cross: true }
|
- { target: arm-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true }
|
||||||
- { os: ubuntu-18.04, target: i686-unknown-linux-gnu , use-cross: true }
|
- { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, use-cross: true }
|
||||||
- { os: ubuntu-18.04, target: i686-unknown-linux-musl , use-cross: true }
|
- { target: i686-pc-windows-msvc , os: windows-2019 }
|
||||||
- { os: ubuntu-18.04, target: x86_64-unknown-linux-gnu }
|
- { target: i686-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true }
|
||||||
- { os: ubuntu-18.04, target: x86_64-unknown-linux-musl , use-cross: true }
|
- { target: i686-unknown-linux-musl , os: ubuntu-20.04, use-cross: true }
|
||||||
- { os: macos-10.15 , target: x86_64-apple-darwin }
|
- { target: x86_64-apple-darwin , os: macos-12 }
|
||||||
# - { os: windows-2019, target: i686-pc-windows-gnu } ## disabled; error: linker `i686-w64-mingw32-gcc` not found
|
- { target: x86_64-pc-windows-gnu , os: windows-2019 }
|
||||||
- { os: windows-2019, target: i686-pc-windows-msvc }
|
- { target: x86_64-pc-windows-msvc , os: windows-2019 }
|
||||||
- { os: windows-2019, target: x86_64-pc-windows-gnu }
|
- { target: x86_64-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true }
|
||||||
- { os: windows-2019, target: x86_64-pc-windows-msvc }
|
- { target: x86_64-unknown-linux-musl , os: ubuntu-20.04, use-cross: true }
|
||||||
|
env:
|
||||||
|
BUILD_CMD: cargo
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout source code
|
- name: Checkout source code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Install prerequisites
|
- name: Install prerequisites
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
case ${{ matrix.job.target }} in
|
case ${{ matrix.job.target }} in
|
||||||
arm-unknown-linux-gnueabihf) sudo apt-get -y update ; sudo apt-get -y install gcc-arm-linux-gnueabihf ;;
|
arm-unknown-linux-*) sudo apt-get -y update ; sudo apt-get -y install gcc-arm-linux-gnueabihf ;;
|
||||||
aarch64-unknown-linux-gnu) sudo apt-get -y update ; sudo apt-get -y install gcc-aarch64-linux-gnu ;;
|
aarch64-unknown-linux-gnu) sudo apt-get -y update ; sudo apt-get -y install gcc-aarch64-linux-gnu ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
- name: Extract crate information
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
echo "PROJECT_NAME=$(sed -n 's/^name = "\(.*\)"/\1/p' Cargo.toml | head -n1)" >> $GITHUB_ENV
|
|
||||||
echo "PROJECT_VERSION=$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -n1)" >> $GITHUB_ENV
|
|
||||||
echo "PROJECT_MAINTAINER=$(sed -n 's/^authors = \["\(.*\)"\]/\1/p' Cargo.toml)" >> $GITHUB_ENV
|
|
||||||
echo "PROJECT_HOMEPAGE=$(sed -n 's/^homepage = "\(.*\)"/\1/p' Cargo.toml)" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Install Rust toolchain
|
- name: Install Rust toolchain
|
||||||
uses: actions-rs/toolchain@v1
|
uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
toolchain: stable
|
targets: ${{ matrix.job.target }}
|
||||||
target: ${{ matrix.job.target }}
|
|
||||||
override: true
|
- name: Install cross
|
||||||
profile: minimal # minimal component installation (ie, no documentation)
|
if: matrix.job.use-cross
|
||||||
|
uses: taiki-e/install-action@v2
|
||||||
|
with:
|
||||||
|
tool: cross
|
||||||
|
|
||||||
|
- name: Overwrite build command env variable
|
||||||
|
if: matrix.job.use-cross
|
||||||
|
shell: bash
|
||||||
|
run: echo "BUILD_CMD=cross" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Show version information (Rust, cargo, GCC)
|
- name: Show version information (Rust, cargo, GCC)
|
||||||
shell: bash
|
shell: bash
|
||||||
@@ -104,21 +122,16 @@ jobs:
|
|||||||
rustc -V
|
rustc -V
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
uses: actions-rs/cargo@v1
|
shell: bash
|
||||||
with:
|
run: $BUILD_CMD build --locked --release --target=${{ matrix.job.target }}
|
||||||
use-cross: ${{ matrix.job.use-cross }}
|
|
||||||
command: build
|
|
||||||
args: --release --target=${{ matrix.job.target }}
|
|
||||||
|
|
||||||
- name: Run example
|
- name: Run example
|
||||||
uses: actions-rs/cargo@v1
|
if: ${{ !matrix.job.use-cross }}
|
||||||
with:
|
shell: bash
|
||||||
use-cross: ${{ matrix.job.use-cross }}
|
run: $BUILD_CMD run --release --target=${{ matrix.job.target }} --example=simple
|
||||||
command: run
|
|
||||||
args: --release --target=${{ matrix.job.target }} --example=simple
|
|
||||||
|
|
||||||
- name: Strip debug information from executable
|
- name: Set binary name & path
|
||||||
id: strip
|
id: bin
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
# Figure out suffix of binary
|
# Figure out suffix of binary
|
||||||
@@ -127,31 +140,13 @@ jobs:
|
|||||||
*-pc-windows-*) EXE_suffix=".exe" ;;
|
*-pc-windows-*) EXE_suffix=".exe" ;;
|
||||||
esac;
|
esac;
|
||||||
|
|
||||||
# Figure out what strip tool to use if any
|
|
||||||
STRIP="strip"
|
|
||||||
case ${{ matrix.job.target }} in
|
|
||||||
arm-unknown-linux-gnueabihf) STRIP="arm-linux-gnueabihf-strip" ;;
|
|
||||||
aarch64-unknown-linux-gnu) STRIP="aarch64-linux-gnu-strip" ;;
|
|
||||||
*-pc-windows-msvc) STRIP="" ;;
|
|
||||||
esac;
|
|
||||||
|
|
||||||
# Setup paths
|
# Setup paths
|
||||||
BIN_DIR="${{ env.CICD_INTERMEDIATES_DIR }}/stripped-release-bin/"
|
BIN_NAME="${{ needs.crate_metadata.outputs.name }}${EXE_suffix}"
|
||||||
mkdir -p "${BIN_DIR}"
|
BIN_PATH="target/${{ matrix.job.target }}/release/${BIN_NAME}"
|
||||||
BIN_NAME="${{ env.PROJECT_NAME }}${EXE_suffix}"
|
|
||||||
BIN_PATH="${BIN_DIR}/${BIN_NAME}"
|
|
||||||
|
|
||||||
# Copy the release build binary to the result location
|
# Let subsequent steps know where to find the binary
|
||||||
cp "target/${{ matrix.job.target }}/release/${BIN_NAME}" "${BIN_DIR}"
|
echo "BIN_PATH=${BIN_PATH}" >> $GITHUB_OUTPUT
|
||||||
|
echo "BIN_NAME=${BIN_NAME}" >> $GITHUB_OUTPUT
|
||||||
# Also strip if possible
|
|
||||||
if [ -n "${STRIP}" ]; then
|
|
||||||
"${STRIP}" "${BIN_PATH}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Let subsequent steps know where to find the (stripped) bin
|
|
||||||
echo ::set-output name=BIN_PATH::${BIN_PATH}
|
|
||||||
echo ::set-output name=BIN_NAME::${BIN_NAME}
|
|
||||||
|
|
||||||
- name: Set testing options
|
- name: Set testing options
|
||||||
id: test-options
|
id: test-options
|
||||||
@@ -159,44 +154,41 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
# test only library unit tests and binary for arm-type targets
|
# test only library unit tests and binary for arm-type targets
|
||||||
unset CARGO_TEST_OPTIONS
|
unset CARGO_TEST_OPTIONS
|
||||||
unset CARGO_TEST_OPTIONS ; case ${{ matrix.job.target }} in arm-* | aarch64-*) CARGO_TEST_OPTIONS="--lib --bin ${PROJECT_NAME}" ;; esac;
|
unset CARGO_TEST_OPTIONS ; case ${{ matrix.job.target }} in arm-* | aarch64-*) CARGO_TEST_OPTIONS="--lib --bin ${{ needs.crate_metadata.outputs.name }}" ;; esac;
|
||||||
echo ::set-output name=CARGO_TEST_OPTIONS::${CARGO_TEST_OPTIONS}
|
echo "CARGO_TEST_OPTIONS=${CARGO_TEST_OPTIONS}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
uses: actions-rs/cargo@v1
|
shell: bash
|
||||||
with:
|
run: $BUILD_CMD test --locked --target=${{ matrix.job.target }} ${{ steps.test-options.outputs.CARGO_TEST_OPTIONS}}
|
||||||
use-cross: ${{ matrix.job.use-cross }}
|
|
||||||
command: test
|
|
||||||
args: --target=${{ matrix.job.target }} ${{ steps.test-options.outputs.CARGO_TEST_OPTIONS}}
|
|
||||||
|
|
||||||
- name: Setup Pandoc
|
- name: Setup Pandoc
|
||||||
uses: r-lib/actions/setup-pandoc@v2
|
uses: r-lib/actions/setup-pandoc@v2
|
||||||
|
|
||||||
- name: Generate man page
|
- name: Generate man page
|
||||||
run: pandoc -s -f markdown -t man -o "doc/${{ env.PROJECT_NAME }}.1" "doc/${{ env.PROJECT_NAME }}.1.md"
|
run: pandoc -s -f markdown -t man -o "doc/${{ needs.crate_metadata.outputs.name }}.1" "doc/${{ needs.crate_metadata.outputs.name }}.1.md"
|
||||||
|
|
||||||
- name: Create tarball
|
- name: Create tarball
|
||||||
id: package
|
id: package
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
PKG_suffix=".tar.gz" ; case ${{ matrix.job.target }} in *-pc-windows-*) PKG_suffix=".zip" ;; esac;
|
PKG_suffix=".tar.gz" ; case ${{ matrix.job.target }} in *-pc-windows-*) PKG_suffix=".zip" ;; esac;
|
||||||
PKG_BASENAME=${PROJECT_NAME}-v${PROJECT_VERSION}-${{ matrix.job.target }}
|
PKG_BASENAME=${{ needs.crate_metadata.outputs.name }}-v${{ needs.crate_metadata.outputs.version }}-${{ matrix.job.target }}
|
||||||
PKG_NAME=${PKG_BASENAME}${PKG_suffix}
|
PKG_NAME=${PKG_BASENAME}${PKG_suffix}
|
||||||
echo ::set-output name=PKG_NAME::${PKG_NAME}
|
echo "PKG_NAME=${PKG_NAME}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
PKG_STAGING="${{ env.CICD_INTERMEDIATES_DIR }}/package"
|
PKG_STAGING="${{ env.CICD_INTERMEDIATES_DIR }}/package"
|
||||||
ARCHIVE_DIR="${PKG_STAGING}/${PKG_BASENAME}/"
|
ARCHIVE_DIR="${PKG_STAGING}/${PKG_BASENAME}/"
|
||||||
mkdir -p "${ARCHIVE_DIR}"
|
mkdir -p "${ARCHIVE_DIR}"
|
||||||
|
|
||||||
# Binary
|
# Binary
|
||||||
cp "${{ steps.strip.outputs.BIN_PATH }}" "$ARCHIVE_DIR"
|
cp "${{ steps.bin.outputs.BIN_PATH }}" "$ARCHIVE_DIR"
|
||||||
|
|
||||||
# Man page
|
|
||||||
cp "doc/${{ env.PROJECT_NAME }}.1" "$ARCHIVE_DIR"
|
|
||||||
|
|
||||||
# README, LICENSE and CHANGELOG files
|
# README, LICENSE and CHANGELOG files
|
||||||
cp "README.md" "LICENSE-MIT" "LICENSE-APACHE" "CHANGELOG.md" "$ARCHIVE_DIR"
|
cp "README.md" "LICENSE-MIT" "LICENSE-APACHE" "CHANGELOG.md" "$ARCHIVE_DIR"
|
||||||
|
|
||||||
|
# Man page
|
||||||
|
cp "doc/${{ needs.crate_metadata.outputs.name }}.1" "$ARCHIVE_DIR"
|
||||||
|
|
||||||
# base compressed package
|
# base compressed package
|
||||||
pushd "${PKG_STAGING}/" >/dev/null
|
pushd "${PKG_STAGING}/" >/dev/null
|
||||||
case ${{ matrix.job.target }} in
|
case ${{ matrix.job.target }} in
|
||||||
@@ -206,7 +198,7 @@ jobs:
|
|||||||
popd >/dev/null
|
popd >/dev/null
|
||||||
|
|
||||||
# Let subsequent steps know where to find the compressed package
|
# Let subsequent steps know where to find the compressed package
|
||||||
echo ::set-output name=PKG_PATH::"${PKG_STAGING}/${PKG_NAME}"
|
echo "PKG_PATH=${PKG_STAGING}/${PKG_NAME}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Create Debian package
|
- name: Create Debian package
|
||||||
id: debian-package
|
id: debian-package
|
||||||
@@ -218,10 +210,10 @@ jobs:
|
|||||||
DPKG_DIR="${DPKG_STAGING}/dpkg"
|
DPKG_DIR="${DPKG_STAGING}/dpkg"
|
||||||
mkdir -p "${DPKG_DIR}"
|
mkdir -p "${DPKG_DIR}"
|
||||||
|
|
||||||
DPKG_BASENAME=${PROJECT_NAME}
|
DPKG_BASENAME=${{ needs.crate_metadata.outputs.name }}
|
||||||
DPKG_CONFLICTS=${PROJECT_NAME}-musl
|
DPKG_CONFLICTS=${{ needs.crate_metadata.outputs.name }}-musl
|
||||||
case ${{ matrix.job.target }} in *-musl) DPKG_BASENAME=${PROJECT_NAME}-musl ; DPKG_CONFLICTS=${PROJECT_NAME} ;; esac;
|
case ${{ matrix.job.target }} in *-musl) DPKG_BASENAME=${{ needs.crate_metadata.outputs.name }}-musl ; DPKG_CONFLICTS=${{ needs.crate_metadata.outputs.name }} ;; esac;
|
||||||
DPKG_VERSION=${PROJECT_VERSION}
|
DPKG_VERSION=${{ needs.crate_metadata.outputs.version }}
|
||||||
|
|
||||||
unset DPKG_ARCH
|
unset DPKG_ARCH
|
||||||
case ${{ matrix.job.target }} in
|
case ${{ matrix.job.target }} in
|
||||||
@@ -233,14 +225,14 @@ jobs:
|
|||||||
esac;
|
esac;
|
||||||
|
|
||||||
DPKG_NAME="${DPKG_BASENAME}_${DPKG_VERSION}_${DPKG_ARCH}.deb"
|
DPKG_NAME="${DPKG_BASENAME}_${DPKG_VERSION}_${DPKG_ARCH}.deb"
|
||||||
echo ::set-output name=DPKG_NAME::${DPKG_NAME}
|
echo "DPKG_NAME=${DPKG_NAME}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
# Binary
|
# Binary
|
||||||
install -Dm755 "${{ steps.strip.outputs.BIN_PATH }}" "${DPKG_DIR}/usr/bin/${{ steps.strip.outputs.BIN_NAME }}"
|
install -Dm755 "${{ steps.bin.outputs.BIN_PATH }}" "${DPKG_DIR}/usr/bin/${{ steps.bin.outputs.BIN_NAME }}"
|
||||||
|
|
||||||
# Man page
|
# Man page
|
||||||
install -Dm644 "doc/${{ env.PROJECT_NAME }}.1" "${DPKG_DIR}/usr/share/man/man1/${{ env.PROJECT_NAME }}.1"
|
install -Dm644 'doc/${{ needs.crate_metadata.outputs.name }}.1' "${DPKG_DIR}/usr/share/man/man1/${{ needs.crate_metadata.outputs.name }}.1"
|
||||||
gzip -n --best "${DPKG_DIR}/usr/share/man/man1/${{ env.PROJECT_NAME }}.1"
|
gzip -n --best "${DPKG_DIR}/usr/share/man/man1/${{ needs.crate_metadata.outputs.name }}.1"
|
||||||
|
|
||||||
# README and LICENSE
|
# README and LICENSE
|
||||||
install -Dm644 "README.md" "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/README.md"
|
install -Dm644 "README.md" "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/README.md"
|
||||||
@@ -251,12 +243,12 @@ jobs:
|
|||||||
|
|
||||||
cat > "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/copyright" <<EOF
|
cat > "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/copyright" <<EOF
|
||||||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||||
Upstream-Name: ${{ env.PROJECT_NAME }}
|
Upstream-Name: ${{ needs.crate_metadata.outputs.name }}
|
||||||
Source: ${{ env.PROJECT_HOMEPAGE }}
|
Source: ${{ needs.crate_metadata.outputs.homepage }}
|
||||||
|
|
||||||
Files: *
|
Files: *
|
||||||
Copyright: ${{ env.PROJECT_MAINTAINER }}
|
Copyright: ${{ needs.crate_metadata.outputs.maintainer }}
|
||||||
Copyright: $COPYRIGHT_YEARS ${{ env.PROJECT_MAINTAINER }}
|
Copyright: $COPYRIGHT_YEARS ${{ needs.crate_metadata.outputs.maintainer }}
|
||||||
License: Apache-2.0 or MIT
|
License: Apache-2.0 or MIT
|
||||||
|
|
||||||
License: Apache-2.0
|
License: Apache-2.0
|
||||||
@@ -297,16 +289,16 @@ jobs:
|
|||||||
Version: ${DPKG_VERSION}
|
Version: ${DPKG_VERSION}
|
||||||
Section: utils
|
Section: utils
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Maintainer: ${{ env.PROJECT_MAINTAINER }}
|
Maintainer: ${{ needs.crate_metadata.outputs.maintainer }}
|
||||||
Homepage: ${{ env.PROJECT_HOMEPAGE }}
|
Homepage: ${{ needs.crate_metadata.outputs.homepage }}
|
||||||
Architecture: ${DPKG_ARCH}
|
Architecture: ${DPKG_ARCH}
|
||||||
Provides: ${{ env.PROJECT_NAME }}
|
Provides: ${{ needs.crate_metadata.outputs.name }}
|
||||||
Conflicts: ${DPKG_CONFLICTS}
|
Conflicts: ${DPKG_CONFLICTS}
|
||||||
Description: colorized hex viewer for the terminal.
|
Description: A command-line benchmarking tool
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
DPKG_PATH="${DPKG_STAGING}/${DPKG_NAME}"
|
DPKG_PATH="${DPKG_STAGING}/${DPKG_NAME}"
|
||||||
echo ::set-output name=DPKG_PATH::${DPKG_PATH}
|
echo "DPKG_PATH=${DPKG_PATH}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
# build dpkg
|
# build dpkg
|
||||||
fakeroot dpkg-deb --build "${DPKG_DIR}" "${DPKG_PATH}"
|
fakeroot dpkg-deb --build "${DPKG_DIR}" "${DPKG_PATH}"
|
||||||
@@ -329,7 +321,7 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
unset IS_RELEASE ; if [[ $GITHUB_REF =~ ^refs/tags/v[0-9].* ]]; then IS_RELEASE='true' ; fi
|
unset IS_RELEASE ; if [[ $GITHUB_REF =~ ^refs/tags/v[0-9].* ]]; then IS_RELEASE='true' ; fi
|
||||||
echo ::set-output name=IS_RELEASE::${IS_RELEASE}
|
echo "IS_RELEASE=${IS_RELEASE}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Publish archives and packages
|
- name: Publish archives and packages
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
|
|||||||
+21
-1
@@ -1,4 +1,24 @@
|
|||||||
# unreleased
|
# v0.12.0
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Only show one panel by default if the terminal width is not wide enough for two panels, see #182 (@sharkdp)
|
||||||
|
- Respect the `NO_COLOR` environment variable, see #179 (@sharifhsn)
|
||||||
|
|
||||||
|
## Bugfixes
|
||||||
|
|
||||||
|
- Do not fail with an error if `--panels=auto` is used and the output is piped, see #184 (@sharkdp)
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
- Breaking: For `xxd`-compatibility reasons, `--group_bytes` has been renamed to `--group-size` (with an `--groupsize` alias), see #121 (@sharkdp)
|
||||||
|
|
||||||
|
## `hexyl` as a library
|
||||||
|
|
||||||
|
- Breaking: `num_group_bytes` has been renamed to `group_size`.
|
||||||
|
|
||||||
|
|
||||||
|
# v0.11.0
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
|
|||||||
Generated
+48
-12
@@ -11,12 +11,6 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "anstyle"
|
|
||||||
version = "0.2.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "58bb0ec944fe041c41e32dcd60b49327fcb7ff761433838878cc97ddecf16db7"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.66"
|
version = "1.0.66"
|
||||||
@@ -37,6 +31,17 @@ dependencies = [
|
|||||||
"wait-timeout",
|
"wait-timeout",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atty"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi 0.1.19",
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
@@ -175,6 +180,15 @@ dependencies = [
|
|||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hermit-abi"
|
||||||
|
version = "0.1.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.2.6"
|
version = "0.2.6"
|
||||||
@@ -186,17 +200,17 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hexyl"
|
name = "hexyl"
|
||||||
version = "0.11.0"
|
version = "0.12.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstyle",
|
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"assert_cmd",
|
"assert_cmd",
|
||||||
"clap",
|
"clap",
|
||||||
"const_format",
|
"const_format",
|
||||||
"is-terminal",
|
|
||||||
"libc",
|
"libc",
|
||||||
|
"owo-colors",
|
||||||
"predicates",
|
"predicates",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
|
"supports-color",
|
||||||
"terminal_size",
|
"terminal_size",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
@@ -217,12 +231,18 @@ version = "0.4.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "927609f78c2913a6f6ac3c27a4fe87f43e2a35367c0c4b0f8265e8f49a104330"
|
checksum = "927609f78c2913a6f6ac3c27a4fe87f43e2a35367c0c4b0f8265e8f49a104330"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hermit-abi",
|
"hermit-abi 0.2.6",
|
||||||
"io-lifetimes",
|
"io-lifetimes",
|
||||||
"rustix",
|
"rustix",
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "is_ci"
|
||||||
|
version = "1.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "itertools"
|
||||||
version = "0.10.5"
|
version = "0.10.5"
|
||||||
@@ -286,6 +306,12 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "owo-colors"
|
||||||
|
version = "3.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "predicates"
|
name = "predicates"
|
||||||
version = "2.1.4"
|
version = "2.1.4"
|
||||||
@@ -371,9 +397,9 @@ checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.36.4"
|
version = "0.36.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cb93e85278e08bb5788653183213d3a60fc242b10cb9be96586f5a73dcb67c23"
|
checksum = "a3807b5d10909833d3e9acd1eb5fb988f79376ff10fce42937de71a449c4c588"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"errno",
|
"errno",
|
||||||
@@ -395,6 +421,16 @@ version = "0.10.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "supports-color"
|
||||||
|
version = "1.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ba6faf2ca7ee42fdd458f4347ae0a9bd6bcc445ad7cb57ad82b383f18870d6f"
|
||||||
|
dependencies = [
|
||||||
|
"atty",
|
||||||
|
"is_ci",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.105"
|
version = "1.0.105"
|
||||||
|
|||||||
+4
-3
@@ -7,15 +7,16 @@ license = "MIT/Apache-2.0"
|
|||||||
name = "hexyl"
|
name = "hexyl"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
repository = "https://github.com/sharkdp/hexyl"
|
repository = "https://github.com/sharkdp/hexyl"
|
||||||
version = "0.11.0"
|
version = "0.12.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
rust-version = "1.60"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anstyle = "0.2"
|
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
const_format = "0.2"
|
const_format = "0.2"
|
||||||
is-terminal = "0.4"
|
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
|
owo-colors = "3"
|
||||||
|
supports-color = "1"
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
terminal_size = "0.2"
|
terminal_size = "0.2"
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ If you use an older version of Ubuntu, you can download
|
|||||||
the latest `.deb` package from the release page and install it via:
|
the latest `.deb` package from the release page and install it via:
|
||||||
|
|
||||||
``` bash
|
``` bash
|
||||||
sudo dpkg -i hexyl_0.11.0_amd64.deb # adapt version number and architecture
|
sudo dpkg -i hexyl_0.12.0_amd64.deb # adapt version number and architecture
|
||||||
```
|
```
|
||||||
|
|
||||||
### On Debian
|
### On Debian
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
% HEXYL(1) hexyl 0.11.0 | General Commands Manual
|
% HEXYL(1) hexyl 0.12.0 | General Commands Manual
|
||||||
%
|
%
|
||||||
% 2022-12-05
|
% 2022-12-05
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ fn main() {
|
|||||||
.with_border_style(BorderStyle::Unicode)
|
.with_border_style(BorderStyle::Unicode)
|
||||||
.enable_squeezing(false)
|
.enable_squeezing(false)
|
||||||
.num_panels(2)
|
.num_panels(2)
|
||||||
.num_group_bytes(1)
|
.group_size(1)
|
||||||
.build();
|
.build();
|
||||||
printer.print_all(&input[..]).unwrap();
|
printer.print_all(&input[..]).unwrap();
|
||||||
}
|
}
|
||||||
+31
-27
@@ -9,8 +9,6 @@ use std::num::{NonZeroI64, NonZeroU64, NonZeroU8};
|
|||||||
use clap::builder::ArgPredicate;
|
use clap::builder::ArgPredicate;
|
||||||
use clap::{crate_name, crate_version, Arg, ArgAction, ColorChoice, Command};
|
use clap::{crate_name, crate_version, Arg, ArgAction, ColorChoice, Command};
|
||||||
|
|
||||||
use is_terminal::IsTerminal;
|
|
||||||
|
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
|
|
||||||
use const_format::formatcp;
|
use const_format::formatcp;
|
||||||
@@ -163,18 +161,21 @@ fn run() -> Result<()> {
|
|||||||
.help(
|
.help(
|
||||||
"Sets the number of hex data panels to be displayed. \
|
"Sets the number of hex data panels to be displayed. \
|
||||||
`--panels=auto` will display the maximum number of hex data panels \
|
`--panels=auto` will display the maximum number of hex data panels \
|
||||||
based on the current terminal width",
|
based on the current terminal width. By default, hexyl will show \
|
||||||
|
two panels, unless the terminal is not wide enough for that.",
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("group_bytes")
|
Arg::new("group_size")
|
||||||
.short('g')
|
.short('g')
|
||||||
.long("group-bytes")
|
.long("group-size")
|
||||||
|
.alias("groupsize")
|
||||||
.num_args(1)
|
.num_args(1)
|
||||||
.value_name("N")
|
.value_name("N")
|
||||||
.help(
|
.help(
|
||||||
"Sets the number of octets per group to be displayed. \
|
"Number of bytes/octets that should be grouped together. \
|
||||||
The possible options are 1, 2, 4, 8. The default is 1",
|
Possible group sizes are 1, 2, 4, 8. The default is 1. \
|
||||||
|
'--groupsize can be used as an alias (xxd-compatibility).",
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
@@ -293,8 +294,10 @@ fn run() -> Result<()> {
|
|||||||
|
|
||||||
let show_color = match matches.get_one::<String>("color").map(String::as_ref) {
|
let show_color = match matches.get_one::<String>("color").map(String::as_ref) {
|
||||||
Some("never") => false,
|
Some("never") => false,
|
||||||
Some("auto") => std::io::stdout().is_terminal(),
|
Some("always") => true,
|
||||||
_ => true,
|
_ => supports_color::on(supports_color::Stream::Stdout)
|
||||||
|
.map(|level| level.has_basic)
|
||||||
|
.unwrap_or(false),
|
||||||
};
|
};
|
||||||
|
|
||||||
let border_style = match matches.get_one::<String>("border").map(String::as_ref) {
|
let border_style = match matches.get_one::<String>("border").map(String::as_ref) {
|
||||||
@@ -322,12 +325,12 @@ fn run() -> Result<()> {
|
|||||||
.transpose()?
|
.transpose()?
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
|
|
||||||
let max_panels_fn = |terminal_width: u64, base_digits: u64, group_bytes: u64| {
|
let max_panels_fn = |terminal_width: u64, base_digits: u64, group_size: u64| {
|
||||||
let offset = if show_position_panel { 10 } else { 1 };
|
let offset = if show_position_panel { 10 } else { 1 };
|
||||||
let col_width = if show_char_panel {
|
let col_width = if show_char_panel {
|
||||||
((8 / group_bytes) * (base_digits * group_bytes + 1)) + 2 + 8
|
((8 / group_size) * (base_digits * group_size + 1)) + 2 + 8
|
||||||
} else {
|
} else {
|
||||||
((8 / group_bytes) * (base_digits * group_bytes + 1)) + 2
|
((8 / group_size) * (base_digits * group_size + 1)) + 2
|
||||||
};
|
};
|
||||||
if (terminal_width - offset) / col_width < 1 {
|
if (terminal_width - offset) / col_width < 1 {
|
||||||
1
|
1
|
||||||
@@ -368,33 +371,31 @@ fn run() -> Result<()> {
|
|||||||
Base::Hexadecimal => 2,
|
Base::Hexadecimal => 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
let group_bytes = if let Some(group_bytes) = matches
|
let group_size = if let Some(group_size) = matches
|
||||||
.get_one::<String>("group_bytes")
|
.get_one::<String>("group_size")
|
||||||
.map(|s| {
|
.map(|s| {
|
||||||
s.parse::<NonZeroU8>().map(u8::from).context(anyhow!(
|
s.parse::<NonZeroU8>().map(u8::from).context(anyhow!(
|
||||||
"failed to parse `--group-bytes`/`-g` arg {:?} as unsigned nonzero integer",
|
"Failed to parse `--group-size`/`-g` argument {:?} as unsigned nonzero integer",
|
||||||
s
|
s
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
.transpose()?
|
.transpose()?
|
||||||
{
|
{
|
||||||
if (group_bytes <= 8) && ((group_bytes & (group_bytes - 1)) == 0) {
|
if (group_size <= 8) && ((group_size & (group_size - 1)) == 0) {
|
||||||
group_bytes
|
group_size
|
||||||
} else {
|
} else {
|
||||||
return Err(anyhow!(
|
return Err(anyhow!(
|
||||||
"Possible options for the `--group-bytes`/`-g` option are 1, 2, 4 or 8. "
|
"Possible sizes for the `--group-size`/`-g` option are 1, 2, 4 or 8. "
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
1
|
1
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let terminal_width = terminal_size().map(|s| s.0 .0 as u64).unwrap_or(80);
|
||||||
|
|
||||||
let panels = if matches.get_one::<String>("panels").map(String::as_ref) == Some("auto") {
|
let panels = if matches.get_one::<String>("panels").map(String::as_ref) == Some("auto") {
|
||||||
max_panels_fn(
|
max_panels_fn(terminal_width, base_digits, group_size.into())
|
||||||
terminal_size().ok_or_else(|| anyhow!("not a TTY"))?.0 .0 as u64,
|
|
||||||
base_digits,
|
|
||||||
group_bytes.into(),
|
|
||||||
)
|
|
||||||
} else if let Some(panels) = matches
|
} else if let Some(panels) = matches
|
||||||
.get_one::<String>("panels")
|
.get_one::<String>("panels")
|
||||||
.map(|s| {
|
.map(|s| {
|
||||||
@@ -416,9 +417,12 @@ fn run() -> Result<()> {
|
|||||||
})
|
})
|
||||||
.transpose()?
|
.transpose()?
|
||||||
{
|
{
|
||||||
max_panels_fn(terminal_width, base_digits, group_bytes.into())
|
max_panels_fn(terminal_width, base_digits, group_size.into())
|
||||||
} else {
|
} else {
|
||||||
2
|
std::cmp::min(
|
||||||
|
2,
|
||||||
|
max_panels_fn(terminal_width, base_digits, group_size.into()),
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let stdout = io::stdout();
|
let stdout = io::stdout();
|
||||||
@@ -431,7 +435,7 @@ fn run() -> Result<()> {
|
|||||||
.with_border_style(border_style)
|
.with_border_style(border_style)
|
||||||
.enable_squeezing(squeeze)
|
.enable_squeezing(squeeze)
|
||||||
.num_panels(panels)
|
.num_panels(panels)
|
||||||
.num_group_bytes(group_bytes)
|
.group_size(group_size)
|
||||||
.with_base(base)
|
.with_base(base)
|
||||||
.build();
|
.build();
|
||||||
printer.display_offset(skip_offset + display_offset);
|
printer.display_offset(skip_offset + display_offset);
|
||||||
@@ -449,7 +453,7 @@ fn main() {
|
|||||||
std::process::exit(0);
|
std::process::exit(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
eprintln!("Error: {:?}", err);
|
eprintln!("Error: {err:?}");
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+48
-85
@@ -4,7 +4,7 @@ pub use input::*;
|
|||||||
|
|
||||||
use std::io::{self, BufReader, Read, Write};
|
use std::io::{self, BufReader, Read, Write};
|
||||||
|
|
||||||
use anstyle::{AnsiColor, Reset};
|
use owo_colors::{colors, Color};
|
||||||
|
|
||||||
pub enum Base {
|
pub enum Base {
|
||||||
Binary,
|
Binary,
|
||||||
@@ -13,24 +13,13 @@ pub enum Base {
|
|||||||
Hexadecimal,
|
Hexadecimal,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq)]
|
const COLOR_NULL: &[u8] = colors::BrightBlack::ANSI_FG.as_bytes();
|
||||||
#[repr(usize)]
|
const COLOR_OFFSET: &[u8] = colors::BrightBlack::ANSI_FG.as_bytes();
|
||||||
enum ByteColor {
|
const COLOR_ASCII_PRINTABLE: &[u8] = colors::Cyan::ANSI_FG.as_bytes();
|
||||||
Null = 0,
|
const COLOR_ASCII_WHITESPACE: &[u8] = colors::Green::ANSI_FG.as_bytes();
|
||||||
Offset = 1,
|
const COLOR_ASCII_OTHER: &[u8] = colors::Green::ANSI_FG.as_bytes();
|
||||||
AsciiPrintable = 2,
|
const COLOR_NONASCII: &[u8] = colors::Yellow::ANSI_FG.as_bytes();
|
||||||
AsciiWhitespace = 3,
|
const COLOR_RESET: &[u8] = colors::Default::ANSI_FG.as_bytes();
|
||||||
AsciiOther = 4,
|
|
||||||
NonAscii = 5,
|
|
||||||
Reset = 6,
|
|
||||||
}
|
|
||||||
|
|
||||||
const COLOR_NULL: AnsiColor = AnsiColor::BrightBlack;
|
|
||||||
const COLOR_OFFSET: AnsiColor = AnsiColor::BrightBlack;
|
|
||||||
const COLOR_ASCII_PRINTABLE: AnsiColor = AnsiColor::Cyan;
|
|
||||||
const COLOR_ASCII_WHITESPACE: AnsiColor = AnsiColor::Green;
|
|
||||||
const COLOR_ASCII_OTHER: AnsiColor = AnsiColor::Magenta;
|
|
||||||
const COLOR_NONASCII: AnsiColor = AnsiColor::Yellow;
|
|
||||||
|
|
||||||
pub enum ByteCategory {
|
pub enum ByteCategory {
|
||||||
Null,
|
Null,
|
||||||
@@ -66,15 +55,15 @@ impl Byte {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn color(self) -> ByteColor {
|
fn color(self) -> &'static [u8] {
|
||||||
use crate::ByteCategory::*;
|
use crate::ByteCategory::*;
|
||||||
|
|
||||||
match self.category() {
|
match self.category() {
|
||||||
Null => ByteColor::Null,
|
Null => COLOR_NULL,
|
||||||
AsciiPrintable => ByteColor::AsciiPrintable,
|
AsciiPrintable => COLOR_ASCII_PRINTABLE,
|
||||||
AsciiWhitespace => ByteColor::AsciiWhitespace,
|
AsciiWhitespace => COLOR_ASCII_WHITESPACE,
|
||||||
AsciiOther => ByteColor::AsciiOther,
|
AsciiOther => COLOR_ASCII_OTHER,
|
||||||
NonAscii => ByteColor::NonAscii,
|
NonAscii => COLOR_NONASCII,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,7 +157,7 @@ pub struct PrinterBuilder<'a, Writer: Write> {
|
|||||||
border_style: BorderStyle,
|
border_style: BorderStyle,
|
||||||
use_squeeze: bool,
|
use_squeeze: bool,
|
||||||
panels: u64,
|
panels: u64,
|
||||||
group_bytes: u8,
|
group_size: u8,
|
||||||
base: Base,
|
base: Base,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,7 +171,7 @@ impl<'a, Writer: Write> PrinterBuilder<'a, Writer> {
|
|||||||
border_style: BorderStyle::Unicode,
|
border_style: BorderStyle::Unicode,
|
||||||
use_squeeze: true,
|
use_squeeze: true,
|
||||||
panels: 2,
|
panels: 2,
|
||||||
group_bytes: 1,
|
group_size: 1,
|
||||||
base: Base::Hexadecimal,
|
base: Base::Hexadecimal,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -217,8 +206,8 @@ impl<'a, Writer: Write> PrinterBuilder<'a, Writer> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn num_group_bytes(mut self, num: u8) -> Self {
|
pub fn group_size(mut self, num: u8) -> Self {
|
||||||
self.group_bytes = num;
|
self.group_size = num;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,7 +225,7 @@ impl<'a, Writer: Write> PrinterBuilder<'a, Writer> {
|
|||||||
self.border_style,
|
self.border_style,
|
||||||
self.use_squeeze,
|
self.use_squeeze,
|
||||||
self.panels,
|
self.panels,
|
||||||
self.group_bytes,
|
self.group_size,
|
||||||
self.base,
|
self.base,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -250,21 +239,19 @@ pub struct Printer<'a, Writer: Write> {
|
|||||||
show_char_panel: bool,
|
show_char_panel: bool,
|
||||||
show_position_panel: bool,
|
show_position_panel: bool,
|
||||||
show_color: bool,
|
show_color: bool,
|
||||||
colors: Vec<String>,
|
curr_color: Option<&'static [u8]>,
|
||||||
curr_color: Option<ByteColor>,
|
|
||||||
border_style: BorderStyle,
|
border_style: BorderStyle,
|
||||||
byte_hex_panel: Vec<String>,
|
byte_hex_panel: Vec<String>,
|
||||||
byte_char_panel: Vec<String>,
|
byte_char_panel: Vec<String>,
|
||||||
// same as previous but in Fixed(242) gray color, for position panel
|
// same as previous but in Fixed(242) gray color, for position panel
|
||||||
byte_hex_panel_g: Vec<String>,
|
byte_hex_panel_g: Vec<String>,
|
||||||
byte_char_panel_g: Vec<String>,
|
|
||||||
squeezer: Squeezer,
|
squeezer: Squeezer,
|
||||||
display_offset: u64,
|
display_offset: u64,
|
||||||
/// The number of panels to draw.
|
/// The number of panels to draw.
|
||||||
panels: u64,
|
panels: u64,
|
||||||
squeeze_byte: usize,
|
squeeze_byte: usize,
|
||||||
/// The number of octets per group.
|
/// The number of octets per group.
|
||||||
group_bytes: u8,
|
group_size: u8,
|
||||||
/// The number of digits used to write the base.
|
/// The number of digits used to write the base.
|
||||||
base_digits: u8,
|
base_digits: u8,
|
||||||
}
|
}
|
||||||
@@ -278,7 +265,7 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
|
|||||||
border_style: BorderStyle,
|
border_style: BorderStyle,
|
||||||
use_squeeze: bool,
|
use_squeeze: bool,
|
||||||
panels: u64,
|
panels: u64,
|
||||||
group_bytes: u8,
|
group_size: u8,
|
||||||
base: Base,
|
base: Base,
|
||||||
) -> Printer<'a, Writer> {
|
) -> Printer<'a, Writer> {
|
||||||
Printer {
|
Printer {
|
||||||
@@ -289,15 +276,6 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
|
|||||||
show_position_panel,
|
show_position_panel,
|
||||||
show_color,
|
show_color,
|
||||||
curr_color: None,
|
curr_color: None,
|
||||||
colors: vec![
|
|
||||||
COLOR_NULL.render_fg().to_string(),
|
|
||||||
COLOR_OFFSET.render_fg().to_string(),
|
|
||||||
COLOR_ASCII_PRINTABLE.render_fg().to_string(),
|
|
||||||
COLOR_ASCII_WHITESPACE.render_fg().to_string(),
|
|
||||||
COLOR_ASCII_OTHER.render_fg().to_string(),
|
|
||||||
COLOR_NONASCII.render_fg().to_string(),
|
|
||||||
Reset.render().to_string(),
|
|
||||||
],
|
|
||||||
border_style,
|
border_style,
|
||||||
byte_hex_panel: (0u8..=u8::MAX)
|
byte_hex_panel: (0u8..=u8::MAX)
|
||||||
.map(|i| match base {
|
.map(|i| match base {
|
||||||
@@ -311,9 +289,6 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
|
|||||||
.map(|i| format!("{}", Byte(i).as_char()))
|
.map(|i| format!("{}", Byte(i).as_char()))
|
||||||
.collect(),
|
.collect(),
|
||||||
byte_hex_panel_g: (0u8..=u8::MAX).map(|i| format!("{i:02x}")).collect(),
|
byte_hex_panel_g: (0u8..=u8::MAX).map(|i| format!("{i:02x}")).collect(),
|
||||||
byte_char_panel_g: (0u8..=u8::MAX)
|
|
||||||
.map(|i| format!("{}", Byte(i).as_char()))
|
|
||||||
.collect(),
|
|
||||||
squeezer: if use_squeeze {
|
squeezer: if use_squeeze {
|
||||||
Squeezer::Ignore
|
Squeezer::Ignore
|
||||||
} else {
|
} else {
|
||||||
@@ -322,7 +297,7 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
|
|||||||
display_offset: 0,
|
display_offset: 0,
|
||||||
panels,
|
panels,
|
||||||
squeeze_byte: 0x00,
|
squeeze_byte: 0x00,
|
||||||
group_bytes,
|
group_size,
|
||||||
base_digits: match base {
|
base_digits: match base {
|
||||||
Base::Binary => 8,
|
Base::Binary => 8,
|
||||||
Base::Octal => 3,
|
Base::Octal => 3,
|
||||||
@@ -339,8 +314,8 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
|
|||||||
|
|
||||||
fn panel_sz(&self) -> usize {
|
fn panel_sz(&self) -> usize {
|
||||||
// add one to include the trailing space of a group
|
// add one to include the trailing space of a group
|
||||||
let group_sz = self.base_digits as usize * self.group_bytes as usize + 1;
|
let group_sz = self.base_digits as usize * self.group_size as usize + 1;
|
||||||
let group_per_panel = 8 / self.group_bytes as usize;
|
let group_per_panel = 8 / self.group_size as usize;
|
||||||
// add one to include the leading space
|
// add one to include the leading space
|
||||||
1 + group_sz * group_per_panel
|
1 + group_sz * group_per_panel
|
||||||
}
|
}
|
||||||
@@ -354,27 +329,27 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
|
|||||||
let h_repeat = h.to_string().repeat(self.panel_sz());
|
let h_repeat = h.to_string().repeat(self.panel_sz());
|
||||||
|
|
||||||
if self.show_position_panel {
|
if self.show_position_panel {
|
||||||
write!(self.writer, "{l}{h8}{c}", l = l, c = c, h8 = h8).ok();
|
write!(self.writer, "{l}{h8}{c}")?;
|
||||||
} else {
|
} else {
|
||||||
write!(self.writer, "{}", l).ok();
|
write!(self.writer, "{l}")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
for _ in 0..self.panels - 1 {
|
for _ in 0..self.panels - 1 {
|
||||||
write!(self.writer, "{h_repeat}{c}", h_repeat = h_repeat, c = c).ok();
|
write!(self.writer, "{h_repeat}{c}")?;
|
||||||
}
|
}
|
||||||
if self.show_char_panel {
|
if self.show_char_panel {
|
||||||
write!(self.writer, "{h_repeat}{c}", h_repeat = h_repeat, c = c).ok();
|
write!(self.writer, "{h_repeat}{c}")?;
|
||||||
} else {
|
} else {
|
||||||
write!(self.writer, "{h_repeat}", h_repeat = h_repeat).ok();
|
write!(self.writer, "{h_repeat}")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.show_char_panel {
|
if self.show_char_panel {
|
||||||
for _ in 0..self.panels - 1 {
|
for _ in 0..self.panels - 1 {
|
||||||
write!(self.writer, "{h8}{c}", h8 = h8, c = c).ok();
|
write!(self.writer, "{h8}{c}")?;
|
||||||
}
|
}
|
||||||
writeln!(self.writer, "{h8}{r}", h8 = h8, r = r).ok();
|
writeln!(self.writer, "{h8}{r}")?;
|
||||||
} else {
|
} else {
|
||||||
writeln!(self.writer, "{r}", r = r).ok();
|
writeln!(self.writer, "{r}")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -402,17 +377,15 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
|
|||||||
.as_bytes(),
|
.as_bytes(),
|
||||||
)?;
|
)?;
|
||||||
if self.show_color {
|
if self.show_color {
|
||||||
self.writer
|
self.writer.write_all(COLOR_OFFSET)?;
|
||||||
.write_all(self.colors[ByteColor::Offset as usize].as_bytes())?;
|
|
||||||
}
|
}
|
||||||
if self.show_position_panel {
|
if self.show_position_panel {
|
||||||
match self.squeezer {
|
match self.squeezer {
|
||||||
Squeezer::Print => {
|
Squeezer::Print => {
|
||||||
self.writer
|
self.writer
|
||||||
.write_all(self.byte_char_panel_g[b'*' as usize].as_bytes())?;
|
.write_all(self.byte_char_panel[b'*' as usize].as_bytes())?;
|
||||||
if self.show_color {
|
if self.show_color {
|
||||||
self.writer
|
self.writer.write_all(COLOR_RESET)?;
|
||||||
.write_all(self.colors[ByteColor::Reset as usize].as_bytes())?;
|
|
||||||
}
|
}
|
||||||
self.writer.write_all(b" ")?;
|
self.writer.write_all(b" ")?;
|
||||||
}
|
}
|
||||||
@@ -427,8 +400,7 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
|
|||||||
.write_all(self.byte_hex_panel_g[byte as usize].as_bytes())?;
|
.write_all(self.byte_hex_panel_g[byte as usize].as_bytes())?;
|
||||||
}
|
}
|
||||||
if self.show_color {
|
if self.show_color {
|
||||||
self.writer
|
self.writer.write_all(COLOR_RESET)?;
|
||||||
.write_all(self.colors[ByteColor::Reset as usize].as_bytes())?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -448,8 +420,7 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
|
|||||||
Squeezer::Ignore | Squeezer::Disabled => {
|
Squeezer::Ignore | Squeezer::Disabled => {
|
||||||
if let Some(&b) = self.line_buf.get(i as usize) {
|
if let Some(&b) = self.line_buf.get(i as usize) {
|
||||||
if self.show_color && self.curr_color != Some(Byte(b).color()) {
|
if self.show_color && self.curr_color != Some(Byte(b).color()) {
|
||||||
self.writer
|
self.writer.write_all(Byte(b).color())?;
|
||||||
.write_all(self.colors[Byte(b).color() as usize].as_bytes())?;
|
|
||||||
self.curr_color = Some(Byte(b).color());
|
self.curr_color = Some(Byte(b).color());
|
||||||
}
|
}
|
||||||
self.writer
|
self.writer
|
||||||
@@ -461,8 +432,7 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
|
|||||||
}
|
}
|
||||||
if i == 8 * self.panels - 1 {
|
if i == 8 * self.panels - 1 {
|
||||||
if self.show_color {
|
if self.show_color {
|
||||||
self.writer
|
self.writer.write_all(COLOR_RESET)?;
|
||||||
.write_all(self.colors[ByteColor::Reset as usize].as_bytes())?;
|
|
||||||
self.curr_color = None;
|
self.curr_color = None;
|
||||||
}
|
}
|
||||||
self.writer.write_all(
|
self.writer.write_all(
|
||||||
@@ -473,8 +443,7 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
|
|||||||
)?;
|
)?;
|
||||||
} else if i % 8 == 7 {
|
} else if i % 8 == 7 {
|
||||||
if self.show_color {
|
if self.show_color {
|
||||||
self.writer
|
self.writer.write_all(COLOR_RESET)?;
|
||||||
.write_all(self.colors[ByteColor::Reset as usize].as_bytes())?;
|
|
||||||
self.curr_color = None;
|
self.curr_color = None;
|
||||||
}
|
}
|
||||||
self.writer.write_all(
|
self.writer.write_all(
|
||||||
@@ -500,19 +469,15 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
|
|||||||
Squeezer::Print => {
|
Squeezer::Print => {
|
||||||
if !self.show_position_panel && i == 0 {
|
if !self.show_position_panel && i == 0 {
|
||||||
if self.show_color {
|
if self.show_color {
|
||||||
self.writer
|
self.writer.write_all(COLOR_OFFSET)?;
|
||||||
.write_all(self.colors[ByteColor::Offset as usize].as_bytes())?;
|
|
||||||
}
|
}
|
||||||
self.writer
|
self.writer
|
||||||
.write_all(self.byte_char_panel_g[b'*' as usize].as_bytes())?;
|
.write_all(self.byte_char_panel[b'*' as usize].as_bytes())?;
|
||||||
if self.show_color {
|
if self.show_color {
|
||||||
self.writer
|
self.writer.write_all(COLOR_RESET)?;
|
||||||
.write_all(self.colors[ByteColor::Reset as usize].as_bytes())?;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if i % (self.group_bytes as usize) == 0 {
|
|
||||||
self.writer.write_all(b" ")?;
|
|
||||||
}
|
}
|
||||||
|
} else if i % (self.group_size as usize) == 0 {
|
||||||
|
self.writer.write_all(b" ")?;
|
||||||
}
|
}
|
||||||
for _ in 0..self.base_digits {
|
for _ in 0..self.base_digits {
|
||||||
self.writer.write_all(b" ")?;
|
self.writer.write_all(b" ")?;
|
||||||
@@ -520,12 +485,11 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
|
|||||||
}
|
}
|
||||||
Squeezer::Delete => self.writer.write_all(b" ")?,
|
Squeezer::Delete => self.writer.write_all(b" ")?,
|
||||||
Squeezer::Ignore | Squeezer::Disabled => {
|
Squeezer::Ignore | Squeezer::Disabled => {
|
||||||
if i % (self.group_bytes as usize) == 0 {
|
if i % (self.group_size as usize) == 0 {
|
||||||
self.writer.write_all(b" ")?;
|
self.writer.write_all(b" ")?;
|
||||||
}
|
}
|
||||||
if self.show_color && self.curr_color != Some(Byte(b).color()) {
|
if self.show_color && self.curr_color != Some(Byte(b).color()) {
|
||||||
self.writer
|
self.writer.write_all(Byte(b).color())?;
|
||||||
.write_all(self.colors[Byte(b).color() as usize].as_bytes())?;
|
|
||||||
self.curr_color = Some(Byte(b).color());
|
self.curr_color = Some(Byte(b).color());
|
||||||
}
|
}
|
||||||
self.writer
|
self.writer
|
||||||
@@ -536,8 +500,7 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
|
|||||||
if i % 8 == 7 {
|
if i % 8 == 7 {
|
||||||
if self.show_color {
|
if self.show_color {
|
||||||
self.curr_color = None;
|
self.curr_color = None;
|
||||||
self.writer
|
self.writer.write_all(COLOR_RESET)?;
|
||||||
.write_all(self.colors[ByteColor::Reset as usize].as_bytes())?;
|
|
||||||
}
|
}
|
||||||
self.writer.write_all(b" ")?;
|
self.writer.write_all(b" ")?;
|
||||||
// byte is last in last panel
|
// byte is last in last panel
|
||||||
|
|||||||
+11
-10
@@ -301,7 +301,7 @@ mod group {
|
|||||||
hexyl()
|
hexyl()
|
||||||
.arg("ascii")
|
.arg("ascii")
|
||||||
.arg("--color=never")
|
.arg("--color=never")
|
||||||
.arg("--group-bytes=2")
|
.arg("--group-size=2")
|
||||||
.assert()
|
.assert()
|
||||||
.success()
|
.success()
|
||||||
.stdout(
|
.stdout(
|
||||||
@@ -316,7 +316,7 @@ mod group {
|
|||||||
hexyl()
|
hexyl()
|
||||||
.arg("ascii")
|
.arg("ascii")
|
||||||
.arg("--color=never")
|
.arg("--color=never")
|
||||||
.arg("--group-bytes=4")
|
.arg("--group-size=4")
|
||||||
.assert()
|
.assert()
|
||||||
.success()
|
.success()
|
||||||
.stdout(
|
.stdout(
|
||||||
@@ -331,7 +331,7 @@ mod group {
|
|||||||
hexyl()
|
hexyl()
|
||||||
.arg("ascii")
|
.arg("ascii")
|
||||||
.arg("--color=never")
|
.arg("--color=never")
|
||||||
.arg("--group-bytes=8")
|
.arg("--group-size=8")
|
||||||
.assert()
|
.assert()
|
||||||
.success()
|
.success()
|
||||||
.stdout(
|
.stdout(
|
||||||
@@ -342,22 +342,22 @@ mod group {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn group_bytes_plain() {
|
fn group_size_plain() {
|
||||||
hexyl()
|
hexyl()
|
||||||
.arg("ascii")
|
.arg("ascii")
|
||||||
.arg("--color=never")
|
.arg("--color=never")
|
||||||
.arg("--plain")
|
.arg("--plain")
|
||||||
.arg("--group-bytes=2")
|
.arg("--group-size=2")
|
||||||
.assert()
|
.assert()
|
||||||
.success()
|
.success()
|
||||||
.stdout(" 3031 3233 3435 3637 3839 6162 6364 650a \n");
|
.stdout(" 3031 3233 3435 3637 3839 6162 6364 650a \n");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn group_bytes_fill_space() {
|
fn group_size_fill_space() {
|
||||||
hexyl()
|
hexyl()
|
||||||
.arg("--color=never")
|
.arg("--color=never")
|
||||||
.arg("--group-bytes=2")
|
.arg("--group-size=2")
|
||||||
.write_stdin("abc")
|
.write_stdin("abc")
|
||||||
.assert()
|
.assert()
|
||||||
.success()
|
.success()
|
||||||
@@ -369,12 +369,12 @@ mod group {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn group_bytes_invalid() {
|
fn group_size_invalid() {
|
||||||
hexyl()
|
hexyl()
|
||||||
.arg("ascii")
|
.arg("ascii")
|
||||||
.arg("--color=never")
|
.arg("--color=never")
|
||||||
.arg("--plain")
|
.arg("--plain")
|
||||||
.arg("--group-bytes=3")
|
.arg("--group-size=3")
|
||||||
.assert()
|
.assert()
|
||||||
.failure();
|
.failure();
|
||||||
}
|
}
|
||||||
@@ -565,7 +565,8 @@ mod base {
|
|||||||
.assert()
|
.assert()
|
||||||
.success()
|
.success()
|
||||||
.pretty_stdout(
|
.pretty_stdout(
|
||||||
" 00110000 00110001 00110010 00110011 00110100 00110101 00110110 00110111 00111000 00111001 01100001 01100010 01100011 01100100 01100101 00001010 \n"
|
" 00110000 00110001 00110010 00110011 00110100 00110101 00110110 00110111 \n \
|
||||||
|
00111000 00111001 01100001 01100010 01100011 01100100 01100101 00001010 \n",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user