repo_description: Empowering everyone to build reliable and efficient software.
repo_topics: compiler language rust
code: //! Rustdoc's FileSystem abstraction module.
  //!
  //! On Windows this indirects IO into threads to work around performance issues
  //! with Defender (and other similar virus scanners that do blocking operations).
  //!
  //! Only calls needed to permit this workaround have been abstracted: thus
  //! fs::read is still done directly via the fs module; if in future rustdoc
  //! needs to read-after-write from a file, then it would be added to this
  //! abstraction.
  
  use std::cmp::max;
  use std::path::{Path, PathBuf};
  use std::sync::mpsc::Sender;
  use std::thread::available_parallelism;
  use std::{fs, io};
  
  use threadpool::ThreadPool;
  
  pub(crate) trait PathError {
      fn new<S, P: AsRef<Path>>(e: S, path: P) -> Self
      where
          S: ToString + Sized;
  }
  
  pub(crate) struct DocFS {
      sync_only: bool,
      errors: Option<Sender<String>>,
      pool: ThreadPool,
  }
  
  impl DocFS {
      pub(crate) fn new(errors: Sender<String>) -> DocFS {
          const MINIMUM_NB_THREADS: usize = 2;
          DocFS {
              sync_only: false,
              errors: Some(errors),
              pool: ThreadPool::new(
                  available_parallelism()
                      .map(|nb| max(nb.get(), MINIMUM_NB_THREADS))
                      .unwrap_or(MINIMUM_NB_THREADS),
              ),
          }
      }
  
      pub(crate) fn set_sync_only(&mut self, sync_only: bool) {
          self.sync_only = sync_only;
      }
  
      pub(crate) fn close(&mut self) {
          self.errors = None;
      }
  
      pub(crate) fn create_dir_all<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
          // For now, dir creation isn't a huge time consideration, do it
          // synchronously, which avoids needing ordering between write() actions
          // and directory creation.
          fs::create_dir_all(path)
      }
  
      pub(crate) fn write<E>(
          &self,
          path: PathBuf,
          contents: impl 'static + Send + AsRef<[u8]>,
      ) -> Result<(), E>
      where
          E: PathError,
      {
          if !self.sync_only {
              // A possible future enhancement after more detailed profiling would
              // be to create the file sync so errors are reported eagerly.
              let sender = self.errors.clone().expect("can't write after closing");
              self.pool.execute(move || {
                  fs::write(&path, contents).unwrap_or_else(|e| {
                      sender.send(format!("\"{path}\": {e}", path = path.display())).unwrap_or_else(
                          |_| panic!("failed to send error on \"{}\"", path.display()),
                      )
                  });
              });
          } else {
              fs::write(&path, contents).map_err(|e| E::new(e, path))?;
          }
  
          Ok(())
      }
  }
  
  impl Drop for DocFS {
      fn drop(&mut self) {
          self.pool.join();
      }
  }
  