From 5c65642ec0e7198c07b8b5e8af2fa0f4b3fb0780 Mon Sep 17 00:00:00 2001 From: Manuel Cillero Date: Wed, 3 Aug 2022 23:52:37 +0200 Subject: [PATCH] =?UTF-8?q?Modifica=20la=20definici=C3=B3n=20de=20apps=20c?= =?UTF-8?q?omo=20un=20m=C3=B3dulo=20m=C3=A1s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- drust/src/main.rs | 17 ++- pagetop-mdbook/Cargo.toml | 19 +++ pagetop-mdbook/src/lib.rs | 191 ++++++++++++++++++++++++++ pagetop/src/app.rs | 3 - pagetop/src/app/application.rs | 43 ++---- pagetop/src/app/definition.rs | 18 --- pagetop/src/core/module/all.rs | 128 +++++++++-------- pagetop/src/core/module/definition.rs | 15 +- pagetop/src/core/theme/all.rs | 12 +- pagetop/src/prelude.rs | 3 +- website/Cargo.toml | 1 + website/src/main.rs | 17 ++- website/src/mdbook.rs | 54 ++++++-- 13 files changed, 384 insertions(+), 137 deletions(-) create mode 100644 pagetop-mdbook/Cargo.toml create mode 100644 pagetop-mdbook/src/lib.rs delete mode 100644 pagetop/src/app/definition.rs diff --git a/drust/src/main.rs b/drust/src/main.rs index 5a1dd565..9f45fc65 100644 --- a/drust/src/main.rs +++ b/drust/src/main.rs @@ -1,24 +1,31 @@ use pagetop::prelude::*; +pub_const_handler!(APP_DRUST); + struct Drust; -impl AppTrait for Drust { - fn enable_modules(&self) -> Vec { +impl ModuleTrait for Drust { + fn handler(&self) -> Handler { + APP_DRUST + } + + fn dependencies(&self) -> Vec { vec![ &pagetop_admin::Admin, &pagetop_user::User, &pagetop_node::Node, + &pagetop::base::module::homepage::DefaultHomePage, ] } - fn disable_modules(&self) -> Vec { + fn uninstall_modules(&self) -> Vec { vec![ - // &pagetop_node::Node, + // &pagetop_node::Node ] } } #[actix_web::main] async fn main() -> std::io::Result<()> { - Application::prepare(Drust).await?.run()?.await + Application::prepare(&Drust).await?.run()?.await } diff --git a/pagetop-mdbook/Cargo.toml b/pagetop-mdbook/Cargo.toml new file mode 100644 index 00000000..1b792aef --- /dev/null +++ b/pagetop-mdbook/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "pagetop-mdbook" +version = "0.0.1" +edition = "2021" + +authors = [ + "Manuel Cillero " +] +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" } diff --git a/pagetop-mdbook/src/lib.rs b/pagetop-mdbook/src/lib.rs new file mode 100644 index 00000000..92924dcd --- /dev/null +++ b/pagetop-mdbook/src/lib.rs @@ -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 { + 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 = ""; + 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::() { + 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::() { + 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!("").map(|i| i + ini) { + let end = end - 1; + return Some(&from[ini..end]); + } + } + None +} diff --git a/pagetop/src/app.rs b/pagetop/src/app.rs index 114da34e..843a32b5 100644 --- a/pagetop/src/app.rs +++ b/pagetop/src/app.rs @@ -13,9 +13,6 @@ pub mod locale; #[cfg(feature = "database")] pub mod db; -mod definition; -pub use definition::AppTrait; - pub mod application; pub mod fatal_error; diff --git a/pagetop/src/app/application.rs b/pagetop/src/app/application.rs index e588cd6f..a5c18c4c 100644 --- a/pagetop/src/app/application.rs +++ b/pagetop/src/app/application.rs @@ -1,11 +1,13 @@ -use super::{fatal_error::FatalError, AppTrait}; +use super::fatal_error::FatalError; use crate::config::SETTINGS; +use crate::core::module::ModuleStaticRef; use crate::core::{module, theme}; use crate::html::Markup; use crate::response::page::ResultPage; -use crate::{base, trace, LazyStatic}; +use crate::LazyStatic; use actix_web::dev::Server; + use std::io::Error; pub struct Application { @@ -13,7 +15,7 @@ pub struct Application { } impl Application { - pub async fn prepare(app: impl AppTrait) -> Result { + pub async fn prepare(app: ModuleStaticRef) -> Result { // Rótulo de presentación. super::banner::print_on_startup(); @@ -23,40 +25,25 @@ impl Application { // Valida el identificador de idioma. LazyStatic::force(&super::locale::LANGID); - // Conecta con la base de datos (opcional). #[cfg(feature = "database")] + // Conecta con la base de datos. LazyStatic::force(&super::db::DBCONN); - // Deshabilita los módulos indicados por la aplicación. - module::all::disable_modules(app.disable_modules()); - // 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 módulos de la aplicación. + module::all::register_modules(app); - // Registra los temas predeterminados. - theme::all::register_themes(vec![ - &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 los temas de los módulos. + module::all::register_themes(); - // Registra las acciones de todos los módulos. + // Registra acciones de los módulos. module::all::register_actions(); - // Ejecuta actualizaciones pendientes de la base de datos (opcional). - #[cfg(feature = "database")] - module::all::run_migrations(); - - // Inicializa los módulos que lo requieran. + // Inicializa los módulos. module::all::init_modules(); - // Ejecuta la función de inicio de la aplicación. - trace::info!("Calling application bootstrap"); - app.bootstrap(); + #[cfg(feature = "database")] + // Ejecuta actualizaciones pendientes de la base de datos. + module::all::run_migrations(); // Prepara el servidor web. let server = super::HttpServer::new(move || { diff --git a/pagetop/src/app/definition.rs b/pagetop/src/app/definition.rs deleted file mode 100644 index 11eb50e6..00000000 --- a/pagetop/src/app/definition.rs +++ /dev/null @@ -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 { - vec![] - } - - fn disable_modules(&self) -> Vec { - vec![] - } - - fn themes(&self) -> Vec { - vec![] - } -} diff --git a/pagetop/src/core/module/all.rs b/pagetop/src/core/module/all.rs index c6cef12c..c4df3f0f 100644 --- a/pagetop/src/core/module/all.rs +++ b/pagetop/src/core/module/all.rs @@ -1,5 +1,6 @@ use super::ModuleStaticRef; use crate::core::hook::add_action; +use crate::core::theme; use crate::{app, trace, LazyStatic}; #[cfg(feature = "database")] @@ -7,47 +8,42 @@ use crate::{db::*, run_now}; use std::sync::RwLock; -// DISABLED MODULES ******************************************************************************** - -static DISABLED_MODULES: LazyStatic>> = - LazyStatic::new(|| RwLock::new(Vec::new())); - -pub fn disable_modules(modules: Vec) { - 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 ********************************************************************************* +// REGISTER MODULES ******************************************************************************** static ENABLED_MODULES: LazyStatic>> = LazyStatic::new(|| RwLock::new(Vec::new())); -pub fn enable_modules(modules: Vec) { - for module in modules { - let mut list: Vec = Vec::new(); - add_to_enabled(&mut list, module); - list.reverse(); - ENABLED_MODULES.write().unwrap().append(&mut list); +static DISCARDED_MODULES: LazyStatic>> = + LazyStatic::new(|| RwLock::new(Vec::new())); + +pub fn register_modules(app: ModuleStaticRef) { + // Revisa los módulos a deshabilitar. + let mut list: Vec = 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 = Vec::new(); + add_to_enabled(&mut list, app); + list.reverse(); + ENABLED_MODULES.write().unwrap().append(&mut list); +} + +fn add_to_discarded(list: &mut Vec, 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, module: ModuleStaticRef) { - if !ENABLED_MODULES - .read() - .unwrap() - .iter() - .any(|m| m.handler() == module.handler()) - && !list.iter().any(|m| m.handler() == module.handler()) - { - if DISABLED_MODULES + if !list.iter().any(|m| m.handler() == module.handler()) { + if DISCARDED_MODULES .read() .unwrap() .iter() @@ -58,7 +54,6 @@ fn add_to_enabled(list: &mut Vec, module: ModuleStaticRef) { module.single_name() ); } else { - trace::debug!("Enabling the \"{}\" module", module.single_name()); list.push(module); let mut dependencies = module.dependencies(); @@ -66,23 +61,21 @@ fn add_to_enabled(list: &mut Vec, module: ModuleStaticRef) { for d in dependencies.iter() { 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() { - m.init_module(); + theme::all::register_themes(m.themes()); } } -pub fn configure_services(cfg: &mut app::web::ServiceConfig) { - for m in ENABLED_MODULES.read().unwrap().iter() { - m.configure_service(cfg); - } -} +// REGISTER ACTIONS ******************************************************************************** pub fn register_actions() { for m in ENABLED_MODULES.read().unwrap().iter() { @@ -92,23 +85,10 @@ pub fn register_actions() { } } -#[cfg(feature = "database")] -pub(crate) fn run_migrations() { - run_now({ - struct Migrator; - impl MigratorTrait for Migrator { - fn migrations() -> Vec { - 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(); +// RUN MIGRATIONS ********************************************************************************** +#[cfg(feature = "database")] +pub fn run_migrations() { run_now({ struct Migrator; impl MigratorTrait for Migrator { @@ -123,4 +103,36 @@ pub(crate) fn run_migrations() { Migrator::up(&app::db::DBCONN, None) }) .unwrap(); + + run_now({ + struct Migrator; + impl MigratorTrait for Migrator { + fn migrations() -> Vec { + 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); + } } diff --git a/pagetop/src/core/module/definition.rs b/pagetop/src/core/module/definition.rs index 4e04050d..5ca54cc1 100644 --- a/pagetop/src/core/module/definition.rs +++ b/pagetop/src/core/module/definition.rs @@ -1,5 +1,6 @@ use crate::app; use crate::core::hook::HookAction; +use crate::core::theme::ThemeStaticRef; use crate::util::{single_type_name, Handler}; #[cfg(feature = "database")] @@ -27,10 +28,13 @@ pub trait ModuleTrait: BaseModule + Send + Sync { vec![] } - fn init_module(&self) {} + fn uninstall_modules(&self) -> Vec { + vec![] + } - #[allow(unused_variables)] - fn configure_service(&self, cfg: &mut app::web::ServiceConfig) {} + fn themes(&self) -> Vec { + vec![] + } fn actions(&self) -> Vec { vec![] @@ -41,6 +45,11 @@ pub trait ModuleTrait: BaseModule + Send + Sync { fn migrations(&self) -> Vec { vec![] } + + fn init(&self) {} + + #[allow(unused_variables)] + fn configure_service(&self, cfg: &mut app::web::ServiceConfig) {} } impl BaseModule for M { diff --git a/pagetop/src/core/theme/all.rs b/pagetop/src/core/theme/all.rs index 9ee3b095..99429838 100644 --- a/pagetop/src/core/theme/all.rs +++ b/pagetop/src/core/theme/all.rs @@ -1,13 +1,19 @@ use super::ThemeStaticRef; -use crate::{app, theme_static_files, trace, LazyStatic}; +use crate::{app, base, theme_static_files, trace, LazyStatic}; use std::sync::RwLock; include!(concat!(env!("OUT_DIR"), "/theme.rs")); // Temas registrados. -static THEMES: LazyStatic>> = - LazyStatic::new(|| RwLock::new(Vec::new())); +static THEMES: LazyStatic>> = LazyStatic::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) { let mut registered_themes = THEMES.write().unwrap(); diff --git a/pagetop/src/prelude.rs b/pagetop/src/prelude.rs index f0935e37..b2b83648 100644 --- a/pagetop/src/prelude.rs +++ b/pagetop/src/prelude.rs @@ -19,12 +19,13 @@ pub use crate::{db, db::*, migration_item, pub_migration}; pub use crate::app; pub use crate::app::application::Application; 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::{hook_action, hook_before_render_component}; pub use crate::response::page::*; +pub use crate::response::ResponseError; pub use crate::base::component::*; diff --git a/website/Cargo.toml b/website/Cargo.toml index cac77481..43e50777 100644 --- a/website/Cargo.toml +++ b/website/Cargo.toml @@ -19,6 +19,7 @@ path = "../pagetop" [dependencies] actix-web = "4.1.0" static-files = "0.2.3" +#pagetop-mdbook = { path = "../pagetop-mdbook" } maud = { git = "https://github.com/lambda-fairy/maud", rev = "e6787cd6" } [build-dependencies] diff --git a/website/src/main.rs b/website/src/main.rs index 80604748..a9736028 100644 --- a/website/src/main.rs +++ b/website/src/main.rs @@ -1,16 +1,25 @@ use pagetop::prelude::*; +pub_const_handler!(APP_PAGETOP_WEBSITE); + mod mdbook; struct PageTopWebSite; -impl AppTrait for PageTopWebSite { - fn enable_modules(&self) -> Vec { - vec![&mdbook::MdBook] +impl ModuleTrait for PageTopWebSite { + fn handler(&self) -> Handler { + APP_PAGETOP_WEBSITE + } + + fn dependencies(&self) -> Vec { + vec![ + &mdbook::MdBook, + &pagetop::base::module::homepage::DefaultHomePage, + ] } } #[actix_web::main] async fn main() -> std::io::Result<()> { - Application::prepare(PageTopWebSite).await?.run()?.await + Application::prepare(&PageTopWebSite).await?.run()?.await } diff --git a/website/src/mdbook.rs b/website/src/mdbook.rs index 23572368..13adc20b 100644 --- a/website/src/mdbook.rs +++ b/website/src/mdbook.rs @@ -5,12 +5,12 @@ use static_files::Resource; use std::collections::HashMap; +pub_const_handler!(MODULE_MDBOOK); + include!(concat!(env!("OUT_DIR"), "/mdbook.rs")); static MDBOOK: LazyStatic> = LazyStatic::new(generate); -pub_const_handler!(MODULE_MDBOOK); - pub struct MdBook; impl ModuleTrait for MdBook { @@ -19,19 +19,41 @@ impl ModuleTrait for MdBook { } fn configure_service(&self, cfg: &mut app::web::ServiceConfig) { - cfg.service( - app::web::scope("/doc") - .route("{tail:.*html$}", app::web::get().to(mdbook_page)) - .route("{tail:.*$}", app::web::get().to(mdbook_resource)), - ); + configure_mdbook_service(cfg, "/doc/", &MDBOOK); } } -async fn mdbook_page(request: app::HttpRequest) -> ResultPage { - // Remove initial "/doc/" from path: - let path = &request.path()[5..]; +fn configure_mdbook_service( + cfg: &mut app::web::ServiceConfig, + 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 { + // 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) { let _lang = extract("Lang", html); let title = match extract("Title", html) { @@ -93,13 +115,17 @@ async fn mdbook_page(request: app::HttpRequest) -> ResultPage app::HttpResponse { +async fn mdbook_resource( + request: app::HttpRequest, + v: usize, + book: &HashMap<&'static str, Resource>, +) -> app::HttpResponse { // 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 // 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!( "{:x}:{:x}", file.data.len(),