shape_runtime/metadata/
unified.rs1use super::types::{FunctionInfo, PropertyInfo};
6use crate::builtin_metadata::builtin_functions_from_macros;
7use crate::stdlib_metadata::{StdlibMetadata, default_stdlib_path};
8
9#[derive(Debug)]
14pub struct UnifiedMetadata {
15 rust_builtins: Vec<FunctionInfo>,
17 stdlib_functions: Vec<FunctionInfo>,
19 stdlib_patterns: Vec<crate::stdlib_metadata::PatternInfo>,
21 type_metadata: Vec<TypeMetadataInfo>,
23}
24
25#[derive(Debug, Clone)]
27pub struct TypeMetadataInfo {
28 pub name: String,
29 pub description: String,
30 pub properties: Vec<PropertyInfo>,
31}
32
33impl UnifiedMetadata {
34 pub fn load() -> Self {
36 use crate::builtin_metadata::collect_type_metadata;
37
38 let mut rust_builtins = builtin_functions_from_macros();
40
41 let stdlib_path = default_stdlib_path();
43 let stdlib = StdlibMetadata::load(&stdlib_path).unwrap_or_else(|e| {
44 eprintln!("Warning: Failed to load stdlib metadata: {}", e);
45 StdlibMetadata::empty()
46 });
47
48 if !stdlib.intrinsic_functions.is_empty() {
51 let intrinsic_by_name: std::collections::HashMap<_, _> = stdlib
52 .intrinsic_functions
53 .iter()
54 .map(|f| (f.name.clone(), f))
55 .collect();
56 for builtin in &mut rust_builtins {
57 if let Some(intrinsic) = intrinsic_by_name.get(&builtin.name) {
58 builtin.signature = intrinsic.signature.clone();
59 builtin.description = intrinsic.description.clone();
60 builtin.parameters = intrinsic.parameters.clone();
61 builtin.return_type = intrinsic.return_type.clone();
62 builtin.comptime_only = intrinsic.comptime_only;
63 }
64 }
65 for intrinsic in &stdlib.intrinsic_functions {
66 if !rust_builtins.iter().any(|f| f.name == intrinsic.name) {
67 rust_builtins.push(intrinsic.clone());
68 }
69 }
70 }
71
72 let type_metadata = collect_type_metadata()
74 .into_iter()
75 .map(|tm| TypeMetadataInfo {
76 name: tm.name.to_string(),
77 description: tm.description.to_string(),
78 properties: tm.to_property_infos(),
79 })
80 .collect();
81
82 Self {
83 rust_builtins,
84 stdlib_functions: stdlib.functions,
85 stdlib_patterns: stdlib.patterns,
86 type_metadata,
87 }
88 }
89
90 pub fn all_functions(&self) -> Vec<&FunctionInfo> {
92 let mut seen = std::collections::HashSet::new();
93 let mut result = Vec::new();
94
95 for func in &self.rust_builtins {
97 if seen.insert(&func.name) {
98 result.push(func);
99 }
100 }
101 for func in &self.stdlib_functions {
102 if seen.insert(&func.name) {
103 result.push(func);
104 }
105 }
106
107 result
108 }
109
110 pub fn get_function(&self, name: &str) -> Option<&FunctionInfo> {
112 if let Some(func) = self.rust_builtins.iter().find(|f| f.name == name) {
114 return Some(func);
115 }
116 self.stdlib_functions.iter().find(|f| f.name == name)
118 }
119
120 pub fn all_patterns(&self) -> &[crate::stdlib_metadata::PatternInfo] {
122 &self.stdlib_patterns
123 }
124
125 pub fn stdlib_functions(&self) -> &[FunctionInfo] {
127 &self.stdlib_functions
128 }
129
130 pub fn rust_builtins(&self) -> &[FunctionInfo] {
132 &self.rust_builtins
133 }
134
135 pub fn get_type_properties(&self, type_name: &str) -> Option<&[PropertyInfo]> {
137 self.type_metadata
138 .iter()
139 .find(|t| t.name.eq_ignore_ascii_case(type_name))
140 .map(|t| t.properties.as_slice())
141 }
142
143 pub fn all_types(&self) -> &[TypeMetadataInfo] {
145 &self.type_metadata
146 }
147}
148
149#[cfg(test)]
150mod unified_metadata_tests {
151 use super::*;
152
153 #[test]
154 fn test_unified_metadata_load() {
155 let metadata = UnifiedMetadata::load();
156
157 let all_funcs: Vec<_> = metadata.all_functions().into_iter().collect();
159 assert!(!all_funcs.is_empty(), "Should have functions");
160
161 assert!(
163 metadata.get_function("abs").is_some(),
164 "abs should be present"
165 );
166 assert!(
167 metadata.get_function("sqrt").is_some(),
168 "sqrt should be present"
169 );
170
171 assert!(
173 metadata.get_function("snapshot").is_some(),
174 "snapshot should be present from stdlib"
175 );
176 }
177
178 #[test]
179 fn test_metadata_coverage() {
180 let metadata = UnifiedMetadata::load();
181
182 println!("\n=== Metadata Coverage ===");
183 println!(
184 "Rust builtins (proc-macro): {}",
185 metadata.rust_builtins().len()
186 );
187 for f in metadata.rust_builtins() {
188 println!(" - {}", f.name);
189 }
190
191 println!("\nStdlib functions: {}", metadata.stdlib_functions().len());
192 assert!(
193 !metadata.stdlib_functions().is_empty(),
194 "stdlib function metadata should not be empty"
195 );
196
197 let all_funcs = metadata.all_functions();
198 println!("\nTotal functions available: {}", all_funcs.len());
199
200 assert!(
202 metadata.get_function("abs").is_some(),
203 "abs should be present from builtins"
204 );
205 assert!(
206 metadata.get_function("sqrt").is_some(),
207 "sqrt should be present from builtins"
208 );
209 assert!(
210 metadata.get_function("snapshot").is_some(),
211 "snapshot should be present from stdlib"
212 );
213 }
214
215 #[test]
216 fn test_intrinsic_std_core_overrides_builtin_docs() {
217 let metadata = UnifiedMetadata::load();
218 let abs = metadata
219 .get_function("abs")
220 .expect("abs should be available in unified metadata");
221 assert_eq!(abs.signature, "abs(value: number) -> number");
222 assert!(
223 abs.description.contains("absolute value"),
224 "abs docs should be sourced from std::core intrinsic declarations"
225 );
226 }
227
228 #[test]
229 fn test_intrinsic_std_core_uses_table_signatures() {
230 let metadata = UnifiedMetadata::load();
231 let resample = metadata
232 .get_function("resample")
233 .expect("resample should be available in unified metadata");
234 assert!(
235 resample.signature.contains("Table<"),
236 "resample signature should use Table<T>, got: {}",
237 resample.signature
238 );
239 }
240}