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::fs;
use std::collections::HashMap;
use std::path::PathBuf;
use std::process::Command;

use crate::{
	lecture_config::LectureConfig,
	phighlighter::{PFileParser, PLocation},
	plectureparser::{
		PReferenceManager,
		PAbstractContent, PAbstractLectureBackend,
		PLectureParser, PLectureData, PLecureBackend,
		PContentTitle, PContentType, PVecContent,
		PRessourceArchive,
		PLabelId,
		phoenix_html_404
	}
};

///Main lecture manager
pub struct PLecture{
	///Configuration of the current lecture
	p_config: LectureConfig,
	///Output path where to generate the lecure website
	p_output_path: PathBuf,
	///Base of the html theme to be used
	p_base_theme_page: String,
	///Parsed content of the lecture
	p_content: PVecContent,
	///Manager of the reference and labels of the lecture
	p_reference_manager: PReferenceManager,
	///First page after index.html
	p_first_next_page: String,
	///Map of all archive to be created for the lecture
	p_map_archive: HashMap<PathBuf, PRessourceArchive>,
}

///Load the lecture
/// # Parameters
/// - `config` : lecture configuration
/// - `output_path` : output path where to generate the lecture website
pub fn create_lecture_website(config: &LectureConfig, output_path: &PathBuf){
	let mut lecture = PLecture::new(config, output_path);
	lecture.load();
}

