forked from http-server-rs/http-server
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfile.rs
106 lines (90 loc) · 2.75 KB
/
file.rs
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
use chrono::{DateTime, Local};
use color_eyre::eyre::Context;
use futures::Stream;
use hyper::body::Bytes;
use mime_guess::{from_path, Mime};
use std::fs::Metadata;
use std::mem::MaybeUninit;
use std::path::PathBuf;
use std::pin::Pin;
use std::task::{self, Poll};
use tokio::io::{AsyncRead, ReadBuf};
pub const FILE_BUFFER_SIZE: usize = 8 * 1024;
pub type FileBuffer = Box<[MaybeUninit<u8>; FILE_BUFFER_SIZE]>;
/// Wrapper around `tokio::fs::File` built from a OS ScopedFileSystem file
/// providing `std::fs::Metadata` and the path to such file
#[derive(Debug)]
pub struct File {
pub path: PathBuf,
pub file: tokio::fs::File,
pub metadata: Metadata,
}
impl File {
pub fn new(path: PathBuf, file: tokio::fs::File, metadata: Metadata) -> Self {
File {
path,
file,
metadata,
}
}
pub fn mime(&self) -> Mime {
from_path(self.path.clone()).first_or_octet_stream()
}
pub fn size(&self) -> u64 {
self.metadata.len()
}
pub fn last_modified(&self) -> color_eyre::Result<DateTime<Local>> {
let modified = self
.metadata
.modified()
.context("Failed to read last modified time for file")?;
let modified: DateTime<Local> = modified.into();
Ok(modified)
}
#[allow(dead_code)]
pub fn bytes(self) -> Vec<u8> {
let byte_stream = ByteStream {
file: self.file,
buffer: Box::new([MaybeUninit::uninit(); FILE_BUFFER_SIZE]),
};
byte_stream
.buffer
.iter()
.map(|muint| unsafe { muint.assume_init() })
.collect::<Vec<u8>>()
}
}
pub struct ByteStream {
file: tokio::fs::File,
buffer: FileBuffer,
}
impl From<File> for ByteStream {
fn from(file: File) -> Self {
ByteStream {
file: file.file,
buffer: Box::new([MaybeUninit::uninit(); FILE_BUFFER_SIZE]),
}
}
}
impl Stream for ByteStream {
type Item = color_eyre::Result<Bytes>;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Option<Self::Item>> {
let ByteStream {
ref mut file,
ref mut buffer,
} = *self;
let mut read_buffer = ReadBuf::uninit(&mut buffer[..]);
match Pin::new(file).poll_read(cx, &mut read_buffer) {
Poll::Ready(Ok(())) => {
let filled = read_buffer.filled();
if filled.is_empty() {
Poll::Ready(None)
} else {
Poll::Ready(Some(Ok(Bytes::copy_from_slice(filled))))
}
}
Poll::Ready(Err(error)) => Poll::Ready(Some(Err(error.into()))),
Poll::Pending => Poll::Pending,
}
}
}