// engine/config-health/rust-checker.ts — SPEC-042
// Rust project configuration health checks.
import { readFile } from 'node:fs/promises';
import { join } from 'node:path';
import { glob } from 'glob';
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
async function readFileSafe(filePath) {
    try {
        return await readFile(filePath, 'utf-8');
    }
    catch {
        return '';
    }
}
// ---------------------------------------------------------------------------
// TOML helpers (minimal, no external deps)
// ---------------------------------------------------------------------------
/**
 * Extract a string value from a TOML-like content.
 * e.g. extractTomlValue('channel = "stable"', 'channel') => "stable"
 */
function extractTomlValue(content, key) {
    const pattern = new RegExp(`^${key}\\s*=\\s*"([^"]+)"`, 'm');
    const match = pattern.exec(content);
    return match?.[1] ?? null;
}
/**
 * Extract all keys in a TOML section.
 */
function extractTomlSectionKeys(content, section) {
    const lines = content.split('\n');
    const sectionPattern = new RegExp(`^\\[${section.replace('.', '\\.')}\\]`);
    const keys = [];
    let inSection = false;
    for (const line of lines) {
        const trimmed = line.trim();
        if (sectionPattern.test(trimmed)) {
            inSection = true;
            continue;
        }
        if (inSection) {
            if (trimmed.startsWith('[')) {
                break;
            }
            const keyMatch = /^([\w-]+)\s*=/.exec(trimmed);
            if (keyMatch?.[1]) {
                keys.push(keyMatch[1]);
            }
        }
    }
    return keys;
}
// ---------------------------------------------------------------------------
// Check: Cargo.toml features that don't exist in dependencies
// ---------------------------------------------------------------------------
/**
 * Detect Cargo.toml [features] entries that reference dependency features
 * not declared in [dependencies].
 */
