extern crate handlebars; extern crate serde_json; extern crate toml; use serde_json::value::Map; use std::env; use std::fmt::Write as FmtWrite; use std::fs::File; use std::io::{Read, Write}; use std::path::Path; use std::result; #[derive(Debug)] pub enum Error { Message(&'static str), Io(::std::io::Error), Fmt(::std::fmt::Error), Render(handlebars::RenderError), Template(handlebars::TemplateError), TomlDe(toml::de::Error), Env(env::VarError), } impl From<&'static str> for Error { fn from(value: &'static str) -> Error { Error::Message(value) } } impl From<::std::io::Error> for Error { fn from(value: ::std::io::Error) -> Error { Error::Io(value) } } impl From<::std::fmt::Error> for Error { fn from(value: ::std::fmt::Error) -> Error { Error::Fmt(value) } } impl From for Error { fn from(value: env::VarError) -> Error { Error::Env(value) } } impl From for Error { fn from(value: handlebars::RenderError) -> Error { Error::Render(value) } } impl From for Error { fn from(value: handlebars::TemplateError) -> Error { Error::Template(value) } } impl From for Error { fn from(value: toml::de::Error) -> Error { Error::TomlDe(value) } } type Result = result::Result; fn read_file(path: &str) -> String { let mut f = File::open(path) .map_err(|e| format!("cannot open: {}: {}", path, e)) .unwrap(); let mut content = String::new(); f.read_to_string(&mut content).unwrap(); content } /// Generate and build themes.rs fn process_colors() -> Result<()> { let out_dir = env::var("OUT_DIR")?; let out_path = Path::new(&out_dir); let colors_content = read_file("themes.toml"); let template_content = read_file("src/static/doc._.css.hbs"); let value: toml::Value = colors_content.parse()?; let schemes = value .as_table() .ok_or_else(|| Error::Message("not a table"))?; let mut handlebar = handlebars::Handlebars::new(); handlebar.register_template_string("doc", template_content)?; let mut entries = Vec::new(); let mut themes = String::new(); let default = schemes .get("default") .unwrap() .as_table() .ok_or_else(|| Error::Message("not a table"))?; for (key, value) in schemes { if key == "default" { continue; } let mut colors_in = default.clone(); colors_in.extend( value .as_table() .ok_or_else(|| Error::Message("not a table"))? .clone(), ); let mut colors = Map::new(); for (k, color) in colors_in { let value = color .as_str() .ok_or_else(|| Error::Message("expected string"))?; colors.insert(k.to_owned(), handlebars::to_json(&value)); } let result = handlebar.render("doc", &colors)?; let key_upper = key.to_uppercase(); let name = format!("doc.{}.css", key); writeln!( themes, "const DOC_CSS_{}: &[u8] = include_bytes!(concat!(env!(\"OUT_DIR\"), \"/{}\"));", key_upper, name )?; entries.push(format!("(\"{}\", DOC_CSS_{})", key, key_upper)); let mut f = File::create(out_path.join(name))?; f.write_all(&result.into_bytes())?; } writeln!(themes, "")?; writeln!( themes, "pub fn build_themes_vec() -> Vec<(&'static str, &'static [u8])> {{" )?; writeln!(themes, " vec![{}]", entries.join(", "))?; writeln!(themes, "}}")?; let mut themes_file = File::create(out_path.join("themes.rs"))?; themes_file.write_all(&themes.into_bytes())?; Ok(()) } fn main() { println!("cargo:rerun-if-changed=themes.toml"); println!("cargo:rerun-if-changed=src/static/doc._.css.hbs"); println!("cargo:rerun-if-changed=src/static/normalize.css"); process_colors().unwrap(); }