1use std::collections::BTreeMap;
10use std::future::{Future, ready};
11use std::io;
12use std::pin::pin;
13use std::sync::{Arc, Mutex, MutexGuard, PoisonError};
14use std::task::{Context, Poll, Waker};
15
16use rig_core::wasm_compat::WasmCompatSend;
17
18use crate::SessionError;
19
20#[derive(Debug, thiserror::Error)]
22pub enum LogWriteError {
23 #[error("the log changed since it was read")]
26 Changed,
27 #[error(transparent)]
29 Io(#[from] io::Error),
30}
31
32impl From<LogWriteError> for io::Error {
33 fn from(error: LogWriteError) -> Self {
36 match error {
37 LogWriteError::Changed => io::Error::other(LogWriteError::Changed.to_string()),
38 LogWriteError::Io(error) => error,
39 }
40 }
41}
42
43pub trait Files: Send + Sync + 'static {
53 fn write_file(&self, name: &str, bytes: &[u8]) -> io::Result<()>;
56
57 fn append_file(&self, name: &str, bytes: &[u8]) -> io::Result<()>;
60
61 fn read_file(&self, name: &str) -> io::Result<Vec<u8>>;
63
64 fn remove_file(&self, name: &str) -> io::Result<()>;
66
67 fn files(&self) -> io::Result<Vec<String>>;
69}
70
71pub trait Store: Files {
76 fn logs(&self) -> io::Result<Vec<String>>;
78
79 fn read_log(&self, name: &str) -> io::Result<Vec<u8>>;
81
82 fn append_log(&self, name: &str, len: u64, bytes: &[u8]) -> Result<(), LogWriteError>;
93
94 fn replace_log(&self, name: &str, len: u64, bytes: &[u8]) -> Result<(), LogWriteError>;
100
101 fn destroy(&self) -> io::Result<()>;
104}
105
106pub trait AsyncStore {
131 fn read_log_async(
133 &self,
134 name: &str,
135 ) -> impl Future<Output = io::Result<Vec<u8>>> + WasmCompatSend;
136
137 fn append_log_async(
140 &self,
141 name: &str,
142 len: u64,
143 bytes: &[u8],
144 ) -> impl Future<Output = Result<(), LogWriteError>> + WasmCompatSend;
145
146 fn replace_log_async(
149 &self,
150 name: &str,
151 len: u64,
152 bytes: &[u8],
153 ) -> impl Future<Output = Result<(), LogWriteError>> + WasmCompatSend;
154
155 fn read_file_async(
157 &self,
158 name: &str,
159 ) -> impl Future<Output = io::Result<Vec<u8>>> + WasmCompatSend;
160
161 fn write_file_async(
163 &self,
164 name: &str,
165 bytes: &[u8],
166 ) -> impl Future<Output = io::Result<()>> + WasmCompatSend;
167}
168
169impl<S: Store + ?Sized> AsyncStore for S {
171 fn read_log_async(
172 &self,
173 name: &str,
174 ) -> impl Future<Output = io::Result<Vec<u8>>> + WasmCompatSend {
175 ready(self.read_log(name))
176 }
177
178 fn append_log_async(
179 &self,
180 name: &str,
181 len: u64,
182 bytes: &[u8],
183 ) -> impl Future<Output = Result<(), LogWriteError>> + WasmCompatSend {
184 ready(self.append_log(name, len, bytes))
185 }
186
187 fn replace_log_async(
188 &self,
189 name: &str,
190 len: u64,
191 bytes: &[u8],
192 ) -> impl Future<Output = Result<(), LogWriteError>> + WasmCompatSend {
193 ready(self.replace_log(name, len, bytes))
194 }
195
196 fn read_file_async(
197 &self,
198 name: &str,
199 ) -> impl Future<Output = io::Result<Vec<u8>>> + WasmCompatSend {
200 ready(self.read_file(name))
201 }
202
203 fn write_file_async(
204 &self,
205 name: &str,
206 bytes: &[u8],
207 ) -> impl Future<Output = io::Result<()>> + WasmCompatSend {
208 ready(self.write_file(name, bytes))
209 }
210}
211
212macro_rules! forward {
215 ($($pointer:ident),*) => {$(
216 impl<S: Files + ?Sized> Files for $pointer<S> {
217 fn write_file(&self, name: &str, bytes: &[u8]) -> io::Result<()> {
218 (**self).write_file(name, bytes)
219 }
220
221 fn append_file(&self, name: &str, bytes: &[u8]) -> io::Result<()> {
222 (**self).append_file(name, bytes)
223 }
224
225 fn read_file(&self, name: &str) -> io::Result<Vec<u8>> {
226 (**self).read_file(name)
227 }
228
229 fn remove_file(&self, name: &str) -> io::Result<()> {
230 (**self).remove_file(name)
231 }
232
233 fn files(&self) -> io::Result<Vec<String>> {
234 (**self).files()
235 }
236 }
237
238 impl<S: Store + ?Sized> Store for $pointer<S> {
239 fn logs(&self) -> io::Result<Vec<String>> {
240 (**self).logs()
241 }
242
243 fn read_log(&self, name: &str) -> io::Result<Vec<u8>> {
244 (**self).read_log(name)
245 }
246
247 fn append_log(&self, name: &str, len: u64, bytes: &[u8]) -> Result<(), LogWriteError> {
248 (**self).append_log(name, len, bytes)
249 }
250
251 fn replace_log(&self, name: &str, len: u64, bytes: &[u8]) -> Result<(), LogWriteError> {
252 (**self).replace_log(name, len, bytes)
253 }
254
255 fn destroy(&self) -> io::Result<()> {
256 (**self).destroy()
257 }
258 }
259 )*};
260}
261
262forward!(Arc, Box);
263
264pub(crate) fn now<T>(
268 work: impl Future<Output = Result<T, SessionError>>,
269) -> Result<T, SessionError> {
270 match pin!(work).poll(&mut Context::from_waker(Waker::noop())) {
271 Poll::Ready(output) => output,
272 Poll::Pending => Err(io::Error::other("a local store's call did not finish").into()),
273 }
274}
275
276pub fn check_name(name: &str) -> io::Result<()> {
292 let bad = |part: &str| matches!(part, "" | "." | "..") || part.contains('\\');
293 if name.split('/').any(bad) {
294 let why = format!("{name:?} is no name in a session");
295 return Err(io::Error::new(io::ErrorKind::InvalidInput, why));
296 }
297 Ok(())
298}
299
300pub(crate) fn check_leaf(name: &str) -> io::Result<()> {
302 check_name(name)?;
303 if name.contains('/') {
304 let why = format!("{name:?} is no log name");
305 return Err(io::Error::new(io::ErrorKind::InvalidInput, why));
306 }
307 Ok(())
308}
309
310#[derive(Clone, Default)]
313pub struct MemoryStore(Arc<Mutex<Memory>>);
314
315#[derive(Default)]
316struct Memory {
317 logs: BTreeMap<String, Vec<u8>>,
318 files: BTreeMap<String, Vec<u8>>,
319}
320
321impl MemoryStore {
322 fn memory(&self) -> MutexGuard<'_, Memory> {
323 self.0.lock().unwrap_or_else(PoisonError::into_inner)
324 }
325}
326
327fn not_found() -> io::Error {
328 io::ErrorKind::NotFound.into()
329}
330
331impl Files for MemoryStore {
332 fn write_file(&self, name: &str, bytes: &[u8]) -> io::Result<()> {
333 check_name(name)?;
334 self.memory().files.insert(name.to_owned(), bytes.to_vec());
335 Ok(())
336 }
337
338 fn append_file(&self, name: &str, bytes: &[u8]) -> io::Result<()> {
339 check_name(name)?;
340 let mut memory = self.memory();
341 let file = memory.files.entry(name.to_owned()).or_default();
342 file.extend_from_slice(bytes);
343 Ok(())
344 }
345
346 fn read_file(&self, name: &str) -> io::Result<Vec<u8>> {
347 check_name(name)?;
348 self.memory().files.get(name).cloned().ok_or_else(not_found)
349 }
350
351 fn remove_file(&self, name: &str) -> io::Result<()> {
352 check_name(name)?;
353 self.memory().files.remove(name);
354 Ok(())
355 }
356
357 fn files(&self) -> io::Result<Vec<String>> {
358 Ok(self.memory().files.keys().cloned().collect())
359 }
360}
361
362impl Store for MemoryStore {
363 fn logs(&self) -> io::Result<Vec<String>> {
364 Ok(self.memory().logs.keys().cloned().collect())
365 }
366
367 fn read_log(&self, name: &str) -> io::Result<Vec<u8>> {
368 self.memory().logs.get(name).cloned().ok_or_else(not_found)
369 }
370
371 fn append_log(&self, name: &str, len: u64, bytes: &[u8]) -> Result<(), LogWriteError> {
372 check_leaf(name)?;
373 let mut memory = self.memory();
374 if memory.logs.get(name).map_or(0, Vec::len) as u64 != len {
375 return Err(LogWriteError::Changed);
376 }
377 let log = memory.logs.entry(name.to_owned()).or_default();
378 log.extend_from_slice(bytes);
379 Ok(())
380 }
381
382 fn replace_log(&self, name: &str, len: u64, bytes: &[u8]) -> Result<(), LogWriteError> {
383 check_leaf(name)?;
384 let mut memory = self.memory();
385 if memory.logs.get(name).map_or(0, Vec::len) as u64 != len {
386 return Err(LogWriteError::Changed);
387 }
388 memory.logs.insert(name.to_owned(), bytes.to_vec());
389 Ok(())
390 }
391
392 fn destroy(&self) -> io::Result<()> {
393 *self.memory() = Memory::default();
394 Ok(())
395 }
396}
397
398pub const PLUGINS: &str = "plugins/";
400
401pub struct PluginFiles(pub Arc<dyn Files>);
404
405impl Files for PluginFiles {
406 fn write_file(&self, name: &str, bytes: &[u8]) -> io::Result<()> {
407 self.0.write_file(&format!("{PLUGINS}{name}"), bytes)
408 }
409
410 fn append_file(&self, name: &str, bytes: &[u8]) -> io::Result<()> {
411 self.0.append_file(&format!("{PLUGINS}{name}"), bytes)
412 }
413
414 fn read_file(&self, name: &str) -> io::Result<Vec<u8>> {
415 self.0.read_file(&format!("{PLUGINS}{name}"))
416 }
417
418 fn remove_file(&self, name: &str) -> io::Result<()> {
419 self.0.remove_file(&format!("{PLUGINS}{name}"))
420 }
421
422 fn files(&self) -> io::Result<Vec<String>> {
423 let files = self.0.files()?.into_iter();
424 let within = files.filter_map(|name| Some(name.strip_prefix(PLUGINS)?.to_owned()));
425 Ok(within.collect())
426 }
427}