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 { 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 = 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 ), )) } }