1 //! Rustdoc's FileSystem abstraction module. 2 //! 3 //! On Windows this indirects IO into threads to work around performance issues 4 //! with Defender (and other similar virus scanners that do blocking operations). 5 //! On other platforms this is a thin shim to fs. 6 //! 7 //! Only calls needed to permit this workaround have been abstracted: thus 8 //! fs::read is still done directly via the fs module; if in future rustdoc 9 //! needs to read-after-write from a file, then it would be added to this 10 //! abstraction. 11 12 use std::fs; 13 use std::io; 14 use std::path::{Path, PathBuf}; 15 use std::string::ToString; 16 use std::sync::mpsc::Sender; 17 18 crate trait PathError { new<S, P: AsRef<Path>>(e: S, path: P) -> Self where S: ToString + Sized19 fn new<S, P: AsRef<Path>>(e: S, path: P) -> Self 20 where 21 S: ToString + Sized; 22 } 23 24 crate struct DocFS { 25 sync_only: bool, 26 errors: Option<Sender<String>>, 27 } 28 29 impl DocFS { new(errors: Sender<String>) -> DocFS30 crate fn new(errors: Sender<String>) -> DocFS { 31 DocFS { sync_only: false, errors: Some(errors) } 32 } 33 set_sync_only(&mut self, sync_only: bool)34 crate fn set_sync_only(&mut self, sync_only: bool) { 35 self.sync_only = sync_only; 36 } 37 close(&mut self)38 crate fn close(&mut self) { 39 self.errors = None; 40 } 41 create_dir_all<P: AsRef<Path>>(&self, path: P) -> io::Result<()>42 crate fn create_dir_all<P: AsRef<Path>>(&self, path: P) -> io::Result<()> { 43 // For now, dir creation isn't a huge time consideration, do it 44 // synchronously, which avoids needing ordering between write() actions 45 // and directory creation. 46 fs::create_dir_all(path) 47 } 48 write<E>( &self, path: PathBuf, contents: impl 'static + Send + AsRef<[u8]>, ) -> Result<(), E> where E: PathError,49 crate fn write<E>( 50 &self, 51 path: PathBuf, 52 contents: impl 'static + Send + AsRef<[u8]>, 53 ) -> Result<(), E> 54 where 55 E: PathError, 56 { 57 if !self.sync_only && cfg!(windows) { 58 // A possible future enhancement after more detailed profiling would 59 // be to create the file sync so errors are reported eagerly. 60 let sender = self.errors.clone().expect("can't write after closing"); 61 rayon::spawn(move || { 62 fs::write(&path, contents).unwrap_or_else(|e| { 63 sender.send(format!("\"{}\": {}", path.display(), e)).unwrap_or_else(|_| { 64 panic!("failed to send error on \"{}\"", path.display()) 65 }) 66 }); 67 }); 68 } else { 69 fs::write(&path, contents).map_err(|e| E::new(e, path))?; 70 } 71 Ok(()) 72 } 73 } 74