rendering configuration from environment variables

This commit is contained in:
Matthijs van Otterdijk 2017-05-19 19:47:53 +02:00
parent f038dcb404
commit 14e2c0e6e4
17 changed files with 545 additions and 39 deletions

View File

@ -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(())
}

View File

@ -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;

View File

@ -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<Error>> {
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"])?;

View File

Before

Width:  |  Height:  |  Size: 348 KiB

After

Width:  |  Height:  |  Size: 348 KiB

233
src/resources/env_config.rs Normal file
View File

@ -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<String>;
}
/// A third-party resource with nothing special to it.
pub enum BasicResource {
UrlWithFallback {
url: String,
source: Option<String>
},
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<String> {
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<String> {
self.resource.source()
}
}
impl Awesome {
pub fn css_source(&self) -> Option<String> {
self.resource.source().map(|p| p + "/css/font-awesome.min.css")
}
pub fn eot_source(&self) -> Option<String> {
self.resource.source().map(|p| p + "/fonts/fontawesome-webfont.eot")
}
pub fn svg_source(&self) -> Option<String> {
self.resource.source().map(|p| p + "/fonts/fontawesome-webfont.svg")
}
pub fn ttf_source(&self) -> Option<String> {
self.resource.source().map(|p| p + "/fonts/fontawesome-webfont.ttf")
}
pub fn woff_source(&self) -> Option<String> {
self.resource.source().map(|p| p + "/fonts/fontawesome-webfont.woff")
}
pub fn woff2_source(&self) -> Option<String> {
self.resource.source().map(|p| p + "/fonts/fontawesome-webfont.woff2")
}
pub fn otf_source(&self) -> Option<String> {
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<String> {
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<String> {
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)
}
}

105
src/resources/mod.rs Normal file
View File

@ -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<u8> {
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<u8> {
assert!(self.conf.highlight.must_embed());
self.resource_content(HIGHLIGHT_JS)
}
pub fn jquery(&self) -> Vec<u8> {
assert!(self.conf.jquery.must_embed());
self.resource_content(JQUERY)
}
pub fn awesome_css(&self) -> Vec<u8> {
assert!(self.conf.awesome.must_embed());
self.resource_content(FONT_AWESOME_CSS)
}
pub fn awesome_eot(&self) -> Vec<u8> {
assert!(self.conf.awesome.must_embed());
self.resource_content(FONT_AWESOME_EOT)
}
pub fn awesome_svg(&self) -> Vec<u8> {
assert!(self.conf.awesome.must_embed());
self.resource_content(FONT_AWESOME_SVG)
}
pub fn awesome_ttf(&self) -> Vec<u8> {
assert!(self.conf.awesome.must_embed());
self.resource_content(FONT_AWESOME_TTF)
}
pub fn awesome_woff(&self) -> Vec<u8> {
assert!(self.conf.awesome.must_embed());
self.resource_content(FONT_AWESOME_WOFF)
}
pub fn awesome_woff2(&self) -> Vec<u8> {
assert!(self.conf.awesome.must_embed());
self.resource_content(FONT_AWESOME_WOFF2)
}
pub fn awesome_otf(&self) -> Vec<u8> {
assert!(self.conf.awesome.must_embed());
self.resource_content(FONT_AWESOME_OTF)
}
}

View File

@ -10,27 +10,40 @@
<base href="{{ path_to_root }}">
<link rel="stylesheet" href="book.css">
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800" rel="stylesheet" type="text/css">
<link href="https://fonts.googleapis.com/css?family=Source+Code+Pro:500" rel="stylesheet" type="text/css">
{{#if open_sans_render_url}}
<link href="{{open_sans_url}}" rel="stylesheet" type="text/css">
{{/if}}
{{#if source_code_pro_render_url}}
<link href="{{source_code_pro_url}}" rel="stylesheet" type="text/css">
{{/if}}
<link rel="shortcut icon" href="{{ favicon }}">
{{#if awesome_render_url}}
<!-- Font Awesome -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
<link rel="stylesheet" href="{{awesome_url}}">
{{/if}}
<link rel="stylesheet" href="highlight.css">
<link rel="stylesheet" href="tomorrow-night.css">
{{#if mathjax_render_url}}
<!-- MathJax -->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script type="text/javascript" src="{{mathjax_url}}"></script>
{{/if}}
<!-- Fetch JQuery from CDN but have a local fallback -->
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
{{#if jquery_render_url }}
<!-- Fetch JQuery -->
<script src="{{jquery_url}}"></script>
{{/if}}
{{#if jquery_render_embed }}
<!-- local fallback for jquery -->
<script>
if (typeof jQuery == 'undefined') {
document.write(unescape("%3Cscript src='jquery.js'%3E%3C/script%3E"));
}
</script>
{{/if}}
</head>
<body class="light">
<!-- Set the theme before any content is loaded, prevents flash -->
@ -101,12 +114,14 @@
</div>
{{#if awesome_render_embed}}
<!-- Local fallback for Font Awesome -->
<script>
if ($(".fa").css("font-family") !== "FontAwesome") {
$('<link rel="stylesheet" type="text/css" href="_FontAwesome/css/font-awesome.css">').prependTo('head');
}
</script>
{{/if}}
<!-- Livereload script (if served using the cli tool) -->
{{{livereload}}}
@ -124,7 +139,9 @@
{{/if}}
<script src="highlight.js"></script>
{{#if highlight_render_url}}
<script src="{{highlight_url}}"></script>
{{/if}}
<script src="book.js"></script>
</body>
</html>

View File

@ -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<u8>,
pub highlight_css: Vec<u8>,
pub tomorrow_night_css: Vec<u8>,
pub highlight_js: Vec<u8>,
pub jquery: Vec<u8>,
}
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();

146
src/utils/env.rs Normal file
View File

@ -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<String>
},
EmbeddedOnly {
source: Option<String>
},
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<String> {
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<String> {
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<String> {
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)
}
}