Modifica la definición de apps como un módulo más

This commit is contained in:
Manuel Cillero 2022-08-03 23:52:37 +02:00
parent 61813a44e5
commit 5c65642ec0
13 changed files with 384 additions and 137 deletions

View file

@ -1,24 +1,31 @@
use pagetop::prelude::*; use pagetop::prelude::*;
pub_const_handler!(APP_DRUST);
struct Drust; struct Drust;
impl AppTrait for Drust { impl ModuleTrait for Drust {
fn enable_modules(&self) -> Vec<ModuleStaticRef> { fn handler(&self) -> Handler {
APP_DRUST
}
fn dependencies(&self) -> Vec<ModuleStaticRef> {
vec![ vec![
&pagetop_admin::Admin, &pagetop_admin::Admin,
&pagetop_user::User, &pagetop_user::User,
&pagetop_node::Node, &pagetop_node::Node,
&pagetop::base::module::homepage::DefaultHomePage,
] ]
} }
fn disable_modules(&self) -> Vec<ModuleStaticRef> { fn uninstall_modules(&self) -> Vec<ModuleStaticRef> {
vec![ vec![
// &pagetop_node::Node, // &pagetop_node::Node
] ]
} }
} }
#[actix_web::main] #[actix_web::main]
async fn main() -> std::io::Result<()> { async fn main() -> std::io::Result<()> {
Application::prepare(Drust).await?.run()?.await Application::prepare(&Drust).await?.run()?.await
} }

19
pagetop-mdbook/Cargo.toml Normal file
View file

@ -0,0 +1,19 @@
[package]
name = "pagetop-mdbook"
version = "0.0.1"
edition = "2021"
authors = [
"Manuel Cillero <manuel@cillero.es>"
]
description = """\
...\
"""
homepage = "https://suitepro.cillero.es/projects/pagetop"
repository = "https://gitlab.com/manuelcillero/pagetop"
license = "Apache-2.0 or MIT"
[dependencies]
pagetop = { path = "../pagetop" }
static-files = "0.2.3"
maud = { git = "https://github.com/lambda-fairy/maud", rev = "e6787cd6" }

191
pagetop-mdbook/src/lib.rs Normal file
View file

