weavatrix_rust/engine/
session.rs1use super::{RepositoryState, Weavatrix};
2use crate::analyzer::Analyzer;
3use crate::model::{Error, Result};
4use std::collections::BTreeMap;
5use std::path::{Path, PathBuf};
6
7impl Weavatrix {
8 pub fn open(root: impl AsRef<Path>) -> Result<Self> {
14 let analyzer = Analyzer::default();
15 let state = RepositoryState::build(&analyzer, root)?;
16 let known_states = BTreeMap::from([(state.root.clone(), state.clone())]);
17 Ok(Self {
18 analyzer,
19 state,
20 known_states,
21 tool_cache: BTreeMap::new(),
22 })
23 }
24
25 pub(crate) fn from_state(state: RepositoryState) -> Self {
26 let known_states = BTreeMap::from([(state.root.clone(), state.clone())]);
27 Self {
28 analyzer: Analyzer::default(),
29 state,
30 known_states,
31 tool_cache: BTreeMap::new(),
32 }
33 }
34
35 #[must_use]
36 pub const fn state(&self) -> &RepositoryState {
37 &self.state
38 }
39
40 pub fn rebuild(&mut self) -> Result<()> {
46 self.state = RepositoryState::build(&self.analyzer, &self.state.root)?;
47 self.tool_cache.clear();
48 self.remember_active_state();
49 Ok(())
50 }
51
52 pub fn refresh_if_stale(&mut self) -> Result<bool> {
59 let scan = self
60 .analyzer
61 .scan(&self.state.root, Some(&self.state.scan))?;
62 if scan.revision == self.state.scan.revision {
63 self.state.scan = scan;
64 self.remember_active_state();
65 return Ok(false);
66 }
67 self.state = RepositoryState::from_scan(&self.analyzer, &self.state.root, scan)?;
68 self.tool_cache.clear();
69 self.remember_active_state();
70 Ok(true)
71 }
72
73 pub fn open_repository(&mut self, root: impl AsRef<Path>) -> Result<()> {
79 self.open_repository_with_build(root, true)?;
80 Ok(())
81 }
82
83 pub fn open_repository_with_build(
94 &mut self,
95 root: impl AsRef<Path>,
96 build: bool,
97 ) -> Result<bool> {
98 if build {
99 let state = RepositoryState::build(&self.analyzer, root)?;
100 self.known_states
101 .insert(self.state.root.clone(), self.state.clone());
102 self.state = state;
103 self.tool_cache.clear();
104 self.remember_active_state();
105 return Ok(true);
106 }
107
108 let requested = root
109 .as_ref()
110 .canonicalize()
111 .map_err(|source| Error::io(root.as_ref(), source))?;
112 if requested == self.state.root {
113 return Ok(false);
114 }
115 let cached = self.known_states.get(&requested).cloned().ok_or_else(|| {
116 Error::Analysis(format!(
117 "no in-process graph for {}; call open_repo with build:true first",
118 requested.display()
119 ))
120 })?;
121 self.known_states
122 .insert(self.state.root.clone(), self.state.clone());
123 self.state = cached;
124 self.tool_cache.clear();
125 Ok(false)
126 }
127
128 pub fn known_roots(&self) -> impl Iterator<Item = &Path> {
129 self.known_states.keys().map(PathBuf::as_path)
130 }
131
132 pub(crate) fn ensure_repository_state(&mut self, root: impl AsRef<Path>) -> Result<PathBuf> {
133 let requested = root
134 .as_ref()
135 .canonicalize()
136 .map_err(|source| Error::io(root.as_ref(), source))?;
137 if requested == self.state.root || self.known_states.contains_key(&requested) {
138 return Ok(requested);
139 }
140 let state = RepositoryState::build(&self.analyzer, &requested)?;
141 self.known_states.insert(requested.clone(), state);
142 Ok(requested)
143 }
144
145 pub(crate) fn known_state(&self, root: &Path) -> Option<&RepositoryState> {
146 if root == self.state.root {
147 Some(&self.state)
148 } else {
149 self.known_states.get(root)
150 }
151 }
152
153 pub(crate) fn cached_tool_result(&self, key: &str) -> Option<blazingly_json::Value> {
154 self.tool_cache.get(key).cloned()
155 }
156
157 pub(crate) fn remember_tool_result(&mut self, key: String, value: blazingly_json::Value) {
158 const MAX_TOOL_CACHE_ENTRIES: usize = 32;
159 if self.tool_cache.len() >= MAX_TOOL_CACHE_ENTRIES {
160 self.tool_cache.clear();
161 }
162 self.tool_cache.insert(key, value);
163 }
164
165 fn remember_active_state(&mut self) {
166 self.known_states
167 .insert(self.state.root.clone(), self.state.clone());
168 }
169}