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