rustyphoenixlecture 1.5.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::read_link;	//To get the same reaklink on C/C++ which allows to find the current location of the current executable
use std::{collections::HashMap, fs, path::PathBuf};
use serde::Deserialize;
use toml::{self, de::Error};

///Configuration of the full lecture serde compatible (yes, there is no Path or PathBuf in it which is very anoying)
#[derive(Debug, Clone, Deserialize)]
pub struct LectureConfig{
	///Vector of files of the lecture
	vec_file: Vec<PathBuf>,
	///Title of the lecture
	title: String,
	///Name of the lecture
	name: String,
	///Vector of authors of the lecture
	vec_author: Vec<String>,
	///Path to the theme directory
	theme_path: PathBuf,
	///Url to the current project (example on gitlab)
	project_url: String,
	/// Url where to find the documentation of the project or the generated lecture (i.e. pages for gitlab)
	documentation_url: String,
	///Url of the master project if the lecture is a part of a bigger project
	master_project: String,
	///Main contact for the lecture
	contact_mail: String,
	///Vector of all parser directories to be used
	vec_parser: Vec<PathBuf>,
	///Vector of all environment directories to be used
	vec_environment: Vec<PathBuf>,
	///Map of lectures from which labels have to be used (key: LectureName, value: lecture documentation url)
	dependencies: HashMap<String, String>,
}

impl LectureConfig{
	///Get the vector of files
	/// # Returns
	/// Vector of files of the lecture
	pub fn get_vec_file(&self) -> &Vec<PathBuf>{
		&self.vec_file
	}
	///Get the title
	/// # Returns
	/// Title of the lecture
	pub fn get_title(&self) -> &String{
		&self.title
	}
	///Get the name
	/// # Returns
	/// Name of the lecture
	pub fn get_name(&self) -> &String{
		&self.name
	}
	///Get the vector of author
	/// # Returns
	/// Vector of author
	pub fn get_vec_author(&self) -> &Vec<String>{
		&self.vec_author
	}
	///Get the theme path
	/// # Returns
	/// Theme path of the lecture
	pub fn get_theme_path(&self) -> &PathBuf{
		&self.theme_path
	}
	///Get url of the project
	/// # Returns
	/// Url of the project
	pub fn get_project_url(&self) -> &String{
		&self.project_url
	}
	///Get url of the project documentation or published lecture
	/// # Returns
	/// Url of the project or published lecture
	pub fn get_documentation_url(&self) -> &String{
		&self.documentation_url
	}
	///Get url of the master project
	/// # Returns
	/// Url of the master project
	pub fn get_master_project(&self) -> &String{
		&self.master_project
	}
	///Get contact mail
	/// # Returns
	/// Contact mail
	pub fn get_contact_mail(&self) -> &String{
		&self.contact_mail
	}
	///Get the vector of parser directories
	/// # Returns
	/// Vector of parser directories of the lecture
	pub fn get_vec_parser(&self) -> &Vec<PathBuf>{
		&self.vec_parser
	}
	///Get the vector of environment directories
	/// # Returns
	/// Vector of environment directories of the lecture
	pub fn get_vec_environment(&self) -> &Vec<PathBuf>{
		&self.vec_environment
	}
	///Get the map of dependencies of the lecture
	/// # Returns
	/// Map of dependencies of the lecture (key: LectureName, value: url of the lecture documentation i.e. pages for gitlab)
	pub fn get_map_dependency(&self) -> &HashMap<String, String>{
		&self.dependencies
	}
}

///Make the absolute path for the config
/// # Parameters
/// - `absolute_config_parent` : parent directory of the current configuration file
/// - `input_path` : input path
/// - `program_prefix_share` : share directory of installation derived from program prefix if it does exist
/// # Returns
/// Output absolute path corresponding to the input path
fn make_abolute_path(absolute_config_parent: &PathBuf, input_path: &PathBuf, program_prefix_share: &Option<PathBuf>) -> PathBuf {
	match program_prefix_share {
		Some(prefix) => {	//If there is a prefix, we use it as a replace for the absolute_config_parent
			let str_input_path: String = String::from(input_path.to_str().unwrap());
			if str_input_path.contains("${PREFIX_SHARE}") {
				let absolute_path: PathBuf = PathBuf::from(str_input_path.replace("${PREFIX_SHARE}", prefix.to_str().unwrap()));
				return absolute_path;
			}else{		//No variable call, so no replace needed
				let absolute_path = absolute_config_parent.join(input_path);
				return absolute_path;
			}
		},
		None => {
			let absolute_path = absolute_config_parent.join(input_path);
			return absolute_path;
		}
	}
}

