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}