file_reopen/
lib.rs

1use std::fs;
2use std::io::{self, Write};
3use std::path::{Path, PathBuf};
4use std::sync::atomic::{AtomicBool, Ordering};
5use std::sync::Arc;
6
7pub struct File {
8    path: PathBuf,
9    file: fs::File,
10    should_rotate: Arc<AtomicBool>,
11    sig_id: signal_hook::SigId,
12}
13
14impl File {
15    pub fn open(path: impl AsRef<Path>) -> Result<Self, io::Error> {
16        let flag = Arc::default();
17        let file = Self::do_open(&path)?;
18
19        let sig_id =
20            signal_hook::flag::register(signal_hook::consts::signal::SIGHUP, Arc::clone(&flag))?;
21
22        let file = File {
23            path: path.as_ref().to_owned(),
24            file,
25            should_rotate: Arc::clone(&flag),
26            sig_id,
27        };
28        Ok(file)
29    }
30
31    fn do_open(path: impl AsRef<Path>) -> Result<fs::File, io::Error> {
32        fs::OpenOptions::new().append(true).create(true).open(path)
33    }
34
35    fn reopen_if_needed(&mut self) -> Result<(), io::Error> {
36        if self.should_rotate.swap(false, Ordering::Relaxed) {
37            self.file.sync_all()?;
38            self.file = Self::do_open(&self.path)?;
39        }
40        Ok(())
41    }
42}
43
44impl Drop for File {
45    fn drop(&mut self) {
46        signal_hook::low_level::unregister(self.sig_id);
47    }
48}
49
50impl Write for File {
51    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
52        self.reopen_if_needed()?;
53        self.file.write(buf)
54    }
55
56    fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
57        self.reopen_if_needed()?;
58        self.file.write_vectored(bufs)
59    }
60
61    fn flush(&mut self) -> io::Result<()> {
62        self.file.flush()
63    }
64}