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                "FunctionAbi" => properties::resolve_function_abi_property(contexts, property_name),
191                "Impl" => properties::resolve_impl_property(contexts, property_name),
192                "Attribute" => properties::resolve_attribute_property(contexts, property_name),
193                "AttributeMetaItem" => {
194                    properties::resolve_attribute_meta_item_property(contexts, property_name)
195                }
196                "Trait" => properties::resolve_trait_property(contexts, property_name, self),
197                "ImplementedTrait" => {
198                    properties::resolve_implemented_trait_property(contexts, property_name, self)
199                }
200                "Static" => properties::resolve_static_property(contexts, property_name),
201                "RawType" | "ResolvedPathType" if matches!(property_name.as_ref(), "name") => {
202                    // fields from "RawType"
203                    properties::resolve_raw_type_property(contexts, property_name)
204                }
205                "AssociatedType" => {
206                    properties::resolve_associated_type_property(contexts, property_name)
207                }
208                "AssociatedConstant" => {
209                    properties::resolve_associated_constant_property(contexts, property_name)
210                }
211                "Constant" => properties::resolve_constant_property(contexts, property_name),
212                "Discriminant" => {
213                    properties::resolve_discriminant_property(contexts, property_name)
214                }
215                "Feature" => properties::resolve_feature_property(contexts, property_name),
216                "DeriveMacroHelperAttribute" => {
217                    properties::resolve_derive_macro_helper_attribute_property(
218                        contexts,
219                        property_name,
220                    )
221                }
222                "GenericParameter"
223                | "GenericTypeParameter"
224                | "GenericLifetimeParameter"
225                | "GenericConstParameter"
226                    if matches!(property_name.as_ref(), "name" | "position") =>
227                {
228                    properties::resolve_generic_parameter_property(contexts, property_name)
229                }
230                "GenericTypeParameter" => properties::resolve_generic_type_parameter_property(
231                    contexts,
232                    property_name,
233                    self,
234                ),
235                "GenericConstParameter" => {
236                    properties::resolve_generic_const_parameter_property(contexts, property_name)
237                }
238                "Receiver" => properties::resolve_receiver_property(contexts, property_name),
239                "RequiredTargetFeature" => {
240                    properties::resolve_required_target_feature_property(contexts, property_name)
241                }
242                _ => unreachable!("resolve_property {type_name} {property_name}"),
243            }
244        }
245    }
246
247    fn resolve_neighbors<V: AsVertex<Self::Vertex> + 'a>(
248        &self,
249        contexts: ContextIterator<'a, V>,
250        type_name: &Arc<str>,
251        edge_name: &Arc<str>,
252        parameters: &EdgeParameters,
253        resolve_info: &ResolveEdgeInfo,
254    ) -> ContextOutcomeIterator<'a, V, VertexIterator<'a, Self::Vertex>> {
255        match type_name.as_ref() {
256            "CrateDiff" => edges::resolve_crate_diff_edge(contexts, edge_name),
257            "Crate" => edges::resolve_crate_edge(self, contexts, edge_name, resolve_info),
258            "Importable"
259            | "ImplOwner"
260            | "Struct"
261            | "Enum"
262            | "Union"
263            | "Trait"
264            | "Function"
265            | "GlobalValue"
266            | "Constant"
267            | "Static"
268            | "Module"
269            | "Macro"
270            | "ProcMacro"
271            | "FunctionLikeProcMacro"
272            | "AttributeProcMacro"
273            | "DeriveProcMacro"
274                if matches!(edge_name.as_ref(), "importable_path" | "canonical_path") =>
275            {
276                edges::resolve_importable_edge(contexts, edge_name, self)
277            }
278            "Item"
279            | "GenericItem"
280            | "ImplOwner"
281            | "Struct"
282            | "StructField"
283            | "Enum"
284            | "Variant"
285            | "PlainVariant"
286            | "TupleVariant"
287            | "Union"
288            | "StructVariant"
289            | "Trait"
290            | "ExportableFunction"
291            | "Function"
292            | "Method"
293            | "Impl"
294            | "GlobalValue"
295            | "Constant"
296            | "Static"
297            | "AssociatedType"
298            | "AssociatedConstant"
299            | "Module"
300            | "Macro"
301            | "ProcMacro"
302            | "FunctionLikeProcMacro"
303            | "AttributeProcMacro"
304            | "DeriveProcMacro"
305                if matches!(edge_name.as_ref(), "span" | "attribute") =>
306            {
307                edges::resolve_item_edge(contexts, edge_name)
308            }
309            "ImplOwner" | "Struct" | "Enum" | "Union"
310                if matches!(edge_name.as_ref(), "impl" | "inherent_impl") =>
311            {
312                edges::resolve_impl_owner_edge(self, contexts, edge_name, resolve_info)
313            }
314            "Function" | "Method" | "FunctionLike" | "ExportableFunction"
315                if matches!(edge_name.as_ref(), "parameter" | "abi") =>
316            {
317                edges::resolve_function_like_edge(contexts, edge_name)
318            }
319            "GenericItem" | "ImplOwner" | "Struct" | "Enum" | "Union" | "Trait" | "Function"
320            | "Method" | "Impl"
321                if matches!(edge_name.as_ref(), "generic_parameter") =>
322            {
323                edges::resolve_generic_parameter_edge(contexts, edge_name)
324            }
325            "Method" if matches!(edge_name.as_ref(), "receiver") => {
326                edges::resolve_receiver_edge(contexts, edge_name)
327            }
328            "Function" | "Method" if matches!(edge_name.as_ref(), "requires_feature") => {
329                edges::resolve_requires_target_feature_edge(contexts, self)
330            }
331            "Module" => edges::resolve_module_edge(contexts, edge_name, self),
332            "Struct" => edges::resolve_struct_edge(contexts, edge_name, self),
333            "Variant" | "PlainVariant" | "TupleVariant" | "StructVariant" => {
334                edges::resolve_variant_edge(contexts, edge_name, self)
335            }
336            "Enum" => edges::resolve_enum_edge(contexts, edge_name, self, resolve_info),
337            "Union" => edges::resolve_union_edge(contexts, edge_name, self),
338            "StructField" => edges::resolve_struct_field_edge(contexts, edge_name),
339            "Impl" => edges::resolve_impl_edge(self, contexts, edge_name, resolve_info),
340            "Trait" => edges::resolve_trait_edge(contexts, edge_name, self),
341            "ImplementedTrait" => edges::resolve_implemented_trait_edge(contexts, edge_name),
342            "Attribute" => edges::resolve_attribute_edge(contexts, edge_name),
343            "AttributeMetaItem" => edges::resolve_attribute_meta_item_edge(contexts, edge_name),
344            "Feature" => edges::resolve_feature_edge(contexts, edge_name, self),
345            "DeriveProcMacro" => edges::resolve_derive_proc_macro_edge(contexts, edge_name),
346            "GenericTypeParameter" => {
347                edges::resolve_generic_type_parameter_edge(contexts, edge_name, self)
348            }
349            _ => unreachable!("resolve_neighbors {type_name} {edge_name} {parameters:?}"),
350        }
351    }
352
353    fn resolve_coercion<V: AsVertex<Self::Vertex> + 'a>(
354        &self,
355        contexts: ContextIterator<'a, V>,
356        type_name: &Arc<str>,
357        coerce_to_type: &Arc<str>,
358        _resolve_info: &ResolveInfo,
359    ) -> ContextOutcomeIterator<'a, V, bool> {
360        let coerce_to_type = coerce_to_type.clone();
361        match type_name.as_ref() {
362            "Item" | "GenericItem" | "Variant" | "FunctionLike" | "ExportableFunction"
363            | "Importable" | "ImplOwner" | "RawType" | "GlobalValue" | "ProcMacro" => {
364                resolve_coercion_with(contexts, move |vertex| {
365                    let actual_type_name = vertex.typename();
366
367                    match coerce_to_type.as_ref() {
368                        "GenericItem" => matches!(
369                            actual_type_name,
370                            "Struct" | "Enum" | "Union" | "Trait" | "Function" | "Method"
371                        ),
372                        "Variant" => matches!(
373                            actual_type_name,
374                            "PlainVariant" | "TupleVariant" | "StructVariant"
375                        ),
376                        "ImplOwner" => matches!(actual_type_name, "Struct" | "Enum" | "Union"),
377                        "GlobalValue" => matches!(actual_type_name, "Constant" | "Static",),
378                        "ProcMacro" => matches!(
379                            actual_type_name,
380                            "FunctionLikeProcMacro" | "AttributeProcMacro" | "DeriveProcMacro"
381                        ),
382                        "ExportableFunction" => matches!(actual_type_name, "Function" | "Method"),
383                        _ => {
384                            // The remaining types are final (don't have any subtypes)
385                            // so we can just compare the actual type name to
386                            // the type we are attempting to coerce to.
387                            actual_type_name == coerce_to_type.as_ref()
388                        }
389                    }
390                })
391            }
392            "GenericParameter" => resolve_coercion_with(contexts, move |vertex| {
393                let actual_type_name = vertex.typename();
394
395                // The possible types are final (don't have any subtypes)
396                // so we can just compare the actual type name to
397                // the type we are attempting to coerce to.
398                actual_type_name == coerce_to_type.as_ref()
399            }),
400            _ => unreachable!("resolve_coercion {type_name} {coerce_to_type}"),
401        }
402    }
403}
404
405pub(crate) fn supported_item_kind(item: &Item) -> bool {
406    matches!(
407        item.inner,
408        rustdoc_types::ItemEnum::Struct(..)
409            | rustdoc_types::ItemEnum::StructField(..)
410            | rustdoc_types::ItemEnum::Enum(..)
411            | rustdoc_types::ItemEnum::Variant(..)
412            | rustdoc_types::ItemEnum::Union(..)
413            | rustdoc_types::ItemEnum::Function(..)
414            | rustdoc_types::ItemEnum::Impl(..)
415            | rustdoc_types::ItemEnum::Trait(..)
416            | rustdoc_types::ItemEnum::Constant { .. }
417            | rustdoc_types::ItemEnum::Static(..)
418            | rustdoc_types::ItemEnum::AssocType { .. }
419            | rustdoc_types::ItemEnum::Module { .. }
420            | rustdoc_types::ItemEnum::Macro { .. }
421            | rustdoc_types::ItemEnum::ProcMacro { .. }
422    )
423}