rulewright 0.1.1

Executable engineering standards for Rust workspaces
Documentation
use ra_ap_syntax::ast;

use super::naming;
use crate::{AstCtx, Example, Violation};

#[rustfmt::skip]
const EXAMPLES: &[Example] = &[
    Example {
        label: "five-word struct name",
        code: "struct GlobalApplicationConfigManagerImpl;",
        pass: false,
    },
    Example {
        label: "five-word enum name",
        code: "enum BookingRequestQueueItemState { A }",
        pass: false,
    },
    Example {
        label: "five-word trait name",
        code: "trait AsyncRemoteAccountDataSource {}",
        pass: false,
    },
    Example {
        label: "five-word type alias",
        code: "type SharedRemoteAccountDataCache = ();",
        pass: false,
    },
    Example {
        label: "two-word name",
        code: "struct AppConfig;",
        pass: true,
    },
    Example {
        label: "four words is at the threshold",
        code: "struct HtmlParserConfigBuilder;",
        pass: true,
    },
    Example {
        label: "acronym run counts as one word",
        code: "struct HTMLParserConfigBuilder;",
        pass: true,
    },
    Example {
        label: "long name in test module",
        code: "#[cfg(test)]\nmod tests {\n    struct GlobalApplicationConfigManagerFake;\n}",
        pass: true,
    },
];

crate::ast_rule!(
    long_compound_name,
    "Flag type definitions whose CamelCase name compounds more than threshold words.",
    "Rust item names are short: `AppConfig` over `GlobalApplicationConfig`; long compounds hide the item's essence.",
    Low,
    params { threshold: i64 = 4 },
);

fn check_long_compound_name(ctx: &AstCtx<'_>) -> Vec<Violation> {
    let threshold = ctx
        .file
        .config
        .get_usize("rust_long_compound_name", &PARAMS[0]);

    ctx.nodes::<ast::Item>()
        .filter(|item| !ctx.is_in_test(item))
        .filter_map(|item| {
            let name = naming::type_def_name(&item)?;
            let name_text = name.text();
            let words = naming::segments(&name_text).len();

            (words > threshold).then(|| {
                ctx.violation(
                    &name,
                    format!(
                        "type name `{name_text}` compounds {words} words (max {threshold}) — shorten it"
                    ),
                )
            })
        })
        .collect()
}

crate::rulewright_ast_test!(check_long_compound_name, {
    crate::example_tests!(EXAMPLES, check_long_compound_name);
});