1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
#![feature(proc_macro_hygiene, decl_macro)]
#![doc(html_root_url = "https://docs.dfstoryteller.com/rust-docs/")]
#![doc(html_favicon_url = "https://docs.dfstoryteller.com/favicon/favicon-16x16.png")]
#![doc(html_logo_url = "https://docs.dfstoryteller.com/logo.svg")]

//! # DF Storyteller - Guide Documentation
//!
//! This crate includes all the documentation about the Guide to get users started.
//!
//!

use colored::*;
#[allow(unused_imports)]
use log::{debug, error, info, trace, warn};
use rocket::config::{Config, Environment, LoggingLevel};
use rocket::http::{ContentType, Status};
use rocket::*;
use rust_embed::RustEmbed;
use std::ffi::OsStr;
use std::io::Cursor;
use std::path::PathBuf;

#[derive(RustEmbed)]
#[folder = "./pages/"]
struct StaticAssets;

/// Guide start page
#[get("/")]
fn index_page<'r>() -> response::Result<'r> {
    StaticAssets::get("index.html").map_or_else(
        || Err(Status::NotFound),
        |d| {
            response::Response::build()
                .header(ContentType::HTML)
                .sized_body(Cursor::new(d))
                .ok()
        },
    )
}

/// Serve other static file from pages folder.
#[get("/<file..>")]
fn static_file<'r>(file: PathBuf) -> response::Result<'r> {
    let filename = file
        .display()
        .to_string()
        // replace the Windows `\` with the unix `/` This effect the windows release build.
        .replace("\\", "/");
    StaticAssets::get(&filename).map_or_else(
        || Err(Status::NotFound),
        |d| {
            let ext = file
                .as_path()
                .extension()
                .and_then(OsStr::to_str)
                .ok_or_else(|| Status::new(400, "Could not get file extension"))?;
            let content_type = match ext {
                "map" => ContentType::JavaScript,
                _ => ContentType::from_extension(ext)
                    .ok_or_else(|| Status::new(400, "Could not get file content type"))?,
            };
            response::Response::build()
                .header(content_type)
                .sized_body(Cursor::new(d))
                .ok()
        },
    )
}

/// Start the Rocket API server that serves the Guide.
pub fn start_guide_server() {
    info!("Starting Guide server");

    info!(
        "----------------------------------------\n\
        Open following link to view guide: {}\n\
        ----------------------------------------\n",
        "http://localhost:20352/".bright_cyan()
    );

    rocket::custom(set_server_config())
        // Provide static assets for Guide.
        .mount("/", routes![index_page, static_file])
        .launch();
}

fn set_server_config() -> Config {
    // Start creating config
    let mut config = Config::build(Environment::Staging)
        .address("127.0.0.1".to_string())
        .port(20352);
    // Set Workers
    config = config.workers(2);
    // Set Keep Alive duration
    config = config.keep_alive(0);
    // Set logging Level for Rocket (not DF Storyteller)
    if cfg!(debug_assertions) {
        config = config.log_level(LoggingLevel::Debug);
    } else {
        config = config.log_level(LoggingLevel::Normal);
    }
    // TODO add on error switch to a different port.
    config.finalize().unwrap()
}