@ -0,0 +1,191 @@
use pagetop::prelude::*;
pub type BookMapResources = std::collections::HashMap<&'static str, static_files::Resource>;
pub_const_handler!(MODULE_MDBOOK);
pub struct MdBook;
impl ModuleTrait for MdBook {
fn handler(&self) -> Handler {
MODULE_MDBOOK
}
}
impl MdBook {
pub fn configure_service_mdbook(
cfg: &mut app::web::ServiceConfig,
mdbook_path: &'static str,
mdbook_map: &'static BookMapResources,
) {
let path = mdbook_path.trim_end_matches('/');
cfg.service(
app::web::scope(path)
.route(
"{tail:.*html$}",
app::web::get().to(move |request: app::HttpRequest| {
mdbook_page(request, path, mdbook_map)
}),
)
.route(
"{tail:.*$}",
app::web::get().to(move |request: app::HttpRequest| {
mdbook_resource(request, path, mdbook_map)
}),
),
);
}
}
async fn mdbook_page(
request: app::HttpRequest,
mdbook_path: &'static str,
mdbook_map: &'static BookMapResources,
) -> ResultPage<Markup, FatalError> {
let path_len = mdbook_path.len() + 1;
if let Some(content) = mdbook_map.get(&request.path()[path_len..]) {
if let Ok(html) = std::str::from_utf8(content.data) {
let _lang = extract("Lang", html);
let title = match extract("Title", html) {
Some(title) => title,
_ => "Documentación",
};
let _print = matches!(extract("Print", html), Some("enabled"));
let _mathjax = matches!(extract("MathJax", html), Some("supported"));
let beginning = {
let separator = "<!-- mdBook -->";
match html.find(separator) {
Some(pos) => pos + separator.len(),
_ => 0,
}
};
Page::new()
.with_title(title)
.with_context(PageOp::AddMetadata("theme-color", "#ffffff"))
.with_context(PageOp::AddStyleSheet(StyleSheet::located(
"/doc/css/variables.css",
)))
.with_context(PageOp::AddStyleSheet(StyleSheet::located(
"/doc/css/general.css",
)))
.with_context(PageOp::AddStyleSheet(StyleSheet::located(
"/doc/css/chrome.css",
)))
.with_context(PageOp::AddStyleSheet(
StyleSheet::located("/doc/css/print.css").for_media(TargetMedia::Print),
))
.with_context(PageOp::AddStyleSheet(StyleSheet::located(
"/doc/FontAwesome/css/font-awesome.css",
)))
.with_context(PageOp::AddStyleSheet(StyleSheet::located(
"/doc/fonts/fonts.css",
)))
.with_context(PageOp::AddStyleSheet(StyleSheet::located(
"/doc/highlight.css",
)))
.with_context(PageOp::AddStyleSheet(StyleSheet::located(
"/doc/tomorrow-night.css",
)))
.with_context(PageOp::AddStyleSheet(StyleSheet::located(
"/doc/ayu-highlight.css",
)))
.add_to(
"region-content",
Container::new()
.with_id("mdbook")
.with_component(Html::with(html! { (PreEscaped(&html[beginning..])) })),
)
.render()
} else {
Err(FatalError::NotFound)
}
} else {
Err(FatalError::NotFound)
}
}
async fn mdbook_resource(
request: app::HttpRequest,
mdbook_path: &'static str,
mdbook_map: &'static BookMapResources,
) -> app::HttpResponse {
let path_len = mdbook_path.len() + 1;
// From https://github.com/kilork/actix-web-static-files/blob/master/src/resource_files.rs, see
// functions respond_to(), any_match() and none_match().
if let Some(file) = &mdbook_map.get(&request.path()[path_len..]) {
let etag = Some(app::http::header::EntityTag::new_strong(format!(
"{:x}:{:x}",
file.data.len(),
file.modified
)));
let precondition_failed = !any_match(etag.as_ref(), &request);
let not_modified = !none_match(etag.as_ref(), &request);
let mut resp = app::HttpResponse::build(app::http::StatusCode::OK);
resp.insert_header((app::http::header::CONTENT_TYPE, file.mime_type));
if let Some(etag) = etag {
resp.insert_header(app::http::header::ETag(etag));
}
if precondition_failed {
return FatalError::PreconditionFailed.error_response();
} else if not_modified {
return FatalError::NotModified.error_response();
}
resp.body(file.data)
} else {
FatalError::NotFound.error_response()
}
}
/// Returns true if `request` has no `If-Match` header or one which matches `etag`.
fn any_match(etag: Option<&app::http::header::EntityTag>, request: &app::HttpRequest) -> bool {
match request.get_header::<app::http::header::IfMatch>() {
None | Some(app::http::header::IfMatch::Any) => true,
Some(app::http::header::IfMatch::Items(ref items)) => {
if let Some(some_etag) = etag {
for item in items {
if item.strong_eq(some_etag) {
return true;
}
}
}
false
}
}
}
/// Returns true if `request` doesn't have an `If-None-Match` header matching `req`.
fn none_match(etag: Option<&app::http::header::EntityTag>, request: &app::HttpRequest) -> bool {
match request.get_header::<app::http::header::IfNoneMatch>() {
Some(app::http::header::IfNoneMatch::Any) => false,
Some(app::http::header::IfNoneMatch::Items(ref items)) => {
if let Some(some_etag) = etag {
for item in items {
if item.weak_eq(some_etag) {
return false;
}
}
}
true
}
None => true,
}
}
fn extract(attr: &'static str, from: &'static str) -> Option<&'static str> {
let search = concat_string!("<!-- ", attr, ":");
if let Some(ini) = from.find(&search) {
let ini = ini + search.len() + 1;
if let Some(end) = from[ini..].find("-->").map(|i| i + ini) {
let end = end - 1;
return Some(&from[ini..end]);
}
}
None
}

