use crate::into::IntoResponse;
use crate::Response;
use tokio::io;
use std::fmt;
use std::path::{Path, PathBuf};
use std::str::Utf8Error;
use percent_encoding::percent_decode_str;
mod file;
pub use file::File;
mod partial_file;
pub use partial_file::{PartialFile, Range};
mod caching;
pub use caching::Caching;
mod static_files;
pub use static_files::{
serve_file, StaticFile, StaticFileOwned, StaticFiles, StaticFilesOwned,
};
mod memory_files;
pub use memory_files::{serve_memory_file, MemoryFile};
#[macro_export]
macro_rules! memory_file {
($uri:expr, $path:expr) => {
$crate::fs::MemoryFile::new($uri, $path, include_bytes!($path))
};
($uri:expr, $path:expr, $duration:expr) => {
$crate::fs::MemoryFile::cache_with_age(
$uri,
$path,
include_bytes!($path),
$duration,
)
};
}
pub(crate) async fn with_file<P>(path: P) -> io::Result<Response>
where
P: AsRef<Path>,
{
File::open(path).await.map(|f| f.into_response())
}
pub(crate) async fn with_partial_file<P>(
path: P,
range: Range,
) -> io::Result<Response>
where
P: AsRef<Path>,
{
PartialFile::open(path, range)
.await
.map(|pf| pf.into_response())
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IntoPathBufError {
TraversalAttack,
InvalidCharacter,
Utf8(Utf8Error),
}
impl fmt::Display for IntoPathBufError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
impl std::error::Error for IntoPathBufError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Utf8(u) => Some(u),
_ => None,
}
}
}
impl From<Utf8Error> for IntoPathBufError {
fn from(e: Utf8Error) -> Self {
Self::Utf8(e)
}
}
pub trait IntoPathBuf {
fn into_path_buf(self) -> Result<PathBuf, IntoPathBufError>;
}
impl IntoPathBuf for &str {
fn into_path_buf(self) -> Result<PathBuf, IntoPathBufError> {
let mut path_buf = PathBuf::new();
for (i, part) in self.split('/').enumerate() {
match (i, part) {
(0, "") => continue,
(_, "..") => {
path_buf.pop();
}
(_, ".") => continue,
(_, p) => {
let dec = percent_decode_str(p).decode_utf8()?;
if dec.contains('\\')
|| dec.contains('/') || dec.starts_with('.')
{
return Err(IntoPathBufError::InvalidCharacter);
}
path_buf.push(dec.as_ref());
}
}
}
Ok(path_buf)
}
}