pyo3_stub_gen/docgen/
default_parser.rs

1//! Parser for default parameter values with type reference extraction
2
3use crate::docgen::ir::{DocDefaultExpression, DocDefaultValue, DocTypeRef};
4use crate::docgen::link::LinkResolver;
5use crate::docgen::util::prefix_stripper;
6use crate::TypeInfo;
7
8/// Parser for default values that identifies and links type references
9pub struct DefaultValueParser<'a> {
10    link_resolver: &'a LinkResolver<'a>,
11    current_module: &'a str,
12}
13
14impl<'a> DefaultValueParser<'a> {
15    pub fn new(link_resolver: &'a LinkResolver<'a>, current_module: &'a str) -> Self {
16        Self {
17            link_resolver,
18            current_module,
19        }
20    }
21
22    /// Parse a default value string and identify type references
23    pub fn parse(&self, default_str: &str, type_info: &TypeInfo) -> DocDefaultValue {
24        // Strip module prefixes from display text
25        let display = self.strip_module_prefixes(default_str);
26
27        // Try to identify attribute references like "C.C1"
28        if let Some(type_ref) = self.try_parse_attribute_ref(&display, type_info) {
29            return DocDefaultValue::Expression(DocDefaultExpression {
30                display: display.clone(),
31                type_refs: vec![type_ref],
32            });
33        }
34
35        // For now, treat everything else as simple values
36        DocDefaultValue::Simple {
37            value: display.clone(),
38        }
39    }
40
41    /// Strip module prefixes like "_core.", "typing.", "builtins." from the display text
42    fn strip_module_prefixes(&self, text: &str) -> String {
43        prefix_stripper::strip_default_value_prefixes(text)
44    }
45
46    /// Try to parse an attribute reference like "C.C1"
47    fn try_parse_attribute_ref(&self, text: &str, _type_info: &TypeInfo) -> Option<DocTypeRef> {
48        // Look for pattern "ClassName.AttributeName"
49        let parts: Vec<&str> = text.split('.').collect();
50        if parts.len() != 2 {
51            return None;
52        }
53
54        let class_name = parts[0];
55        let attribute_name = parts[1];
56
57        // Search for the class in the export map
58        // The class might be exported from any module, so we need to search for FQNs ending with ".ClassName"
59        let class_suffix = format!(".{}", class_name);
60        let class_fqn = self
61            .link_resolver
62            .export_map()
63            .keys()
64            .find(|fqn| fqn.ends_with(&class_suffix) || *fqn == class_name)?;
65
66        // Try to resolve the attribute link
67        let link_target = self.link_resolver.resolve_attribute_link(
68            class_fqn,
69            attribute_name,
70            self.current_module,
71        )?;
72
73        Some(DocTypeRef {
74            text: text.to_string(),
75            offset: 0,
76            link_target: Some(link_target),
77        })
78    }
79}