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;
use crate::phighlighter::{
	pactionhighlight::PActionHighLight, pfileparser::PFileParserIter, phighlighter::PHighlighterGetUntilReplace, phighlither_configuration::{
		PHighlighterGetUntil,
		PHighlighterSequence,
	}
};

///Part of the highlighting tree
#[derive(Debug,Default,Clone)]
pub struct PNodeHighLight{
	///Index of the corresponding action to perform once none p_map_sequence or p_vec_oneof have a match (there will be a lot of action dupplication so we diminish the needed memory by factorizing it in the PHighLighter::p_map_action)
	p_action_index: usize,
	///Pattern or sequence of char to be tested by the node
	p_pattern: String,
	///Vector of node which match a sequence
	p_map_sequence: HashMap<u8, PNodeHighLight>,
	///Vector of node which match a oneof (one char match in all chars of the String p_pattern)
	p_vec_oneof: Vec<PNodeHighLight>,
}

///Update the available oneof those which as still available while we are trying to parse an other token
/// # Parameters
/// - `vec_available_oneof` : vector of available oneof to be updated with the given char
/// - `vec_current_oneof` : vector of oneof of the current PNodeHighlight
/// - `current_char` : current char which as to be in the remaining oneof
fn update_available_oneof(vec_available_oneof: &mut Vec<PNodeHighLight>, vec_current_oneof: &Vec<PNodeHighLight>, current_char: u8){
	//Nothing to update, we stop
	if vec_available_oneof.len() == 0 && vec_current_oneof.len() == 0 {
		return;
	}
	let str_char = String::from(current_char as char);
	let mut vec_remaning_oneof: Vec<PNodeHighLight> = vec![];
	for node in vec_available_oneof.iter() {
		if node.p_pattern.contains(&str_char) {
			vec_remaning_oneof.push(node.clone());
		}
	}
	for node in vec_current_oneof.iter() {
		if node.p_pattern.contains(&str_char) {
			vec_remaning_oneof.push(node.clone());
		}
	}
	*vec_available_oneof = vec_remaning_oneof.clone();
}

impl PNodeHighLight {
	///Constructor of a PNodeHighLight
	/// # Parameters
	/// - `action_index` : index of the action to perform if no more match is found
	/// - `pattern` : pattern for 'OneOf' search
	/// # Returns
	/// Initialised PNodeHighLight
	pub fn new(action_index: usize, pattern: &String) -> Self{
		PNodeHighLight{
			p_action_index: action_index,
			p_pattern: pattern.clone(),
			p_map_sequence: HashMap::default(),
			p_vec_oneof: vec![]
		}
	}
	///Add a token in the PNodeHighLight
	/// # Parameters
	/// - `token` : token to be parsed
	/// - `action_index` : index of the action to perform if there is no more match
	pub fn add_token(&mut self, token: &String, action_index: usize){
		let vec_byte: Vec<u8> = Vec::from(token.clone().into_bytes());
		self.add_vec_u8(&vec_byte, action_index);
	}
	
