use crate::mixins::RubyOwnerRelationFact;
use brokk_bifrost_core::analyzer::capabilities::{ImportAnalysisProvider, TypeHierarchyProvider};
use brokk_bifrost_core::analyzer::model::RubyMethodDispatchMode;
use brokk_bifrost_core::analyzer::type_relations::{TypeRelation, TypeRelationKind};
use brokk_bifrost_core::analyzer::{CodeUnit, CodeUnitIndex, ProjectFile};
use brokk_bifrost_core::hash::{HashMap, HashSet};
use std::sync::Arc;
pub trait RubySource: CodeUnitIndex + TypeHierarchyProvider + ImportAnalysisProvider {
fn all_files(&self) -> Vec<ProjectFile>;
fn autoload_constant_files(&self) -> &HashMap<String, HashSet<ProjectFile>>;
fn has_zeitwerk_autoload_conventions(&self) -> bool;
fn zeitwerk_autoload_files(&self) -> &HashSet<ProjectFile>;
fn zeitwerk_consumer_files(&self) -> &HashSet<ProjectFile>;
fn zeitwerk_autoload_code_units(&self) -> &HashSet<CodeUnit>;
fn reverse_import_index(&self) -> Arc<HashMap<ProjectFile, Arc<HashSet<ProjectFile>>>>;
fn mixin_relations(&self) -> &[TypeRelation];
fn semantic_facts(&self) -> &RubySemanticFacts;
fn types_by_identifier(&self) -> &HashMap<String, Vec<CodeUnit>>;
fn method_dispatch_mode(&self, unit: &CodeUnit) -> RubyMethodDispatchMode;
fn forward_owner_relation_facts(&self, owner: &CodeUnit) -> Vec<RubyOwnerRelationFact>;
}
pub struct RubySemanticFacts {
pub ancestors: HashMap<String, HashSet<String>>,
pub mixin_included_owners: HashMap<String, Vec<String>>,
pub mixin_prepended_owners: HashMap<String, Vec<String>>,
pub mixin_class_owners: HashMap<String, Vec<String>>,
}
pub fn build_ruby_semantic_facts(ruby: &dyn RubySource) -> RubySemanticFacts {
let mut ancestors = HashMap::default();
let mut mixin_included_owners: HashMap<String, Vec<String>> = HashMap::default();
let mut mixin_prepended_owners: HashMap<String, Vec<String>> = HashMap::default();
let mut mixin_class_owners: HashMap<String, Vec<String>> = HashMap::default();
for unit in ruby
.all_declarations()
.filter(|unit| unit.is_class() || unit.is_module())
{
let direct = ruby
.get_direct_ancestors(&unit)
.into_iter()
.map(|ancestor| ancestor.fq_name())
.collect();
ancestors.insert(unit.fq_name(), direct);
}
for relation in ruby.mixin_relations() {
let entry = match relation.kind {
TypeRelationKind::MixinInclude => &mut mixin_included_owners,
TypeRelationKind::MixinPrepend => &mut mixin_prepended_owners,
TypeRelationKind::MixinExtend => &mut mixin_class_owners,
_ => continue,
};
push_ordered_mixin(entry, relation.from.fq_name(), relation.to.fq_name());
}
RubySemanticFacts {
ancestors,
mixin_included_owners,
mixin_prepended_owners,
mixin_class_owners,
}
}
fn push_ordered_mixin(index: &mut HashMap<String, Vec<String>>, from: String, to: String) {
let owners = index.entry(from).or_default();
if !owners.contains(&to) {
owners.push(to);
}
}