1use crate::types::{Event, Function};
11use dashmap::DashMap;
12use serde::{Deserialize, Serialize};
13use std::collections::HashMap;
14use std::sync::Arc;
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct EventField {
22 pub name: String,
23 #[serde(default)]
24 pub description: String,
25}
26
27#[derive(Debug, Clone)]
29pub struct MetadataSource {
30 pub extension: String,
31 pub functions_url: Option<String>,
32 pub enums_url: Option<String>,
33 pub events_url: Option<String>,
34}
35
36impl MetadataSource {
37 pub fn new(extension: impl Into<String>) -> Self {
39 Self {
40 extension: extension.into(),
41 functions_url: None,
42 enums_url: None,
43 events_url: None,
44 }
45 }
46
47 pub fn with_functions(mut self, url: impl Into<String>) -> Self {
49 self.functions_url = Some(url.into());
50 self
51 }
52
53 pub fn with_enums(mut self, url: impl Into<String>) -> Self {
55 self.enums_url = Some(url.into());
56 self
57 }
58
59 pub fn with_events(mut self, url: impl Into<String>) -> Self {
61 self.events_url = Some(url.into());
62 self
63 }
64}
65
66#[derive(Debug, Clone)]
71pub enum MetadataError {
72 NetworkError(String),
73 ParseError(String),
74 NotFound(String),
75 InvalidData(String),
76 CacheError(String),
77}
78
79impl std::fmt::Display for MetadataError {
80 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
81 match self {
82 Self::NetworkError(e) => write!(f, "Network error: {}", e),
83 Self::ParseError(e) => write!(f, "Parse error: {}", e),
84 Self::NotFound(e) => write!(f, "Not found: {}", e),
85 Self::InvalidData(e) => write!(f, "Invalid data: {}", e),
86 Self::CacheError(e) => write!(f, "Cache error: {}", e),
87 }
88 }
89}
90
91impl std::error::Error for MetadataError {}
92
93pub type Result<T> = std::result::Result<T, MetadataError>;
94
95#[derive(Default)]
100struct TrieNode {
101 children: HashMap<char, Box<TrieNode>>,
102 value: Option<Arc<Function>>,
103}
104
105#[derive(Default)]
107pub struct FunctionTrie {
108 root: TrieNode,
109 count: usize,
110}
111
112impl FunctionTrie {
113 #[inline]
115 pub fn new() -> Self {
116 Self::default()
117 }
118
119 pub fn insert(&mut self, key: &str, func: Arc<Function>) {
121 let mut node = &mut self.root;
122
123 for ch in key.to_lowercase().chars() {
125 node = node
126 .children
127 .entry(ch)
128 .or_insert_with(|| Box::new(TrieNode::default()));
129 }
130
131 if node.value.is_none() {
132 self.count += 1;
133 }
134 node.value = Some(func);
135 }
136
137 pub fn get_exact(&self, key: &str) -> Option<Arc<Function>> {
139 let mut node = &self.root;
140
141 for ch in key.to_lowercase().chars() {
142 match node.children.get(&ch) {
143 Some(next) => node = next,
144 None => return None,
145 }
146 }
147
148 node.value.clone()
149 }
150
151 pub fn get_prefix(&self, text: &str) -> Option<(String, Arc<Function>)> {
153 let chars: Vec<char> = text.to_lowercase().chars().collect();
154 let mut best_match: Option<(String, Arc<Function>)> = None;
155
156 for start in 0..chars.len() {
157 let mut node = &self.root;
158 let mut matched = String::with_capacity(chars.len() - start);
159
160 for &ch in &chars[start..] {
161 match node.children.get(&ch) {
162 Some(next) => {
163 matched.push(ch);
164 node = next;
165
166 if let Some(func) = &node.value {
167 best_match = Some((matched.clone(), func.clone()));
168 }
169 }
170 None => break,
171 }
172 }
173 }
174
175 best_match
176 }
177
178 pub fn get_completions(&self, prefix: &str) -> Vec<Arc<Function>> {
180 let mut node = &self.root;
181
182 for ch in prefix.to_lowercase().chars() {
184 match node.children.get(&ch) {
185 Some(next) => node = next,
186 None => return Vec::new(),
187 }
188 }
189
190 let mut results = Vec::new();
192 self.collect_all(node, &mut results);
193 results
194 }
195
196 fn collect_all(&self, node: &TrieNode, results: &mut Vec<Arc<Function>>) {
197 if let Some(func) = &node.value {
198 results.push(func.clone());
199 }
200
201 for child in node.children.values() {
202 self.collect_all(child, results);
203 }
204 }
205
206 pub fn all_functions(&self) -> Vec<Arc<Function>> {
208 let mut results = Vec::with_capacity(self.count);
209 self.collect_all(&self.root, &mut results);
210 results
211 }
212
213 #[inline]
215 pub fn len(&self) -> usize {
216 self.count
217 }
218
219 #[inline]
221 pub fn is_empty(&self) -> bool {
222 self.count == 0
223 }
224
225 pub fn clear(&mut self) {
227 self.root = TrieNode::default();
228 self.count = 0;
229 }
230}
231
232pub struct Fetcher {
238 client: reqwest::Client,
239}
240
241impl Fetcher {
242 pub fn new() -> Self {
244 #[cfg(not(target_arch = "wasm32"))]
245 let client = reqwest::Client::builder()
246 .timeout(std::time::Duration::from_secs(30))
247 .build()
248 .unwrap_or_else(|_| reqwest::Client::new());
249
250 #[cfg(target_arch = "wasm32")]
251 let client = reqwest::Client::builder()
252 .build()
253 .unwrap_or_else(|_| reqwest::Client::new());
254
255 Self { client }
256 }
257
258 pub async fn fetch_json<T: serde::de::DeserializeOwned>(&self, url: &str) -> Result<T> {
260 let response =
262 self.client.get(url).send().await.map_err(|e| {
263 MetadataError::NetworkError(format!("Failed to fetch {}: {}", url, e))
264 })?;
265
266 let status = response.status();
268 if status == reqwest::StatusCode::NOT_FOUND {
269 return Err(MetadataError::NotFound(format!("URL not found: {}", url)));
270 }
271
272 if !status.is_success() {
273 return Err(MetadataError::NetworkError(format!(
274 "HTTP {}: {}",
275 status, url
276 )));
277 }
278
279 let text = response.text().await.map_err(|e| {
281 MetadataError::NetworkError(format!("Failed to read response from {}: {}", url, e))
282 })?;
283
284 serde_json::from_str(&text).map_err(|e| {
285 let preview: String = text.chars().take(200).collect();
287 MetadataError::ParseError(format!(
288 "Failed to parse JSON from {}: {}\nJSON preview: {}…",
289 url, e, preview
290 ))
291 })
292 }
293
294 pub async fn fetch_functions(&self, url: &str, extension: String) -> Result<Vec<Function>> {
297 let response =
298 self.client.get(url).send().await.map_err(|e| {
299 MetadataError::NetworkError(format!("Failed to fetch {}: {}", url, e))
300 })?;
301
302 let status = response.status();
303 if status == reqwest::StatusCode::NOT_FOUND {
304 return Err(MetadataError::NotFound(format!("URL not found: {}", url)));
305 }
306 if !status.is_success() {
307 return Err(MetadataError::NetworkError(format!(
308 "HTTP {}: {}",
309 status, url
310 )));
311 }
312
313 let text = response.text().await.map_err(|e| {
314 MetadataError::NetworkError(format!("Failed to read response from {}: {}", url, e))
315 })?;
316
317 let raw_items: Vec<serde_json::Value> = serde_json::from_str(&text).map_err(|e| {
319 let preview: String = text.chars().take(200).collect();
320 MetadataError::ParseError(format!(
321 "Failed to parse JSON array from {}: {}\nJSON preview: {}…",
322 url, e, preview
323 ))
324 })?;
325
326 let mut functions = Vec::with_capacity(raw_items.len());
327 for (i, raw) in raw_items.into_iter().enumerate() {
328 match serde_json::from_value::<Function>(raw) {
329 Ok(mut func) => {
330 func.extension = Some(extension.clone());
331 func.source_url = Some(url.to_string());
332 functions.push(func);
333 }
334 Err(e) => {
335 eprintln!("[forge-kit] Skipping function #{} from {}: {}", i, url, e);
337 }
338 }
339 }
340
341 Ok(functions)
342 }
343
344 pub async fn fetch_enums(&self, url: &str) -> Result<HashMap<String, Vec<String>>> {
346 self.fetch_json(url).await
347 }
348
349 pub async fn fetch_events(&self, url: &str) -> Result<Vec<Event>> {
351 self.fetch_json(url).await
352 }
353}
354
355impl Default for Fetcher {
356 fn default() -> Self {
357 Self::new()
358 }
359}
360
361pub struct MetadataManager {
367 trie: std::sync::RwLock<FunctionTrie>,
368 enums: DashMap<String, Vec<String>>,
369 events: DashMap<String, Event>,
370 sources: std::sync::RwLock<Vec<MetadataSource>>,
371 fetcher: Fetcher,
372}
373
374impl MetadataManager {
375 pub fn new() -> Self {
377 Self {
378 trie: std::sync::RwLock::new(FunctionTrie::new()),
379 enums: DashMap::new(),
380 events: DashMap::new(),
381 sources: std::sync::RwLock::new(Vec::new()),
382 fetcher: Fetcher::new(),
383 }
384 }
385
386 pub fn add_source(&self, source: MetadataSource) {
388 self.sources.write().unwrap().push(source);
389 }
390
391 pub async fn fetch_all(&self) -> Result<FetchStats> {
393 let sources = self.sources.read().unwrap().clone();
394
395 let mut total_functions = 0;
396 let mut total_enums = 0;
397 let mut total_events = 0;
398 let mut errors = Vec::new();
399
400 for source in sources {
401 if let Some(url) = &source.functions_url {
403 match self
404 .fetcher
405 .fetch_functions(url, source.extension.clone())
406 .await
407 {
408 Ok(functions) => {
409 total_functions += functions.len();
410 self.add_functions(functions);
411 }
412 Err(MetadataError::NotFound(_)) => {
413 }
415 Err(e) => {
416 errors.push(format!("Functions from {}: {}", source.extension, e));
417 }
418 }
419 }
420
421 if let Some(url) = &source.enums_url {
423 match self.fetcher.fetch_enums(url).await {
424 Ok(enums) => {
425 total_enums += enums.len();
426 for (name, values) in enums {
427 self.enums.insert(name, values);
428 }
429 }
430 Err(e) => {
431 if !matches!(e, MetadataError::NotFound(_)) {
432 errors.push(format!("Enums from {}: {}", source.extension, e));
433 }
434 }
435 }
436 }
437
438 if let Some(url) = &source.events_url {
440 match self.fetcher.fetch_events(url).await {
441 Ok(events) => {
442 total_events += events.len();
443 for event in events {
444 self.events.insert(event.name.clone(), event);
445 }
446 }
447 Err(e) => {
448 if !matches!(e, MetadataError::NotFound(_)) {
449 errors.push(format!("Events from {}: {}", source.extension, e));
450 }
451 }
452 }
453 }
454 }
455
456 Ok(FetchStats {
457 functions: total_functions,
458 enums: total_enums,
459 events: total_events,
460 errors,
461 })
462 }
463
464 fn add_functions(&self, functions: Vec<Function>) {
466 let mut trie = self.trie.write().unwrap();
467
468 for func in functions {
469 let arc_func = Arc::new(func.clone());
470
471 trie.insert(&func.name, arc_func.clone());
473
474 if let Some(aliases) = &func.aliases {
476 for alias in aliases {
477 let alias_name = if alias.starts_with('$') {
478 alias.clone()
479 } else {
480 format!("${}", alias)
481 };
482
483 let mut alias_func = (*arc_func).clone();
485 alias_func.name = alias_name.clone();
486 trie.insert(&alias_name, Arc::new(alias_func));
487 }
488 }
489 }
490 }
491
492 #[inline]
494 pub fn get_exact(&self, name: &str) -> Option<Arc<Function>> {
495 self.trie.read().unwrap().get_exact(name)
496 }
497
498 #[inline]
500 pub fn get_prefix(&self, text: &str) -> Option<(String, Arc<Function>)> {
501 self.trie.read().unwrap().get_prefix(text)
502 }
503
504 pub fn get(&self, name: &str) -> Option<Arc<Function>> {
506 let trie = self.trie.read().unwrap();
507
508 if let Some(func) = trie.get_exact(name) {
510 return Some(func);
511 }
512
513 trie.get_prefix(name).map(|(_, func)| func)
515 }
516
517 pub fn get_with_match(&self, name: &str) -> Option<(String, Arc<Function>)> {
519 let trie = self.trie.read().unwrap();
520
521 if let Some(func) = trie.get_exact(name) {
523 return Some((name.to_string(), func));
524 }
525
526 trie.get_prefix(name)
528 }
529
530 pub fn get_many(&self, names: &[&str]) -> Vec<Option<Arc<Function>>> {
532 names.iter().map(|name| self.get(name)).collect()
533 }
534
535 #[inline]
537 pub fn get_completions(&self, prefix: &str) -> Vec<Arc<Function>> {
538 self.trie.read().unwrap().get_completions(prefix)
539 }
540
541 #[inline]
543 pub fn all_functions(&self) -> Vec<Arc<Function>> {
544 self.trie.read().unwrap().all_functions()
545 }
546
547 #[inline]
549 pub fn get_enum(&self, name: &str) -> Option<Vec<String>> {
550 self.enums.get(name).map(|v| v.clone())
551 }
552
553 pub fn all_enums(&self) -> HashMap<String, Vec<String>> {
555 self.enums
556 .iter()
557 .map(|e| (e.key().clone(), e.value().clone()))
558 .collect()
559 }
560
561 #[inline]
563 pub fn get_event(&self, name: &str) -> Option<Event> {
564 self.events.get(name).map(|v| v.clone())
565 }
566
567 pub fn all_events(&self) -> Vec<Event> {
569 self.events.iter().map(|e| e.value().clone()).collect()
570 }
571
572 #[inline]
574 pub fn function_count(&self) -> usize {
575 self.trie.read().unwrap().len()
576 }
577
578 #[inline]
580 pub fn enum_count(&self) -> usize {
581 self.enums.len()
582 }
583
584 #[inline]
586 pub fn event_count(&self) -> usize {
587 self.events.len()
588 }
589
590 pub fn clear(&self) {
592 self.trie.write().unwrap().clear();
593 self.enums.clear();
594 self.events.clear();
595 }
596}
597
598impl Default for MetadataManager {
599 fn default() -> Self {
600 Self::new()
601 }
602}
603
604#[derive(Debug, Clone)]
606pub struct FetchStats {
607 pub functions: usize,
608 pub enums: usize,
609 pub events: usize,
610 pub errors: Vec<String>,
611}
612
613impl std::fmt::Display for FetchStats {
614 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
615 write!(
616 f,
617 "Fetched {} functions, {} enums, {} events",
618 self.functions, self.enums, self.events
619 )?;
620
621 if !self.errors.is_empty() {
622 write!(f, " ({} errors)", self.errors.len())?;
623 }
624
625 Ok(())
626 }
627}
628
629#[derive(Debug, Serialize, Deserialize)]
635pub struct MetadataCache {
636 pub functions: Vec<Function>,
637 pub enums: HashMap<String, Vec<String>>,
638 pub events: Vec<Event>,
639 pub version: u32,
640}
641
642impl MetadataCache {
643 const VERSION: u32 = 1;
644
645 pub fn new(
647 functions: Vec<Function>,
648 enums: HashMap<String, Vec<String>>,
649 events: Vec<Event>,
650 ) -> Self {
651 Self {
652 functions,
653 enums,
654 events,
655 version: Self::VERSION,
656 }
657 }
658}
659
660impl MetadataManager {
661 pub fn export_cache(&self) -> MetadataCache {
663 MetadataCache::new(
664 self.all_functions().iter().map(|f| (**f).clone()).collect(),
665 self.all_enums(),
666 self.all_events(),
667 )
668 }
669
670 pub fn import_cache(&self, cache: MetadataCache) -> Result<()> {
672 if cache.version != MetadataCache::VERSION {
673 return Err(MetadataError::CacheError(format!(
674 "Incompatible cache version: expected {}, got {}",
675 MetadataCache::VERSION,
676 cache.version
677 )));
678 }
679
680 self.clear();
682
683 self.add_functions(cache.functions);
685
686 for (name, values) in cache.enums {
688 self.enums.insert(name, values);
689 }
690
691 for event in cache.events {
693 self.events.insert(event.name.clone(), event);
694 }
695
696 Ok(())
697 }
698
699 pub fn cache_to_json(&self) -> Result<String> {
701 let cache = self.export_cache();
702 serde_json::to_string(&cache)
703 .map_err(|e| MetadataError::CacheError(format!("Serialization failed: {}", e)))
704 }
705
706 pub fn cache_from_json(&self, json: &str) -> Result<()> {
708 let cache: MetadataCache = serde_json::from_str(json)
709 .map_err(|e| MetadataError::CacheError(format!("Deserialization failed: {}", e)))?;
710 self.import_cache(cache)
711 }
712}
713
714#[cfg(not(target_arch = "wasm32"))]
719impl MetadataManager {
720 pub fn save_cache_to_file(&self, path: impl AsRef<std::path::Path>) -> Result<()> {
722 use std::io::Write;
723
724 let json = self.cache_to_json()?;
725 let mut file = std::fs::File::create(path)
726 .map_err(|e| MetadataError::CacheError(format!("Failed to create file: {}", e)))?;
727
728 file.write_all(json.as_bytes())
729 .map_err(|e| MetadataError::CacheError(format!("Failed to write file: {}", e)))?;
730
731 Ok(())
732 }
733
734 pub fn load_cache_from_file(&self, path: impl AsRef<std::path::Path>) -> Result<()> {
736 let json = std::fs::read_to_string(path)
737 .map_err(|e| MetadataError::CacheError(format!("Failed to read file: {}", e)))?;
738
739 self.cache_from_json(&json)
740 }
741}
742
743pub fn github_source(extension: impl Into<String>, repo: &str, branch: &str) -> MetadataSource {
749 let base = format!("https://raw.githubusercontent.com/{}/{}/", repo, branch);
750
751 MetadataSource::new(extension)
752 .with_functions(format!("{}functions.json", base))
753 .with_enums(format!("{}enums.json", base))
754 .with_events(format!("{}events.json", base))
755}
756
757pub fn custom_source(extension: impl Into<String>) -> MetadataSource {
759 MetadataSource::new(extension)
760}