1use std::{
4 error::Error,
5 pin::Pin,
6 task::{Context, Poll, ready},
7};
8
9use bytes::Bytes;
10use futures_core::{Stream, stream::BoxStream};
11use http_body::Frame;
12
13pub mod headers;
14pub mod http;
15
16pub trait Embed {
18 fn get(path: &str) -> impl Future<Output = std::io::Result<Embedded>> + Send + 'static;
20}
21
22pub struct Embedded {
24 pub content: Content,
26 pub metadata: Metadata,
28}
29
30pub type BoxError = Box<dyn Error + Send + Sync>;
32
33pub struct Content(BoxStream<'static, Result<Bytes, BoxError>>);
35
36impl Content {
37 pub fn from_static(bytes: &'static [u8]) -> Self {
39 Self(Box::pin(StaticContent::new(bytes)))
40 }
41
42 pub fn from_stream<S, E>(stream: S) -> Self
44 where
45 S: Stream<Item = Result<Bytes, E>> + Send + 'static,
46 E: Into<BoxError>,
47 {
48 Self(Box::pin(StreamContent(stream)))
49 }
50}
51
52impl Stream for Content {
53 type Item = Result<Frame<Bytes>, BoxError>;
54
55 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
56 self.0.as_mut().poll_next(cx).map_ok(Frame::data)
57 }
58}
59
60struct StaticContent(Option<&'static [u8]>);
61
62impl StaticContent {
63 pub fn new(bytes: &'static [u8]) -> Self {
64 Self(Some(bytes))
65 }
66}
67
68impl Stream for StaticContent {
69 type Item = Result<Bytes, BoxError>;
70
71 fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
72 self.0
73 .take()
74 .map(|bytes| Ok(Bytes::from_static(bytes)))
75 .into()
76 }
77}
78
79struct StreamContent<S>(S);
80
81impl<S, E> Stream for StreamContent<S>
82where
83 S: Stream<Item = Result<Bytes, E>>,
84 E: Into<BoxError>,
85{
86 type Item = Result<Bytes, BoxError>;
87
88 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
89 let inner = unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().0) };
90 match ready!(inner.poll_next(cx)) {
91 Some(Ok(bytes)) => Some(Ok(bytes)),
92 Some(Err(err)) => Some(Err(err.into())),
93 None => None,
94 }
95 .into()
96 }
97}
98
99#[derive(Clone, Debug)]
101pub struct Metadata {
102 pub content_type: headers::ContentType,
104 pub etag: Option<headers::ETag>,
106 pub last_modified: Option<headers::LastModified>,
108}
109
110pub fn last_modified(path: &std::path::Path) -> std::io::Result<headers::LastModified> {
112 std::fs::metadata(path)
113 .and_then(|metadata| metadata.modified())
114 .map(headers::LastModified)
115}
116
117pub fn content_type(path: &std::path::Path) -> headers::ContentType {
119 mime_guess::from_path(path)
120 .first()
121 .map(headers::ContentType)
122 .unwrap_or_else(headers::ContentType::octet_stream)
123}
124
125pub fn etag(content: &[u8]) -> headers::ETag {
127 use std::hash::Hasher;
128
129 let hash: u64 = {
130 let mut hasher = rapidhash::fast::RapidHasher::default_const();
131 hasher.write(content);
132 hasher.finish()
133 };
134
135 let etag = format!("{:016x}", hash);
136 headers::ETag::new(&etag).unwrap()
137}