vtcode_commons/
reference.rs1use std::path::{Path, PathBuf};
2use std::sync::{Arc, Mutex};
3
4use anyhow::{Error, Result};
5
6use crate::{ErrorReporter, TelemetrySink, WorkspacePaths};
7
8#[derive(Debug, Clone)]
16pub struct StaticWorkspacePaths {
17 root: PathBuf,
18 config: PathBuf,
19 cache: Option<PathBuf>,
20 telemetry: Option<PathBuf>,
21}
22
23impl StaticWorkspacePaths {
24 pub fn new(root: impl Into<PathBuf>, config: impl Into<PathBuf>) -> Self {
27 Self {
28 root: root.into(),
29 config: config.into(),
30 cache: None,
31 telemetry: None,
32 }
33 }
34
35 pub fn with_cache_dir(mut self, cache: impl Into<PathBuf>) -> Self {
37 self.cache = Some(cache.into());
38 self
39 }
40
41 pub fn with_telemetry_dir(mut self, telemetry: impl Into<PathBuf>) -> Self {
43 self.telemetry = Some(telemetry.into());
44 self
45 }
46}
47
48impl WorkspacePaths for StaticWorkspacePaths {
49 fn workspace_root(&self) -> &Path {
50 &self.root
51 }
52
53 fn config_dir(&self) -> PathBuf {
54 self.config.clone()
55 }
56
57 fn cache_dir(&self) -> Option<PathBuf> {
58 self.cache.clone()
59 }
60
61 fn telemetry_dir(&self) -> Option<PathBuf> {
62 self.telemetry.clone()
63 }
64}
65
66#[derive(Debug, Default, Clone)]
73pub struct MemoryTelemetry<Event> {
74 events: Arc<Mutex<Vec<Event>>>,
75}
76
77impl<Event> MemoryTelemetry<Event> {
78 pub fn new() -> Self {
80 Self {
81 events: Arc::new(Mutex::new(Vec::new())),
82 }
83 }
84
85 pub fn take(&self) -> Vec<Event> {
87 let mut events = match self.events.lock() {
88 Ok(guard) => guard,
89 Err(poisoned) => poisoned.into_inner(),
90 };
91 std::mem::take(&mut *events)
92 }
93}
94
95impl<Event> TelemetrySink<Event> for MemoryTelemetry<Event>
96where
97 Event: Clone + Send + Sync,
98{
99 fn record(&self, event: &Event) -> Result<()> {
100 let mut events = match self.events.lock() {
101 Ok(guard) => guard,
102 Err(poisoned) => poisoned.into_inner(),
103 };
104 events.push(event.clone());
105 Ok(())
106 }
107}
108
109#[derive(Debug, Default, Clone)]
115pub struct MemoryErrorReporter {
116 messages: Arc<Mutex<Vec<String>>>,
117}
118
119impl MemoryErrorReporter {
120 pub fn new() -> Self {
122 Self {
123 messages: Arc::new(Mutex::new(Vec::new())),
124 }
125 }
126
127 pub fn take(&self) -> Vec<String> {
129 let mut messages = match self.messages.lock() {
130 Ok(guard) => guard,
131 Err(poisoned) => poisoned.into_inner(),
132 };
133 std::mem::take(&mut *messages)
134 }
135}
136
137impl ErrorReporter for MemoryErrorReporter {
138 fn capture(&self, error: &Error) -> Result<()> {
139 let mut messages = match self.messages.lock() {
140 Ok(guard) => guard,
141 Err(poisoned) => poisoned.into_inner(),
142 };
143 messages.push(format!("{error:?}"));
144 Ok(())
145 }
146}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151 use std::path::PathBuf;
152
153 #[test]
154 fn static_paths_exposes_optional_directories() {
155 let paths = StaticWorkspacePaths::new("/tmp/work", "/tmp/work/config")
156 .with_cache_dir("/tmp/work/cache")
157 .with_telemetry_dir("/tmp/work/telemetry");
158
159 assert_eq!(paths.workspace_root(), Path::new("/tmp/work"));
160 assert_eq!(paths.config_dir(), PathBuf::from("/tmp/work/config"));
161 assert_eq!(paths.cache_dir(), Some(PathBuf::from("/tmp/work/cache")));
162 assert_eq!(
163 paths.telemetry_dir(),
164 Some(PathBuf::from("/tmp/work/telemetry"))
165 );
166 }
167
168 #[test]
169 fn memory_telemetry_records_events() {
170 let telemetry = MemoryTelemetry::new();
171 telemetry.record(&"event-1").unwrap();
172 telemetry.record(&"event-2").unwrap();
173
174 assert_eq!(telemetry.take(), vec!["event-1", "event-2"]);
175 assert!(telemetry.take().is_empty());
176 }
177
178 #[test]
179 fn memory_error_reporter_captures_messages() {
180 let reporter = MemoryErrorReporter::new();
181 reporter.capture(&Error::msg("error-1")).unwrap();
182 reporter.capture(&Error::msg("error-2")).unwrap();
183
184 let messages = reporter.take();
185 assert_eq!(messages.len(), 2);
186 assert!(messages[0].contains("error-1"));
187 assert!(messages[1].contains("error-2"));
188 assert!(reporter.take().is_empty());
189 }
190}