rulewright 0.1.1

Executable engineering standards for Rust workspaces
Documentation
use super::{assignment_line, is_cargo_manifest};
use crate::{Example, TomlCtx, Violation, violation};

#[rustfmt::skip]
const EXAMPLES: &[Example] = &[
    Example { label: "additive std feature", code: "[features]\ndefault = [\"std\"]\nstd = []\n", pass: true },
    Example { label: "no features table", code: "[package]\nname = \"foo\"\n", pass: true },
    Example { label: "no-std feature", code: "[features]\nno-std = []\n", pass: false },
    Example { label: "no_std feature", code: "[features]\nno_std = []\n", pass: false },
    Example { label: "nostd feature", code: "[features]\nnostd = []\n", pass: false },
];

crate::toml_rule!(
    toml_cargo_feature_no_std,
    "Ban subtractive no-std Cargo features; provide an additive std feature instead.",
    "Features must be additive so any combination compiles; a no-std feature that removes functionality breaks feature unification.",
    Medium,
);

fn check_toml_cargo_feature_no_std(ctx: &TomlCtx<'_>) -> Vec<Violation> {
    if !is_cargo_manifest(ctx.file.rel) || !ctx.parse.errors.is_empty() {
        return Vec::new();
    }

    let Ok(document) = toml::from_str::<toml::Table>(ctx.file.contents) else {
        return Vec::new();
    };
    let Some(features) = document.get("features").and_then(toml::Value::as_table) else {
        return Vec::new();
    };

    features
        .keys()
        .filter(|name| matches!(name.as_str(), "no-std" | "no_std" | "nostd"))
        .map(|name| {
            violation(
                ctx.file.rel,
                assignment_line(ctx.file.lines, name),
                format!(
                    "feature `{name}` is subtractive; invert it into an additive `std` feature"
                ),
            )
        })
        .collect()
}

#[cfg(test)]
mod tests {
    use super::*;

    fn run(source: &str) -> Vec<Violation> {
        crate::test_support::check_source_toml_at(
            "crates/foo/Cargo.toml",
            source,
            check_toml_cargo_feature_no_std,
        )
    }

    crate::example_tests!(EXAMPLES, check_toml_cargo_feature_no_std);
}