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