From 14e2c0e6e440d024bba8ea3addaf0660cb447359 Mon Sep 17 00:00:00 2001 From: Matthijs van Otterdijk Date: Fri, 19 May 2017 19:47:53 +0200 Subject: [PATCH] rendering configuration from environment variables --- src/book/mod.rs | 4 - src/lib.rs | 1 + src/renderer/html_handlebars/hbs_renderer.rs | 45 +++- .../_FontAwesome/css/font-awesome.min.css | 0 .../_FontAwesome/fonts/FontAwesome.otf | Bin .../fonts/fontawesome-webfont.eot | Bin .../fonts/fontawesome-webfont.svg | 0 .../fonts/fontawesome-webfont.ttf | Bin .../fonts/fontawesome-webfont.woff | Bin .../fonts/fontawesome-webfont.woff2 | Bin src/resources/env_config.rs | 233 ++++++++++++++++++ src/{theme => resources}/highlight.js | 0 src/{theme => resources}/jquery-2.1.4.min.js | 0 src/resources/mod.rs | 105 ++++++++ src/theme/index.hbs | 31 ++- src/theme/mod.rs | 19 -- src/utils/env.rs | 146 +++++++++++ 17 files changed, 545 insertions(+), 39 deletions(-) rename src/{theme => resources}/_FontAwesome/css/font-awesome.min.css (100%) rename src/{theme => resources}/_FontAwesome/fonts/FontAwesome.otf (100%) rename src/{theme => resources}/_FontAwesome/fonts/fontawesome-webfont.eot (100%) rename src/{theme => resources}/_FontAwesome/fonts/fontawesome-webfont.svg (100%) rename src/{theme => resources}/_FontAwesome/fonts/fontawesome-webfont.ttf (100%) rename src/{theme => resources}/_FontAwesome/fonts/fontawesome-webfont.woff (100%) rename src/{theme => resources}/_FontAwesome/fonts/fontawesome-webfont.woff2 (100%) create mode 100644 src/resources/env_config.rs rename src/{theme => resources}/highlight.js (100%) rename src/{theme => resources}/jquery-2.1.4.min.js (100%) create mode 100644 src/resources/mod.rs create mode 100644 src/utils/env.rs diff --git a/src/book/mod.rs b/src/book/mod.rs index 0a0e1099..e7236fb9 100644 --- a/src/book/mod.rs +++ b/src/book/mod.rs @@ -300,10 +300,6 @@ impl MDBook { let mut highlight_css = File::create(&theme_dir.join("highlight.css"))?; highlight_css.write_all(theme::HIGHLIGHT_CSS)?; - // highlight.js - let mut highlight_js = File::create(&theme_dir.join("highlight.js"))?; - highlight_js.write_all(theme::HIGHLIGHT_JS)?; - Ok(()) } diff --git a/src/lib.rs b/src/lib.rs index 303bb23b..5be3e267 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -83,6 +83,7 @@ mod parse; pub mod renderer; pub mod theme; pub mod utils; +pub mod resources; pub use book::MDBook; pub use book::BookItem; diff --git a/src/renderer/html_handlebars/hbs_renderer.rs b/src/renderer/html_handlebars/hbs_renderer.rs index cb452000..7ac2403e 100644 --- a/src/renderer/html_handlebars/hbs_renderer.rs +++ b/src/renderer/html_handlebars/hbs_renderer.rs @@ -3,6 +3,7 @@ use renderer::Renderer; use book::MDBook; use book::bookitem::BookItem; use {utils, theme}; +use resources::{Resources,Resource}; use regex::{Regex, Captures}; use std::ascii::AsciiExt; @@ -30,6 +31,7 @@ impl HtmlHandlebars { impl Renderer for HtmlHandlebars { fn render(&self, book: &MDBook) -> Result<(), Box> { debug!("[fn]: render"); + let res = Resources::new(); let mut handlebars = Handlebars::new(); // Load theme @@ -95,6 +97,22 @@ impl Renderer for HtmlHandlebars { data.insert("chapter_title".to_owned(), json!(ch.name)); data.insert("path_to_root".to_owned(), json!(utils::fs::path_to_root(&ch.path))); + { + let mut insert_from_env = |name: &str, conf: &Resource| { + data.insert(name.to_owned() + "_render_url", json!(conf.must_render_url())); + data.insert(name.to_owned() + "_render_embed", json!(conf.must_embed())); + if conf.must_render_url() { + data.insert(name.to_owned() + "_url", json!(conf.url())); + } + }; + insert_from_env("highlight", &res.conf.highlight); + insert_from_env("jquery", &res.conf.jquery); + insert_from_env("mathjax", &res.conf.mathjax); + insert_from_env("awesome", &res.conf.awesome); + insert_from_env("source_code_pro", &res.conf.source_code_pro); + insert_from_env("open_sans", &res.conf.open_sans); + } + // Render the handlebars template with the data debug!("[*]: Render template"); let rendered = handlebars.render("index", &data)?; @@ -167,17 +185,26 @@ impl Renderer for HtmlHandlebars { book.write_file("book.js", &theme.js)?; book.write_file("book.css", &theme.css)?; book.write_file("favicon.png", &theme.favicon)?; - book.write_file("jquery.js", &theme.jquery)?; book.write_file("highlight.css", &theme.highlight_css)?; book.write_file("tomorrow-night.css", &theme.tomorrow_night_css)?; - book.write_file("highlight.js", &theme.highlight_js)?; - book.write_file("_FontAwesome/css/font-awesome.css", theme::FONT_AWESOME)?; - book.write_file("_FontAwesome/fonts/fontawesome-webfont.eot", theme::FONT_AWESOME_EOT)?; - book.write_file("_FontAwesome/fonts/fontawesome-webfont.svg", theme::FONT_AWESOME_SVG)?; - book.write_file("_FontAwesome/fonts/fontawesome-webfont.ttf", theme::FONT_AWESOME_TTF)?; - book.write_file("_FontAwesome/fonts/fontawesome-webfont.woff", theme::FONT_AWESOME_WOFF)?; - book.write_file("_FontAwesome/fonts/fontawesome-webfont.woff2", theme::FONT_AWESOME_WOFF2)?; - book.write_file("_FontAwesome/fonts/FontAwesome.ttf", theme::FONT_AWESOME_TTF)?; + + if res.conf.jquery.must_embed() { + book.write_file("jquery.js", &res.jquery())?; + } + + if res.conf.highlight.must_embed() { + book.write_file("highlight.js", &res.highlight_js())?; + } + + if res.conf.awesome.must_embed() { + book.write_file("_FontAwesome/css/font-awesome.css", &res.awesome_css())?; + book.write_file("_FontAwesome/fonts/fontawesome-webfont.eot", &res.awesome_eot())?; + book.write_file("_FontAwesome/fonts/fontawesome-webfont.svg", &res.awesome_svg())?; + book.write_file("_FontAwesome/fonts/fontawesome-webfont.ttf", &res.awesome_ttf())?; + book.write_file("_FontAwesome/fonts/fontawesome-webfont.woff", &res.awesome_woff())?; + book.write_file("_FontAwesome/fonts/fontawesome-webfont.woff2", &res.awesome_woff2())?; + book.write_file("_FontAwesome/fonts/FontAwesome.ttf", &res.awesome_ttf())?; + } // Copy all remaining files utils::fs::copy_files_except_ext(book.get_src(), book.get_dest(), true, &["md"])?; diff --git a/src/theme/_FontAwesome/css/font-awesome.min.css b/src/resources/_FontAwesome/css/font-awesome.min.css similarity index 100% rename from src/theme/_FontAwesome/css/font-awesome.min.css rename to src/resources/_FontAwesome/css/font-awesome.min.css diff --git a/src/theme/_FontAwesome/fonts/FontAwesome.otf b/src/resources/_FontAwesome/fonts/FontAwesome.otf similarity index 100% rename from src/theme/_FontAwesome/fonts/FontAwesome.otf rename to src/resources/_FontAwesome/fonts/FontAwesome.otf diff --git a/src/theme/_FontAwesome/fonts/fontawesome-webfont.eot b/src/resources/_FontAwesome/fonts/fontawesome-webfont.eot similarity index 100% rename from src/theme/_FontAwesome/fonts/fontawesome-webfont.eot rename to src/resources/_FontAwesome/fonts/fontawesome-webfont.eot diff --git a/src/theme/_FontAwesome/fonts/fontawesome-webfont.svg b/src/resources/_FontAwesome/fonts/fontawesome-webfont.svg similarity index 100% rename from src/theme/_FontAwesome/fonts/fontawesome-webfont.svg rename to src/resources/_FontAwesome/fonts/fontawesome-webfont.svg diff --git a/src/theme/_FontAwesome/fonts/fontawesome-webfont.ttf b/src/resources/_FontAwesome/fonts/fontawesome-webfont.ttf similarity index 100% rename from src/theme/_FontAwesome/fonts/fontawesome-webfont.ttf rename to src/resources/_FontAwesome/fonts/fontawesome-webfont.ttf diff --git a/src/theme/_FontAwesome/fonts/fontawesome-webfont.woff b/src/resources/_FontAwesome/fonts/fontawesome-webfont.woff similarity index 100% rename from src/theme/_FontAwesome/fonts/fontawesome-webfont.woff rename to src/resources/_FontAwesome/fonts/fontawesome-webfont.woff diff --git a/src/theme/_FontAwesome/fonts/fontawesome-webfont.woff2 b/src/resources/_FontAwesome/fonts/fontawesome-webfont.woff2 similarity index 100% rename from src/theme/_FontAwesome/fonts/fontawesome-webfont.woff2 rename to src/resources/_FontAwesome/fonts/fontawesome-webfont.woff2 diff --git a/src/resources/env_config.rs b/src/resources/env_config.rs new file mode 100644 index 00000000..13be69cb --- /dev/null +++ b/src/resources/env_config.rs @@ -0,0 +1,233 @@ +//! Configuration options that can be passed through environment variables. +//! +//! By default, mdBook will try to retrieve external resources through a content +//! delivery network. If that fails, it'll load an embedded version of the +//! resource. Using environment variables this behavior can be changed in +//! various ways. +//! +//! The variables are as follows. [name] can be one of JQUERY, MATHJAX, +//! HIGHLIGHT, AWESOME, OPEN_SANS, SOURCE_CODE_PRO: +//! +//! - MDBOOK_GLOBAL_STRATEGY: Strategy for finding external resources. One of +//! UrlWithFallback (the default), UrlOnly and Omit. +//! - MDBOOK_[name]_URL: The URL of the resource. +//! - MDBOOK_[name]_SOURCE: The source file for the embedded resource. +//! - MDBOOK_[name]_STRATEGY: Strategy for this particular resource. This +//! overrides the global strategy. +//! +//! All variables have sane defaults which were determined at compile time. +//! the SOURCE variables, when left empty, ensure that a version is used that +//! was embedded into mdBook itself at compile time. + +use std::env; + +pub static JQUERY_URL: &str = &"https://code.jquery.com/jquery-2.1.4.min.js"; +pub static MATHJAX_URL: &str = &"https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"; +pub static HIGHLIGHT_URL: &str = &"highlight.js"; +pub static AWESOME_URL: &str = &"https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css"; +pub static OPEN_SANS_URL: &str = &"https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800"; +pub static SOURCE_CODE_PRO_URL: &str = &"https://fonts.googleapis.com/css?family=Source+Code+Pro:500"; + +/// A third-party resource +pub trait Resource { + /// returns true if this resource has to be rendered as an url + fn must_render_url(&self) -> bool; + /// returns true if this resource has to be embedded in the book + fn must_embed(&self) -> bool; + /// returns the url for this resource. This panics if must_render_url returns false. + fn url(&self) -> String; + /// returns the source location for this resource, or None if none was configured. + /// This panics if must_embed returns false. + fn source(&self) -> Option; +} + +/// A third-party resource with nothing special to it. +pub enum BasicResource { + UrlWithFallback { + url: String, + source: Option + }, + UrlOnly { + url: String + }, + Omit +} + +impl Resource for BasicResource { + fn must_render_url(&self) -> bool { + match self { + &BasicResource::UrlWithFallback{url: _, source: _} => true, + &BasicResource::UrlOnly{url: _} => true, + _ => false + } + } + + fn must_embed(&self) -> bool { + match self { + &BasicResource::UrlWithFallback{url: _, source: _} => true, + _ => false + } + } + + fn url(&self) -> String { + match self { + &BasicResource::UrlWithFallback{ref url, source: _} => url.clone(), + &BasicResource::UrlOnly{ref url} => url.clone(), + _ => panic!("no url available") + } + } + + fn source(&self) -> Option { + match self { + &BasicResource::UrlWithFallback{url: _, ref source} => source.clone(), + _ => panic!("no source available") + } + } +} + +/// Special struct for font-awesome, as it consists of multiple embedded files +/// but is configured as a directory. Methods implemented for this struct +/// calculate the paths of all the files in this directory. +pub struct Awesome { + resource: BasicResource +} + +impl Resource for Awesome { + fn must_render_url(&self) -> bool { + self.resource.must_render_url() + } + + fn must_embed(&self) -> bool { + self.resource.must_embed() + } + + fn url(&self) -> String { + self.resource.url() + } + + fn source(&self) -> Option { + self.resource.source() + } +} + +impl Awesome { + pub fn css_source(&self) -> Option { + self.resource.source().map(|p| p + "/css/font-awesome.min.css") + } + + pub fn eot_source(&self) -> Option { + self.resource.source().map(|p| p + "/fonts/fontawesome-webfont.eot") + } + + pub fn svg_source(&self) -> Option { + self.resource.source().map(|p| p + "/fonts/fontawesome-webfont.svg") + } + + pub fn ttf_source(&self) -> Option { + self.resource.source().map(|p| p + "/fonts/fontawesome-webfont.ttf") + } + + pub fn woff_source(&self) -> Option { + self.resource.source().map(|p| p + "/fonts/fontawesome-webfont.woff") + } + + pub fn woff2_source(&self) -> Option { + self.resource.source().map(|p| p + "/fonts/fontawesome-webfont.woff2") + } + + pub fn otf_source(&self) -> Option { + self.resource.source().map(|p| p + "/fonts/FontAwesome.otf") + } +} + +/// Configuration information for all the third-party resources. This specifies +/// for every resource whether it should be included at all, and if so, just as +/// an URL or also as an embedded resource. +pub struct Configuration { + pub jquery: BasicResource, + pub mathjax: BasicResource, + pub highlight: BasicResource, + pub awesome: Awesome, + pub open_sans: BasicResource, + pub source_code_pro: BasicResource +} + +/// Calculate an environment variable name of the format MDBOOK_[name]_[key] +fn varname(resource : &str, key: &str) -> String { + "MDBOOK_".to_string() + &resource.to_uppercase() + "_" + key +} + +/// Returns the contents of the environment variable of the given resource with +/// the given key, or None if the environment variable was not set. +fn var(resource: &str, key: &str) -> Option { + env::var(varname(resource, key)).ok() +} + +/// Returns a variable as var would. If the variable could not be found, return +/// the given default. +fn var_default(resource: &str, key: &str, default: &str) -> String { + var(resource, key).unwrap_or(String::from(default)) +} + +/// The rendering strategy for a resource +enum Strategy { + /// Render both an inclusion through an URL, and a fallback if it exists. + UrlWithFallback, + /// Render only the inclusion through an URL + UrlOnly, + /// Omit the resource altogether + Omit +} + +/// Return a strategy from an environment variable, or the default +/// `UrlWithFallback` if it can't be found. +fn strategy(resource: &str) -> Strategy { + match var(resource, "STRATEGY") + .unwrap_or(var_default("DEFAULT", "STRATEGY", "UrlWithFallback")).as_ref() { + "UrlOnly" => Strategy::UrlOnly, + "Omit" => Strategy::Omit, + _ => Strategy::UrlWithFallback + } +} + +/// return an URL from an environment variable, or the given default if the URL +/// was not set. +fn url(resource: &str, default: &str) -> String { + var_default(resource, "URL", default) +} + +/// return a source location from an environment variable, if it can't be found. +fn source(resource : &str) -> Option { + var(resource, "EMBED_SOURCE") +} + +/// return a resource from various environment variables. +fn resource(resource: &str, url_default: &str) -> BasicResource { + match strategy(resource) { + Strategy::UrlWithFallback => BasicResource::UrlWithFallback { + url: url(resource, url_default), + source: source(resource) + }, + Strategy::UrlOnly => BasicResource::UrlOnly { + url: url(resource, url_default) + }, + Strategy::Omit => BasicResource::Omit + } +} + +/// parse font_awesome configuration from environment variables. +fn awesome() -> Awesome { + Awesome { resource: resource("AWESOME", AWESOME_URL) } +} + +/// Parse the configuration from the environment variables. +pub fn configuration_from_env() -> Configuration { + Configuration { + jquery: resource("JQUERY", JQUERY_URL), + mathjax: resource("MATHJAX", MATHJAX_URL), + highlight: resource("HIGHLIGHT", HIGHLIGHT_URL), + awesome: awesome(), + open_sans: resource("OPEN_SANS", OPEN_SANS_URL), + source_code_pro: resource("SOURCE_CODE_PRO", SOURCE_CODE_PRO_URL) + } +} diff --git a/src/theme/highlight.js b/src/resources/highlight.js similarity index 100% rename from src/theme/highlight.js rename to src/resources/highlight.js diff --git a/src/theme/jquery-2.1.4.min.js b/src/resources/jquery-2.1.4.min.js similarity index 100% rename from src/theme/jquery-2.1.4.min.js rename to src/resources/jquery-2.1.4.min.js diff --git a/src/resources/mod.rs b/src/resources/mod.rs new file mode 100644 index 00000000..e059b1f7 --- /dev/null +++ b/src/resources/mod.rs @@ -0,0 +1,105 @@ +//! Resource management +//! +//! mdBook uses various third-party javascript libraries and fonts. This module +//! manages their resolution, dependent on run-time configuration, allowing the +//! renderer to determine how to include the resource. +//! +//! By default, resources are retrieved with an URL, usually a CDN. If this is not +//! possible, an embedded fallback is used instead. This improves rendering +//! speed while still allowing the book to be readable without an internet +//! connection. +//! +//! In some cases, this default behavior is unwanted. One might want to load the +//! library from a different URL, or one might want to embed a different +//! fallback resource. For those cases, resource resolution can be overridden +//! through environment variables. See the env_config module for more details. + +mod env_config; +use self::env_config::{Configuration,configuration_from_env}; +pub use self::env_config::{Resource}; +use std::fs::File; +use std::io::Read; + +static HIGHLIGHT_JS: &'static [u8] = include_bytes!("highlight.js"); +static JQUERY: &'static [u8] = include_bytes!("jquery-2.1.4.min.js"); +static FONT_AWESOME_CSS: &'static [u8] = include_bytes!("_FontAwesome/css/font-awesome.min.css"); +static FONT_AWESOME_EOT: &'static [u8] = include_bytes!("_FontAwesome/fonts/fontawesome-webfont.eot"); +static FONT_AWESOME_SVG: &'static [u8] = include_bytes!("_FontAwesome/fonts/fontawesome-webfont.svg"); +static FONT_AWESOME_TTF: &'static [u8] = include_bytes!("_FontAwesome/fonts/fontawesome-webfont.ttf"); +static FONT_AWESOME_WOFF: &'static [u8] = include_bytes!("_FontAwesome/fonts/fontawesome-webfont.woff"); +static FONT_AWESOME_WOFF2: &'static [u8] = include_bytes!("_FontAwesome/fonts/fontawesome-webfont.woff2"); +static FONT_AWESOME_OTF: &'static [u8] = include_bytes!("_FontAwesome/fonts/FontAwesome.otf"); + +/// handle for loading resource contents, based on a configuration +pub struct Resources { + /// the configuration read from the environment variables + pub conf: Configuration +} + +impl Resources { + /// create a new Resources object, based on the environment variables + pub fn new() -> Resources { + Resources { + conf: configuration_from_env() + } + } + + /// Returns the contents that should be embedded in a book. + /// This returns a version of the resource that was embedded at compile + /// time, passed as the default argument, unless an alternative was set + /// through an environment variable. + fn resource_content(&self, default : &[u8]) -> Vec { + if let Some(s) = self.conf.highlight.source() { + if let Ok(mut f) = File::open(s) { + let mut vec = Vec::new(); + f.read_to_end(&mut vec).expect("read failed"); + vec + } + else { + Vec::from(default) + } + } + else { + Vec::from(default) + } + } + + pub fn highlight_js(&self) -> Vec { + assert!(self.conf.highlight.must_embed()); + self.resource_content(HIGHLIGHT_JS) + } + + pub fn jquery(&self) -> Vec { + assert!(self.conf.jquery.must_embed()); + self.resource_content(JQUERY) + } + + pub fn awesome_css(&self) -> Vec { + assert!(self.conf.awesome.must_embed()); + self.resource_content(FONT_AWESOME_CSS) + } + pub fn awesome_eot(&self) -> Vec { + assert!(self.conf.awesome.must_embed()); + self.resource_content(FONT_AWESOME_EOT) + } + pub fn awesome_svg(&self) -> Vec { + assert!(self.conf.awesome.must_embed()); + self.resource_content(FONT_AWESOME_SVG) + } + pub fn awesome_ttf(&self) -> Vec { + assert!(self.conf.awesome.must_embed()); + self.resource_content(FONT_AWESOME_TTF) + } + pub fn awesome_woff(&self) -> Vec { + assert!(self.conf.awesome.must_embed()); + self.resource_content(FONT_AWESOME_WOFF) + } + pub fn awesome_woff2(&self) -> Vec { + assert!(self.conf.awesome.must_embed()); + self.resource_content(FONT_AWESOME_WOFF2) + } + pub fn awesome_otf(&self) -> Vec { + assert!(self.conf.awesome.must_embed()); + self.resource_content(FONT_AWESOME_OTF) + } +} diff --git a/src/theme/index.hbs b/src/theme/index.hbs index b3985fbc..76e2f72c 100644 --- a/src/theme/index.hbs +++ b/src/theme/index.hbs @@ -10,27 +10,40 @@ - - + {{#if open_sans_render_url}} + + {{/if}} + {{#if source_code_pro_render_url}} + + {{/if}} + {{#if awesome_render_url}} - + + {{/if}} + {{#if mathjax_render_url}} - + + {{/if}} - - + {{#if jquery_render_url }} + + + {{/if}} + {{#if jquery_render_embed }} + + {{/if}} @@ -101,12 +114,14 @@ + {{#if awesome_render_embed}} + {{/if}} {{{livereload}}} @@ -124,7 +139,9 @@ {{/if}} - + {{#if highlight_render_url}} + + {{/if}} diff --git a/src/theme/mod.rs b/src/theme/mod.rs index 6082ff51..491f1e09 100644 --- a/src/theme/mod.rs +++ b/src/theme/mod.rs @@ -7,17 +7,8 @@ pub static INDEX: &'static [u8] = include_bytes!("index.hbs"); pub static CSS: &'static [u8] = include_bytes!("book.css"); pub static FAVICON: &'static [u8] = include_bytes!("favicon.png"); pub static JS: &'static [u8] = include_bytes!("book.js"); -pub static HIGHLIGHT_JS: &'static [u8] = include_bytes!("highlight.js"); pub static TOMORROW_NIGHT_CSS: &'static [u8] = include_bytes!("tomorrow-night.css"); pub static HIGHLIGHT_CSS: &'static [u8] = include_bytes!("highlight.css"); -pub static JQUERY: &'static [u8] = include_bytes!("jquery-2.1.4.min.js"); -pub static FONT_AWESOME: &'static [u8] = include_bytes!("_FontAwesome/css/font-awesome.min.css"); -pub static FONT_AWESOME_EOT: &'static [u8] = include_bytes!("_FontAwesome/fonts/fontawesome-webfont.eot"); -pub static FONT_AWESOME_SVG: &'static [u8] = include_bytes!("_FontAwesome/fonts/fontawesome-webfont.svg"); -pub static FONT_AWESOME_TTF: &'static [u8] = include_bytes!("_FontAwesome/fonts/fontawesome-webfont.ttf"); -pub static FONT_AWESOME_WOFF: &'static [u8] = include_bytes!("_FontAwesome/fonts/fontawesome-webfont.woff"); -pub static FONT_AWESOME_WOFF2: &'static [u8] = include_bytes!("_FontAwesome/fonts/fontawesome-webfont.woff2"); -pub static FONT_AWESOME_OTF: &'static [u8] = include_bytes!("_FontAwesome/fonts/FontAwesome.otf"); /// The `Theme` struct should be used instead of the static variables because /// the `new()` method @@ -35,8 +26,6 @@ pub struct Theme { pub js: Vec, pub highlight_css: Vec, pub tomorrow_night_css: Vec, - pub highlight_js: Vec, - pub jquery: Vec, } impl Theme { @@ -50,8 +39,6 @@ impl Theme { js: JS.to_owned(), highlight_css: HIGHLIGHT_CSS.to_owned(), tomorrow_night_css: TOMORROW_NIGHT_CSS.to_owned(), - highlight_js: HIGHLIGHT_JS.to_owned(), - jquery: JQUERY.to_owned(), }; // Check if the given path exists @@ -85,12 +72,6 @@ impl Theme { let _ = f.read_to_end(&mut theme.favicon); } - // highlight.js - if let Ok(mut f) = File::open(&src.join("highlight.js")) { - theme.highlight_js.clear(); - let _ = f.read_to_end(&mut theme.highlight_js); - } - // highlight.css if let Ok(mut f) = File::open(&src.join("highlight.css")) { theme.highlight_css.clear(); diff --git a/src/utils/env.rs b/src/utils/env.rs new file mode 100644 index 00000000..66980c46 --- /dev/null +++ b/src/utils/env.rs @@ -0,0 +1,146 @@ +//! Configuration options that can be passed through environment variables. +//! +//! By default, mdBook will try to retrieve external resources through a content +//! delivery network. If that fails, it'll load an embedded version of the +//! resource. Using environment variables this behavior can be changed in +//! various ways. +//! +//! The variables are as follows. [name] can be one of JQUERY, MATHJAX, +//! HIGHLIGHT, AWESOME, OPEN_SANS or SOURCE_CODE_PRO: +//! +//! - MDBOOK_DEFAULT_STRATEGY: Strategy for finding external resources. One of +//! UrlWithFallback (the default), EmbeddedOnly, UrlOnly and Omit. +//! - MDBOOK_[name]_URL: The URL of the resource. When omitted, an URL embedded at compile time will be used. +//! - MDBOOK_[name]_SOURCE: The source file for the embedded resource. When omitted, a version embeded at compile time will be used. +//! - MDBOOK_[name]_STRATEGY: Strategy for this particular resource. This +//! overrides the global strategy. + +use std::env; + +pub static JQUERY_URL: &str = &"https://code.jquery.com/jquery-2.1.4.min.js"; +pub static MATHJAX_URL: &str = &"https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"; +pub static HIGHLIGHT_URL: &str = &"highlight.js"; +pub static AWESOME_URL: &str = &"https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css"; +pub static OPEN_SANS_URL: &str = &"https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800"; +pub static SOURCE_CODE_PRO_URL: &str = &"https://fonts.googleapis.com/css?family=Source+Code+Pro:500"; + +pub enum Resource { + UrlWithFallback { + url: String, + source: Option + }, + EmbeddedOnly { + source: Option + }, + UrlOnly { + url: String + }, + Omit +} + +impl Resource { + pub fn must_render_url(&self) -> bool { + match self { + &Resource::UrlWithFallback{url: _, source: _} => true, + &Resource::UrlOnly{url: _} => true, + _ => false + } + } + + pub fn must_embed(&self) -> bool { + match self { + &Resource::UrlWithFallback{url: _, source: _} => true, + &Resource::EmbeddedOnly{source: _} => true, + _ => false + } + } + + pub fn url(&self) -> String { + match self { + &Resource::UrlWithFallback{url: ref url, source: _} => url.clone(), + &Resource::UrlOnly{url: ref url} => url.clone(), + _ => panic!("no url available") + } + } + + pub fn source(&self) -> Option { + match self { + &Resource::UrlWithFallback{url: _, source: ref source} => source.clone(), + &Resource::EmbeddedOnly{source: ref source} => source.clone(), + _ => panic!("no source available") + } + } +} + +pub struct Configuration { + pub jquery: Resource, + pub mathjax: Resource, + pub highlight: Resource, + pub awesome: Resource, + pub open_sans: Resource, + pub source_code_pro: Resource +} + +fn varname(resource : &str, suffix: &str) -> String { + "MDBOOK_".to_string() + &resource.to_uppercase() + "_" + suffix +} + +fn var(resource: &str, key: &str) -> Option { + env::var(varname(resource, key)).ok() +} + +fn var_default(resource: &str, key: &str, default: &str) -> String { + var(resource, key).unwrap_or(String::from(default)) +} + +pub enum Strategy { + UrlWithFallback, + EmbeddedOnly, + UrlOnly, + Omit +} + +fn strategy(resource: &str) -> Strategy { + match var(resource, "STRATEGY") + .unwrap_or(var_default("DEFAULT", "STRATEGY", "UrlWithFallback")).as_ref() { + "EmbeddedOnly" => Strategy::EmbeddedOnly, + "UrlOnly" => Strategy::UrlOnly, + "Omit" => Strategy::Omit, + _ => Strategy::UrlWithFallback + } +} + +fn url(resource: &str, default: &str) -> String { + var_default(resource, "URL", default) +} + +fn source(resource : &str) -> Option { + var(resource, "EMBED_SOURCE") +} + +fn resource(resource: &str, url_default: &str) -> Resource { + match strategy(resource) { + Strategy::UrlWithFallback => Resource::UrlWithFallback { + url: url(resource, url_default), + source: source(resource) + }, + Strategy::EmbeddedOnly => Resource::EmbeddedOnly { + source: source(resource) + }, + Strategy::UrlOnly => Resource::UrlOnly { + url: url(resource, url_default) + }, + Strategy::Omit => Resource::Omit + } +} + +pub fn configuration_from_env() -> Configuration { + Configuration { + jquery: resource("JQUERY", JQUERY_URL), + mathjax: resource("MATHJAX", MATHJAX_URL), + highlight: resource("HIGHLIGHT", HIGHLIGHT_URL), + awesome: resource("AWESOME", AWESOME_URL), + open_sans: resource("OPEN_SANS", OPEN_SANS_URL), + source_code_pro: resource("SOURCE_CODE_PRO", SOURCE_CODE_PRO_URL) + } +}