Skip to main content

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