rustyphoenixlecture 1.0.0

This project aims to provide a simple a powerfull lecture compilation to generate html web sites
/***************************************
	Auteur : Pierre Aubert
	Mail : pierre.aubert@lapp.in2p3.fr
	Licence : CeCILL-C
****************************************/

use std::{collections::HashMap, fs, path::PathBuf, process::Command};
use serde::{Serialize, Deserialize};
use toml;

///Map of all formulae of the lecture
#[derive(Debug, Default, Clone, Deserialize, Serialize)]
struct MapFormula{
	///Map of the all formulae of the lecture
	p_map_formula: HashMap<String, String>,
}

///Load the MapFormula configuration file
/// # Parameters
/// - `filename` : name of the MapFormula configuration file (in toml)
/// # Returns
/// Replace parsed MapFormula configuration or error on fail
fn load_map_formula(filename: &PathBuf) -> MapFormula {
	let config: MapFormula = match fs::read_to_string(filename) {
		Ok(str) => match toml::from_str(&str){
			Ok(value) => value,
			Err(error) => panic!("load_map_formula : cannot parse file contaning formulae {:?}, parsing error {}", filename, error)
		},
		Err(_) => Default::default()
	};
	return config;
}

///Save the map of formula
/// # Parameters
/// - `filename` : file to write
/// - `map_formula` : map of formula to be saved
fn save_map_formula(filename: &PathBuf, map_formula: &MapFormula){
	let body: String = match toml::to_string(map_formula) {
		Ok(value) => value,
		Err(err) => panic!("save_map_formula : cannot serialise MapFormula in toml\n\tError {}", err)
	};
	match fs::write(filename, body) {
		Ok(_) => {},
		Err(err) => panic!("save_map_formula : cannot save the map of formula in the file {:?}\n\tError {}", filename, err)
	}
}

///Manager of all the formula in the lecture
pub struct PFormulaManager{
	///Output directory of the full web site
	p_output_directory: PathBuf,
	///Configuration file of the formulae
	p_formula_config_file: PathBuf,
	///Map of the all formulae of the lecture
	p_map_formula: MapFormula,
	///Script to be used to create formula
	p_script_create_formula: PathBuf,
}

impl Drop for PFormulaManager{
	///Destructor of the PFormulaManager
	fn drop(&mut self) {
		fs::remove_file(&self.p_script_create_formula).unwrap();
		save_map_formula(&self.p_formula_config_file, &self.p_map_formula);
	}
}

impl PFormulaManager {
	///Contructor of the PFormulaManager
	/// # Parameters
	/// - `output_directory` : output directory where to create the web site
	pub fn new(output_directory: &PathBuf) -> Self{
		let config_formula: PathBuf = output_directory.join(PathBuf::from("formulae.toml"));
		let script_create_formula: PathBuf = output_directory.join(PathBuf::from("script_create_formula.sh"));
		let output_formula_dir: PathBuf = output_directory.join(PathBuf::from("formulae"));
		match fs::create_dir_all(&output_formula_dir){
			Ok(_) => {},
			Err(err) => panic!("PFormulaManager::new : cannot create output formula directory {:?}\n\tError {}", output_formula_dir, err)
		}
		create_script_formula(&script_create_formula);
		PFormulaManager{
			p_output_directory: output_directory.clone(),
			p_formula_config_file: config_formula.clone(),
			p_map_formula: load_map_formula(&config_formula),
			p_script_create_formula: script_create_formula.clone(),
		}
	}
	///Convert a formula into html
	/// # Parameters
	/// - `formula` : formula to be converted into html
	/// # Returns
	/// Corresponding html
	pub fn formula_to_html(&mut self, formula: &String) -> String{
		let trim_formula = String::from(formula.trim());
		let matched_formula: String = match self.p_map_formula.p_map_formula.get(&trim_formula) {
			Some(html) => html.clone(),
			None => String::from("")
		};
		//If we found the formula, we return it, no need to create it again
		if !matched_formula.is_empty() {
			return matched_formula;
		}
		//Here we have to create the formula image
		return self.create_formula(&trim_formula);
	}
	///Create a missing formula
	/// # Parameters
	/// - `formula` : trimmed formula to be converted into html
	/// # Returns
	/// Corresponding html
	fn create_formula(&mut self, trim_formula: &String) -> String{
		// println!("PFormulaManager::create_formula : formula '{}'", trim_formula);
		let image_index: usize = self.p_map_formula.p_map_formula.len() + 1;
		let image = String::from(&format!("formulae/{}.png", image_index));
		let absolute_image_path: PathBuf = self.p_output_directory.join(PathBuf::from(&image));
		//We have to create the latex file
		create_latex_formula_file(&self.p_output_directory, image_index, &trim_formula);
		
		//Let's generate the formula
		match Command::new(&self.p_script_create_formula)
			.arg(image_index.to_string())
			.arg(&absolute_image_path)
			.current_dir(&self.p_output_directory)
			.output()
		{
			Ok(_) => {},
			Err(err) => panic!("PFormulaManager::create_formula : cannot create file {:?}\n\tWith formula '{}'\n\tError {}", absolute_image_path, trim_formula, err)
		}
		
		let html = String::from(&format!("<img src=\"{}\" alt=\"formula\" />", image));
		self.p_map_formula.p_map_formula.insert(trim_formula.clone(), html.clone());
		return html;
	}
}

