]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/docfs.rs
Auto merge of #67540 - Mark-Simulacrum:fmt-the-world, r=Centril
[rust.git] / src / librustdoc / docfs.rs
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 errors;
13
14 use std::fs;
15 use std::io;
16 use std::path::Path;
17 use std::sync::mpsc::{channel, Receiver, Sender};
18 use std::sync::Arc;
19
20 macro_rules! try_err {
21     ($e:expr, $file:expr) => {{
22         match $e {
23             Ok(e) => e,
24             Err(e) => return Err(E::new(e, $file)),
25         }
26     }};
27 }
28
29 pub trait PathError {
30     fn new<P: AsRef<Path>>(e: io::Error, path: P) -> Self;
31 }
32
33 pub struct ErrorStorage {
34     sender: Option<Sender<Option<String>>>,
35     receiver: Receiver<Option<String>>,
36 }
37
38 impl ErrorStorage {
39     pub fn new() -> ErrorStorage {
40         let (sender, receiver) = channel();
41         ErrorStorage { sender: Some(sender), receiver }
42     }
43
44     /// Prints all stored errors. Returns the number of printed errors.
45     pub fn write_errors(&mut self, diag: &errors::Handler) -> usize {
46         let mut printed = 0;
47         // In order to drop the sender part of the channel.
48         self.sender = None;
49
50         for msg in self.receiver.iter() {
51             if let Some(ref error) = msg {
52                 diag.struct_err(&error).emit();
53                 printed += 1;
54             }
55         }
56         printed
57     }
58 }
59
60 pub struct DocFS {
61     sync_only: bool,
62     errors: Arc<ErrorStorage>,
63 }
64
65 impl DocFS {
66     pub fn new(errors: &Arc<ErrorStorage>) -> DocFS {
67         DocFS { sync_only: false, errors: Arc::clone(errors) }
68     }
69
70     pub fn set_sync_only(&mut self, sync_only: bool) {
71         self.sync_only = sync_only;
72     }
73
74     pub fn create_dir_all<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
75         // For now, dir creation isn't a huge time consideration, do it
76         // synchronously, which avoids needing ordering between write() actions
77         // and directory creation.
78         fs::create_dir_all(path)
79     }
80
81     pub fn write<P, C, E>(&self, path: P, contents: C) -> Result<(), E>
82     where
83         P: AsRef<Path>,
84         C: AsRef<[u8]>,
85         E: PathError,
86     {
87         if !self.sync_only && cfg!(windows) {
88             // A possible future enhancement after more detailed profiling would
89             // be to create the file sync so errors are reported eagerly.
90             let contents = contents.as_ref().to_vec();
91             let path = path.as_ref().to_path_buf();
92             let sender = self.errors.sender.clone().unwrap();
93             rayon::spawn(move || match fs::write(&path, &contents) {
94                 Ok(_) => {
95                     sender
96                         .send(None)
97                         .expect(&format!("failed to send error on \"{}\"", path.display()));
98                 }
99                 Err(e) => {
100                     sender
101                         .send(Some(format!("\"{}\": {}", path.display(), e)))
102                         .expect(&format!("failed to send non-error on \"{}\"", path.display()));
103                 }
104             });
105             Ok(())
106         } else {
107             Ok(try_err!(fs::write(&path, contents), path))
108         }
109     }
110 }