The website generator used to generate the webpage at teasca.de, but can be used to generate any webpage with any content.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

260 lines
9.0 KiB

use std::path::PathBuf;
use std::path::Path;
use std::fs::File;
use std::io::Read;
use std::error::Error as STDError;
use pathdiff;
use config::Config;
use template::Template;
use renderer;
use logger::{LogLevel, Logger};
use error::Error;
use options::{BuildOpt, Opt};
use file_writer;
const DEFAULT_CSS: &'static str = include_str!("templates/default-css.css");
const DEFAULT_JS: &'static str = include_str!("templates/default-js.js");
const PAGE_TEMPLATE: &'static str = include_str!("templates/page-template.html");
const NAVBAR_ITEM: &'static str = include_str!("templates/navbar/item-template.html");
const NAVBAR_IMAGE_ITEM: &'static str = include_str!("templates/navbar/image-item-template.html");
const CSS_TAG: &'static str = include_str!("templates/meta_tags/css_tag.html");
const JS_TAG: &'static str = include_str!("templates/meta_tags/js_tag.html");
fn fetch_config() -> Result<Config, Error> {
match Config::new() {
Ok(config) => Ok(config),
Err(err) => Err(Error::from(err)),
}
}
pub fn build(logger: &Logger, opt: &Opt, _: &BuildOpt) -> Result<(), Error> {
logger.log(LogLevel::INFO, "Starting build");
let config = fetch_config()?;
if config.global_config.website.use_default_css {
let css_path = file_writer::add_to_beginning(
PathBuf::from("css/default.css"),
config
.clone()
.global_config
.website
.output
.unwrap_or("public".to_owned()),
);
logger.log(LogLevel::DETAIL, format!("Adding {:?}", css_path));
file_writer::write_file(&css_path, DEFAULT_CSS, opt.overwrite)?;
}
if config.global_config.website.use_default_js {
let js_path = file_writer::add_to_beginning(
PathBuf::from("js/default.js"),
config
.clone()
.global_config
.website
.output
.unwrap_or("public".to_owned()),
);
logger.log(LogLevel::DETAIL, format!("Adding {:?}", js_path));
file_writer::write_file(&js_path, DEFAULT_JS, opt.overwrite)?;
}
logger.log(LogLevel::INFO, "Generating page templates");
let page_template = Template::new(PAGE_TEMPLATE);
let css_tag_template = Template::new(CSS_TAG);
let js_tag_template = Template::new(JS_TAG);
let mut navbar_content = String::new();
if config.global_config.navbar.is_some() {
let navbar_item_template = Template::new(NAVBAR_ITEM);
let navbar_image_item_template = Template::new(NAVBAR_IMAGE_ITEM);
logger.log(LogLevel::DETAIL, "Rendering Navbar");
navbar_content =
renderer::render_navbar(&config, navbar_item_template, navbar_image_item_template)?;
}
logger.log(LogLevel::INFO, "Rendering");
let configs = config.get_configs()?;
let mut renders = Vec::new();
for config in configs.clone() {
logger.log(
LogLevel::DETAILER,
format!("Setting up to render {}", config.page_config.page.html_path),
);
// Generate CSS tagstags
logger.log(LogLevel::DETAILER, "Generating CSS tags");
let mut css_string = String::new();
let mut css_list = config.global_config.website.css.clone();
if let Some(mut list) = config.page_config.page.css.clone() {
css_list.append(&mut list);
}
for item in css_list {
let data = Template::css_tag_data_from(item);
css_string += &*css_tag_template.render(&data);
}
// Generate JS tags
logger.log(LogLevel::DETAILER, "Generating JS tags");
let mut js_string = String::new();
let mut js_list = config.global_config.website.javascript.clone();
if let Some(mut list) = config.page_config.page.javascript.clone() {
js_list.append(&mut list);
}
for item in js_list {
let data = Template::css_tag_data_from(item);
js_string += &*js_tag_template.render(&data);
}
// Generate and render Injections
logger.log(LogLevel::DETAILER, "Rendering injections.");
let (before_navbar, before_content, after_content) =
renderer::render_injections(&logger, &config)?;
logger.log(LogLevel::DETAILER, "Rendering");
let markdown = renderer::render_markdown_content(&config)?;
let markdown = renderer::render_custom_markdown(markdown)?;
let data = Template::page_data_from(
config.clone(),
navbar_content.clone(),
before_navbar,
before_content,
after_content,
css_string,
js_string,
markdown,
);
let rendered = page_template.render(&data);
renders.push(rendered);
logger.log(
LogLevel::DETAIL,
format!("Rendered {}", config.page_config.page.html_path),
);
}
logger.log(LogLevel::INFO, "Writing");
for (idx, config) in configs.clone().iter().enumerate() {
let html_path = config.clone().page_config.page.html_path;
let html_path = file_writer::add_to_beginning(
PathBuf::from(html_path),
config
.clone()
.global_config
.website
.output
.unwrap_or("public".to_owned()),
);
logger.log(LogLevel::DETAIL, format!("Writing {:?}", html_path));
if let Some(render) = renders.get(idx) {
file_writer::write_file(&html_path, render.clone(), opt.overwrite)?;
} else {
return Err(Error::new(
LogLevel::SEVERE,
format!("Internal error; Render not found for file: {:?}", html_path),
));
}
}
logger.log(LogLevel::INFO, "Copying resources");
if let Some(resources) = config.global_config.resources.clone() {
for resource in resources.values() {
let path = Path::new(&resource.source);
if path.exists() {
let dest = file_writer::add_to_beginning(
PathBuf::from(resource.destination.clone()),
config
.clone()
.global_config
.website
.output
.unwrap_or("public".to_owned()),
);
match write_recursive_resource(
path.to_path_buf(),
PathBuf::from(resource.source.clone()),
dest,
) {
Ok(_) => logger.log(LogLevel::DETAIL, "Resource successfully copied."),
Err(err) => return Err(err),
}
} else {
logger.log(
LogLevel::WARNING,
format!("Resource does not exist: {:?}", path),
);
}
}
}
logger.log(LogLevel::INFO, "Done!");
Ok(())
}
fn write_recursive_resource(
resource_path: PathBuf,
source: PathBuf,
destination: PathBuf,
) -> Result<(), Error> {
if let Some(relative) = pathdiff::diff_paths(&resource_path, &source) {
if resource_path.is_dir() {
for item in resource_path.read_dir().unwrap() {
match item {
Ok(item) => {
match write_recursive_resource(
item.path(),
source.clone(),
destination.clone(),
) {
Ok(_) => {}
Err(err) => return Err(err),
};
}
Err(error) => return Err(Error::from(error)),
}
}
Ok(())
} else {
match File::open(resource_path.clone()) {
Ok(mut read_file) => {
let bytes: Vec<u8> = match read_file.bytes().collect() {
Ok(bytes) => bytes,
Err(_) => Vec::new(),
};
let mut dest_path = destination.clone();
if source.is_dir() {
dest_path = dest_path.join(relative.clone());
dest_path.set_file_name(resource_path.file_name().unwrap());
}
file_writer::write_bytes(&dest_path, &bytes, true)?;
Ok(())
}
Err(err) => Err(Error::new(
LogLevel::SEVERE,
format!(
"Failed to open read file {:?}: {}",
resource_path,
err.description().to_owned()
),
)),
}
}
} else {
Err(Error::new(
LogLevel::SEVERE,
format!(
"Internal Error: resource relative path invalid: {:?} to {:?}",
&resource_path, &source
),
))
}
}