rrubocop 0.8.14

10x faster RuboCop drop-in. Same output format and configs, without maintaining RuboCop gems.
//! Bundler/GemFilename — Gemfile vs gems.rb naming.

use std::path::Path;

use crate::cop::{Cop, CopConfig};
use crate::diagnostic::Diagnostic;
use crate::parse::source::SourceFile;

pub struct GemFilename;

impl Cop for GemFilename {
    fn name(&self) -> &'static str {
        "Bundler/GemFilename"
    }

    fn default_include(&self) -> &'static [&'static str] {
        &["**/Gemfile", "**/gems.rb"]
    }

    fn uses_line_phase(&self) -> bool {
        true
    }

    fn check_lines(
        &self,
        source: &SourceFile,
        config: &CopConfig,
        diagnostics: &mut Vec<Diagnostic>,
        _corrections: Option<&mut Vec<crate::correction::Correction>>,
    ) {
        if let Some(msg) = mismatch_msg(
            config.get_str("EnforcedStyle", "Gemfile"),
            Path::new(source.path_str()).file_name().and_then(|s| s.to_str()).unwrap_or(""),
            source.path_str(),
        ) {
            diagnostics.push(self.diagnostic(source, 1, 0, msg));
        }
    }
}

fn mismatch_msg(style: &str, file_name: &str, path: &str) -> Option<String> {
    match (style, file_name) {
        ("Gemfile", "gems.rb") => Some(format!(
            "`gems.rb` file was found but `Gemfile` is required (file path: {path})."
        )),
        ("Gemfile", "gems.locked") => Some(format!(
            "Expected a `Gemfile.lock` with `Gemfile` but found `gems.locked` file (file path: {path})."
        )),
        ("gems.rb", "Gemfile") => Some(format!(
            "`Gemfile` was found but `gems.rb` is required (file path: {path})."
        )),
        ("gems.rb", "Gemfile.lock") => Some(format!(
            "Expected a `gems.locked` with `gems.rb` but found `Gemfile.lock` file (file path: {path})."
        )),
        _ => None,
    }
}