1use std::{
2 path::{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 #[tracing::instrument(level = "trace", skip(self))]
92 fn mount(
93 &self,
94 name: String,
95 path: &Path,
96 fs: Box<dyn FileSystem + Send + Sync>,
97 ) -> crate::Result<()> {
98 self.0.mount(name, path, fs)
99 }
100}
101
102impl<F> FileOpener for TraceFileSystem<F>
103where
104 F: FileSystem,
105{
106 #[tracing::instrument(level = "trace", skip(self))]
107 fn open(
108 &self,
109 path: &std::path::Path,
110 conf: &OpenOptionsConfig,
111 ) -> crate::Result<Box<dyn crate::VirtualFile + Send + Sync + 'static>> {
112 let file = self.0.new_open_options().options(conf.clone()).open(path)?;
113 Ok(Box::new(TraceFile {
114 file,
115 path: path.to_owned(),
116 }))
117 }
118}
119
120#[derive(Debug)]
121struct TraceFile {
122 path: PathBuf,
123 file: Box<dyn crate::VirtualFile + Send + Sync + 'static>,
124}
125
126impl VirtualFile for TraceFile {
127 #[tracing::instrument(level = "trace", skip(self), fields(path=%self.path.display()))]
128 fn last_accessed(&self) -> u64 {
129 self.file.last_accessed()
130 }
131
132 #[tracing::instrument(level = "trace", skip(self), fields(path=%self.path.display()))]
133 fn last_modified(&self) -> u64 {
134 self.file.last_modified()
135 }
136
137 #[tracing::instrument(level = "trace", skip(self), fields(path=%self.path.display()))]
138 fn created_time(&self) -> u64 {
139 self.file.created_time()
140 }
141
142 #[tracing::instrument(level = "trace", skip(self), fields(path=%self.path.display()))]
143 fn set_times(&mut self, atime: Option<u64>, mtime: Option<u64>) -> crate::Result<()> {
144 self.file.set_times(atime, mtime)
145 }
146
147 #[tracing::instrument(level = "trace", skip(self), fields(path=%self.path.display()))]
148 fn size(&self) -> u64 {
149 self.file.size()
150 }
151
152 #[tracing::instrument(level = "trace", skip(self), fields(path=%self.path.display()), err)]
153 fn set_len(&mut self, new_size: u64) -> crate::Result<()> {
154 self.file.set_len(new_size)
155 }
156
157 fn unlink(&mut self) -> crate::Result<()> {
158 self.file.unlink()
159 }
160
161 #[tracing::instrument(level = "trace", skip_all, fields(path=%self.path.display()))]
162 fn poll_read_ready(
163 mut self: Pin<&mut Self>,
164 cx: &mut Context<'_>,
165 ) -> Poll<std::io::Result<usize>> {
166 let result = Pin::new(&mut *self.file).poll_read_ready(cx);
167
168 if let Poll::Ready(Err(e)) = &result {
169 tracing::trace!(error = e as &dyn std::error::Error);
170 }
171
172 result
173 }
174
175 #[tracing::instrument(level = "trace", skip_all, fields(path=%self.path.display()))]
176 fn poll_write_ready(
177 mut self: Pin<&mut Self>,
178 cx: &mut Context<'_>,
179 ) -> Poll<std::io::Result<usize>> {
180 let result = Pin::new(&mut *self.file).poll_write_ready(cx);
181
182 if let Poll::Ready(Err(e)) = &result {
183 tracing::trace!(error = e as &dyn std::error::Error);
184 }
185
186 result
187 }
188}
189
190impl AsyncRead for TraceFile {
191 #[tracing::instrument(level = "trace", skip_all, fields(path=%self.path.display()))]
192 fn poll_read(
193 mut self: Pin<&mut Self>,
194 cx: &mut Context<'_>,
195 buf: &mut ReadBuf<'_>,
196 ) -> Poll<std::io::Result<()>> {
197 let result = Pin::new(&mut *self.file).poll_read(cx, buf);
198
199 if let Poll::Ready(Err(e)) = &result {
200 tracing::trace!(error = e as &dyn std::error::Error);
201 }
202
203 result
204 }
205}
206
207impl AsyncWrite for TraceFile {
208 #[tracing::instrument(level = "trace", skip_all, fields(path=%self.path.display()))]
209 fn poll_write(
210 mut self: Pin<&mut Self>,
211 cx: &mut Context<'_>,
212 buf: &[u8],
213 ) -> Poll<Result<usize, std::io::Error>> {
214 let result = Pin::new(&mut *self.file).poll_write(cx, buf);
215
216 if let Poll::Ready(Err(e)) = &result {
217 tracing::trace!(error = e as &dyn std::error::Error);
218 }
219
220 result
221 }
222
223 #[tracing::instrument(level = "trace", skip_all, fields(path=%self.path.display()))]
224 fn poll_flush(
225 mut self: Pin<&mut Self>,
226 cx: &mut Context<'_>,
227 ) -> Poll<Result<(), std::io::Error>> {
228 let result = Pin::new(&mut *self.file).poll_flush(cx);
229
230 if let Poll::Ready(Err(e)) = &result {
231 tracing::trace!(error = e as &dyn std::error::Error);
232 }
233
234 result
235 }
236
237 #[tracing::instrument(level = "trace", skip_all, fields(path=%self.path.display()))]
238 fn poll_shutdown(
239 mut self: Pin<&mut Self>,
240 cx: &mut Context<'_>,
241 ) -> Poll<Result<(), std::io::Error>> {
242 let result = Pin::new(&mut *self.file).poll_shutdown(cx);
243
244 if let Poll::Ready(Err(e)) = &result {
245 tracing::trace!(error = e as &dyn std::error::Error);
246 }
247
248 result
249 }
250}
251
252impl AsyncSeek for TraceFile {
253 #[tracing::instrument(level = "trace", skip_all, fields(path=%self.path.display()), err)]
254 fn start_seek(mut self: Pin<&mut Self>, position: std::io::SeekFrom) -> std::io::Result<()> {
255 Pin::new(&mut *self.file).start_seek(position)
256 }
257
258 #[tracing::instrument(level = "trace", skip_all, fields(path=%self.path.display()))]
259 fn poll_complete(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<u64>> {
260 let result = Pin::new(&mut *self.file).poll_complete(cx);
261
262 if let Poll::Ready(Err(e)) = &result {
263 tracing::trace!(error = e as &dyn std::error::Error);
264 }
265
266 result
267 }
268}