virtual_fs/
static_file.rs

1use std::{
2    convert::TryInto,
3    io::{self, Cursor},
4    pin::Pin,
5    task::{Context, Poll},
6};
7
8use shared_buffer::OwnedBuffer;
9use tokio::io::{AsyncRead, AsyncSeek, AsyncWrite};
10
11use crate::{FsError, VirtualFile};
12
13/// An immutable file backed by an [`OwnedBuffer`].
14#[derive(Debug, Clone, PartialEq)]
15pub struct StaticFile(Cursor<OwnedBuffer>);
16
17impl StaticFile {
18    pub fn new(bytes: impl Into<OwnedBuffer>) -> Self {
19        StaticFile(Cursor::new(bytes.into()))
20    }
21
22    /// Access the underlying buffer.
23    pub fn contents(&self) -> &OwnedBuffer {
24        self.0.get_ref()
25    }
26}
27
28#[async_trait::async_trait]
29impl VirtualFile for StaticFile {
30    fn last_accessed(&self) -> u64 {
31        0
32    }
33
34    fn last_modified(&self) -> u64 {
35        0
36    }
37
38    fn created_time(&self) -> u64 {
39        0
40    }
41
42    fn size(&self) -> u64 {
43        self.0.get_ref().len().try_into().unwrap()
44    }
45
46    fn set_len(&mut self, _new_size: u64) -> Result<(), FsError> {
47        Err(FsError::PermissionDenied)
48    }
49
50    fn unlink(&mut self) -> Result<(), FsError> {
51        Err(FsError::PermissionDenied)
52    }
53
54    fn poll_read_ready(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<usize>> {
55        let remaining = self.size() - self.0.position();
56        Poll::Ready(Ok(remaining.try_into().unwrap()))
57    }
58
59    fn poll_write_ready(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<usize>> {
60        Poll::Ready(Err(std::io::ErrorKind::PermissionDenied.into()))
61    }
62}
63
64impl AsyncRead for StaticFile {
65    fn poll_read(
66        mut self: Pin<&mut Self>,
67        cx: &mut Context<'_>,
68        buf: &mut tokio::io::ReadBuf<'_>,
69    ) -> Poll<io::Result<()>> {
70        Pin::new(&mut self.0).poll_read(cx, buf)
71    }
72}
73
74// WebC file is not writable, the FileOpener will return a MemoryFile for writing instead
75// This code should never be executed (since writes are redirected to memory instead).
76impl AsyncWrite for StaticFile {
77    fn poll_write(
78        self: Pin<&mut Self>,
79        _cx: &mut Context<'_>,
80        _buf: &[u8],
81    ) -> Poll<io::Result<usize>> {
82        Poll::Ready(Err(std::io::ErrorKind::PermissionDenied.into()))
83    }
84    fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
85        Poll::Ready(Ok(()))
86    }
87    fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
88        Poll::Ready(Ok(()))
89    }
90}
91
92impl AsyncSeek for StaticFile {
93    fn start_seek(mut self: Pin<&mut Self>, pos: io::SeekFrom) -> io::Result<()> {
94        Pin::new(&mut self.0).start_seek(pos)
95    }
96
97    fn poll_complete(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<u64>> {
98        Pin::new(&mut self.0).poll_complete(cx)
99    }
100}
101
102#[cfg(test)]
103mod tests {
104    use tokio::io::AsyncReadExt;
105
106    use super::*;
107
108    #[tokio::test]
109    async fn read_a_static_file_to_end() {
110        let mut file = StaticFile::new(OwnedBuffer::from_static(b"Hello, World!"));
111        let mut buffer = [0; 5];
112
113        let bytes_read = file.read(&mut buffer).await.unwrap();
114        assert_eq!(bytes_read, 5);
115        assert_eq!(&buffer[..bytes_read], b"Hello");
116        assert_eq!(file.0.position(), 5);
117
118        let bytes_read = file.read(&mut buffer).await.unwrap();
119        assert_eq!(bytes_read, 5);
120        assert_eq!(&buffer[..bytes_read], b", Wor");
121        assert_eq!(file.0.position(), 10);
122
123        let bytes_read = file.read(&mut buffer).await.unwrap();
124        assert_eq!(bytes_read, 3);
125        assert_eq!(&buffer[..bytes_read], b"ld!");
126        assert_eq!(file.0.position(), 13);
127
128        let bytes_read = file.read(&mut buffer).await.unwrap();
129        assert_eq!(bytes_read, 0);
130        assert_eq!(&buffer[..bytes_read], b"");
131        assert_eq!(file.0.position(), 13);
132    }
133}