sklears_core/api_reference_generator.rs
1//! Rich API-reference types for the trait graph visualization subsystem.
2//!
3//! [`crate::api_data_structures`] defines a deliberately simple `TraitInfo`
4//! shape (`description`/`path`/`methods: Vec<MethodInfo>` as plain strings)
5//! that backs the modularized API reference generator
6//! (`api_analyzers`/`api_formatters`/`api_generator_config`/...).
7//!
8//! The trait graph visualization system
9//! (`crate::trait_explorer::graph_visualization`) needs a richer shape that
10//! carries structured documentation, visibility, source location, and
11//! feature-flag metadata for each trait, associated type, and method so that
12//! it can render meaningful graphs (node coloring by stability, tooltips with
13//! signatures, filtering by feature flag, etc.). This module provides that
14//! richer shape.
15//!
16//! The two `TraitInfo` shapes are intentionally independent types: this
17//! module does not modify or replace [`crate::api_data_structures::TraitInfo`],
18//! which remains in use by the already-enabled modularized API reference
19//! system.
20
21use crate::api_data_structures::ParameterInfo;
22
23/// Visibility levels for API items.
24///
25/// Re-exported from [`crate::api_data_structures`] so callers that only deal
26/// with the graph-visualization API surface can refer to
27/// `crate::api_reference_generator::Visibility` without an additional
28/// dependency on [`crate::api_data_structures`].
29pub use crate::api_data_structures::Visibility;
30
31/// Rich description of a Rust trait definition.
32///
33/// This is the shape consumed by
34/// [`crate::trait_explorer::graph_visualization::graph_generator::TraitGraphGenerator`]
35/// when building a [`crate::trait_explorer::graph_visualization::TraitGraph`]
36/// from source-level trait information.
37#[derive(Debug, Clone)]
38pub struct TraitInfo {
39 /// Name of the trait (e.g. `"Estimator"`).
40 pub name: String,
41 /// Rustdoc documentation attached to the trait, if any.
42 pub docs: Option<String>,
43 /// Module path the trait is declared in (e.g. `"sklears_core::traits"`).
44 pub module_path: Option<String>,
45 /// Visibility of the trait declaration.
46 pub visibility: Visibility,
47 /// Generic parameters declared on the trait, e.g. `["T: Clone + Send"]`.
48 pub generics: Vec<String>,
49 /// Names of the supertraits this trait requires.
50 pub supertraits: Vec<String>,
51 /// Associated types declared by the trait.
52 pub associated_types: Vec<AssociatedType>,
53 /// Methods declared by the trait (required and provided).
54 pub methods: Vec<MethodInfo>,
55 /// Source file the trait is declared in, if known.
56 pub source_file: Option<String>,
57 /// Line number of the trait declaration within `source_file`, if known.
58 pub source_line: Option<u32>,
59 /// Cargo feature flags that must be enabled for this trait to be
60 /// available (e.g. `["experimental"]`).
61 pub feature_flags: Vec<String>,
62}
63
64/// An associated type declared by a trait.
65#[derive(Debug, Clone)]
66pub struct AssociatedType {
67 /// Name of the associated type (e.g. `"Output"`).
68 pub name: String,
69 /// Trait bounds placed on the associated type (e.g. `["Clone", "Send"]`).
70 pub bounds: Vec<String>,
71 /// Default type, if the associated type declares one.
72 pub default: Option<String>,
73}
74
75/// A method declared by a trait.
76#[derive(Debug, Clone)]
77pub struct MethodInfo {
78 /// Name of the method.
79 pub name: String,
80 /// Full method signature as written in source, e.g.
81 /// `"fn fit(&mut self, x: &Array2<f64>) -> Result<()>"`.
82 pub signature: String,
83 /// Rustdoc documentation attached to the method, if any.
84 pub docs: Option<String>,
85 /// Whether the method has no default implementation (must be implemented
86 /// by every conforming type).
87 pub is_required: bool,
88 /// Whether the method is declared `async`.
89 pub is_async: bool,
90 /// Whether the method is declared `unsafe`.
91 pub is_unsafe: bool,
92 /// Generic parameters declared on the method itself (independent of the
93 /// trait's own generics).
94 pub generics: Vec<String>,
95 /// Return type of the method, if it returns something other than `()`.
96 pub return_type: Option<String>,
97 /// Parameters accepted by the method (excluding `self`).
98 pub arguments: Vec<ParameterInfo>,
99}
100
101#[allow(non_snake_case)]
102#[cfg(test)]
103mod tests {
104 use super::*;
105
106 fn sample_trait_info() -> TraitInfo {
107 TraitInfo {
108 name: "Estimator".to_string(),
109 docs: Some("Base trait for ML estimators.".to_string()),
110 module_path: Some("sklears_core::traits".to_string()),
111 visibility: Visibility::Public,
112 generics: vec!["T: Clone".to_string()],
113 supertraits: vec!["Send".to_string(), "Sync".to_string()],
114 associated_types: vec![AssociatedType {
115 name: "Config".to_string(),
116 bounds: vec!["Default".to_string()],
117 default: None,
118 }],
119 methods: vec![MethodInfo {
120 name: "fit".to_string(),
121 signature: "fn fit(&mut self, x: &Array2<f64>) -> Result<()>".to_string(),
122 docs: Some("Fit the estimator to training data.".to_string()),
123 is_required: true,
124 is_async: false,
125 is_unsafe: false,
126 generics: Vec::new(),
127 return_type: Some("Result<()>".to_string()),
128 arguments: vec![ParameterInfo {
129 name: "x".to_string(),
130 param_type: "&Array2<f64>".to_string(),
131 description: "training features".to_string(),
132 optional: false,
133 }],
134 }],
135 source_file: Some("src/traits.rs".to_string()),
136 source_line: Some(10),
137 feature_flags: Vec::new(),
138 }
139 }
140
141 #[test]
142 fn test_trait_info_construction() {
143 let trait_info = sample_trait_info();
144 assert_eq!(trait_info.name, "Estimator");
145 assert_eq!(trait_info.supertraits.len(), 2);
146 assert_eq!(trait_info.associated_types.len(), 1);
147 assert_eq!(trait_info.methods.len(), 1);
148 assert!(matches!(trait_info.visibility, Visibility::Public));
149 }
150
151 #[test]
152 fn test_associated_type_construction() {
153 let assoc = AssociatedType {
154 name: "Item".to_string(),
155 bounds: vec!["Clone".to_string(), "Debug".to_string()],
156 default: Some("()".to_string()),
157 };
158 assert_eq!(assoc.name, "Item");
159 assert_eq!(assoc.bounds.len(), 2);
160 assert_eq!(assoc.default.as_deref(), Some("()"));
161 }
162
163 #[test]
164 fn test_method_info_construction() {
165 let trait_info = sample_trait_info();
166 let method = &trait_info.methods[0];
167 assert_eq!(method.name, "fit");
168 assert!(method.is_required);
169 assert!(!method.is_async);
170 assert!(!method.is_unsafe);
171 assert_eq!(method.arguments.len(), 1);
172 assert_eq!(method.arguments[0].name, "x");
173 }
174
175 #[test]
176 fn test_trait_info_clone() {
177 let trait_info = sample_trait_info();
178 let cloned = trait_info.clone();
179 assert_eq!(trait_info.name, cloned.name);
180 assert_eq!(trait_info.methods.len(), cloned.methods.len());
181 }
182
183 #[test]
184 fn test_visibility_reexport_matches_api_data_structures() {
185 // The re-exported `Visibility` must be the exact same type as
186 // `crate::api_data_structures::Visibility`, not a copy.
187 let v: Visibility = crate::api_data_structures::Visibility::Private;
188 assert!(matches!(v, Visibility::Private));
189 }
190}