trustfall_rustdoc_adapter/adapter/
mod.rs

1use std::sync::Arc;
2
3use rustdoc_types::Item;
4use trustfall::{
5    provider::{
6        resolve_coercion_with, Adapter, AsVertex, ContextIterator, ContextOutcomeIterator,
7        EdgeParameters, ResolveEdgeInfo, ResolveInfo, Typename, VertexIterator,
8    },
9    FieldValue, Schema,
10};
11
12use crate::PackageIndex;
13
14use self::{
15    origin::Origin,
16    vertex::{Vertex, VertexKind},
17};
18
19mod edges;
20mod enum_variant;
21mod optimizations;
22mod origin;
23mod properties;
24mod receiver;
25mod rust_type_name;
26mod vertex;
27
28#[cfg(test)]
29mod tests;
30
31#[non_exhaustive]
32pub struct RustdocAdapter<'a> {
33    current_crate: &'a PackageIndex<'a>,
34    previous_crate: Option<&'a PackageIndex<'a>>,
35}
36
37impl<'a> RustdocAdapter<'a> {
38    pub fn new(
39        current_crate: &'a PackageIndex<'a>,
40        previous_crate: Option<&'a PackageIndex<'a>>,
41    ) -> Self {
42        Self {
43            current_crate,
44            previous_crate,
45        }
46    }
47
48    pub fn schema() -> Schema {
49        Schema::parse(include_str!("../rustdoc_schema.graphql")).expect("schema not valid")
50    }
51}
52
53impl Drop for RustdocAdapter<'_> {
54    fn drop(&mut self) {}
55}
56
57impl<'a> Adapter<'a> for &'a RustdocAdapter<'a> {
58    type Vertex = Vertex<'a>;
59
60    fn resolve_starting_vertices(
61        &self,
62        edge_name: &Arc<str>,
63        _parameters: &EdgeParameters,
64        _resolve_info: &ResolveInfo,
65    ) -> VertexIterator<'a, Self::Vertex> {
66        match edge_name.as_ref() {
67            "Crate" => Box::new(std::iter::once(Vertex::new_crate(
68                Origin::CurrentCrate,
69                self.current_crate,
70            ))),
71            "CrateDiff" => {
72                let previous_crate = self.previous_crate.expect("no previous crate provided");
73                Box::new(std::iter::once(Vertex {
74                    origin: Origin::CurrentCrate,
75                    kind: VertexKind::CrateDiff((self.current_crate, previous_crate)),
76                }))
77            }
78            _ => unreachable!("resolve_starting_vertices {edge_name}"),
79        }
80    }
81
82    fn resolve_property<V: AsVertex<Self::Vertex> + 'a>(
83        &self,
84        contexts: ContextIterator<'a, V>,
85        type_name: &Arc<str>,
86        property_name: &Arc<str>,
87        _resolve_info: &ResolveInfo,
88    ) -> ContextOutcomeIterator<'a, V, FieldValue> {
89        if property_name.as_ref() == "__typename" {
90            Box::new(contexts.map(|ctx| match ctx.active_vertex() {
91                Some(vertex) => {
92                    let value = vertex.typename().into();
93                    (ctx, value)
94                }
95                None => (ctx, FieldValue::Null),
96            }))
97        } else {
98            match type_name.as_ref() {
99                "Crate" => properties::resolve_crate_property(contexts, property_name),
100                "Item" | "GenericItem" => {
101                    properties::resolve_item_property(contexts, property_name)
102                }
103                "ImplOwner"
104                | "Struct"
105                | "StructField"
106                | "Enum"
107                | "Variant"
108                | "PlainVariant"
109                | "TupleVariant"
110                | "StructVariant"
111                | "Union"
112                | "Trait"
113                | "Function"
114                | "Method"
115                | "Impl"
116                | "GlobalValue"
117                | "Constant"
118                | "Static"
119                | "AssociatedType"
120                | "AssociatedConstant"
121                | "Module"
122                | "Macro"
123                | "ProcMacro"
124                | "FunctionLikeProcMacro"
125                | "AttributeProcMacro"
126                | "DeriveProcMacro"
127                    if matches!(
128                        property_name.as_ref(),
129                        "id" | "crate_id"
130                            | "name"
131                            | "docs"
132                            | "attrs"
133                            | "doc_hidden"
134                            | "deprecated"
135                            | "public_api_eligible"
136                            | "visibility_limit"
137                    ) =>
138                {
139                    // properties inherited from Item, accesssed on Item subtypes
140                    properties::resolve_item_property(contexts, property_name)
141                }
142                "Module" => properties::resolve_module_property(contexts, property_name),
143                "Struct" => properties::resolve_struct_property(contexts, property_name),
144                "StructField" => properties::resolve_struct_field_property(contexts, property_name),
145                "Enum" => properties::resolve_enum_property(contexts, property_name),
146                "Variant" | "PlainVariant" | "TupleVariant" | "StructVariant" => {
147                    properties::resolve_enum_variant_property(contexts, property_name)
148                }
149                "Union" => properties::resolve_union_property(contexts, property_name),
150                "Span" => properties::resolve_span_property(contexts, property_name),
151                "Path" => properties::resolve_path_property(contexts, property_name),
152                "ImportablePath" => {
153                    properties::resolve_importable_path_property(contexts, property_name)
154                }
155                "FunctionLike" | "Function" | "Method"
156                    if matches!(
157                        property_name.as_ref(),
158                        "const" | "unsafe" | "async" | "has_body" | "signature"
159                    ) =>
160                {
161                    properties::resolve_function_like_property(contexts, property_name)
162                }
163                "Function" => properties::resolve_function_property(contexts, property_name),
164                "FunctionParameter" => {
165                    properties::resolve_function_parameter_property(contexts, property_name)
166                }
167                "FunctionAbi" => properties::resolve_function_abi_property(contexts, property_name),
168                "Impl" => properties::resolve_impl_property(contexts, property_name),
169                "Attribute" => properties::resolve_attribute_property(contexts, property_name),
170                "AttributeMetaItem" => {
171                    properties::resolve_attribute_meta_item_property(contexts, property_name)
172                }
173                "Trait" => properties::resolve_trait_property(
174                    contexts,
175                    property_name,
176                    self.current_crate,
177                    self.previous_crate,
178                ),
179                "ImplementedTrait" => properties::resolve_implemented_trait_property(
180                    contexts,
181                    property_name,
182                    self.current_crate,
183                    self.previous_crate,
184                ),
185                "Static" => properties::resolve_static_property(contexts, property_name),
186                "RawType" | "ResolvedPathType" if matches!(property_name.as_ref(), "name") => {
187                    // fields from "RawType"
188                    properties::resolve_raw_type_property(contexts, property_name)
189                }
190                "AssociatedType" => {
191                    properties::resolve_associated_type_property(contexts, property_name)
192                }
193                "AssociatedConstant" => {
194                    properties::resolve_associated_constant_property(contexts, property_name)
195                }
196                "Constant" => properties::resolve_constant_property(contexts, property_name),
197                "Discriminant" => {
198                    properties::resolve_discriminant_property(contexts, property_name)
199                }
200                "Feature" => properties::resolve_feature_property(contexts, property_name),
201                "DeriveMacroHelperAttribute" => {
202                    properties::resolve_derive_macro_helper_attribute_property(
203                        contexts,
204                        property_name,
205                    )
206                }
207                "GenericParameter"
208                | "GenericTypeParameter"
209                | "GenericLifetimeParameter"
210                | "GenericConstParameter"
211                    if matches!(property_name.as_ref(), "name" | "position") =>
212                {
213                    properties::resolve_generic_parameter_property(contexts, property_name)
214                }
215                "GenericTypeParameter" => {
216                    properties::resolve_generic_type_parameter_property(contexts, property_name)
217                }
218                "GenericConstParameter" => {
219                    properties::resolve_generic_const_parameter_property(contexts, property_name)
220                }
221                "Receiver" => properties::resolve_receiver_property(contexts, property_name),
222                _ => unreachable!("resolve_property {type_name} {property_name}"),
223            }
224        }
225    }
226
227    fn resolve_neighbors<V: AsVertex<Self::Vertex> + 'a>(
228        &self,
229        contexts: ContextIterator<'a, V>,
230        type_name: &Arc<str>,
231        edge_name: &Arc<str>,
232        parameters: &EdgeParameters,
233        resolve_info: &ResolveEdgeInfo,
234    ) -> ContextOutcomeIterator<'a, V, VertexIterator<'a, Self::Vertex>> {
235        match type_name.as_ref() {
236            "CrateDiff" => edges::resolve_crate_diff_edge(contexts, edge_name),
237            "Crate" => edges::resolve_crate_edge(self, contexts, edge_name, resolve_info),
238            "Importable"
239            | "ImplOwner"
240            | "Struct"
241            | "Enum"
242            | "Union"
243            | "Trait"
244            | "Function"
245            | "GlobalValue"
246            | "Constant"
247            | "Static"
248            | "Module"
249            | "Macro"
250            | "ProcMacro"
251            | "FunctionLikeProcMacro"
252            | "AttributeProcMacro"
253            | "DeriveProcMacro"
254                if matches!(edge_name.as_ref(), "importable_path" | "canonical_path") =>
255            {
256                edges::resolve_importable_edge(
257                    contexts,
258                    edge_name,
259                    self.current_crate,
260                    self.previous_crate,
261                )
262            }
263            "Item"
264            | "GenericItem"
265            | "ImplOwner"
266            | "Struct"
267            | "StructField"
268            | "Enum"
269            | "Variant"
270            | "PlainVariant"
271            | "TupleVariant"
272            | "Union"
273            | "StructVariant"
274            | "Trait"
275            | "Function"
276            | "Method"
277            | "Impl"
278            | "GlobalValue"
279            | "Constant"
280            | "Static"
281            | "AssociatedType"
282            | "AssociatedConstant"
283            | "Module"
284            | "Macro"
285            | "ProcMacro"
286            | "FunctionLikeProcMacro"
287            | "AttributeProcMacro"
288            | "DeriveProcMacro"
289                if matches!(edge_name.as_ref(), "span" | "attribute") =>
290            {
291                edges::resolve_item_edge(contexts, edge_name)
292            }
293            "ImplOwner" | "Struct" | "Enum" | "Union"
294                if matches!(edge_name.as_ref(), "impl" | "inherent_impl") =>
295            {
296                edges::resolve_impl_owner_edge(self, contexts, edge_name, resolve_info)
297            }
298            "Function" | "Method" | "FunctionLike"
299                if matches!(edge_name.as_ref(), "parameter" | "abi") =>
300            {
301                edges::resolve_function_like_edge(contexts, edge_name)
302            }
303            "GenericItem" | "ImplOwner" | "Struct" | "Enum" | "Union" | "Trait" | "Function"
304            | "Method" | "Impl"
305                if matches!(edge_name.as_ref(), "generic_parameter") =>
306            {
307                edges::resolve_generic_parameter_edge(contexts, edge_name)
308            }
309            "Method" if matches!(edge_name.as_ref(), "receiver") => {
310                edges::resolve_receiver_edge(contexts, edge_name)
311            }
312            "Module" => edges::resolve_module_edge(
313                contexts,
314                edge_name,
315                self.current_crate,
316                self.previous_crate,
317            ),
318            "Struct" => edges::resolve_struct_edge(
319                contexts,
320                edge_name,
321                self.current_crate,
322                self.previous_crate,
323            ),
324            "Variant" | "PlainVariant" | "TupleVariant" | "StructVariant" => {
325                edges::resolve_variant_edge(
326                    contexts,
327                    edge_name,
328                    self.current_crate,
329                    self.previous_crate,
330                )
331            }
332            "Enum" => edges::resolve_enum_edge(
333                contexts,
334                edge_name,
335                self.current_crate,
336                self.previous_crate,
337            ),
338            "Union" => edges::resolve_union_edge(
339                contexts,
340                edge_name,
341                self.current_crate,
342                self.previous_crate,
343            ),
344            "StructField" => edges::resolve_struct_field_edge(contexts, edge_name),
345            "Impl" => edges::resolve_impl_edge(self, contexts, edge_name, resolve_info),
346            "Trait" => edges::resolve_trait_edge(
347                contexts,
348                edge_name,
349                self.current_crate,
350                self.previous_crate,
351            ),
352            "ImplementedTrait" => edges::resolve_implemented_trait_edge(contexts, edge_name),
353            "Attribute" => edges::resolve_attribute_edge(contexts, edge_name),
354            "AttributeMetaItem" => edges::resolve_attribute_meta_item_edge(contexts, edge_name),
355            "Feature" => edges::resolve_feature_edge(
356                contexts,
357                edge_name,
358                self.current_crate,
359                self.previous_crate,
360            ),
361            "DeriveProcMacro" => edges::resolve_derive_proc_macro_edge(contexts, edge_name),
362            "GenericTypeParameter" => edges::resolve_generic_type_parameter_edge(
363                contexts,
364                edge_name,
365                self.current_crate,
366                self.previous_crate,
367            ),
368            _ => unreachable!("resolve_neighbors {type_name} {edge_name} {parameters:?}"),
369        }
370    }
371
372    fn resolve_coercion<V: AsVertex<Self::Vertex> + 'a>(
373        &self,
374        contexts: ContextIterator<'a, V>,
375        type_name: &Arc<str>,
376        coerce_to_type: &Arc<str>,
377        _resolve_info: &ResolveInfo,
378    ) -> ContextOutcomeIterator<'a, V, bool> {
379        let coerce_to_type = coerce_to_type.clone();
380        match type_name.as_ref() {
381            "Item" | "GenericItem" | "Variant" | "FunctionLike" | "Importable" | "ImplOwner"
382            | "RawType" | "GlobalValue" | "ProcMacro" => {
383                resolve_coercion_with(contexts, move |vertex| {
384                    let actual_type_name = vertex.typename();
385
386                    match coerce_to_type.as_ref() {
387                        "GenericItem" => matches!(
388                            actual_type_name,
389                            "Struct" | "Enum" | "Union" | "Trait" | "Function" | "Method"
390                        ),
391                        "Variant" => matches!(
392                            actual_type_name,
393                            "PlainVariant" | "TupleVariant" | "StructVariant"
394                        ),
395                        "ImplOwner" => matches!(actual_type_name, "Struct" | "Enum" | "Union"),
396                        "GlobalValue" => matches!(actual_type_name, "Constant" | "Static",),
397                        "ProcMacro" => matches!(
398                            actual_type_name,
399                            "FunctionLikeProcMacro" | "AttributeProcMacro" | "DeriveProcMacro"
400                        ),
401                        _ => {
402                            // The remaining types are final (don't have any subtypes)
403                            // so we can just compare the actual type name to
404                            // the type we are attempting to coerce to.
405                            actual_type_name == coerce_to_type.as_ref()
406                        }
407                    }
408                })
409            }
410            "GenericParameter" => resolve_coercion_with(contexts, move |vertex| {
411                let actual_type_name = vertex.typename();
412
413                // The possible types are final (don't have any subtypes)
414                // so we can just compare the actual type name to
415                // the type we are attempting to coerce to.
416                actual_type_name == coerce_to_type.as_ref()
417            }),
418            _ => unreachable!("resolve_coercion {type_name} {coerce_to_type}"),
419        }
420    }
421}
422
423pub(crate) fn supported_item_kind(item: &Item) -> bool {
424    matches!(
425        item.inner,
426        rustdoc_types::ItemEnum::Struct(..)
427            | rustdoc_types::ItemEnum::StructField(..)
428            | rustdoc_types::ItemEnum::Enum(..)
429            | rustdoc_types::ItemEnum::Variant(..)
430            | rustdoc_types::ItemEnum::Union(..)
431            | rustdoc_types::ItemEnum::Function(..)
432            | rustdoc_types::ItemEnum::Impl(..)
433            | rustdoc_types::ItemEnum::Trait(..)
434            | rustdoc_types::ItemEnum::Constant { .. }
435            | rustdoc_types::ItemEnum::Static(..)
436            | rustdoc_types::ItemEnum::AssocType { .. }
437            | rustdoc_types::ItemEnum::Module { .. }
438            | rustdoc_types::ItemEnum::Macro { .. }
439            | rustdoc_types::ItemEnum::ProcMacro { .. }
440    )
441}