1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
use std::{
    convert::TryInto,
    io::{self, Cursor},
    pin::Pin,
    task::{Context, Poll},
};

use shared_buffer::OwnedBuffer;
use tokio::io::{AsyncRead, AsyncSeek, AsyncWrite};

use crate::{FsError, VirtualFile};

/// An immutable file backed by an [`OwnedBuffer`].
#[derive(Debug, Clone, PartialEq)]
pub struct StaticFile(Cursor<OwnedBuffer>);

impl StaticFile {
    pub fn new(bytes: impl Into<OwnedBuffer>) -> Self {
        StaticFile(Cursor::new(bytes.into()))
    }

    /// Access the underlying buffer.
    pub fn contents(&self) -> &OwnedBuffer {
        self.0.get_ref()
    }
}

#[async_trait::async_trait]
impl VirtualFile for StaticFile {
    fn last_accessed(&self) -> u64 {
        0
    }

    fn last_modified(&self) -> u64 {
        0
    }

    fn created_time(&self) -> u64 {
        0
    }

    fn size(&self) -> u64 {
        self.0.get_ref().len().try_into().unwrap()
    }

    fn set_len(&mut self, _new_size: u64) -> Result<(), FsError> {
        Err(FsError::PermissionDenied)
    }

    fn unlink(&mut self) -> Result<(), FsError> {
        Err(FsError::PermissionDenied)
    }

    fn poll_read_ready(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<usize>> {
        let remaining = self.size() - self.0.position();
        Poll::Ready(Ok(remaining.try_into().unwrap()))
    }

    fn poll_write_ready(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<usize>> {
        Poll::Ready(Err(std::io::ErrorKind::PermissionDenied.into()))
    }
}

impl AsyncRead for StaticFile {
    fn poll_read(
        mut self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &mut tokio::io::ReadBuf<'_>,
    ) -> Poll<io::Result<()>> {
        Pin::new(&mut self.0).poll_read(cx, buf)
    }
}

// WebC file is not writable, the FileOpener will return a MemoryFile for writing instead
// This code should never be executed (since writes are redirected to memory instead).
impl AsyncWrite for StaticFile {
    fn poll_write(
        self: Pin<&mut Self>,
        _cx: &mut Context<'_>,
        _buf: &[u8],
    ) -> Poll<io::Result<usize>> {
        Poll::Ready(Err(std::io::ErrorKind::PermissionDenied.into()))
    }
    fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
        Poll::Ready(Ok(()))
    }
    fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
        Poll::Ready(Ok(()))
    }
}

impl AsyncSeek for StaticFile {
    fn start_seek(mut self: Pin<&mut Self>, pos: io::SeekFrom) -> io::Result<()> {
        Pin::new(&mut self.0).start_seek(pos)
    }

    fn poll_complete(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<u64>> {
        Pin::new(&mut self.0).poll_complete(cx)
    }
}

#[cfg(test)]
mod tests {
    use tokio::io::AsyncReadExt;

    use super::*;

    #[tokio::test]
    async fn read_a_static_file_to_end() {
        let mut file = StaticFile::new(OwnedBuffer::from_static(b"Hello, World!"));
        let mut buffer = [0; 5];

        let bytes_read = file.read(&mut buffer).await.unwrap();
        assert_eq!(bytes_read, 5);
        assert_eq!(&buffer[..bytes_read], b"Hello");
        assert_eq!(file.0.position(), 5);

        let bytes_read = file.read(&mut buffer).await.unwrap();
        assert_eq!(bytes_read, 5);
        assert_eq!(&buffer[..bytes_read], b", Wor");
        assert_eq!(file.0.position(), 10);

        let bytes_read = file.read(&mut buffer).await.unwrap();
        assert_eq!(bytes_read, 3);
        assert_eq!(&buffer[..bytes_read], b"ld!");
        assert_eq!(file.0.position(), 13);

        let bytes_read = file.read(&mut buffer).await.unwrap();
        assert_eq!(bytes_read, 0);
        assert_eq!(&buffer[..bytes_read], b"");
        assert_eq!(file.0.position(), 13);
    }
}