impl PLecture{
	///Create the lecture
	/// # Parameters
	/// - `config` : lecture configuration
	/// - `output_path` : output path where to generate the lecture website
	/// # Returns
	/// Initialised PLecture
	fn new(config: &LectureConfig, output_path: &PathBuf) -> Self{
		let mut other = PLecture {
			p_config: config.clone(),
			p_output_path: output_path.canonicalize().unwrap(),
			p_base_theme_page: Default::default(),
			p_content: Default::default(),
			p_reference_manager: Default::default(),
			p_first_next_page: String::from(""),
			p_map_archive: Default::default(),
		};
		let theme_file: PathBuf = other.p_config.get_theme_path().join("theme.html");
		other.p_base_theme_page = match fs::read_to_string(&theme_file) {
				Ok(str) => str,
				Err(err) => panic!("PLecture::new : cannot load theme at {:?}. Error {}", theme_file, err)
		};
		other.load_dependency();
		return other;
	}
	///Load the dependencies of the current lecture
	/// # Errors
	/// This method can panic if it cannot load a required dependency or fetch a required dependency
	fn load_dependency(&mut self){
		if self.p_config.get_map_dependency().len() == 0 {
			return;
		}
		let dependency_config_dir: PathBuf = PathBuf::from("dependencies");
		if !dependency_config_dir.exists() {
			match fs::create_dir_all(&dependency_config_dir) {
				Ok(_) => {},
				Err(err) => panic!("PLecture::load_dependency : cannot create dependency directory {:?}\n\tError {}", dependency_config_dir, err)
			}
		}
		//We iterate on dependencies
		for (name, url) in self.p_config.get_map_dependency().iter(){
			//Let' define the configuration file we want to load
			let dependency_config: PathBuf = dependency_config_dir.join(PathBuf::from(format!("{}.toml", name)));
			//If we do not found if, we fetch it with its url
			if !dependency_config.exists() {
				match Command::new("wget").arg("-O").arg(&dependency_config).arg(format!("{}/lecture_citation.toml",url)).output() {
					Ok(output) => {
						if !output.status.success() {
							panic!("PLecture::load_dependency : cannot get the lecture dependency '{}'\n\tfrom url '{}'\n\tMaybe the url does not exist or there is no internet", name, url)
						}
					},
					Err(err) => panic!("PLecture::load_dependency : cannot call wget to get the lecture dependency '{}'\n\tfrom url '{}'\n\tError {}", name, url, err)
				}
			}
			//If we alreay found it, we load it with the self.p_reference_manager
			self.p_reference_manager.load_lecture_dependency(name, &dependency_config);
		}
	}
	///Load all the written lecture and save the generated website
	fn load(&mut self){
		println!("PLecture::load : loading lecture files");
		//We have to create the output path and theme dir here because the PLectureParser will strip sources directly there
		self.save_theme();
		//Let's create the lecture parser and parse the current file :
		let lecture_parser = PLectureParser::new(&self.p_output_path, self.p_config.get_vec_parser(), self.p_config.get_vec_environment());
		
		for input_file in self.p_config.get_vec_file().iter(){
			let parser: PFileParser = PFileParser::from_file(input_file);
			let mut data = PLectureData::new(0, &parser);
			if ! lecture_parser.parse(&mut self.p_content, &mut data) {
				panic!("PLecture::load : cannot parse the file {:?}", parser.get_filename());
			}
			//Let's update the declared label in the map fo label of the lecture
			self.p_reference_manager.add_reference(&data);
			//Let's add all found archive in the PLecture
			for (output_path, archive) in data.get_map_archive().iter(){
				self.p_map_archive.insert(output_path.clone(), archive.clone());
			}
		}
		self.update_title_number();
		self.update_prev_next_section();
		self.p_reference_manager.solve_reference(&mut self.p_content);
		self.save_website();
		//Here we create archives
		self.create_all_archive();
	}
	///Update the title number
	fn update_title_number(&mut self){
		println!("PLecture::update_title_number : updating title number");
		let mut vec_title_number: Vec<usize> = vec![];
		self.p_content.update_title_number(&mut vec_title_number);
	}
	///Update references and index
	fn update_prev_next_section(&mut self){
		println!("PLecture::update_prev_next_section : updating previous and next section");
		let mut prev_page = String::from("index.html");
		let mut prev_section: Option<&mut PContentTitle> = None;
		for content in self.p_content.get_vec_child_mut().iter_mut() {
			match content {
				PContentType::Title(section) => {
					match prev_section {
						Some(prev) => prev.set_next_filename(section.get_data().get_output_filename()),
						None => {}
					};
					section.get_data_mut().set_previous_filename(&prev_page);
					prev_page = section.get_data().get_output_filename().clone();
					prev_section = Some(section.get_data_mut());
					if self.p_first_next_page.is_empty() {
						self.p_first_next_page = prev_page.clone();
					}
				},
				&mut _ => {}
			};
		}
		//We do not forget to update the next section of the last title which will be itself
		match prev_section {
			Some(prev) => prev.set_next_filename(&prev.get_output_filename().clone()),
			None => {}
		};
	}
	///Create the output_path of the website
	fn create_output_path(&self){
		match fs::create_dir_all(&self.p_output_path){
			Ok(_) => {},
			Err(err) => panic!("PLecture::create_output_path : cannot create output directory {:?}. Error {}", self.p_output_path, err)
		};
	}
	///Save the theme
	fn save_theme(&self){
		println!("PLecture::save_theme : saving theme");
		self.create_output_path();
		Command::new("cp")
			.arg("-r")
			.arg(self.p_config.get_theme_path())
			.arg(&self.p_output_path)
			.spawn().expect("PLecture::save_website : Cannot copy theme into output directory");
	}
	///Save the website
	fn save_website(&self){
		println!("PLecture::save_website : saving website");
		let current_branch = String::from("main");	//TODO : find a way to get the branch properly (even without git)
		let mut backend = PLecureBackend::new(&self.p_output_path, &self.p_config.get_project_url(), &self.p_config.get_contact_mail(), &current_branch, &self.p_config.get_master_project(), &self.p_content);
		
		let first_lecture_source: String = self.get_first_source_file();
		backend.create_file(&String::from(self.p_output_path.join("index.html").to_str().unwrap()), &self.p_config.get_title(),
			&PLocation::new(&PathBuf::from(first_lecture_source), 1, 1), &String::from("index.html"), &self.p_first_next_page);
		self.create_main_page(&mut backend);
		self.p_content.to_html(&mut backend, &PLabelId::new(0));
		phoenix_html_404(&self.p_output_path);
		self.p_reference_manager.save_redirection_html(&self.p_output_path);
		self.p_reference_manager.save_lecture_citation_config(&self.p_output_path, &self.p_config.get_name(), &self.p_config.get_documentation_url());
	}
	///Create the main page
	fn create_main_page(&self, backend: &mut PLecureBackend){
		backend.write(&format!("<h1 class=\"mainTitle\">{}</h1>\n", self.p_config.get_title()));
		backend.write(&String::from("<h5 class=\"author\">"));
		let mut comma = String::from("");
		for author in self.p_config.get_vec_author().iter(){
			backend.write(&format!("{}{}",comma, author));
			comma = String::from(", ");
		}
		backend.write(&String::from("</h5>\n"));
	}
	///Get the first source file of the lecture
	/// # Returns
	/// First source file of the lecture
	fn get_first_source_file(&self) -> String {
		match self.p_config.get_vec_file().get(0) {
			Some(filename) => String::from(filename.join(&self.p_output_path).to_str().unwrap()),
			None => String::from("index.md")
		}
	}
	///Create all archive of the lecture
	fn create_all_archive(&self){
		println!("PLecture::create_all_archive : creating {} archives of the lecture", self.p_map_archive.len());
		for (_, archive) in self.p_map_archive.iter(){
			archive.create_archive();
		}
	}
}