molex 0.2.2

Parse, analyze, and transform molecular structure data (PDB, mmCIF, BinaryCIF, MRC)
Documentation
# Use PowerShell on Windows (bash resolves to WSL on some machines)
set windows-shell := ["powershell.exe", "-NoLogo", "-Command"]

# Run all checks (what CI runs)
check: fmt-check clippy test doc

# Format check (nightly)
fmt-check:
    cargo +nightly fmt --check

# Format (nightly)
fmt:
    cargo +nightly fmt

# Clippy with all targets
clippy:
    cargo clippy --all-targets --all-features -- -D warnings

# Run tests
test:
    cargo test --all-features

# Build docs and check for warnings
doc $RUSTDOCFLAGS="-D warnings":
    cargo doc --no-deps --document-private-items

# Dependency audit
deny:
    cargo deny check

# Check for unused dependencies
machete:
    cargo machete

# Check file lengths (max 800 lines)
file-lengths:
    python3 -c "import os, sys; files = [os.path.join(r,f) for r,_,fs in os.walk('src') for f in fs if f.endswith('.rs') and 'target' not in r]; bad = [(f,sum(1 for _ in open(f,encoding='utf-8',errors='replace'))) for f in files]; bad = [(f,n) for f,n in bad if n > 800]; [print(f'ERROR: {f} has {n} lines (max 800)') for f,n in bad]; sys.exit(1 if bad else 0)"

# One-time repo setup (hooks + commit template)
setup:
    git config core.hooksPath .githooks
    git config commit.template .gitmessage
    @echo "Done. Hooks and commit template activated."

# Count clippy errors
errors:
    cargo clippy --all-targets --all-features 2>&1 | rg '^error' | wc -l

# Count clippy warnings
warnings:
    cargo clippy --all-targets --all-features 2>&1 | rg '^warning' | wc -l

# Clippy violations per-rule, per-module (optionally filter by dir)
# Usage: just lint [dir]
# Examples: just lint              (all modules)
#           just lint adapters     (only src/adapters/)
#           just lint types        (only src/types/)
lint dir="":
    #!/usr/bin/env bash
    cargo clippy --all-targets --all-features --message-format=json 2>/dev/null \
    | python3 -c "
    import sys, json
    filt = '{{dir}}'
    seen = set()
    rows = []
    for line in sys.stdin:
        line = line.strip()
        if not line:
            continue
        try:
            msg = json.loads(line)
        except ValueError:
            continue
        if msg.get('reason') != 'compiler-message':
            continue
        m = msg['message']
        code = m.get('code')
        if not code or not code.get('code'):
            continue
        rule = code['code']
        spans = m.get('spans', [])
        primary = next((s for s in spans if s.get('is_primary')), None)
        if not primary:
            continue
        f = primary['file_name']
        # skip non-source files (Cargo.toml metadata lints, etc.)
        if not f.startswith('src/'):
            continue
        if filt and not f.startswith('src/' + filt):
            continue
        # deduplicate across targets (lib vs test emit the same diagnostic)
        key = (f, primary.get('line_start'), rule)
        if key in seen:
            continue
        seen.add(key)
        # module = first two path components under src/
        parts = f.split('/')
        if len(parts) >= 3:
            mod_name = parts[1] + '/' + parts[2].replace('.rs', '')
        elif len(parts) == 2:
            mod_name = parts[1].replace('.rs', '')
        else:
            mod_name = f
        rows.append((mod_name, rule))
    if not rows:
        print('No violations found.')
        sys.exit(0)
    # Count per (module, rule)
    from collections import Counter
    counts = Counter(rows)
    by_mod = {}
    for (mod_name, rule), n in counts.items():
        by_mod.setdefault(mod_name, []).append((rule, n))
    total = sum(counts.values())
    for mod_name in sorted(by_mod):
        rules = sorted(by_mod[mod_name], key=lambda x: -x[1])
        mod_total = sum(n for _, n in rules)
        print(f'\n  {mod_name} ({mod_total})')
        for rule, n in rules:
            print(f'    {n:3d}  {rule}')
    print(f'\n  total: {total}')
    "

# Run everything including optional tools
check-all: check deny machete file-lengths