endpoint 2.0 dysnomia.ptr.pet
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 390 lines 12 kB view raw
1//! An ephemeral in-memory file system, intended mainly for unit tests 2 3use core::cmp; 4use std::fmt; 5use std::fmt::{Debug, Formatter}; 6use std::io::{Cursor, Read, Seek, SeekFrom, Write}; 7use std::mem::swap; 8use std::sync::Arc; 9use std::time::SystemTime; 10use vfs::error::VfsErrorKind; 11use vfs::{FileSystem, VfsFileType}; 12use vfs::{SeekAndRead, VfsMetadata}; 13use vfs::{SeekAndWrite, VfsResult}; 14 15use crate::globals::current_time; 16 17type MemoryFsHandle = Arc<MemoryFsImpl>; 18 19/// An ephemeral in-memory file system, intended mainly for unit tests 20pub struct MemoryFS { 21 handle: MemoryFsHandle, 22} 23 24impl Debug for MemoryFS { 25 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 26 f.write_str("In Memory File System") 27 } 28} 29 30impl MemoryFS { 31 /// Create a new in-memory filesystem 32 pub fn new() -> Self { 33 MemoryFS { 34 handle: Arc::new(MemoryFsImpl::new()), 35 } 36 } 37 38 fn ensure_has_parent(&self, path: &str) -> VfsResult<()> { 39 let separator = path.rfind('/'); 40 if let Some(index) = separator { 41 if self.exists(&path[..index])? { 42 return Ok(()); 43 } 44 } 45 Err(VfsErrorKind::Other("Parent path does not exist".into()).into()) 46 } 47} 48 49impl Default for MemoryFS { 50 fn default() -> Self { 51 Self::new() 52 } 53} 54 55struct WritableFile { 56 content: Cursor<Vec<u8>>, 57 destination: String, 58 fs: MemoryFsHandle, 59} 60 61impl Seek for WritableFile { 62 fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> { 63 self.content.seek(pos) 64 } 65} 66 67impl Write for WritableFile { 68 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> { 69 self.content.write(buf) 70 } 71 72 fn flush(&mut self) -> std::io::Result<()> { 73 self.content.flush()?; 74 let mut content = self.content.get_ref().clone(); 75 swap(&mut content, self.content.get_mut()); 76 let content = Arc::new(content); 77 78 let new_file = self 79 .fs 80 .files 81 .peek_with(&self.destination, |_, previous_file| MemoryFile { 82 file_type: VfsFileType::File, 83 content: content.clone(), 84 created: previous_file.created, 85 modified: current_time(), 86 accessed: previous_file.accessed, 87 }) 88 .unwrap_or_else(|| { 89 let time = current_time(); 90 MemoryFile { 91 file_type: VfsFileType::File, 92 content, 93 created: time, 94 modified: time, 95 accessed: None, 96 } 97 }); 98 99 // Remove old entry if it exists, then insert new one 100 self.fs.files.remove_sync(&self.destination); 101 let _ = self 102 .fs 103 .files 104 .insert_sync(self.destination.clone(), new_file); 105 Ok(()) 106 } 107} 108 109impl Drop for WritableFile { 110 fn drop(&mut self) { 111 self.flush() 112 .expect("Flush failed while dropping in-memory file"); 113 } 114} 115 116struct ReadableFile { 117 #[allow(clippy::rc_buffer)] // to allow accessing the same object as writable 118 content: Arc<Vec<u8>>, 119 position: u64, 120} 121 122impl ReadableFile { 123 fn len(&self) -> u64 { 124 self.content.len() as u64 - self.position 125 } 126} 127 128impl Read for ReadableFile { 129 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> { 130 let amt = cmp::min(buf.len(), self.len() as usize); 131 132 if amt == 1 { 133 buf[0] = self.content[self.position as usize]; 134 } else { 135 buf[..amt].copy_from_slice( 136 &self.content.as_slice()[self.position as usize..self.position as usize + amt], 137 ); 138 } 139 self.position += amt as u64; 140 Ok(amt) 141 } 142} 143 144impl Seek for ReadableFile { 145 fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> { 146 match pos { 147 SeekFrom::Start(offset) => self.position = offset, 148 SeekFrom::Current(offset) => self.position = (self.position as i64 + offset) as u64, 149 SeekFrom::End(offset) => self.position = (self.content.len() as i64 + offset) as u64, 150 } 151 Ok(self.position) 152 } 153} 154 155impl FileSystem for MemoryFS { 156 fn read_dir(&self, path: &str) -> VfsResult<Box<dyn Iterator<Item = String> + Send>> { 157 let prefix = format!("{}/", path); 158 159 // Ensure the directory exists 160 if !self.handle.files.contains(path) { 161 return Err(VfsErrorKind::FileNotFound.into()); 162 } 163 164 let guard = scc::Guard::new(); 165 let mut entries = Vec::new(); 166 167 let range_start = prefix.clone(); 168 for (candidate_path, _) in self.handle.files.range(range_start.., &guard) { 169 if candidate_path.starts_with(&prefix) { 170 let rest = &candidate_path[prefix.len()..]; 171 if !rest.contains('/') { 172 entries.push(rest.to_string()); 173 } 174 } else { 175 // we are in a different directory if the prefix doesnt match 176 break; 177 } 178 } 179 180 Ok(Box::new(entries.into_iter())) 181 } 182 183 fn create_dir(&self, path: &str) -> VfsResult<()> { 184 self.ensure_has_parent(path)?; 185 186 // Check if path already exists and return appropriate error 187 if let Some(result) = self 188 .handle 189 .files 190 .peek_with(path, |_, file| match file.file_type { 191 VfsFileType::File => Err(VfsErrorKind::FileExists.into()), 192 VfsFileType::Directory => Err(VfsErrorKind::DirectoryExists.into()), 193 }) 194 { 195 return result; 196 } 197 198 let new_dir = MemoryFile { 199 file_type: VfsFileType::Directory, 200 content: Default::default(), 201 created: current_time(), 202 modified: current_time(), 203 accessed: current_time(), 204 }; 205 206 self.handle 207 .files 208 .insert_sync(path.to_string(), new_dir) 209 .map_err(|_| VfsErrorKind::DirectoryExists.into()) 210 } 211 212 fn open_file(&self, path: &str) -> VfsResult<Box<dyn SeekAndRead + Send>> { 213 if let Some(time) = current_time() { 214 let _ = self.set_access_time(path, time); 215 } 216 217 let content = self 218 .handle 219 .files 220 .peek_with(path, |_, file| { 221 ensure_file(file)?; 222 VfsResult::Ok(file.content.clone()) 223 }) 224 .ok_or(VfsErrorKind::FileNotFound)??; 225 226 Ok(Box::new(ReadableFile { 227 content, 228 position: 0, 229 })) 230 } 231 232 fn create_file(&self, path: &str) -> VfsResult<Box<dyn SeekAndWrite + Send>> { 233 self.ensure_has_parent(path)?; 234 let content = Arc::new(Vec::<u8>::new()); 235 let new_file = MemoryFile { 236 file_type: VfsFileType::File, 237 content, 238 created: current_time(), 239 modified: current_time(), 240 accessed: current_time(), 241 }; 242 243 // Remove old entry if it exists, then insert new one 244 self.handle.files.remove_sync(path); 245 let _ = self.handle.files.insert_sync(path.to_string(), new_file); 246 247 let writer = WritableFile { 248 content: Cursor::new(vec![]), 249 destination: path.to_string(), 250 fs: self.handle.clone(), 251 }; 252 Ok(Box::new(writer)) 253 } 254 255 fn append_file(&self, path: &str) -> VfsResult<Box<dyn SeekAndWrite + Send>> { 256 let content = self 257 .handle 258 .files 259 .peek_with(path, |_, file| file.content.clone()) 260 .ok_or(VfsErrorKind::FileNotFound)?; 261 let mut content = Cursor::new(content.as_slice().to_vec()); 262 content.seek(SeekFrom::End(0))?; 263 let writer = WritableFile { 264 content, 265 destination: path.to_string(), 266 fs: self.handle.clone(), 267 }; 268 Ok(Box::new(writer)) 269 } 270 271 fn metadata(&self, path: &str) -> VfsResult<VfsMetadata> { 272 self.handle 273 .files 274 .peek_with(path, |_, file| VfsMetadata { 275 file_type: file.file_type, 276 len: file.content.len() as u64, 277 modified: file.modified, 278 created: file.created, 279 accessed: file.accessed, 280 }) 281 .ok_or(VfsErrorKind::FileNotFound.into()) 282 } 283 284 fn set_creation_time(&self, path: &str, time: SystemTime) -> VfsResult<()> { 285 let updated = self 286 .handle 287 .files 288 .peek_with(path, |_, file| MemoryFile { 289 created: Some(time), 290 ..file.clone() 291 }) 292 .ok_or(VfsErrorKind::FileNotFound)?; 293 294 self.handle.files.remove_sync(path); 295 let _ = self.handle.files.insert_sync(path.to_string(), updated); 296 Ok(()) 297 } 298 299 fn set_modification_time(&self, path: &str, time: SystemTime) -> VfsResult<()> { 300 let updated = self 301 .handle 302 .files 303 .peek_with(path, |_, file| MemoryFile { 304 modified: Some(time), 305 ..file.clone() 306 }) 307 .ok_or(VfsErrorKind::FileNotFound)?; 308 309 self.handle.files.remove_sync(path); 310 let _ = self.handle.files.insert_sync(path.to_string(), updated); 311 Ok(()) 312 } 313 314 fn set_access_time(&self, path: &str, time: SystemTime) -> VfsResult<()> { 315 let updated = self 316 .handle 317 .files 318 .peek_with(path, |_, file| MemoryFile { 319 accessed: Some(time), 320 ..file.clone() 321 }) 322 .ok_or(VfsErrorKind::FileNotFound)?; 323 324 self.handle.files.remove_sync(path); 325 let _ = self.handle.files.insert_sync(path.to_string(), updated); 326 Ok(()) 327 } 328 329 fn exists(&self, path: &str) -> VfsResult<bool> { 330 Ok(self.handle.files.contains(path)) 331 } 332 333 fn remove_file(&self, path: &str) -> VfsResult<()> { 334 self.handle 335 .files 336 .remove_sync(path) 337 .then_some(Ok(())) 338 .unwrap_or_else(|| Err(VfsErrorKind::FileNotFound.into())) 339 } 340 341 fn remove_dir(&self, path: &str) -> VfsResult<()> { 342 if self.read_dir(path)?.next().is_some() { 343 return Err(VfsErrorKind::Other("Directory to remove is not empty".into()).into()); 344 } 345 self.handle 346 .files 347 .remove_sync(path) 348 .then_some(Ok(())) 349 .unwrap_or_else(|| Err(VfsErrorKind::FileNotFound.into())) 350 } 351} 352 353struct MemoryFsImpl { 354 files: scc::TreeIndex<String, MemoryFile>, 355} 356 357impl MemoryFsImpl { 358 pub fn new() -> Self { 359 let files = scc::TreeIndex::new(); 360 // Add root directory 361 let _ = files.insert_sync( 362 "".to_string(), 363 MemoryFile { 364 file_type: VfsFileType::Directory, 365 content: Arc::new(vec![]), 366 created: current_time(), 367 modified: None, 368 accessed: None, 369 }, 370 ); 371 Self { files } 372 } 373} 374 375#[derive(Clone)] 376struct MemoryFile { 377 file_type: VfsFileType, 378 content: Arc<Vec<u8>>, 379 380 created: Option<SystemTime>, 381 modified: Option<SystemTime>, 382 accessed: Option<SystemTime>, 383} 384 385fn ensure_file(file: &MemoryFile) -> VfsResult<()> { 386 if file.file_type != VfsFileType::File { 387 return Err(VfsErrorKind::Other("Not a file".into()).into()); 388 } 389 Ok(()) 390}