1use std::collections::HashMap;
6
7use redb::ReadableTable;
8use sinter_core::{CorpusScope, Node};
9
10use crate::error::StoreError;
11use crate::store::{FILE_SCOPE, NODE_SCOPE, Store};
12
13pub(crate) fn resolve(
14 node_scope: impl Fn(&str) -> Option<CorpusScope>,
15 file_scope: Option<CorpusScope>,
16 id: &str,
17 file: &str,
18) -> CorpusScope {
19 node_scope(id)
20 .or_else(|| node_scope(file))
21 .or(file_scope)
22 .unwrap_or_else(|| CorpusScope::classify_path(file))
23}
24
25#[derive(Debug, Default, Clone)]
27pub struct ScopeIndex {
28 files: HashMap<String, CorpusScope>,
29 nodes: HashMap<String, CorpusScope>,
30}
31
32impl ScopeIndex {
33 pub fn scope_of(&self, node: &Node) -> CorpusScope {
34 self.scope_of_id(node.id.as_str(), &node.file)
35 }
36
37 pub fn scope_of_id(&self, id: &str, file: &str) -> CorpusScope {
38 resolve(
39 |key| self.nodes.get(key).copied(),
40 self.files.get(file).copied(),
41 id,
42 file,
43 )
44 }
45
46 pub fn file_scope(&self, file: &str) -> CorpusScope {
48 self.scope_of_id(file, file)
49 }
50}
51
52impl Store {
53 pub fn scope_index(&self) -> Result<ScopeIndex, StoreError> {
54 let files = self.file_scopes()?;
55 let txn = self.db.begin_read()?;
56 let table = txn.open_table(NODE_SCOPE)?;
57 let mut nodes = HashMap::new();
58 for entry in table.iter()? {
59 let (id, scope) = entry?;
60 if let Some(scope) = CorpusScope::from_str_opt(scope.value()) {
61 nodes.insert(id.value().to_string(), scope);
62 }
63 }
64 Ok(ScopeIndex { files, nodes })
65 }
66
67 pub fn node_scope(&self, node: &Node) -> Result<CorpusScope, StoreError> {
68 let txn = self.db.begin_read()?;
69 let nodes = txn.open_table(NODE_SCOPE)?;
70 let files = txn.open_table(FILE_SCOPE)?;
71 let lookup = |key: &str| {
72 nodes
73 .get(key)
74 .ok()
75 .flatten()
76 .and_then(|g| CorpusScope::from_str_opt(g.value()))
77 };
78 let file_scope = files
79 .get(node.file.as_str())?
80 .and_then(|g| CorpusScope::from_str_opt(g.value()));
81 Ok(resolve(lookup, file_scope, node.id.as_str(), &node.file))
82 }
83}