diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c06dc6a..2b51f07 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,71 +1,66 @@ -on: [push, pull_request] +name: CI -name: Continuous integration +on: + push: + branches: [ main, dev ] + pull_request: + branches: [ main, dev ] + workflow_dispatch: -jobs: - check: - name: Check - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - - uses: actions-rs/cargo@v1 - with: - command: check +defaults: + run: + shell: bash +jobs: test: - name: Test Suite + name: Test runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - - uses: actions-rs/cargo@v1 - with: - command: test - args: --all-features + - uses: actions/checkout@v5 + - if: github.event_name != 'release' && github.event_name != 'workflow_dispatch' + uses: Swatinem/rust-cache@v2 + - uses: taiki-e/install-action@v2 + with: { tool: 'just,cargo-binstall' } + - run: just ci-test - fmt: - name: Rustfmt + test-msrv: + name: Test MSRV runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - - run: rustup component add rustfmt - - uses: actions-rs/cargo@v1 + - uses: actions/checkout@v5 + - if: github.event_name != 'release' && github.event_name != 'workflow_dispatch' + uses: Swatinem/rust-cache@v2 + - uses: taiki-e/install-action@v2 + with: { tool: 'just' } + - name: Read MSRV + id: msrv + run: echo "value=$(just get-msrv)" >> $GITHUB_OUTPUT + - name: Install MSRV Rust ${{ steps.msrv.outputs.value }} + uses: dtolnay/rust-toolchain@stable with: - command: fmt - args: --all -- --check + toolchain: ${{ steps.msrv.outputs.value }} + - run: just ci_mode=0 ci-test-msrv # Ignore warnings in MSRV - clippy: - name: Clippy + coverage: + name: Code Coverage + if: github.event_name != 'release' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - - run: rustup component add clippy - - uses: actions-rs/cargo@v1 - with: - command: clippy - args: -- -D warnings + - uses: actions/checkout@v5 + - uses: Swatinem/rust-cache@v2 + - uses: taiki-e/install-action@v2 + with: { tool: 'just,cargo-llvm-cov' } + - name: Generate code coverage + run: just ci-coverage +# TODO: create CODECOV_TOKEN token at codecov.io +# - name: Upload coverage to Codecov +# uses: codecov/codecov-action@v5 +# with: +# token: ${{ secrets.CODECOV_TOKEN }} +# files: target/llvm-cov/codecov.info cargo-deny: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 - uses: EmbarkStudios/cargo-deny-action@v1 diff --git a/.gitignore b/.gitignore index 869df07..dde7fc1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,12 @@ -/target -Cargo.lock \ No newline at end of file +**/*.rs.bk +*.pdb +.idea/ +.vscode/ +debug/ +target/ +temp/ +tmp/ +venv/ + +# This is a library, no lock +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml index 3b529ed..75420fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ repository = "https://github.com/marcelbuesing/can-dbc.git" authors = ["marcelbuesing "] keywords = ["dbc", "can", "automotive", "ecu"] edition = "2021" +rust-version = "1.74" [badges] travis-ci = { repository = "marcelbuesing/can-dbc", branch = "dev" } @@ -26,3 +27,28 @@ insta = { version = "1.39", features = ["yaml"] } [features] with-serde = ["serde", "serde_derive"] + +[lints.rust] +unsafe_code = "forbid" +unused_qualifications = "warn" + +[lints.clippy] +cargo = { level = "warn", priority = -1 } +pedantic = { level = "warn", priority = -1 } +# TODO: fix these if it makes sense +doc_markdown = "allow" # 36 +enum_variant_names = "allow" # 20 +must_use_candidate = "allow" # 12 +struct_field_names = "allow" # 10 +missing_errors_doc = "allow" # 8 +similar_names = "allow" # 4 +redundant_feature_names = "allow" # 4 +needless_pass_by_value = "allow" # 4 +cargo_common_metadata = "allow" # 4 +upper_case_acronyms = "allow" # 2 +trivially_copy_pass_by_ref = "allow" # 2 +ref_option = "allow" # 2 +match_wildcard_for_single_variants = "allow" # 2 +cast_possible_truncation = "allow" # 2 +match_same_arms = "allow" # 1 +items_after_test_module = "allow" # 1 diff --git a/README.md b/README.md index fabc1a7..f9a3e87 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # can-dbc -[![LICENSE](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) +[![LICENSE](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE) [![VERSION](https://img.shields.io/crates/v/can-dbc.svg)](https://crates.io/crates/can-dbc) [![Actions Status](https://github.com/marcelbuesing/can-dbc/workflows/Continuous%20integration/badge.svg)](https://github.com/marcelbuesing/can-dbc/actions?query=workflow%3A"Continuous+integration") [![codecov](https://codecov.io/gh/marcelbuesing/can-dbc/branch/dev/graph/badge.svg)](https://codecov.io/gh/marcelbuesing/can-dbc) @@ -57,7 +57,7 @@ cargo test && ./target/debug/examples/file_parser -i examples/sample.dbc # Installation can-dbc is available on crates.io and can be included in your Cargo enabled project like this: -```yml +```toml [dependencies] can-dbc = "3.0" ``` @@ -107,4 +107,7 @@ cargo deny check ``` # Development -This project uses [insta](https://insta.rs) for snapshot testing. To update the snapshots run `cargo insta test --all-feature --accept` +* This project is easier to develop with [just](https://just.systems/man/en/), a modern alternative to `make`. +* To get a list of available commands, run `just`. +* To run tests, use `just test`. +* This project uses [insta](https://insta.rs) for snapshot testing. To update the snapshots run `just bless` diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 0000000..3616445 --- /dev/null +++ b/clippy.toml @@ -0,0 +1,2 @@ +allow-unwrap-in-tests = true +avoid-breaking-exported-api = false diff --git a/justfile b/justfile new file mode 100755 index 0000000..4019e1d --- /dev/null +++ b/justfile @@ -0,0 +1,162 @@ +#!/usr/bin/env just --justfile + +main_crate := file_name(justfile_directory()) +packages := '--workspace' # All crates in the workspace +features := '--all-features' # Enable all features +targets := '--all-targets' # For all targets (lib, bin, tests, examples, benches) + +# if running in CI, treat warnings as errors by setting RUSTFLAGS and RUSTDOCFLAGS to '-D warnings' unless they are already set +# Use `CI=true just ci-test` to run the same tests as in GitHub CI. +# Use `just env-info` to see the current values of RUSTFLAGS and RUSTDOCFLAGS +ci_mode := if env('CI', '') != '' {'1'} else {''} +# cargo-binstall needs a workaround due to caching +# ci_mode might be manually set by user, so re-check the env var +binstall_args := if env('CI', '') != '' {'--no-confirm --no-track --disable-telemetry'} else {''} +export RUSTFLAGS := env('RUSTFLAGS', if ci_mode == '1' {'-D warnings'} else {''}) +export RUSTDOCFLAGS := env('RUSTDOCFLAGS', if ci_mode == '1' {'-D warnings'} else {''}) +export RUST_BACKTRACE := env('RUST_BACKTRACE', if ci_mode == '1' {'1'} else {''}) + +@_default: + {{just_executable()}} --list + +# Run integration tests and save its output as the new expected output +bless *args: (cargo-install 'cargo-insta') + cargo insta test --accept --unreferenced=delete {{features}} {{args}} + +# Build the project +build: + cargo build {{packages}} {{features}} {{targets}} + +# Quick compile without building a binary +check: + cargo check {{packages}} {{features}} {{targets}} + +# Generate code coverage report to upload to codecov.io +ci-coverage: env-info && \ + (coverage '--codecov --output-path target/llvm-cov/codecov.info') + # ATTENTION: the full file path above is used in the CI workflow + mkdir -p target/llvm-cov + +# Run all tests as expected by CI +ci-test: env-info test-fmt clippy test test-doc && assert-git-is-clean + +# Run minimal subset of tests to ensure compatibility with MSRV +ci-test-msrv: env-info check test + +# Clean all build artifacts +clean: + cargo clean + rm -f Cargo.lock + +# Run cargo clippy to lint the code +clippy *args: + cargo clippy {{packages}} {{features}} {{targets}} {{args}} + +# Generate code coverage report. Will install `cargo llvm-cov` if missing. +coverage *args='--no-clean --open': (cargo-install 'cargo-llvm-cov') + cargo llvm-cov {{packages}} {{features}} {{targets}} --include-build-script {{args}} + +# Build and open code documentation +docs *args='--open': + DOCS_RS=1 cargo doc --no-deps {{args}} {{packages}} {{features}} + +# Print environment info +env-info: + @echo "Running for '{{main_crate}}' crate {{if ci_mode == '1' {'in CI mode'} else {'in dev mode'} }} on {{os()}} / {{arch()}}" + @echo "PWD $(pwd)" + {{just_executable()}} --version + rustc --version + cargo --version + rustup --version + @echo "RUSTFLAGS='$RUSTFLAGS'" + @echo "RUSTDOCFLAGS='$RUSTDOCFLAGS'" + @echo "RUST_BACKTRACE='$RUST_BACKTRACE'" + +# Reformat all code `cargo fmt`. If nightly is available, use it for better results +fmt: + #!/usr/bin/env bash + set -euo pipefail + if (rustup toolchain list | grep nightly && rustup component list --toolchain nightly | grep rustfmt) &> /dev/null; then + echo 'Reformatting Rust code using nightly Rust fmt to sort imports' + cargo +nightly fmt --all -- --config imports_granularity=Module,group_imports=StdExternalCrate + else + echo 'Reformatting Rust with the stable cargo fmt. Install nightly with `rustup install nightly` for better results' + cargo fmt --all + fi + +# Get any package's field from the metadata +get-crate-field field package=main_crate: (assert-cmd 'jq') + cargo metadata --format-version 1 | jq -e -r '.packages | map(select(.name == "{{package}}")) | first | .{{field}} // error("Field \"{{field}}\" is missing in Cargo.toml for package {{package}}")' + +# Get the minimum supported Rust version (MSRV) for the crate +get-msrv package=main_crate: (get-crate-field 'rust_version' package) + +# Find the minimum supported Rust version (MSRV) using cargo-msrv extension, and update Cargo.toml +msrv: (cargo-install 'cargo-msrv') + cargo msrv find --write-msrv --ignore-lockfile {{features}} + +# Run cargo-release +release *args='': (cargo-install 'release-plz') + release-plz {{args}} + +# Check semver compatibility with prior published version. Install it with `cargo install cargo-semver-checks` +semver *args: (cargo-install 'cargo-semver-checks') + cargo semver-checks {{features}} {{args}} + +# Run all unit and integration tests +test: + cargo test {{packages}} {{features}} {{targets}} + cargo test --doc {{packages}} {{features}} + +# Test documentation generation +test-doc: (docs '') + +# Test code formatting +test-fmt: + cargo fmt --all -- --check + +# Find unused dependencies. Install it with `cargo install cargo-udeps` +udeps: (cargo-install 'cargo-udeps') + cargo +nightly udeps {{packages}} {{features}} {{targets}} + +# Update all dependencies, including breaking changes. Requires nightly toolchain (install with `rustup install nightly`) +update: + cargo +nightly -Z unstable-options update --breaking + cargo update + +# Ensure that a certain command is available +[private] +assert-cmd command: + @if ! type {{command}} > /dev/null; then \ + echo "Command '{{command}}' could not be found. Please make sure it has been installed on your computer." ;\ + exit 1 ;\ + fi + +# Make sure the git repo has no uncommitted changes +[private] +assert-git-is-clean: + @if [ -n "$(git status --untracked-files --porcelain)" ]; then \ + >&2 echo "ERROR: git repo is no longer clean. Make sure compilation and tests artifacts are in the .gitignore, and no repo files are modified." ;\ + >&2 echo "######### git status ##########" ;\ + git status ;\ + git --no-pager diff ;\ + exit 1 ;\ + fi + +# Check if a certain Cargo command is installed, and install it if needed +[private] +cargo-install $COMMAND $INSTALL_CMD='' *args='': + #!/usr/bin/env bash + set -euo pipefail + if ! command -v $COMMAND > /dev/null; then + echo "$COMMAND could not be found. Installing..." + if ! command -v cargo-binstall > /dev/null; then + set -x + cargo install ${INSTALL_CMD:-$COMMAND} --locked {{args}} + { set +x; } 2>/dev/null + else + set -x + cargo binstall ${INSTALL_CMD:-$COMMAND} {{binstall_args}} --locked {{args}} + { set +x; } 2>/dev/null + fi + fi diff --git a/src/parser.rs b/src/parser.rs index 8e38daf..92d71ec 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -752,7 +752,7 @@ fn message_id(s: &str) -> IResult<&str, MessageId> { Ok((s, MessageId::Extended(parsed_value & 0x1FFF_FFFF))) } else { // FIXME: use u16::try_from and handle error - #[expect(clippy::cast_possible_truncation)] + #[allow(clippy::cast_possible_truncation)] Ok((s, MessageId::Standard(parsed_value as u16))) } }