use std::collections::HashMap;
use rustdoc_types::{Id, Item};
use trustfall::{
provider::{
resolve_neighbors_with, AsVertex, CandidateValue, ContextIterator, ContextOutcomeIterator,
ResolveEdgeInfo, VertexInfo, VertexIterator,
},
FieldValue,
};
use crate::{indexed_crate::ImplEntry, IndexedCrate};
use super::super::{origin::Origin, vertex::Vertex, RustdocAdapter};
pub(crate) fn resolve_owner_impl<'a, V: AsVertex<Vertex<'a>> + 'a>(
adapter: &RustdocAdapter<'a>,
contexts: ContextIterator<'a, V>,
edge_name: &str,
resolve_info: &ResolveEdgeInfo,
) -> ContextOutcomeIterator<'a, V, VertexIterator<'a, Vertex<'a>>> {
let current_crate = adapter.current_crate;
let previous_crate = adapter.previous_crate;
let inherent_impls_only = match edge_name {
"inherent_impl" => true,
"impl" => false,
_ => unreachable!("unexpected edge name: {edge_name}"),
};
if let Some(method_vertex_info) = resolve_info
.destination()
.first_edge("method")
.as_ref()
.map(|e| e.destination())
{
resolve_owner_impl_based_on_method_info(
adapter,
contexts,
current_crate,
previous_crate,
inherent_impls_only,
method_vertex_info,
)
} else {
resolve_neighbors_with(contexts, move |vertex| {
resolve_owner_impl_slow_path(vertex, current_crate, previous_crate, inherent_impls_only)
})
}
}
fn resolve_owner_impl_based_on_method_info<'a, V: AsVertex<Vertex<'a>> + 'a>(
adapter: &RustdocAdapter<'a>,
contexts: ContextIterator<'a, V>,
current_crate: &'a IndexedCrate<'a>,
previous_crate: Option<&'a IndexedCrate<'a>>,
inherent_impls_only: bool,
method_vertex_info: &impl VertexInfo,
) -> ContextOutcomeIterator<'a, V, VertexIterator<'a, Vertex<'a>>> {
if let Some(resolver) = method_vertex_info.dynamically_required_property("name") {
resolver.resolve_with(adapter, contexts, move |vertex, candidate| {
resolve_impl_based_on_method_name_candidate(
vertex,
current_crate,
previous_crate,
inherent_impls_only,
candidate,
)
})
} else if let Some(candidate) = method_vertex_info.statically_required_property("name") {
resolve_neighbors_with(contexts, move |vertex| {
resolve_impl_based_on_method_name_candidate(
vertex,
current_crate,
previous_crate,
inherent_impls_only,
candidate.clone(),
)
})
} else {
resolve_neighbors_with(contexts, move |vertex| {
resolve_owner_impl_slow_path(vertex, current_crate, previous_crate, inherent_impls_only)
})
}
}
fn resolve_impl_based_on_method_name_candidate<'a>(
vertex: &Vertex<'a>,
current_crate: &'a IndexedCrate<'a>,
previous_crate: Option<&'a IndexedCrate<'a>>,
inherent_impls_only: bool,
method_name: CandidateValue<FieldValue>,
) -> VertexIterator<'a, Vertex<'a>> {
let origin = vertex.origin;
let impl_index = match origin {
Origin::CurrentCrate => current_crate
.impl_index
.as_ref()
.expect("no impl index present"),
Origin::PreviousCrate => previous_crate
.expect("no previous crate provided")
.impl_index
.as_ref()
.expect("no impl index provided"),
};
let item_id = &vertex.as_item().expect("not an item").id;
match method_name {
CandidateValue::Impossible => Box::new(std::iter::empty()),
CandidateValue::Single(value) => {
let method_name = value.as_str().expect("method name was not a string");
resolve_impl_based_on_method_name(
origin,
impl_index,
inherent_impls_only,
item_id,
method_name,
)
}
CandidateValue::Multiple(values) => Box::new(values.into_iter().flat_map(move |value| {
let method_name = value.as_str().expect("method name was not a string");
resolve_impl_based_on_method_name(
origin,
impl_index,
inherent_impls_only,
item_id,
method_name,
)
})),
_ => {
resolve_owner_impl_slow_path(vertex, current_crate, previous_crate, inherent_impls_only)
}
}
}
fn resolve_impl_based_on_method_name<'a>(
origin: Origin,
impl_index: &'a HashMap<ImplEntry<'a>, Vec<(&'a Item, &'a Item)>>,
inherent_impls_only: bool,
item_id: &Id,
method_name: &str,
) -> VertexIterator<'a, Vertex<'a>> {
if let Some(method_ids) = impl_index.get(&(item_id, method_name)) {
Box::new(method_ids.iter().filter_map(move |(impl_item, _)| {
let impl_content = match &impl_item.inner {
rustdoc_types::ItemEnum::Impl(imp) => imp,
_ => unreachable!(
"\
the `impl_index` returned a value where the `impl_item` was not an impl: {impl_item:?}"
),
};
if !inherent_impls_only || impl_content.trait_.is_none() {
Some(origin.make_item_vertex(impl_item))
} else {
None
}
}))
} else {
Box::new(std::iter::empty())
}
}
fn resolve_owner_impl_slow_path<'a>(
vertex: &Vertex<'a>,
current_crate: &'a IndexedCrate<'a>,
previous_crate: Option<&'a IndexedCrate<'a>>,
inherent_impls_only: bool,
) -> VertexIterator<'a, Vertex<'a>> {
let origin = vertex.origin;
let item_index = match origin {
Origin::CurrentCrate => ¤t_crate.inner.index,
Origin::PreviousCrate => {
&previous_crate
.expect("no previous crate provided")
.inner
.index
}
};
let impl_ids = vertex
.as_struct()
.map(|s| &s.impls)
.or_else(|| vertex.as_enum().map(|e| &e.impls))
.or_else(|| vertex.as_union().map(|u| &u.impls))
.expect("vertex was neither a struct, enum, or union");
Box::new(impl_ids.iter().filter_map(move |item_id| {
let next_item = item_index.get(item_id);
next_item.and_then(|next_item| match &next_item.inner {
rustdoc_types::ItemEnum::Impl(imp) => {
if !inherent_impls_only || imp.trait_.is_none() {
Some(origin.make_item_vertex(next_item))
} else {
None
}
}
_ => None,
})
}))
}