View file

@ -13,9 +13,6 @@ pub mod locale;
#[cfg(feature = "database")] #[cfg(feature = "database")]
pub mod db; pub mod db;
mod definition;
pub use definition::AppTrait;
pub mod application; pub mod application;
pub mod fatal_error; pub mod fatal_error;

View file

@ -1,11 +1,13 @@
use super::{fatal_error::FatalError, AppTrait}; use super::fatal_error::FatalError;
use crate::config::SETTINGS; use crate::config::SETTINGS;
use crate::core::module::ModuleStaticRef;
use crate::core::{module, theme}; use crate::core::{module, theme};
use crate::html::Markup; use crate::html::Markup;
use crate::response::page::ResultPage; use crate::response::page::ResultPage;
use crate::{base, trace, LazyStatic}; use crate::LazyStatic;
use actix_web::dev::Server; use actix_web::dev::Server;
use std::io::Error; use std::io::Error;
pub struct Application { pub struct Application {
@ -13,7 +15,7 @@ pub struct Application {
} }
impl Application { impl Application {
pub async fn prepare(app: impl AppTrait) -> Result<Self, Error> { pub async fn prepare(app: ModuleStaticRef) -> Result<Self, Error> {
// Rótulo de presentación. // Rótulo de presentación.
super::banner::print_on_startup(); super::banner::print_on_startup();
@ -23,40 +25,25 @@ impl Application {
// Valida el identificador de idioma. // Valida el identificador de idioma.
LazyStatic::force(&super::locale::LANGID); LazyStatic::force(&super::locale::LANGID);
// Conecta con la base de datos (opcional).
#[cfg(feature = "database")] #[cfg(feature = "database")]
// Conecta con la base de datos.
LazyStatic::force(&super::db::DBCONN); LazyStatic::force(&super::db::DBCONN);
// Deshabilita los módulos indicados por la aplicación. // Registra los módulos de la aplicación.
module::all::disable_modules(app.disable_modules()); module::all::register_modules(app);
// Habilita los módulos predeterminados.
module::all::enable_modules(vec![&base::module::homepage::DefaultHomePage]);
// Habilita los módulos de la aplicación.
module::all::enable_modules(app.enable_modules());
// Registra los temas predeterminados. // Registra los temas de los módulos.
theme::all::register_themes(vec![ module::all::register_themes();
&base::theme::aliner::Aliner,
&base::theme::minimal::Minimal,
&base::theme::bootsier::Bootsier,
&base::theme::bulmix::Bulmix,
]);
// Registra los temas de la aplicación.
theme::all::register_themes(app.themes());
// Registra las acciones de todos los módulos. // Registra acciones de los módulos.
module::all::register_actions(); module::all::register_actions();
// Ejecuta actualizaciones pendientes de la base de datos (opcional). // Inicializa los módulos.
#[cfg(feature = "database")]
module::all::run_migrations();
// Inicializa los módulos que lo requieran.
module::all::init_modules(); module::all::init_modules();
// Ejecuta la función de inicio de la aplicación. #[cfg(feature = "database")]
trace::info!("Calling application bootstrap"); // Ejecuta actualizaciones pendientes de la base de datos.
app.bootstrap(); module::all::run_migrations();
// Prepara el servidor web. // Prepara el servidor web.
let server = super::HttpServer::new(move || { let server = super::HttpServer::new(move || {

View file

@ -1,18 +0,0 @@
use crate::core::module::ModuleStaticRef;
use crate::core::theme::ThemeStaticRef;
pub trait AppTrait: Send + Sync {
fn bootstrap(&self) {}
fn enable_modules(&self) -> Vec<ModuleStaticRef> {
vec![]
}
fn disable_modules(&self) -> Vec<ModuleStaticRef> {
vec![]
}
fn themes(&self) -> Vec<ThemeStaticRef> {
vec![]
}
}

View file

@ -1,5 +1,6 @@
use super::ModuleStaticRef; use super::ModuleStaticRef;
use crate::core::hook::add_action; use crate::core::hook::add_action;
use crate::core::theme;
use crate::{app, trace, LazyStatic}; use crate::{app, trace, LazyStatic};
#[cfg(feature = "database")] #[cfg(feature = "database")]
@ -7,47 +8,42 @@ use crate::{db::*, run_now};
use std::sync::RwLock; use std::sync::RwLock;
// DISABLED MODULES ******************************************************************************** // REGISTER MODULES ********************************************************************************
static DISABLED_MODULES: LazyStatic<RwLock<Vec<ModuleStaticRef>>> =
LazyStatic::new(|| RwLock::new(Vec::new()));
pub fn disable_modules(modules: Vec<ModuleStaticRef>) {
let mut disabled_modules = DISABLED_MODULES.write().unwrap();
for module in modules {
if !disabled_modules
.iter()
.any(|m| m.handler() == module.handler())
{
trace::debug!("Disabling the \"{}\" module", module.single_name());
disabled_modules.push(module);
}
}
}
// ENABLED MODULES *********************************************************************************
static ENABLED_MODULES: LazyStatic<RwLock<Vec<ModuleStaticRef>>> = static ENABLED_MODULES: LazyStatic<RwLock<Vec<ModuleStaticRef>>> =
LazyStatic::new(|| RwLock::new(Vec::new())); LazyStatic::new(|| RwLock::new(Vec::new()));
pub fn enable_modules(modules: Vec<ModuleStaticRef>) { static DISCARDED_MODULES: LazyStatic<RwLock<Vec<ModuleStaticRef>>> =
for module in modules { LazyStatic::new(|| RwLock::new(Vec::new()));
let mut list: Vec<ModuleStaticRef> = Vec::new();
add_to_enabled(&mut list, module); pub fn register_modules(app: ModuleStaticRef) {
list.reverse(); // Revisa los módulos a deshabilitar.
ENABLED_MODULES.write().unwrap().append(&mut list); let mut list: Vec<ModuleStaticRef> = Vec::new();
add_to_discarded(&mut list, app);
DISCARDED_MODULES.write().unwrap().append(&mut list);
// Habilita los módulos de la aplicación.
let mut list: Vec<ModuleStaticRef> = Vec::new();
add_to_enabled(&mut list, app);
list.reverse();
ENABLED_MODULES.write().unwrap().append(&mut list);
}
fn add_to_discarded(list: &mut Vec<ModuleStaticRef>, module: ModuleStaticRef) {
for u in module.uninstall_modules().iter() {
if !list.iter().any(|m| m.handler() == u.handler()) {
list.push(*u);
trace::debug!("Module \"{}\" discarded", u.single_name());
}
}
for d in module.dependencies().iter() {
add_to_discarded(list, *d);
} }
} }
fn add_to_enabled(list: &mut Vec<ModuleStaticRef>, module: ModuleStaticRef) { fn add_to_enabled(list: &mut Vec<ModuleStaticRef>, module: ModuleStaticRef) {
if !ENABLED_MODULES if !list.iter().any(|m| m.handler() == module.handler()) {
.read() if DISCARDED_MODULES
.unwrap()
.iter()
.any(|m| m.handler() == module.handler())
&& !list.iter().any(|m| m.handler() == module.handler())
{
if DISABLED_MODULES
.read() .read()
.unwrap() .unwrap()
.iter() .iter()
@ -58,7 +54,6 @@ fn add_to_enabled(list: &mut Vec<ModuleStaticRef>, module: ModuleStaticRef) {
module.single_name() module.single_name()
); );
} else { } else {
trace::debug!("Enabling the \"{}\" module", module.single_name());
list.push(module); list.push(module);
let mut dependencies = module.dependencies(); let mut dependencies = module.dependencies();
@ -66,23 +61,21 @@ fn add_to_enabled(list: &mut Vec<ModuleStaticRef>, module: ModuleStaticRef) {
for d in dependencies.iter() { for d in dependencies.iter() {
add_to_enabled(list, *d); add_to_enabled(list, *d);
} }
trace::debug!("Enabling \"{}\" module", module.single_name());
} }
} }
} }
// CONFIGURE MODULES ******************************************************************************* // REGISTER THEMES *********************************************************************************
pub fn init_modules() { pub fn register_themes() {
for m in ENABLED_MODULES.read().unwrap().iter() { for m in ENABLED_MODULES.read().unwrap().iter() {
m.init_module(); theme::all::register_themes(m.themes());
} }
} }
pub fn configure_services(cfg: &mut app::web::ServiceConfig) { // REGISTER ACTIONS ********************************************************************************
for m in ENABLED_MODULES.read().unwrap().iter() {
m.configure_service(cfg);
}
}
pub fn register_actions() { pub fn register_actions() {
for m in ENABLED_MODULES.read().unwrap().iter() { for m in ENABLED_MODULES.read().unwrap().iter() {
@ -92,23 +85,10 @@ pub fn register_actions() {
} }
} }
#[cfg(feature = "database")] // RUN MIGRATIONS **********************************************************************************
pub(crate) fn run_migrations() {
run_now({
struct Migrator;
impl MigratorTrait for Migrator {
fn migrations() -> Vec<MigrationItem> {
let mut migrations = vec![];
for m in DISABLED_MODULES.read().unwrap().iter() {
migrations.append(&mut m.migrations());
}
migrations
}
}
Migrator::down(&app::db::DBCONN, None)
})
.unwrap();
#[cfg(feature = "database")]
pub fn run_migrations() {
run_now({ run_now({
struct Migrator; struct Migrator;
impl MigratorTrait for Migrator { impl MigratorTrait for Migrator {
@ -123,4 +103,36 @@ pub(crate) fn run_migrations() {
Migrator::up(&app::db::DBCONN, None) Migrator::up(&app::db::DBCONN, None)
}) })
.unwrap(); .unwrap();
run_now({
struct Migrator;
impl MigratorTrait for Migrator {
fn migrations() -> Vec<MigrationItem> {
let mut migrations = vec![];
for m in DISCARDED_MODULES.read().unwrap().iter() {
migrations.append(&mut m.migrations());
}
migrations
}
}
Migrator::down(&app::db::DBCONN, None)
})
.unwrap();
}
// INIT MODULES ************************************************************************************
pub fn init_modules() {
trace::info!("Calling application bootstrap");
for m in ENABLED_MODULES.read().unwrap().iter() {
m.init();
}
}
// CONFIGURE SERVICES ******************************************************************************
pub fn configure_services(cfg: &mut app::web::ServiceConfig) {
for m in ENABLED_MODULES.read().unwrap().iter() {
m.configure_service(cfg);
}
} }

View file

@ -1,5 +1,6 @@
use crate::app; use crate::app;
use crate::core::hook::HookAction; use crate::core::hook::HookAction;
use crate::core::theme::ThemeStaticRef;
use crate::util::{single_type_name, Handler}; use crate::util::{single_type_name, Handler};
#[cfg(feature = "database")] #[cfg(feature = "database")]
@ -27,10 +28,13 @@ pub trait ModuleTrait: BaseModule + Send + Sync {
vec![] vec![]
} }
fn init_module(&self) {} fn uninstall_modules(&self) -> Vec<ModuleStaticRef> {
vec![]
}
#[allow(unused_variables)] fn themes(&self) -> Vec<ThemeStaticRef> {
fn configure_service(&self, cfg: &mut app::web::ServiceConfig) {} vec![]
}
fn actions(&self) -> Vec<HookAction> { fn actions(&self) -> Vec<HookAction> {
vec![] vec![]
@ -41,6 +45,11 @@ pub trait ModuleTrait: BaseModule + Send + Sync {
fn migrations(&self) -> Vec<MigrationItem> { fn migrations(&self) -> Vec<MigrationItem> {
vec![] vec![]
} }
fn init(&self) {}
#[allow(unused_variables)]
fn configure_service(&self, cfg: &mut app::web::ServiceConfig) {}
} }
impl<M: ?Sized + ModuleTrait> BaseModule for M { impl<M: ?Sized + ModuleTrait> BaseModule for M {

View file

@ -1,13 +1,19 @@
use super::ThemeStaticRef; use super::ThemeStaticRef;
use crate::{app, theme_static_files, trace, LazyStatic}; use crate::{app, base, theme_static_files, trace, LazyStatic};
use std::sync::RwLock; use std::sync::RwLock;
include!(concat!(env!("OUT_DIR"), "/theme.rs")); include!(concat!(env!("OUT_DIR"), "/theme.rs"));
// Temas registrados. // Temas registrados.
static THEMES: LazyStatic<RwLock<Vec<ThemeStaticRef>>> = static THEMES: LazyStatic<RwLock<Vec<ThemeStaticRef>>> = LazyStatic::new(|| {
LazyStatic::new(|| RwLock::new(Vec::new())); RwLock::new(vec![
&base::theme::aliner::Aliner,
&base::theme::minimal::Minimal,
&base::theme::bootsier::Bootsier,
&base::theme::bulmix::Bulmix,
])
});
pub fn register_themes(themes: Vec<ThemeStaticRef>) { pub fn register_themes(themes: Vec<ThemeStaticRef>) {
let mut registered_themes = THEMES.write().unwrap(); let mut registered_themes = THEMES.write().unwrap();

View file

@ -19,12 +19,13 @@ pub use crate::{db, db::*, migration_item, pub_migration};
pub use crate::app; pub use crate::app;
pub use crate::app::application::Application; pub use crate::app::application::Application;
pub use crate::app::fatal_error::FatalError; pub use crate::app::fatal_error::FatalError;
pub use crate::app::{AppTrait, HttpMessage}; pub use crate::app::HttpMessage;
pub use crate::core::{component::*, hook::*, module::*, theme::*}; pub use crate::core::{component::*, hook::*, module::*, theme::*};
pub use crate::{hook_action, hook_before_render_component}; pub use crate::{hook_action, hook_before_render_component};
pub use crate::response::page::*; pub use crate::response::page::*;
pub use crate::response::ResponseError;
pub use crate::base::component::*; pub use crate::base::component::*;

View file

@ -19,6 +19,7 @@ path = "../pagetop"
[dependencies] [dependencies]
actix-web = "4.1.0" actix-web = "4.1.0"
static-files = "0.2.3" static-files = "0.2.3"
#pagetop-mdbook = { path = "../pagetop-mdbook" }
maud = { git = "https://github.com/lambda-fairy/maud", rev = "e6787cd6" } maud = { git = "https://github.com/lambda-fairy/maud", rev = "e6787cd6" }
[build-dependencies] [build-dependencies]

View file

@ -1,16 +1,25 @@
use pagetop::prelude::*; use pagetop::prelude::*;
pub_const_handler!(APP_PAGETOP_WEBSITE);
mod mdbook; mod mdbook;
struct PageTopWebSite; struct PageTopWebSite;
impl AppTrait for PageTopWebSite { impl ModuleTrait for PageTopWebSite {
fn enable_modules(&self) -> Vec<ModuleStaticRef> { fn handler(&self) -> Handler {
vec![&mdbook::MdBook] APP_PAGETOP_WEBSITE
}
fn dependencies(&self) -> Vec<ModuleStaticRef> {
vec![
&mdbook::MdBook,
&pagetop::base::module::homepage::DefaultHomePage,
]
} }
} }
#[actix_web::main] #[actix_web::main]
async fn main() -> std::io::Result<()> { async fn main() -> std::io::Result<()> {
Application::prepare(PageTopWebSite).await?.run()?.await Application::prepare(&PageTopWebSite).await?.run()?.await
} }

View file

@ -5,12 +5,12 @@ use static_files::Resource;
use std::collections::HashMap; use std::collections::HashMap;
pub_const_handler!(MODULE_MDBOOK);
include!(concat!(env!("OUT_DIR"), "/mdbook.rs")); include!(concat!(env!("OUT_DIR"), "/mdbook.rs"));
static MDBOOK: LazyStatic<HashMap<&'static str, Resource>> = LazyStatic::new(generate); static MDBOOK: LazyStatic<HashMap<&'static str, Resource>> = LazyStatic::new(generate);
pub_const_handler!(MODULE_MDBOOK);
pub struct MdBook; pub struct MdBook;
impl ModuleTrait for MdBook { impl ModuleTrait for MdBook {
@ -19,19 +19,41 @@ impl ModuleTrait for MdBook {
} }
fn configure_service(&self, cfg: &mut app::web::ServiceConfig) { fn configure_service(&self, cfg: &mut app::web::ServiceConfig) {
cfg.service( configure_mdbook_service(cfg, "/doc/", &MDBOOK);
app::web::scope("/doc")
.route("{tail:.*html$}", app::web::get().to(mdbook_page))
.route("{tail:.*$}", app::web::get().to(mdbook_resource)),
);
} }
} }
async fn mdbook_page(request: app::HttpRequest) -> ResultPage<Markup, FatalError> { fn configure_mdbook_service(
// Remove initial "/doc/" from path: cfg: &mut app::web::ServiceConfig,
let path = &request.path()[5..]; url_root: &'static str,
book: &'static HashMap<&'static str, Resource>,
) {
let url = url_root.trim_end_matches('/');
let url_len = url.len() + 1;
cfg.service(
app::web::scope(url)
.route(
"{tail:.*html$}",
app::web::get()
.to(move |request: app::HttpRequest| mdbook_page(request, url_len, book)),
)
.route(
"{tail:.*$}",
app::web::get()
.to(move |request: app::HttpRequest| mdbook_resource(request, url_len, book)),
),
);
}
if let Some(content) = MDBOOK.get(path) { async fn mdbook_page(
request: app::HttpRequest,
v: usize,
book: &HashMap<&'static str, Resource>,
) -> ResultPage<Markup, FatalError> {
// Remove initial "/doc/" from path:
let path = &request.path()[v..];
if let Some(content) = book.get(path) {
if let Ok(html) = std::str::from_utf8(content.data) { if let Ok(html) = std::str::from_utf8(content.data) {
let _lang = extract("Lang", html); let _lang = extract("Lang", html);
let title = match extract("Title", html) { let title = match extract("Title", html) {
@ -93,13 +115,17 @@ async fn mdbook_page(request: app::HttpRequest) -> ResultPage<Markup, FatalError
} }
} }
async fn mdbook_resource(request: app::HttpRequest) -> app::HttpResponse { async fn mdbook_resource(
request: app::HttpRequest,
v: usize,
book: &HashMap<&'static str, Resource>,
) -> app::HttpResponse {
// Remove initial "/doc/" from path: // Remove initial "/doc/" from path:
let path = &request.path()[5..]; let path = &request.path()[v..];
// From https://github.com/kilork/actix-web-static-files/blob/master/src/resource_files.rs, see // From https://github.com/kilork/actix-web-static-files/blob/master/src/resource_files.rs, see
// functions respond_to(), any_match() and none_match(). // functions respond_to(), any_match() and none_match().
if let Some(file) = &MDBOOK.get(path) { if let Some(file) = &book.get(path) {
let etag = Some(app::http::header::EntityTag::new_strong(format!( let etag = Some(app::http::header::EntityTag::new_strong(format!(
"{:x}:{:x}", "{:x}:{:x}",
file.data.len(), file.data.len(),