trustfall_rustdoc_adapter/adapter/
mod.rs

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