Skip to main content

moirai_async/fs/
file.rs

1use moirai_pal::fs::AsyncFile;
2use std::io::{self, SeekFrom};
3use std::path::{Path, PathBuf};
4use std::pin::Pin;
5use std::task::{Context, Poll};
6
7use crate::fs::options::FileOpenOptions;
8use crate::fs::stats::FileStats;
9use crate::io::{AsyncRead, AsyncWrite};
10
11/// High-performance async file handle with native implementation
12pub struct File {
13    inner: AsyncFile,
14    path: PathBuf,
15    stats: FileStats,
16}
17
18impl File {
19    /// Open a file with default options (read-only)
20    pub async fn open<P: AsRef<Path>>(path: P) -> io::Result<Self> {
21        Self::open_with_options(path, FileOpenOptions::read_only()).await
22    }
23
24    /// Create a new file for writing (truncates if exists)
25    pub async fn create<P: AsRef<Path>>(path: P) -> io::Result<Self> {
26        Self::open_with_options(path, FileOpenOptions::write_only()).await
27    }
28
29    /// Open a file with custom options
30    pub async fn open_with_options<P: AsRef<Path>>(
31        path: P,
32        options: FileOpenOptions,
33    ) -> io::Result<Self> {
34        let path_buf = path.as_ref().to_path_buf();
35        let inner = AsyncFile::open_with_options(
36            &path_buf,
37            options.read,
38            options.write,
39            options.create,
40            options.append,
41            options.truncate,
42        )
43        .await?;
44
45        Ok(Self {
46            inner,
47            path: path_buf,
48            stats: FileStats::default(),
49        })
50    }
51
52    /// Read entire file contents into a string
53    pub async fn read_to_string(&mut self) -> io::Result<String> {
54        let mut contents = String::new();
55        self.inner.read_to_string(&mut contents).await?;
56        self.stats.bytes_read += contents.len() as u64;
57        self.stats.read_operations += 1;
58        Ok(contents)
59    }
60
61    /// Read entire file contents into a byte vector
62    pub async fn read_to_end(&mut self) -> io::Result<Vec<u8>> {
63        let mut contents = Vec::new();
64        self.inner.read_to_end(&mut contents).await?;
65        self.stats.bytes_read += contents.len() as u64;
66        self.stats.read_operations += 1;
67        Ok(contents)
68    }
69
70    /// Read data into a buffer
71    pub async fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
72        let bytes_read = self.inner.read(buf).await?;
73        self.stats.bytes_read += bytes_read as u64;
74        self.stats.read_operations += 1;
75        Ok(bytes_read)
76    }
77
78    /// Write data from a buffer
79    pub async fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
80        let bytes_written = self.inner.write(buf).await?;
81        self.stats.bytes_written += bytes_written as u64;
82        self.stats.write_operations += 1;
83        Ok(bytes_written)
84    }
85
86    /// Write all data from a buffer
87    pub async fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
88        let mut written = 0;
89        while written < buf.len() {
90            let n = self.write(&buf[written..]).await?;
91            if n == 0 {
92                return Err(io::Error::new(
93                    io::ErrorKind::WriteZero,
94                    "failed to write whole buffer",
95                ));
96            }
97            written += n;
98        }
99        Ok(())
100    }
101
102    /// Write a string to the file
103    pub async fn write_str(&mut self, s: &str) -> io::Result<()> {
104        self.write_all(s.as_bytes()).await
105    }
106
107    /// Flush any buffered data to disk
108    pub async fn flush(&mut self) -> io::Result<()> {
109        self.inner.flush().await?;
110        Ok(())
111    }
112
113    /// Synchronize all data and metadata to disk
114    pub async fn sync_all(&mut self) -> io::Result<()> {
115        self.inner.sync_all().await?;
116        Ok(())
117    }
118
119    /// Synchronize data (but not metadata) to disk
120    pub async fn sync_data(&mut self) -> io::Result<()> {
121        self.inner.sync_data().await?;
122        Ok(())
123    }
124
125    /// Seek to a specific position in the file
126    pub async fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
127        let new_pos = self.inner.seek(pos).await?;
128        self.stats.seek_operations += 1;
129        Ok(new_pos)
130    }
131
132    /// Get current position in the file
133    pub async fn stream_position(&mut self) -> io::Result<u64> {
134        self.inner.seek(SeekFrom::Current(0)).await
135    }
136
137    /// Get file metadata
138    pub async fn metadata(&self) -> io::Result<std::fs::Metadata> {
139        self.inner.metadata().await
140    }
141
142    /// Get file path
143    pub fn path(&self) -> &Path {
144        &self.path
145    }
146
147    /// Get file statistics
148    pub fn stats(&self) -> &FileStats {
149        &self.stats
150    }
151}
152
153impl AsyncRead for File {
154    fn poll_read(
155        mut self: Pin<&mut Self>,
156        cx: &mut Context<'_>,
157        buf: &mut [u8],
158    ) -> Poll<io::Result<usize>> {
159        match Pin::new(&mut self.inner).poll_read(cx, buf) {
160            Poll::Ready(Ok(n)) => {
161                self.stats.bytes_read += n as u64;
162                self.stats.read_operations += 1;
163                Poll::Ready(Ok(n))
164            }
165            res => res,
166        }
167    }
168}
169
170impl AsyncWrite for File {
171    fn poll_write(
172        mut self: Pin<&mut Self>,
173        cx: &mut Context<'_>,
174        buf: &[u8],
175    ) -> Poll<io::Result<usize>> {
176        match Pin::new(&mut self.inner).poll_write(cx, buf) {
177            Poll::Ready(Ok(n)) => {
178                self.stats.bytes_written += n as u64;
179                self.stats.write_operations += 1;
180                Poll::Ready(Ok(n))
181            }
182            res => res,
183        }
184    }
185
186    fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
187        Pin::new(&mut self.inner).poll_flush(cx)
188    }
189
190    fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
191        Poll::Ready(Ok(()))
192    }
193}