export async function checkCargoFeatures(projectPath) {
    const findings = [];
    const cargoContent = await readFileSafe(join(projectPath, 'Cargo.toml'));
    if (!cargoContent) {
        return findings;
    }
    // Extract [features] section
    const featureKeys = extractTomlSectionKeys(cargoContent, 'features');
    if (featureKeys.length === 0) {
        return findings;
    }
    // Extract dependency names from [dependencies] and [dev-dependencies]
    const depKeys = extractTomlSectionKeys(cargoContent, 'dependencies');
    const devDepKeys = extractTomlSectionKeys(cargoContent, 'dev-dependencies');
    const allDeps = new Set([...depKeys, ...devDepKeys]);
    // Feature values can reference "dep/feature" — check that dep exists
    const lines = cargoContent.split('\n');
    let inFeaturesSection = false;
    for (let i = 0; i < lines.length; i++) {
        const line = lines[i]?.trim() ?? '';
        if (line === '[features]') {
            inFeaturesSection = true;
            continue;
        }
        if (inFeaturesSection && line.startsWith('[')) {
            break;
        }
        if (!inFeaturesSection || !line || line.startsWith('#')) {
            continue;
        }
        // Feature definition: feature_name = ["dep/feature", "other-dep"]
        const featureMatch = /^([\w-]+)\s*=\s*\[([^\]]*)\]/.exec(line);
        if (!featureMatch?.[2]) {
            continue;
        }
        const deps = featureMatch[2]
            .split(',')
            .map((s) => s.trim().replace(/"/g, ''))
            .filter(Boolean);
        for (const dep of deps) {
            // dep/feature references a dependency feature
            if (dep.includes('/')) {
                const depName = dep.split('/')[0] ?? '';
                if (depName && !allDeps.has(depName)) {
                    findings.push({
                        severity: 'error',
                        checker: 'cargo-features',
                        file: 'Cargo.toml',
                        line: i + 1,
                        message: `Feature '${featureMatch[1]}' references dependency '${depName}' which is not in [dependencies]`,
                        fix: `Add '${depName}' to [dependencies] in Cargo.toml or remove the feature reference`,
                    });
                }
            }
        }
    }
    return findings;
}
// ---------------------------------------------------------------------------
// Check: clippy.toml / .cargo/config.toml unknown options
// ---------------------------------------------------------------------------
const KNOWN_CLIPPY_OPTIONS = [
    'avoid-breaking-exported-api',
    'cognitive-complexity-threshold',
    'disallowed-macros',
    'disallowed-methods',
    'disallowed-names',
    'disallowed-types',
    'doc-valid-idents',
    'enable-raw-pointer-heuristic-for-send',
    'large-error-threshold',
    'max-fn-params-without-body',
    'max-include-file-size',
    'max-struct-bools',
    'max-trait-bounds',
    'min-ident-chars-threshold',
    'msrv',
    'pass-by-value-size-limit',
    'semicolon-inside-block-ignore-singleline',
    'semicolon-outside-block-ignore-multiline',
    'single-char-binding-names-threshold',
    'too-large-for-stack',
    'too-many-arguments-threshold',
    'too-many-lines-threshold',
    'trivial-copy-size-limit',
    'type-complexity-threshold',
    'verbose-bit-mask-threshold',
];
/**
 * Detect unknown options in clippy.toml.
 */
export async function checkClippyConfig(projectPath) {
    const findings = [];
    const clippyContent = await readFileSafe(join(projectPath, 'clippy.toml'));
    if (!clippyContent) {
        return findings;
    }
    const lines = clippyContent.split('\n');
    for (let i = 0; i < lines.length; i++) {
        const line = lines[i]?.trim() ?? '';
        if (!line || line.startsWith('#')) {
            continue;
        }
        const keyMatch = /^([\w-]+)\s*=/.exec(line);
        if (keyMatch?.[1] && !KNOWN_CLIPPY_OPTIONS.includes(keyMatch[1])) {
            findings.push({
                severity: 'warning',
                checker: 'clippy-config',
                file: 'clippy.toml',
                line: i + 1,
                message: `'${keyMatch[1]}' is not a known clippy.toml option`,
                fix: `Check the Clippy documentation for valid configuration keys. Remove '${keyMatch[1]}' if it's a typo`,
            });
        }
    }
    return findings;
}
// ---------------------------------------------------------------------------
// Check: rust-toolchain.toml channel vs feature flags used
// ---------------------------------------------------------------------------
/**
 * Detect rust-toolchain.toml specifying "stable" channel when nightly-only
 * features are used in source code (#![feature(...)]).
 */
export async function checkRustToolchain(projectPath) {
    const findings = [];
    const toolchainContent = await readFileSafe(join(projectPath, 'rust-toolchain.toml'));
    const legacyToolchainContent = await readFileSafe(join(projectPath, 'rust-toolchain'));
    const legacyChannel = legacyToolchainContent.trim() || null;
    const channel = extractTomlValue(toolchainContent, 'channel') ?? legacyChannel;
    if (!channel || channel === 'nightly') {
        return findings;
    }
    // Check if source files use nightly-only feature flags
    let rustFiles;
    try {
        rustFiles = await glob('src/**/*.rs', {
            cwd: projectPath,
            nodir: true,
        });
    }
    catch {
        return findings;
    }
    // Sample first 10 files
    const filesToCheck = rustFiles.slice(0, 10);
    for (const rsFile of filesToCheck) {
        const content = await readFileSafe(join(projectPath, rsFile));
        if (!content) {
            continue;
        }
        // Detect #![feature(...)] which requires nightly
        const featurePattern = /#!\[feature\(([^)]+)\)\]/;
        const match = featurePattern.exec(content);
        if (match) {
            findings.push({
                severity: 'error',
                checker: 'clippy-config',
                file: rsFile,
                message: `File uses nightly-only #![feature(${match[1]})] but rust-toolchain uses '${channel}' channel`,
                fix: `Either switch to 'channel = "nightly"' in rust-toolchain.toml or remove the nightly feature flag`,
            });
        }
    }
    return findings;
}
//# sourceMappingURL=rust-checker.js.map