	///Add a token in the PNodeHighLight
	/// # Parameters
	/// - `token` : token to be parsed
	/// - `action_index` : index of the action to perform if there is no more match
	pub fn add_vec_u8(&mut self, token: &Vec<u8>, action_index: usize){
		//Here we need to walk on the vector of u8 and add all the necessary nodes for the parsing
		// All new nodes have the action 0 except the last one which has action action_index
		let mut last_node = self;
		for ch in token.iter() {
			let map_sequence = &mut last_node.p_map_sequence;
			//If the node does not exist, we create it
			if !map_sequence.contains_key(ch) {
				let node: PNodeHighLight = Default::default();
				map_sequence.insert(*ch, node);
			}
			//Then we get the next node, and it always exist
			last_node = map_sequence.get_mut(ch).unwrap();
			// last_node = match map_sequence.get_mut(ch) {
				// Some(node) => node,
				// None => &mut tmp_node
			// };
		}
		last_node.p_action_index = action_index;
	}
	///Add a getuntil in the PNodeHighLight
	/// # Parameters
	/// - `getuntil` : getuntil configuration to be used
	/// - `action_index` : index of the action to perform if there is no more match
	pub fn add_getuntil(&mut self, getuntil: &PHighlighterGetUntil, action_index: usize){
		let vec_byte: Vec<u8> = Vec::from(getuntil.start_token.clone().into_bytes());
		self.add_vec_u8(&vec_byte, action_index);
	}
	///Add a getuntilreplace in the PNodeHighLight
	/// # Parameters
	/// - `getuntilreplace` : getuntilreplace configuration to be used
	/// - `action_index` : index of the action to perform if there is no more match
	pub fn add_getuntilreplace(&mut self, getuntilreplace: &PHighlighterGetUntilReplace, action_index: usize){
		let vec_byte: Vec<u8> = Vec::from(getuntilreplace.start_token.clone().into_bytes());
		self.add_vec_u8(&vec_byte, action_index);
	}
	///Add a sequence in the PNodeHighligth
	/// # Parameters
	/// - `sequence` : sequence configuration to be used
	/// - `action_index` : index of the action to perform if there is no more match
	pub fn add_sequence(&mut self, sequence: &PHighlighterSequence, action_index: usize){
		//Let's add all steps of the sequence
		let mut last_node = self;
		for step in sequence.vec_step.iter(){
			//Depending if the step is a match or a getStrComposedOf
			// There is no need for an optionnal attribute because we can end after each step
			match &step.token {
				Some(token) => {
					// println!("PNodeHighLight::add_sequence : Add token '{}'", token);
					//We have to add the token in the node before calling the other steps
					// So we just change the last_node to be used, and where we will call the oneof at some point
					let vec_byte: Vec<u8> = Vec::from(token.clone().into_bytes());
					last_node = last_node.get_child_mut(&vec_byte);
					last_node.p_action_index = action_index;
				},
				None => match &step.oneof {
					Some(oneof) => {
						// println!("PNodeHighLight::add_sequence : Add oneof '{}'", oneof);
						//We have to add a get_str_of in the current node
						// First, we create the node
						let oneof_node = PNodeHighLight::new(action_index, oneof);
						last_node.p_vec_oneof.push(oneof_node);
						//And we add the node into the current one
						last_node = last_node.p_vec_oneof.last_mut().unwrap();
					},
					None => {}
				}
			};
		}
		// last_node.p_action_index = action_index;
	}
	///Get a mutable child at the given token
	/// # Parameters
	/// - `token` : token to be parsed
	/// # Returns
	/// Mutable reference to the correspondign child (it will be created if it does not exist)
	fn get_child_mut(&mut self, token: &Vec<u8>) -> &mut PNodeHighLight{
		let mut last_node = self;
		for ch in token.iter() {
			let map_sequence = &mut last_node.p_map_sequence;
			//If the node does not exist, we create it
			if !map_sequence.contains_key(ch) {
				let node: PNodeHighLight = Default::default();
				map_sequence.insert(*ch, node);
			}
			//Then we get the next node, and it always exist
			last_node = map_sequence.get_mut(ch).unwrap();
			// last_node = match map_sequence.get_mut(ch) {
				// Some(node) => node,
				// None => &mut tmp_node
			// };
		}
		return last_node;
	}
	///Do the highlight on a PNodeHighLight
	/// # Parameters
	/// - `body` : output of the highlighting
	/// - `current_match_token` : current matched token when walking on the tree of PNodeHighLight children
	/// - `it` : iterator on the parser to use
	/// - `vec_available_oneof` : 
	/// - `vec_action` : vector of action to be performed by the highlighter
	/// - `token_charset` : charset which contains all characters which composed a token
	pub fn highlight(&self, body: &mut String, current_match_token: &mut String, it: &mut PFileParserIter, vec_available_oneof: &mut Vec<PNodeHighLight>,
		vec_action: &Vec<PActionHighLight>, token_charset: &String)
	{
		match it.get_current_char() {
			Some(current_char) => {
				// println!("PNodeHighlight::highlight : current char = '{}', vec_available_oneof.len() = {}, self.p_vec_oneof.len() = {}", String::from(current_char as char), vec_available_oneof.len(), self.p_vec_oneof.len());
				// println!("PNodeHighlight::highlight : current char = '{}'", String::from(current_char as char));
				match self.p_map_sequence.get(&current_char) {
					Some(child) => {	//We found a match, so we jump on the next PNodeHighLight
						it.next();	//We get the next char
						*current_match_token += &String::from(current_char as char);	//We update the curently parsed token
						//Here we have to update the vector of available oneof
						update_available_oneof(vec_available_oneof, &self.p_vec_oneof, current_char);
						child.highlight(body, current_match_token, it, vec_available_oneof, vec_action, token_charset);	//Then we call it on the found child
					},
					None => {	//No more match, so we check the oneof
						let mut last_action_index: Option<usize> = None;
						for node in self.p_vec_oneof.iter() {
							if node.check_oneof(current_match_token, it, &mut last_action_index) {
								break;
							}
						}
						//If we found nothing maybe we have a previous oneof which is still valid
						if last_action_index == None {
							for node in vec_available_oneof.iter(){
								if node.check_oneof(current_match_token, it, &mut last_action_index) {
									break;
								}
							}
						}
						match last_action_index {
							Some(action_index) => self.run_action(action_index, current_char, body, current_match_token, it, vec_action, token_charset),
							None => self.run_action(self.p_action_index, current_char, body, current_match_token, it, vec_action, token_charset)
						};
					}
				}
			},
			None => {}	//This is the end of parsing
		};
	}
	///Check the oneof of the current PNodeHighlight
	/// # Parameters
	/// - `current_match_token` : current matched token when walking on the tree of PNodeHighLight children
	/// - `it` : iterator on the parser to use
	/// - `last_action_index` : index of the last action
	/// # Returns
	/// True if the oneof matches, false otherwise
	fn check_oneof(&self, current_match_token: &mut String, it: &mut PFileParserIter, last_action_index: &mut Option<usize>) -> bool{
		let str_composed_of: String = it.get_str_of(&self.p_pattern);
		if str_composed_of.len() > 0 {
			// println!("PNodeHighLight::highlight : find str of '{}'", str_composed_of);
			*current_match_token += &str_composed_of;
			//Let's see if we can have a last match
			// node.highlight(body, current_match_token, it, vec_action);
			*last_action_index = Some(self.p_action_index);
			return true;
		}
		return false;
	}
	///Run the given action_index
	/// # Parameters
	/// - `action_index` : index of the action to be performed
	/// - `body` : output of the highlighting
	/// - `current_match_token` : current matched token when walking on the tree of PNodeHighLight children
	/// - `it` : iterator on the parser to use
	/// - `vec_action` : vector of action to be performed by the highlighter
	/// - `token_charset` : charset which contains all characters which composed a token
	fn run_action(&self, action_index: usize, current_char: u8, body: &mut String, current_match_token: &mut String, it: &mut PFileParserIter, vec_action: &Vec<PActionHighLight>, token_charset: &String){
		// println!("PNodeHighlight::run_action : current_char = '{}', current_match_token = '{}'", String::from(current_char as char), current_match_token);
		match vec_action.get(action_index) {
			Some(action) => {
				if current_match_token.len() == 0 {	//They were no previous match
					*body += &String::from(current_char as char);
					it.next();	//We get the next char
				}else{
					//Let's performed the associated action
					*body += &action.run(it, &current_match_token, token_charset);
					//The current char does not match here, but maybe at the begining of a new token
					*current_match_token = String::from("");	//We found a token, so we reset the matched token
				}
			},
			None => panic!("PNodeHighLight::run_action : no action {} to be performed at {}", action_index, it.get_location())
		}
	}
}