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#[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}