use std::collections::{BTreeMap, BTreeSet};
use std::sync::Arc;
use super::index::{EmbeddedType, Embedding, Index};
use super::types::Receiver;
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
struct Reached {
embedded: EmbeddedType,
through_pointer: bool,
multiples: bool,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub(super) struct Member {
pub(super) slot: usize,
pub(super) through_pointer: bool,
pub(super) multiples: bool,
}
pub(super) struct MethodSet {
answered: BTreeMap<Arc<str>, Box<[Member]>>,
}
impl MethodSet {
pub(super) fn of(index: &Index, receiver: Receiver) -> Self {
let mut answered: BTreeMap<Arc<str>, Vec<Member>> = BTreeMap::new();
let mut visited: BTreeSet<EmbeddedType> = BTreeSet::new();
let mut level: Vec<Reached> = owner(index, receiver).into_iter().collect();
while !level.is_empty() {
let next = deeper(index, &level, &visited);
record_level(index, &level, &mut answered);
visited.extend(level.drain(..).map(|reached| reached.embedded));
level = next;
}
Self {
answered: answered
.into_iter()
.map(|(name, found)| (name, found.into_boxed_slice()))
.collect(),
}
}
pub(super) fn named(&self, member: &str) -> &[Member] {
self.answered
.get(member)
.map(|found| &**found)
.unwrap_or_default()
}
}
pub(super) fn promoted(index: &Index, receiver: Receiver, member: &str) -> Box<[Member]> {
let found = MethodSet::of(index, receiver);
Box::from(found.named(member))
}
fn owner(index: &Index, receiver: Receiver) -> Option<Reached> {
index.slot(receiver.slot).map(|slot| Reached {
embedded: EmbeddedType {
unit: slot.unit,
name: Arc::clone(&slot.name),
},
through_pointer: false,
multiples: false,
})
}
fn record_level(index: &Index, level: &[Reached], answered: &mut BTreeMap<Arc<str>, Vec<Member>>) {
let mut depth: BTreeMap<Arc<str>, Vec<Member>> = BTreeMap::new();
for (name, member) in at_depth(index, level) {
depth.entry(name).or_default().push(member);
}
for (name, found) in depth {
answered.entry(name).or_insert_with(|| sorted(found));
}
}
fn sorted(mut found: Vec<Member>) -> Vec<Member> {
found.sort_unstable();
found.dedup();
found
}
fn at_depth<'a>(
index: &'a Index,
level: &'a [Reached],
) -> impl Iterator<Item = (Arc<str>, Member)> + 'a {
level
.iter()
.flat_map(move |reached| declared(index, reached))
}
fn declared<'a>(
index: &'a Index,
reached: &'a Reached,
) -> impl Iterator<Item = (Arc<str>, Member)> + 'a {
index
.unit(reached.embedded.unit)
.into_iter()
.flat_map(move |tables| tables.held(&reached.embedded.name))
.filter_map(move |slot| index.slot(*slot).map(|found| (found, *slot)))
.map(move |(found, slot)| {
(
Arc::clone(&found.name),
Member {
slot,
through_pointer: reached.through_pointer,
multiples: reached.multiples,
},
)
})
}
fn deeper(index: &Index, level: &[Reached], visited: &BTreeSet<EmbeddedType>) -> Vec<Reached> {
let mut next: Vec<Reached> = carried(index, level)
.filter(|reached| unwalked(reached, level, visited))
.collect();
next.sort_unstable();
consolidated(next)
}
fn carried<'a>(index: &'a Index, level: &'a [Reached]) -> impl Iterator<Item = Reached> + 'a {
level
.iter()
.flat_map(move |reached| embedded(index, reached))
}
fn embedded<'a>(index: &'a Index, holder: &'a Reached) -> impl Iterator<Item = Reached> + 'a {
index
.unit(holder.embedded.unit)
.into_iter()
.flat_map(move |tables| tables.embedded(&holder.embedded.name))
.map(move |embedding| promotes(holder, embedding))
}
fn promotes(holder: &Reached, embedding: &Embedding) -> Reached {
Reached {
embedded: embedding.embedded.clone(),
through_pointer: holder.through_pointer || embedding.pointer,
multiples: false,
}
}
fn unwalked(reached: &Reached, level: &[Reached], visited: &BTreeSet<EmbeddedType>) -> bool {
!visited.contains(&reached.embedded)
&& !level.iter().any(|open| open.embedded == reached.embedded)
}
fn consolidated(sorted: Vec<Reached>) -> Vec<Reached> {
let mut held: Vec<Reached> = Vec::new();
for reached in sorted {
match held.last_mut() {
Some(last) if last.embedded == reached.embedded => last.multiples = true,
_ => held.push(reached),
}
}
held
}