1use dashmap::DashMap;
2use oak_symbols::SymbolInformation;
3use std::path::PathBuf;
4use url::Url;
5
6use std::sync::RwLock;
7
8pub trait ModuleResolver: Send + Sync {
10 fn resolve(&self, base_uri: &str, import_path: &str) -> Option<String>;
12}
13
14pub struct StandardResolver {
16 root_dirs: RwLock<Vec<PathBuf>>,
17}
18
19impl StandardResolver {
20 pub fn new(root_dirs: Vec<PathBuf>) -> Self {
21 Self { root_dirs: RwLock::new(root_dirs) }
22 }
23
24 pub fn set_root_dirs(&self, root_dirs: Vec<PathBuf>) {
26 if let Ok(mut dirs) = self.root_dirs.write() {
27 *dirs = root_dirs;
28 }
29 }
30}
31
32impl ModuleResolver for StandardResolver {
33 fn resolve(&self, base_uri: &str, import_path: &str) -> Option<String> {
34 let root_dirs = self.root_dirs.read().ok()?;
35
36 if let Ok(base_url) = Url::parse(base_uri) {
37 if let Ok(base_path) = base_url.to_file_path() {
38 let base_dir = base_path.parent()?;
39 let resolved_path = base_dir.join(import_path);
40
41 if resolved_path.exists() {
42 return Url::from_file_path(resolved_path).ok().map(|u| u.to_string());
43 }
44 }
45 }
46
47 for root in root_dirs.iter() {
49 let resolved_path = root.join(import_path);
50 if resolved_path.exists() {
51 return Url::from_file_path(resolved_path).ok().map(|u| u.to_string());
52 }
53 }
54
55 None
56 }
57}
58
59pub struct GlobalSymbolTable {
61 file_symbols: DashMap<String, Vec<SymbolInformation>>,
63 qualified_symbols: DashMap<String, SymbolInformation>,
65}
66
67impl GlobalSymbolTable {
68 pub fn new() -> Self {
69 Self { file_symbols: DashMap::new(), qualified_symbols: DashMap::new() }
70 }
71
72 pub fn update_file_symbols(&self, uri: String, symbols: Vec<SymbolInformation>) {
74 if let Some((_, old_symbols)) = self.file_symbols.remove(&uri) {
76 for sym in old_symbols {
77 let fqn = self.make_qualified_name(&sym);
78 self.qualified_symbols.remove(&fqn);
79 }
80 }
81
82 for sym in &symbols {
84 let fqn = self.make_qualified_name(sym);
85 self.qualified_symbols.insert(fqn, sym.clone());
86 }
87 self.file_symbols.insert(uri, symbols);
88 }
89
90 fn make_qualified_name(&self, sym: &SymbolInformation) -> String {
91 match &sym.container_name {
92 Some(container) => format!("{}::{}", container, sym.name),
93 None => sym.name.clone(),
94 }
95 }
96
97 pub fn lookup(&self, fqn: &str) -> Option<SymbolInformation> {
99 self.qualified_symbols.get(fqn).map(|r| r.value().clone())
100 }
101
102 pub fn query_file(&self, uri: &str) -> Vec<SymbolInformation> {
104 self.file_symbols.get(uri).map(|r| r.value().clone()).unwrap_or_default()
105 }
106
107 pub fn query(&self, query: &str) -> Vec<SymbolInformation> {
109 self.qualified_symbols.iter().filter(|r| r.key().contains(query)).map(|r| r.value().clone()).collect()
110 }
111}
112
113impl Default for GlobalSymbolTable {
114 fn default() -> Self {
115 Self::new()
116 }
117}