open-cypher 0.2.0

An unofficial Rust parser for the openCypher query language.
Documentation
name: CI

on:
  push:
    branches: [main]
  pull_request:

permissions:
  contents: read

env:
  CARGO_TERM_COLOR: always
  RUST_BACKTRACE: 1

jobs:
  quality:
    name: Format, lint, and test (stable Linux)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: clippy, rustfmt
      - uses: Swatinem/rust-cache@v2
      - name: Verify generated parser is current
        run: cargo run --locked --package open-cypher-xtask -- grammar verify
      - name: Verify pinned openCypher snapshot
        run: cargo run --locked --package open-cypher-xtask -- spec verify
      - name: Check formatting
        run: cargo fmt --all -- --check
      - name: Check fuzz-target formatting
        run: cargo fmt --manifest-path fuzz/Cargo.toml -- --check
      - name: Check locked fuzz workspace
        run: cargo check --manifest-path fuzz/Cargo.toml --bins --locked
      - name: Lint all targets and features
        run: cargo clippy --workspace --all-targets --all-features --locked -- -D warnings
      - name: Test workspace with default features
        run: cargo test --workspace --locked
      - name: Test workspace and all features
        run: cargo test --workspace --all-features --locked
      - name: Build documentation
        env:
          RUSTDOCFLAGS: -D warnings
        run: cargo doc --workspace --all-features --no-deps --locked

  package:
    name: Verify packaged crate
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@v7
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      - name: Package and test exact archive contents
        shell: bash
        run: |
          set -euo pipefail
          scratch="$(mktemp -d)"
          package_target="$scratch/package-target"

          cargo package --package open-cypher --all-features --locked \
            --target-dir "$package_target"

          shopt -s nullglob
          archives=("$package_target"/package/open-cypher-*.crate)
          if (( ${#archives[@]} != 1 )); then
            printf 'expected one .crate archive, found %d\n' \
              "${#archives[@]}" >&2
            exit 1
          fi

          mkdir "$scratch/extracted"
          tar -xzf "${archives[0]}" -C "$scratch/extracted"
          roots=("$scratch"/extracted/open-cypher-*)
          if (( ${#roots[@]} != 1 )) || [[ ! -d "${roots[0]}" ]]; then
            printf 'expected one extracted package root, found %d\n' \
              "${#roots[@]}" >&2
            exit 1
          fi

          CARGO_TARGET_DIR="$scratch/test-target" \
            cargo check --manifest-path "${roots[0]}/Cargo.toml" \
              --all-targets --all-features --locked
          CARGO_TARGET_DIR="$scratch/test-target" \
            cargo test --manifest-path "${roots[0]}/Cargo.toml" \
              --all-features --locked

  compatibility:
    name: Test (${{ matrix.name }})
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        include:
          - name: MSRV 1.88 / Linux
            os: ubuntu-latest
            toolchain: "1.88"
          - name: stable / macOS
            os: macos-latest
            toolchain: stable
          - name: stable / Windows
            os: windows-latest
            toolchain: stable
    steps:
      - uses: actions/checkout@v7
      - uses: dtolnay/rust-toolchain@master
        with:
          toolchain: ${{ matrix.toolchain }}
      - uses: Swatinem/rust-cache@v2
      - run: cargo test --workspace --all-features --locked
      - name: Check independent fuzz workspace on MSRV
        if: matrix.toolchain == '1.88'
        run: cargo check --manifest-path fuzz/Cargo.toml --bins --locked

  conformance:
    name: openCypher 2024.3 syntax projection
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@v7
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      - name: Verify deterministic TCK projection
        run: cargo run --locked --package open-cypher-xtask -- tck verify
      - name: Check projected syntax expectations
        run: cargo run --locked --package open-cypher-xtask -- tck check
      - name: Enforce production and conformance closure
        run: cargo run --locked --package open-cypher-xtask -- spec release-check
      - name: Write machine-readable report
        if: always()
        run: cargo run --locked --package open-cypher-xtask -- tck report
      - name: Upload conformance report
        if: always()
        uses: actions/upload-artifact@v7
        with:
          name: tck-syntax-report-${{ github.sha }}
          path: target/tck-syntax-report.json
          if-no-files-found: error
          retention-days: 14

  coverage:
    name: Coverage
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: llvm-tools-preview
      - uses: taiki-e/install-action@v2
        with:
          tool: cargo-llvm-cov
      - uses: Swatinem/rust-cache@v2
      - name: Enforce handwritten-source line coverage
        run: >-
          cargo llvm-cov
          --package open-cypher
          --all-features
          --locked
          --ignore-filename-regex '(/target/|/tests/|/benches/|/fuzz/|/src/generated/)'
          --fail-under-lines 85

  fuzz-smoke:
    name: Fuzz smoke (${{ matrix.target }})
    runs-on: ubuntu-latest
    timeout-minutes: 10
    strategy:
      fail-fast: false
      matrix:
        target: [lex_str, parse_strict, parse_recovering, parse_generated_valid, parse_tck_mutations]
    steps:
      - uses: actions/checkout@v7
      - uses: dtolnay/rust-toolchain@nightly
      - uses: taiki-e/install-action@v2
        with:
          tool: cargo-fuzz@0.13.2
      - uses: Swatinem/rust-cache@v2
        with:
          workspaces: fuzz -> fuzz/target
      - name: Build fuzz target
        # cargo-fuzz 0.13.2 can infer musl from its prebuilt installer binary,
        # but AddressSanitizer requires the runner's dynamically linked libc.
        run: >-
          cargo +nightly fuzz build
          --target x86_64-unknown-linux-gnu
          ${{ matrix.target }}
      - name: Replay seed and regression corpus
        env:
          FUZZ_TARGET: ${{ matrix.target }}
        run: |
          set -euo pipefail
          corpora=("fuzz/corpus/$FUZZ_TARGET")
          if [[ "$FUZZ_TARGET" == "parse_strict" ]]; then
            corpora+=("tests/fixtures/regressions/parse_strict")
          fi
          cargo +nightly fuzz run \
            --target x86_64-unknown-linux-gnu \
            "$FUZZ_TARGET" "${corpora[@]}" -- \
            -dict=fuzz/cypher.dict \
            -runs=0 \
            -max_len=65536 \
            -timeout=5 \
            -rss_limit_mb=2048

  benchmark-build:
    name: Build benchmarks
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      - run: cargo bench --workspace --all-features --no-run --locked