use std::borrow::Cow;
use actix_files::{Files, NamedFile};
use actix_service::fn_service;
use actix_web::dev::{HttpServiceFactory, ResourceDef, ServiceRequest, ServiceResponse};
use tracing::trace;
#[cfg_attr(docsrs, doc(cfg(feature = "spa")))]
#[derive(Debug, Clone)]
pub struct Spa {
index_file: Cow<'static, str>,
static_resources_mount: Cow<'static, str>,
static_resources_location: Cow<'static, str>,
}
impl Spa {
pub fn index_file(mut self, index_file: impl Into<Cow<'static, str>>) -> Self {
self.index_file = index_file.into();
self
}
pub fn static_resources_mount(
mut self,
static_resources_mount: impl Into<Cow<'static, str>>,
) -> Self {
self.static_resources_mount = static_resources_mount.into();
self
}
pub fn static_resources_location(
mut self,
static_resources_location: impl Into<Cow<'static, str>>,
) -> Self {
self.static_resources_location = static_resources_location.into();
self
}
pub fn finish(self) -> impl HttpServiceFactory {
let index_file = self.index_file.into_owned();
let static_resources_location = self.static_resources_location.into_owned();
let static_resources_mount = self.static_resources_mount.into_owned();
let files = {
let index_file = index_file.clone();
Files::new(&static_resources_mount, static_resources_location)
.index_file("extremely-unlikely-to-exist-!@$%^&*.txt")
.default_handler(move |req| serve_index(req, index_file.clone()))
};
SpaService { index_file, files }
}
}
#[derive(Debug)]
struct SpaService {
index_file: String,
files: Files,
}
impl HttpServiceFactory for SpaService {
fn register(self, config: &mut actix_web::dev::AppService) {
self.files.register(config);
let rdef = ResourceDef::root_prefix("");
config.register_service(
rdef,
None,
fn_service(move |req| serve_index(req, self.index_file.clone())),
None,
);
}
}
async fn serve_index(
req: ServiceRequest,
index_file: String,
) -> Result<ServiceResponse, actix_web::Error> {
trace!("serving default SPA page");
let (req, _) = req.into_parts();
let file = NamedFile::open_async(&index_file).await?;
let res = file.into_response(&req);
Ok(ServiceResponse::new(req, res))
}
impl Default for Spa {
fn default() -> Self {
Self {
index_file: Cow::Borrowed("./index.html"),
static_resources_mount: Cow::Borrowed("/"),
static_resources_location: Cow::Borrowed("./"),
}
}
}