structured-email-address 0.0.21

RFC 5321/5322/6531 email address parser, validator, and normalizer. Subaddress extraction, provider-aware normalization, PSL domain validation, anti-homoglyph protection.
Documentation
name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

permissions:
  contents: read

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

env:
  CARGO_TERM_COLOR: always

# Every job below builds Cargo-controlled code — dependency build scripts and
# proc macros — and none of them push, so each checkout sets
# `persist-credentials: false`: the token has no use after that step and is not
# left in .git/config for that code to read.
jobs:
  lint:
    timeout-minutes: 10
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
        with:
          persist-credentials: false
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: rustfmt, clippy
      - uses: Swatinem/rust-cache@v2
      - name: Format
        run: cargo fmt --all -- --check
      - name: Clippy (all features)
        run: cargo clippy --all-features -- -D warnings
      - name: Clippy (no default features)
        run: cargo clippy --no-default-features -- -D warnings

  test:
    needs: lint
    timeout-minutes: 15
    strategy:
      fail-fast: true
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v7
        with:
          persist-credentials: false
      - uses: dtolnay/rust-toolchain@stable
      - uses: taiki-e/install-action@nextest
      - uses: Swatinem/rust-cache@v2
        with:
          prefix-key: ${{ runner.os }}-cargo
      - name: Resolve the newest published PSL data
        # The crate accepts any structured-public-domains 0.x, so the committed
        # lockfile is a starting point rather than a contract, and resolving it
        # fresh tests what a consumer building today actually gets rather than
        # what was pinned months ago. This job runs on pushes and pull requests,
        # so a data release published between two runs reaches consumers before
        # it is seen here; both crates are maintained together, and the parser
        # holds no opinion about which suffixes the list contains, so that gap is
        # accepted rather than papered over with a scheduled run.
        # The msrv job deliberately keeps the lockfile, so the toolchain floor
        # stays a property of this repository and not of the newest upstream
        # release.
        run: cargo update -p structured-public-domains
      - name: Test (all features)
        run: cargo nextest run --profile ci --all-features
      - name: Test (no default features)
        run: cargo nextest run --profile ci --no-default-features
      - name: Doc tests
        run: cargo test --doc --all-features

  no-std:
    needs: lint
    timeout-minutes: 10
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
        with:
          persist-credentials: false
      - uses: dtolnay/rust-toolchain@stable
        with:
          # Bare-metal targets, which is the point: a host check cannot fail on
          # a target constraint the host does not have. `std` is present there,
          # so code that reaches for it still compiles; thumbv6m additionally
          # has no compare-and-swap, which catches a dependency reaching for
          # pointer-width atomics.
          targets: thumbv7em-none-eabihf, thumbv6m-none-eabi
      - uses: Swatinem/rust-cache@v2
        with:
          prefix-key: no-std
      - name: Check (no_std + alloc)
        run: cargo check --target thumbv7em-none-eabihf --no-default-features --features alloc
      # Every advertised combination, not just the smallest one: a feature that
      # compiles alone can still fail beside another (serde needs its own `alloc`
      # to have `String` impls at all).
      - name: Check (no_std + alloc + serde)
        run: cargo check --target thumbv7em-none-eabihf --no-default-features --features alloc,serde
      - name: Check (no_std + alloc, no compare-and-swap)
        run: cargo check --target thumbv6m-none-eabi --no-default-features --features alloc
      - name: Clippy (no_std + alloc)
        run: cargo clippy --target thumbv7em-none-eabihf --no-default-features --features alloc -- -D warnings

  cross-i686:
    needs: lint
    timeout-minutes: 15
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
        with:
          persist-credentials: false
      - uses: dtolnay/rust-toolchain@stable
        with:
          targets: i686-unknown-linux-gnu
      - uses: taiki-e/install-action@nextest
      - name: Install 32-bit libs
        run: sudo apt-get update && sudo apt-get install -y gcc-multilib
      - uses: Swatinem/rust-cache@v2
        with:
          prefix-key: cross-i686
      - name: Test (i686)
        run: cargo nextest run --profile ci --all-features --target i686-unknown-linux-gnu

  msrv:
    needs: lint
    timeout-minutes: 15
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
        with:
          persist-credentials: false
      - uses: dtolnay/rust-toolchain@1.92
      - uses: taiki-e/install-action@nextest
      - uses: Swatinem/rust-cache@v2
      - name: Test (all features)
        run: cargo nextest run --profile ci --all-features
      - name: Test (no default features)
        run: cargo nextest run --profile ci --no-default-features

  codecov:
    needs: lint
    timeout-minutes: 15
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
        with:
          persist-credentials: false
      - uses: dtolnay/rust-toolchain@nightly
        with:
          components: llvm-tools-preview
      - uses: Swatinem/rust-cache@v2
      - uses: taiki-e/install-action@cargo-llvm-cov
      - run: cargo llvm-cov --all-features --lcov --output-path lcov.info
      - uses: codecov/codecov-action@v7
        with:
          files: lcov.info
        env:
          CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

  bench:
    needs: lint
    timeout-minutes: 15
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
        with:
          persist-credentials: false
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
        with:
          prefix-key: bench
      - name: Run benchmarks
        run: cargo bench --all-features -- --output-format bencher | tee bench-output.txt
      - name: Upload benchmark results
        uses: actions/upload-artifact@v7
        with:
          name: benchmark-results
          path: |
            bench-output.txt
            target/criterion/