///Load the lecture configuration file
/// # Parameters
/// - `filename` : name of the highlighter configuration file (in toml)
/// # Returns
/// Lecture configuration or error on fail
pub fn load_lecture_config(filename: &String) -> Result<LectureConfig, Error> {
	let mut config: LectureConfig = match fs::read_to_string(filename) {
		Ok(str) => match toml::from_str(&str){
			Ok(value) => value,
			Err(error) => panic!("load_lecture_config : cannot parse file '{}', parsing error {}", filename, error)
		},
		Err(err) => panic!("load_lecture_config : cannot read file '{}', error: {}", filename, err)
	};
	let config_path = PathBuf::from(filename);
	let absolute_config = match config_path.canonicalize() {
		Ok(absolute_config_path) => absolute_config_path,
		Err(err) => panic!("load_lecture_config : cannot get absolute path of '{}'. Error {}", filename, err)
	};
	let absolute_config_parent: PathBuf = match absolute_config.parent() {
		Some(abs_path) => PathBuf::from(abs_path),
		None => panic!("load_lecture_config : no parent found for file {:?}", absolute_config)
	};
	//Let's create the program prefix if it does exist
	let program_prefix_share: Option<PathBuf> = match read_link(&PathBuf::from("/proc/self/exe")) {
		//Let's get share directory from program file in bin
		Ok(prefix) => Some(PathBuf::from(prefix.parent().unwrap().parent().unwrap().join("share"))),
		Err(_) => None
	};
	let theme_path = PathBuf::from(config.get_theme_path());
	if theme_path.is_relative() {
		//Let's make the theme path absolute
		// config.theme_path = absolute_config_parent.join(config.theme_path);
		config.theme_path = make_abolute_path(&absolute_config_parent, &config.theme_path, &program_prefix_share);
	}
	//Let's make the vec_file absolute
	for input_file in config.vec_file.iter_mut() {
		if input_file.is_relative() {
			// *input_file = absolute_config_parent.join(&input_file);
			*input_file = make_abolute_path(&absolute_config_parent, &input_file, &program_prefix_share);
		}
	}
	//Let's make the vec_parser absolute
	for input_dir in config.vec_parser.iter_mut() {
		if input_dir.is_relative() {
			// *input_dir = absolute_config_parent.join(&input_dir);
			*input_dir = make_abolute_path(&absolute_config_parent, &input_dir, &program_prefix_share);
		}
	}
	//Let's make the vec_environment absolute
	for input_dir in config.vec_environment.iter_mut() {
		if input_dir.is_relative() {
			// *input_dir = absolute_config_parent.join(&input_dir);
			*input_dir = make_abolute_path(&absolute_config_parent, &input_dir, &program_prefix_share);
		}
	}
	Ok(config)
}

#[cfg(test)]
mod tests{
	use super::*;
	
	///Test the load lecture config
	#[test]
	fn test_load_lecture_config(){
		let config: LectureConfig = load_lecture_config(&String::from("tests/LectureWithDependency/lecture.toml")).unwrap();
		assert_eq!(config.get_vec_file().len(), 1);
		assert!(config.get_vec_file().get(0).unwrap().ends_with(&String::from("index.md")));
		
		assert_eq!(config.get_title().clone(), String::from("A great lecture"));
		assert_eq!(config.get_name().clone(), String::from("TestLecture"));
		assert_eq!(config.get_project_url().clone(), String::from("https://gitlab.in2p3.fr/CTA-LAPP/PHOENIX_LIBS2/static-site-generator/RustyPhoenixLecture"));
		assert_eq!(config.get_documentation_url().clone(), String::from("https://cta-lapp.pages.in2p3.fr/PHOENIX_LIBS2/static-site-generator/RustyPhoenixLecture"));
		assert_eq!(config.get_master_project().clone(), String::from("https://paubert.pages.in2p3.fr/Lectures/"));
		assert_eq!(config.get_contact_mail().clone(), String::from("pierre.aubert@lapp.in2p3.fr"));
		
		assert_eq!(config.get_vec_author().len(), 1);
		assert_eq!(config.get_vec_author().get(0).unwrap().clone(), String::from("Pierre Aubert"));
		assert!(config.get_theme_path().ends_with(&String::from("themes/book")));
		
		assert_eq!(config.get_map_dependency().len(), 1);
		assert_eq!(config.get_map_dependency().get(&String::from("performance_with_stencil")).unwrap(), &String::from("https://pages.gitlab.com/url/to/this/lecture/"));
	}
}