///Create the latex file which contains the formula
/// # Parameters
/// - `output_directory` : output directory where to write the file
/// - `image_index` : index of the image to be created
/// - `trim_formula` : formula to be saved
fn create_latex_formula_file(output_directory: &PathBuf, image_index: usize, trim_formula: &String){
	let mut body = String::from("");
	if trim_formula.starts_with('$') {
		body += &String::from("\\documentclass[8pt]{article}\n");
	}else{
		body += &String::from("\\documentclass[20pt]{article}\n");
	}
	body += &String::from("\\usepackage[T1]{fontenc}\n\\usepackage[utf8]{inputenc}\n\\usepackage{lmodern}\n");
	body += &String::from("\\usepackage{calc}\n\\usepackage{amssymb}\n\\usepackage{color}\n\\usepackage{amsfonts}\n\\usepackage{bbm}\n\\pagestyle{empty}\n");
	body += &String::from("\\usepackage{amsmath}\n\\usepackage{esint}\n");
	body += &String::from("\\begin{document}\n");
	body += &String::from("\\def\\sgn{\\mathop{\\mathgroup\\symoperators sgn}\\nolimits}\n");
	body += &String::from("\\pagecolor{black}\\color{white}");
	body += &String::from(trim_formula);
	body += &String::from("\n");
	body += &String::from("\\end{document}\n");
	let output_file = output_directory.join(format!("{}_tmp_file.tex", image_index));
	match fs::write(&output_file, &body) {
		Ok(_) => {},
		Err(err) => panic!("Cannot create latex file {:?}\n\tError {}", output_file, err)
	}
}

///Save the horible latex script
/// # Parameters
/// - `filename` : name of the script to be created
fn create_script_formula(filename: &PathBuf){
	let body = String::from(r#"#!/bin/bash

#fichier png que l'on veut générer avec le fichier tex
IMAGE_INDEX="$1"
FILE_OUTPUT_PNG="$2"

#fichier tex que l'on veut générer en dvi
FILE_TEX="${IMAGE_INDEX}_tmp_file.tex"
FILE_DVI="${IMAGE_INDEX}_tmp_file.dvi"
# FILE_TMP_LOG="/tmp/fileTmpLogTex.txt"

# cd /tmp/

# latex -halt-on-error ${FILE_TEX} > FILE_TMP_LOG && dvipng -D 200 ${FILE_DVI} -T tight -bg transparent -o ${FILE_OUTPUT_PNG} > FILE_TMP_LOG && rm file.tex file.aux file.log file.dvi

#le fichier de sortie console de latex
LOG_FILE="logOfLatex.log"

#Je commence à en avoir marre que bash se foute de moi
function quitOnError {
		echo "====== Fail to create image formula :"
		cat ${FILE_TEX}
		echo "====== Error :"
		cat $LOG_FILE | tail -n 100
		exit 1
}

latex -halt-on-error ${FILE_TEX} ${FILE_DVI} > $LOG_FILE  || quitOnError
# echo "Latex Success"

dvipng -D 180 ${FILE_DVI} -T tight -bg transparent -o ${FILE_OUTPUT_PNG} > /dev/null
# dvipng -D 130 ${FILE_DVI} -T tight -o ${FILE_OUTPUT_PNG} > /dev/null

# echo "Génération de l'image OK"

# rm ${FILE_DVI} *.aux *.log
rm ${FILE_DVI} *.aux *.log ${FILE_TEX}

exit 0

"#);
	match fs::write(&filename, &body){
		Ok(_) => println!("PFormulaManager::create_script_formula : Create formula script {:?}", filename),
		Err(err) => panic!("create_script_formula : Cannot save the script {:?}\n\tError {}", filename, err)
	}
	Command::new("chmod").arg("a+x").arg(&filename).output().expect(&format!("create_script_formula : Cannot change permission of the script {:?}", filename));
}