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