virtual_fs/
trace_fs.rs

1use std::{
2    path::PathBuf,
3    pin::Pin,
4    task::{Context, Poll},
5};
6
7use futures::future::BoxFuture;
8use tokio::io::{AsyncRead, AsyncSeek, AsyncWrite, ReadBuf};
9
10use crate::{FileOpener, FileSystem, OpenOptionsConfig, VirtualFile};
11
12/// A [`FileSystem`] wrapper that will automatically log all operations at the
13/// `trace` level.
14///
15/// To see these logs, you will typically need to set the `$RUST_LOG`
16/// environment variable to `virtual_fs::trace_fs=trace`.
17#[derive(Debug, Clone, PartialEq, Eq)]
18pub struct TraceFileSystem<F>(pub F);
19
20impl<F> TraceFileSystem<F> {
21    pub fn new(filesystem: F) -> Self {
22        TraceFileSystem(filesystem)
23    }
24
25    pub fn inner(&self) -> &F {
26        &self.0
27    }
28
29    pub fn inner_mut(&mut self) -> &mut F {
30        &mut self.0
31    }
32
33    pub fn into_inner(self) -> F {
34        self.0
35    }
36}
37
38impl<F> FileSystem for TraceFileSystem<F>
39where
40    F: FileSystem,
41{
42    #[tracing::instrument(level = "trace", skip(self), err)]
43    fn readlink(&self, path: &std::path::Path) -> crate::Result<PathBuf> {
44        self.0.readlink(path)
45    }
46
47    #[tracing::instrument(level = "trace", skip(self), err)]
48    fn read_dir(&self, path: &std::path::Path) -> crate::Result<crate::ReadDir> {
49        self.0.read_dir(path)
50    }
51
52    #[tracing::instrument(level = "trace", skip(self), err)]
53    fn create_dir(&self, path: &std::path::Path) -> crate::Result<()> {
54        self.0.create_dir(path)
55    }
56
57    #[tracing::instrument(level = "trace", skip(self), err)]
58    fn remove_dir(&self, path: &std::path::Path) -> crate::Result<()> {
59        self.0.remove_dir(path)
60    }
61
62    #[tracing::instrument(level = "trace", skip(self), err)]
63    fn rename<'a>(
64        &'a self,
65        from: &'a std::path::Path,
66        to: &'a std::path::Path,
67    ) -> BoxFuture<'a, crate::Result<()>> {
68        Box::pin(async { self.0.rename(from, to).await })
69    }
70
71    #[tracing::instrument(level = "trace", skip(self), err)]
72    fn metadata(&self, path: &std::path::Path) -> crate::Result<crate::Metadata> {
73        self.0.metadata(path)
74    }
75
76    #[tracing::instrument(level = "trace", skip(self), err)]
77    fn symlink_metadata(&self, path: &std::path::Path) -> crate::Result<crate::Metadata> {
78        self.0.symlink_metadata(path)
79    }
80
81    #[tracing::instrument(level = "trace", skip(self), err)]
82    fn remove_file(&self, path: &std::path::Path) -> crate::Result<()> {
83        self.0.remove_file(path)
84    }
85
86    #[tracing::instrument(level = "trace", skip(self))]
87    fn new_open_options(&self) -> crate::OpenOptions<'_> {
88        crate::OpenOptions::new(self)
89    }
90}
91
92impl<F> FileOpener for TraceFileSystem<F>
93where
94    F: FileSystem,
95{
96    #[tracing::instrument(level = "trace", skip(self))]
97    fn open(
98        &self,
99        path: &std::path::Path,
100        conf: &OpenOptionsConfig,
101    ) -> crate::Result<Box<dyn crate::VirtualFile + Send + Sync + 'static>> {
102        let file = self.0.new_open_options().options(conf.clone()).open(path)?;
103        Ok(Box::new(TraceFile {
104            file,
105            path: path.to_owned(),
106        }))
107    }
108}
109
110#[derive(Debug)]
111struct TraceFile {
112    path: PathBuf,
113    file: Box<dyn crate::VirtualFile + Send + Sync + 'static>,
114}
115
116impl VirtualFile for TraceFile {
117    #[tracing::instrument(level = "trace", skip(self), fields(path=%self.path.display()))]
118    fn last_accessed(&self) -> u64 {
119        self.file.last_accessed()
120    }
121
122    #[tracing::instrument(level = "trace", skip(self), fields(path=%self.path.display()))]
123    fn last_modified(&self) -> u64 {
124        self.file.last_modified()
125    }
126
127    #[tracing::instrument(level = "trace", skip(self), fields(path=%self.path.display()))]
128    fn created_time(&self) -> u64 {
129        self.file.created_time()
130    }
131
132    #[tracing::instrument(level = "trace", skip(self), fields(path=%self.path.display()))]
133    fn set_times(&mut self, atime: Option<u64>, mtime: Option<u64>) -> crate::Result<()> {
134        self.file.set_times(atime, mtime)
135    }
136
137    #[tracing::instrument(level = "trace", skip(self), fields(path=%self.path.display()))]
138    fn size(&self) -> u64 {
139        self.file.size()
140    }
141
142    #[tracing::instrument(level = "trace", skip(self), fields(path=%self.path.display()), err)]
143    fn set_len(&mut self, new_size: u64) -> crate::Result<()> {
144        self.file.set_len(new_size)
145    }
146
147    fn unlink(&mut self) -> crate::Result<()> {
148        self.file.unlink()
149    }
150
151    #[tracing::instrument(level = "trace", skip_all, fields(path=%self.path.display()))]
152    fn poll_read_ready(
153        mut self: Pin<&mut Self>,
154        cx: &mut Context<'_>,
155    ) -> Poll<std::io::Result<usize>> {
156        let result = Pin::new(&mut *self.file).poll_read_ready(cx);
157
158        if let Poll::Ready(Err(e)) = &result {
159            tracing::trace!(error = e as &dyn std::error::Error);
160        }
161
162        result
163    }
164
165    #[tracing::instrument(level = "trace", skip_all, fields(path=%self.path.display()))]
166    fn poll_write_ready(
167        mut self: Pin<&mut Self>,
168        cx: &mut Context<'_>,
169    ) -> Poll<std::io::Result<usize>> {
170        let result = Pin::new(&mut *self.file).poll_write_ready(cx);
171
172        if let Poll::Ready(Err(e)) = &result {
173            tracing::trace!(error = e as &dyn std::error::Error);
174        }
175
176        result
177    }
178}
179
180impl AsyncRead for TraceFile {
181    #[tracing::instrument(level = "trace", skip_all, fields(path=%self.path.display()))]
182    fn poll_read(
183        mut self: Pin<&mut Self>,
184        cx: &mut Context<'_>,
185        buf: &mut ReadBuf<'_>,
186    ) -> Poll<std::io::Result<()>> {
187        let result = Pin::new(&mut *self.file).poll_read(cx, buf);
188
189        if let Poll::Ready(Err(e)) = &result {
190            tracing::trace!(error = e as &dyn std::error::Error);
191        }
192
193        result
194    }
195}
196
197impl AsyncWrite for TraceFile {
198    #[tracing::instrument(level = "trace", skip_all, fields(path=%self.path.display()))]
199    fn poll_write(
200        mut self: Pin<&mut Self>,
201        cx: &mut Context<'_>,
202        buf: &[u8],
203    ) -> Poll<Result<usize, std::io::Error>> {
204        let result = Pin::new(&mut *self.file).poll_write(cx, buf);
205
206        if let Poll::Ready(Err(e)) = &result {
207            tracing::trace!(error = e as &dyn std::error::Error);
208        }
209
210        result
211    }
212
213    #[tracing::instrument(level = "trace", skip_all, fields(path=%self.path.display()))]
214    fn poll_flush(
215        mut self: Pin<&mut Self>,
216        cx: &mut Context<'_>,
217    ) -> Poll<Result<(), std::io::Error>> {
218        let result = Pin::new(&mut *self.file).poll_flush(cx);
219
220        if let Poll::Ready(Err(e)) = &result {
221            tracing::trace!(error = e as &dyn std::error::Error);
222        }
223
224        result
225    }
226
227    #[tracing::instrument(level = "trace", skip_all, fields(path=%self.path.display()))]
228    fn poll_shutdown(
229        mut self: Pin<&mut Self>,
230        cx: &mut Context<'_>,
231    ) -> Poll<Result<(), std::io::Error>> {
232        let result = Pin::new(&mut *self.file).poll_shutdown(cx);
233
234        if let Poll::Ready(Err(e)) = &result {
235            tracing::trace!(error = e as &dyn std::error::Error);
236        }
237
238        result
239    }
240}
241
242impl AsyncSeek for TraceFile {
243    #[tracing::instrument(level = "trace", skip_all, fields(path=%self.path.display()), err)]
244    fn start_seek(mut self: Pin<&mut Self>, position: std::io::SeekFrom) -> std::io::Result<()> {
245        Pin::new(&mut *self.file).start_seek(position)
246    }
247
248    #[tracing::instrument(level = "trace", skip_all, fields(path=%self.path.display()))]
249    fn poll_complete(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<u64>> {
250        let result = Pin::new(&mut *self.file).poll_complete(cx);
251
252        if let Poll::Ready(Err(e)) = &result {
253            tracing::trace!(error = e as &dyn std::error::Error);
254        }
255
256        result
257    }
258}