rrubocop 0.7.2

Fast RuboCop-compatible Ruby linter in Rust (tree-sitter). No Ruby for linting; ERB configs need ruby.
Documentation
//! Top-level config loading and inheritance resolution.

use std::collections::HashMap;
use std::path::{Path, PathBuf};

use anyhow::Result;

use super::discover::load_dir_overrides;
use super::load_defaults::try_load_rubocop_defaults;
use super::load_resolve::{
    apply_disabled_by_default, build_resolved, empty_resolved_no_config, load_project_layer,
    project_enabled_depts, resolve_config_path, resolve_lockfile_meta, resolve_path_base_dir,
    resolve_start_dir, resolve_target_ruby_version, ResolvedParts,
};
use super::merge::merge_layer_into;
use super::resolved::ResolvedConfig;

fn merge_project_onto_defaults(
    config_path: &Path,
    config_dir: &Path,
    gem_cache: Option<&HashMap<String, PathBuf>>,
    base: &mut super::types::ConfigLayer,
) -> Result<(
    std::collections::HashSet<String>,
    std::collections::HashSet<String>,
    std::collections::HashSet<String>,
)> {
    let project_layer = load_project_layer(config_path, config_dir, gem_cache)?;
    let cops = project_layer.user_mentioned_cops.clone();
    let depts = project_layer.user_mentioned_depts.clone();
    let enabled = project_enabled_depts(&project_layer);
    merge_layer_into(base, &project_layer, None);
    apply_disabled_by_default(base, &cops, &depts);
    Ok((cops, depts, enabled))
}

fn config_parent(config_path: &Path) -> PathBuf {
    match config_path.parent() {
        Some(p) if !p.as_os_str().is_empty() => p.to_path_buf(),
        _ => PathBuf::from("."),
    }
}

fn dir_overrides_for(explicit: bool, config_dir: &Path) -> Vec<(PathBuf, super::types::ConfigLayer)> {
    if explicit {
        Vec::new()
    } else {
        load_dir_overrides(config_dir)
    }
}

fn assemble_resolved(
    explicit_config_path: bool,
    config_path: PathBuf,
    gem_cache: Option<&HashMap<String, PathBuf>>,
) -> Result<ResolvedConfig> {
    let config_dir = config_parent(&config_path);
    let base_dir = resolve_path_base_dir(&config_path, &config_dir);
    let (mut base, rubocop_known_cops) = try_load_rubocop_defaults(&config_dir, gem_cache);
    let (project_mentioned_cops, project_mentioned_depts, project_enabled_depts) =
        merge_project_onto_defaults(&config_path, &config_dir, gem_cache, &mut base)?;
    Ok(build_resolved(ResolvedParts {
        target_ruby_version: resolve_target_ruby_version(&base, &config_dir),
        lock: resolve_lockfile_meta(&base, &base_dir),
        dir_overrides: dir_overrides_for(explicit_config_path, &config_dir),
        base,
        config_dir,
        base_dir,
        rubocop_known_cops,
        project_mentioned_cops,
        project_mentioned_depts,
        project_enabled_depts,
    }))
}

pub fn load_config(
    path: Option<&Path>,
    target_dir: Option<&Path>,
    gem_cache: Option<&HashMap<String, PathBuf>>,
) -> Result<ResolvedConfig> {
    let explicit_config_path = path.is_some();
    let start_dir = resolve_start_dir(target_dir);
    if path.is_some() && resolve_config_path(path, start_dir.as_ref()).is_none() {
        return Ok(ResolvedConfig::empty());
    }
    let Some(config_path) = resolve_config_path(path, start_dir.as_ref()) else {
        let Some(config_dir) = start_dir else {
            return Ok(ResolvedConfig::empty());
        };
        return Ok(empty_resolved_no_config(config_dir));
    };
    assemble_resolved(explicit_config_path, config_path, gem_cache)
}