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