use std::fs;
use std::path::PathBuf;
use crate::phighlighter::{
PFileParser, PHighlighter, PHighlighterManager, PLocation
};
use crate::pcontent::{
PContentDetail,
PContentParagraph,
PLabeler,
PContent,
PContentParser,
PContentReference,
PContentUrl,
PContentFootnote,
PContentTitle,
PContentText,
PMediaType,
PContentEnvironment,
PContentFormula,
PItemType, PContentItem,
PContentCell, PContentRow, PContentTable,
PContentEnvList,
PContentType,
PVecContent
};
use crate::ptimetable::create_content_timetable;
use crate::ptimetable::load_vec_speaker;
use crate::plectureparser::{
pressourcefile::PRessourceFile,
pressourcearchive::PRessourceArchive,
plecturedata::PLectureData,
penvironmentmanager::PEnvironmentManager
};
fn get_file_extension(media_url: &String) -> String{
let media_file = PathBuf::from(media_url);
match media_file.extension() {
Some(ext) => match ext.to_str() {
Some(s) => String::from(s),
None => String::from("")
},
None => String::from("")
}
}
fn make_lecture_comment(begin_comment: &String) -> String{
if begin_comment.is_empty() {
return String::from("");
}
return String::from(format!("{}{{", begin_comment));
}
pub struct PLectureParser{
p_output_path: PathBuf,
p_highligher_manager: PHighlighterManager,
p_environement_manager: PEnvironmentManager,
p_vec_highlighter: Vec<String>,
}
impl PLectureParser{
pub fn new(output_path: &PathBuf, vec_config_directory: &Vec<PathBuf>, vec_env_directory: &Vec<PathBuf>) -> Self{
let mut other = PLectureParser {
p_output_path: output_path.clone(),
p_highligher_manager: PHighlighterManager::new(vec_config_directory),
p_environement_manager: PEnvironmentManager::new(vec_env_directory),
p_vec_highlighter: vec![],
};
other.p_vec_highlighter = other.p_highligher_manager.get_vec_highlighter_name().clone();
other.p_environement_manager.write_css(&output_path.join(&PathBuf::from("book/environment.css")));
return other;
}
pub fn get_output_path(&self) -> &PathBuf{
&self.p_output_path
}
pub fn parse(&self, content: &mut PVecContent, data: &mut PLectureData) -> bool{
while !data.get_file_iter().is_end_of_file() {
if self.parse_content(content, data) {}
else { data.increment_current_char();
}
}
data.play_text(content);
return true;
}
pub fn parse_content(&self, content: &mut PVecContent, data: &mut PLectureData) -> bool{
if self.parse_section_title(content, data, &String::from("##### "), 5) {}
else if self.parse_section_title(content, data, &String::from("#### "), 4) {}
else if self.parse_section_title(content, data, &String::from("### "), 3) {}
else if self.parse_section_title(content, data, &String::from("## "), 2) {}
else if self.parse_section_title(content, data, &String::from("# "), 1) {}
else if self.parse_inline_formula(content, data) {}
else if self.parse_label(content, data) {}
else if self.parse_archive(content, data) {}
else if self.parse_speaker(content, data) {}
else if self.parse_timetable(content, data) {}
else if self.parse_envlist(content, data) {}
else if self.parse_media_highlight(content, data) {}
else if self.parse_bold(content, data) {}
else if self.parse_italic(content, data) {}
else if self.parse_environment(content, data) {}
else if self.parse_code_snipet(content, data) {}
else if self.parse_comment(content, data) {}
else if self.parse_auto_url(content, data, &String::from("https://")) {}
else if self.parse_auto_url(content, data, &String::from("http://")) {}
else if self.parse_url(content, data) {}
else if self.parse_reference(content, data) {}
else if self.parse_item_list(content, data) {}
else if self.parse_table(content, data) {}
else if self.parse_environement_example(content, data) {}
else if self.parse_parser_example(content, data) {}
else{
match self.p_highligher_manager.get_highlighter_by_name(&String::from("lecture_keyword")) {
Some(highligher) => {
let highligh_result: String = highligher.highlight_iter(data.get_file_iter());
data.add_text(content, &highligh_result);
return true;
},
None => {}
}
return false;
}
return true;
}
fn parse_section_title(&self, content: &mut PVecContent, data: &mut PLectureData, section_token: &String, section_level: usize) -> bool{
let current_indentation: Option<usize> = data.get_file_iter().get_indentation_level().clone();
if current_indentation == None {
return false;
}
if !data.get_file_iter().is_match(section_token) {
return false;
}
data.play_text(content);
let mut section: PContentTitle = PContentTitle::new(section_level, data.is_numbered_title_enable(), &data.get_file_iter().get_location());
self.parse_content_sequence(&mut section.get_title_mut(), data, &String::from(""), &String::from("\n"), false);
let id = data.get_current_id();
data.add_child(content, &PContentType::Title(PLabeler::new(id, §ion)));
return true;
}
fn parse_inline_formula(&self, content: &mut PVecContent, data: &mut PLectureData) -> bool{
if !data.get_file_iter().is_match(&String::from("$")) {
return false;
}
if data.get_file_iter().is_match(&String::from("{")) {
data.add_text(content, &String::from("${"));
return false;
}
data.play_text(content);
let location: PLocation = data.get_file_iter().get_location().clone();
let formula: String = data.get_file_iter().get_until(&String::from("$"));
let trim_formula: String = String::from(formula.trim());
if !trim_formula.is_empty() {
let id = data.get_current_id();
data.add_child(content, &PContentType::Formula(PLabeler::new(id, &PContentFormula::new(&String::from(format!("${}$", trim_formula)), true, &location))));
}else{
data.add_text(content, &String::from("$"));
}
return true;
}
fn parse_label(&self, content: &mut PVecContent, data: &mut PLectureData) -> bool{
if !data.get_file_iter().is_match(&String::from("#")) {
return false;
}
let label: String = data.get_file_iter().get_str_of(&String::from("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"));
if !data.add_label(content, &label) {
panic!("PLectureParser::add_label : Error at {}\n\tCannot create label '{}'", data.get_parser_location(), label);
}
return true;
}
fn parse_reference(&self, content: &mut PVecContent, data: &mut PLectureData) -> bool{
if data.get_file_iter().is_current_char_in_charset(&String::from("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_")) {
return false;
}
if !data.get_file_iter().is_match(&String::from("@")) {
return false;
}
let location = data.get_file_iter().get_location().clone(); let reference: String = data.get_file_iter().get_str_of(&String::from("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"));
if reference.is_empty() {
panic!("PLectureParser::parse_reference : Error at {}\n\tReference cannot be empty", data.get_file_iter().get_location());
}
data.play_text(content);
let id = data.get_current_id();
if data.get_file_iter().is_match(&String::from("::")) { let other_lecture_reference: String = data.get_file_iter().get_str_of(&String::from("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"));
data.add_child(content, &PContentType::Reference(PLabeler::new(id, &PContentReference::from_other_lecture(&other_lecture_reference, &reference))));
data.add_lecture_reference(&reference, &other_lecture_reference, &location);
}else{
data.add_child(content, &PContentType::Reference(PLabeler::new(id, &PContentReference::new(&reference))));
data.add_reference(&reference, &location);
}
return true;
}
fn parse_item_list(&self, content: &mut PVecContent, data: &mut PLectureData) -> bool{
let current_indentation: Option<usize> = data.get_file_iter().get_indentation_level().clone();
match current_indentation {
Some(indentation_level) => {
if !data.get_file_iter().is_match(&String::from("- ")) {
return false;
}
let id = data.get_current_id();
data.add_child(content, &PContentType::ListItem(PLabeler::new(id, &PContentItem::new(indentation_level, &PItemType::Item))));
return true;
},
None => false
}
}
fn parse_table(&self, content: &mut PVecContent, data: &mut PLectureData) -> bool{
if !data.get_file_iter().is_match(&String::from("|")){
return false;
}
let mut add_new_line: bool = true;
let table_location: PLocation = data.get_file_iter().get_location().clone();
let mut table = PContentTable::new();
let table_id = data.get_current_id();
while !data.get_file_iter().is_end_of_file() {
if add_new_line {
table.get_vec_row_mut().push(PContentRow::new());
add_new_line = false;
}
let mut current_cell = PContentCell::new();
self.parse_content_sequence(&mut current_cell.get_content_mut(), data, &String::from(""), &String::from("|"), true);
table.add_cell_in_last_row(¤t_cell);
if data.get_file_iter().is_match(&String::from("\n\n")){
break;
}
if data.get_file_iter().is_match(&String::from("\n|")){
add_new_line = true;
}
};
if !table.check_table(){
panic!("PLectureParser::parse_table : Error at {}\n\tThe number of columns are not coherent!", table_location)
}
content.add_child(&PContentType::Table(PLabeler::new(table_id, &table)));
return true;
}
fn parse_archive(&self, content: &mut PVecContent, data: &mut PLectureData) -> bool{
if !data.get_file_iter().is_match(&String::from(") {
return false;
}
data.play_text(content);
let directory_name = PathBuf::from(data.get_file_iter().get_until(&String::from(")")));
let archive_ressource = PRessourceArchive::new(&directory_name, data.get_file_iter().get_location(), &self.p_output_path);
let mut content_url = PContentUrl::new();
let url_id = data.get_current_id();
content_url.get_text_mut().add_child(&PContentType::Text(PLabeler::new(data.get_current_id(), &PContentText::new(&archive_ressource.get_archive_file_name()))));
content_url.set_url(&String::from(archive_ressource.get_relative_output_archive_file().to_str().unwrap()));
data.add_child(content, &PContentType::Url(PLabeler::new(url_id, &content_url)));
data.add_archive(&archive_ressource);
return true;
}
fn parse_speaker(&self, content: &mut PVecContent, data: &mut PLectureData) -> bool{
if !data.get_file_iter().is_match(&String::from(") {
return false;
}
data.play_text(content);
let location = data.get_parser_location().clone();
let config_name = PathBuf::from(data.get_file_iter().get_until(&String::from(")")));
let input_speaker_config: PathBuf = location.get_filename().parent().unwrap().join(PathBuf::from(config_name));
let vec_speaker = match load_vec_speaker(&PathBuf::from(input_speaker_config)) {
Ok(value) => value,
Err(err) => panic!("PLectureParser::parse_speaker : Error at {}\n\tError {}", location, err)
};
let mut full_speaker_part = PContentTitle::new(1, true, &location);
full_speaker_part.get_title_mut().add_child(&PContentType::from_text(data.get_current_id(), &String::from("Speakers")));
content.add_child(&PContentType::Title(PLabeler::new(data.get_current_id(), &full_speaker_part)));
data.add_label(content, &String::from("sec_full_speakers"));
data.add_text(content, &String::from("Here you can find all speakers."));
for speaker in vec_speaker.speaker.iter() {
let mut speaker_part = PContentTitle::new(2, true, &location);
speaker_part.get_title_mut().add_child(&PContentType::from_text(data.get_current_id(), &speaker.name));
content.add_child(&PContentType::Title(PLabeler::new(data.get_current_id(), &speaker_part)));
data.add_label(content, &speaker.label);
content.add_child(&PContentType::from_textbf(data.get_id_mut(), &String::from("Speaker : ")));
let mut speaker_name = String::from("");
if !speaker.title.is_empty() {
speaker_name += &format!("{} ", speaker.title);
}
speaker_name += &speaker.name;
content.add_child(&PContentType::from_text(data.get_current_id(), &speaker_name));
content.add_child(&PContentType::NewLine);
content.add_child(&PContentType::from_textbf(data.get_id_mut(), &String::from("Affiliation : ")));
content.add_child(&PContentType::from_text(data.get_current_id(), &speaker.affiliation));
content.add_child(&PContentType::NewLine);
content.add_child(&PContentType::from_textbf(data.get_id_mut(), &String::from("Function : ")));
content.add_child(&PContentType::from_text(data.get_current_id(), &speaker.function));
content.add_child(&PContentType::NewLine);
let trim_description = speaker.description.trim();
if !trim_description.is_empty() {
let mut bio_title = PContentTitle::new(3, false, &location);
bio_title.get_title_mut().add_child(&PContentType::from_text(data.get_current_id(), &&String::from("Bio")));
content.add_child(&PContentType::Title(PLabeler::new(data.get_current_id(), &bio_title)));
let parser: PFileParser = PFileParser::from_content(&String::from(format!("{}\n", trim_description)));
let mut lecture_data = PLectureData::new(data.get_current_id(), &parser);
self.parse(content, &mut lecture_data);
data.set_current_id(lecture_data.get_current_id());
}
}
return true;
}
fn parse_timetable(&self, content: &mut PVecContent, data: &mut PLectureData) -> bool{
if !data.get_file_iter().is_match(&String::from(") {
return false;
}
data.play_text(content);
let location = data.get_parser_location().clone();
let config_name = PathBuf::from(data.get_file_iter().get_until(&String::from(")")));
let input_timetable_config: PathBuf = location.get_filename().parent().unwrap().join(PathBuf::from(config_name));
create_content_timetable(content, data, self, &input_timetable_config);
return true;
}
fn parse_envlist(&self, content: &mut PVecContent, data: &mut PLectureData) -> bool{
if !data.get_file_iter().is_match(&String::from(") {
return false;
}
data.play_text(content);
let environment_name = data.get_file_iter().get_until(&String::from(")"));
let current_location = data.get_parser_location().clone();
if !self.p_environement_manager.get_map_environment().contains_key(&environment_name) {
panic!("PLectureParser::parse_envlist : Error at {}\n\tNo environement named '{}'\n\tPossible values are {:?}", current_location, environment_name, self.p_environement_manager.get_vec_env_name());
}
let env_list = PContentEnvList::new(&environment_name);
let id = data.get_current_id();
data.get_search_environment_mut().add_env(&environment_name, content.get_vec_child().len(), ¤t_location);
data.add_child(content, &PContentType::EnvList(PLabeler::new(id, &env_list)));
return true;
}
fn parse_environement_example(&self, content: &mut PVecContent, data: &mut PLectureData) -> bool{
if !data.get_file_iter().is_match(&String::from("__all_environment_example__")){
return false;
}
let example_text = String::from("Patricia mon petit, je ne voudrais pas te paraître vieux jeu et encore moins grossier.\nL'homme de la pampa parfois rude reste toujours courtoit mais l'honneteté m'oblige à te le dire :\nton Antoine commence à me les briser menu !");
content.add_child(&PContentType::NewLine);
for env_name in self.p_environement_manager.get_vec_env_name().iter(){
let env = self.p_environement_manager.get_map_environment().get(env_name).unwrap();
let mut full_text_env = PContent::new();
let full_text_env_content = full_text_env.get_content_mut();
full_text_env_content.add_child(&PContentType::NewLine);
full_text_env_content.add_child(&PContentType::from_text(data.get_current_id(), &String::from("Example of the environment ")));
full_text_env_content.add_child(&PContentType::from_textbf(data.get_id_mut(), &env.name));
if env.balise == String::from("pre") {
full_text_env_content.add_child(&PContentType::from_text(data.get_current_id(), &String::from(" (")));
full_text_env_content.add_child(&PContentType::from_text_style(data.get_id_mut(), &String::from("text_warning"), &String::from("dynamic word wrap is not supported !")));
full_text_env_content.add_child(&PContentType::from_text(data.get_current_id(), &String::from(")")));
}
full_text_env_content.add_child(&PContentType::from_text(data.get_current_id(), &String::from(" :")));
content.add_child(&PContentType::Content(PLabeler::new(data.get_current_id(), &full_text_env)));
let mut environment: PContentEnvironment = PContentEnvironment::new(&env.name, &env.balise, &env.image, &PLocation::new(&PathBuf::from("generated file"), 0, 0));
let environment_content = environment.get_content_mut();
environment_content.add_child(&&PContentType::from_text(data.get_current_id(), &example_text));
content.add_child(&PContentType::Environment(PLabeler::new(data.get_current_id(), &environment)));
}
return true;
}
fn parse_parser_example(&self, content: &mut PVecContent, data: &mut PLectureData) -> bool{
if !data.get_file_iter().is_match(&String::from("__all_parser_example__")){
return false;
}
let mut vec_highlighter_name = self.p_highligher_manager.get_vec_highlighter_name();
if vec_highlighter_name.len() == 0 { data.add_text(content, &String::from("Sorry, but there is no parser available in this configuration."));
return true;
}
vec_highlighter_name.sort();
let section_level: usize = 2; let mut example_title = PContentTitle::new(section_level, true, data.get_parser_location());
example_title.get_title_mut().add_child(&&PContentType::from_text(data.get_current_id(), &String::from("All Parsers of lecture")));
content.add_child(&PContentType::Title(PLabeler::new(data.get_current_id(), &example_title)));
assert!(data.add_label(content, &String::from("secAllParserExample")));
data.add_text(content, &String::from("This section contains all examples from all parsers available in this lecture."));
for parser_name in vec_highlighter_name.iter(){
let mut parser_title = PContentTitle::new(section_level + 1, true, data.get_parser_location());
parser_title.get_title_mut().add_child(&PContentType::from_text(data.get_current_id(), &String::from(format!("Parser : {}", parser_name))));
content.add_child(&PContentType::Title(PLabeler::new(data.get_current_id(), &parser_title)));
assert!(data.add_label(content, &String::from(format!("secAllParserExample{}", parser_name))));
data.add_text(content, &String::from("This is an example of the '"));
let text_bold = PContentType::from_text_style(data.get_id_mut(), &String::from("program_language"), &String::from(format!("{}", parser_name)));
data.add_child(content, &text_bold);
data.add_text(content, &String::from("' parser available in this lecture."));
match self.p_highligher_manager.get_highlighter_by_name(parser_name) {
Some(highlighter) => {
let highlighted_code = highlighter.highlight_example();
let id = data.get_current_id();
data.add_child(content, &PContentType::Parser(PLabeler::new(id, &PContentParser::new(&highlighted_code, highlighter.get_language().get_is_line_number(), true))));
},
None => {} };
}
return true;
}
fn parse_environment(&self, content: &mut PVecContent, data: &mut PLectureData) -> bool {
if !data.get_file_iter().is_match(&String::from("```")) {
return false;
}
data.play_text(content);
let environment_name: String = data.get_file_iter().get_str_of(&String::from("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-"));
if environment_name.is_empty() {
panic!("PLectureParser::parse_environment : Error at {}\n\tMissing environment name. Possible values are :\n\t- footnote, formula, detail\n\t- environment : {:?}\n\t- parser : {:?}", data.get_parser_location(), self.p_environement_manager.get_vec_env_name(), self.p_highligher_manager.get_vec_highlighter_name());
}
if environment_name == String::from("footnote") {
let mut footnote: PContentFootnote = PContentFootnote::new();
let mut footnote_content = footnote.get_content_mut();
self.parse_content_sequence(&mut footnote_content, data, &String::from(""), &String::from("```\n"), false);
let id = data.get_current_id();
data.add_child(content, &PContentType::FootNote(PLabeler::new(id, &footnote)));
return true;
}else if environment_name == String::from("formula") {
let location: PLocation = data.get_file_iter().get_location().clone();
let text_formula = data.get_file_iter().get_until(&String::from("```\n"));
let id = data.get_current_id();
data.add_child(content, &PContentType::Formula(PLabeler::new(id, &PContentFormula::new(&text_formula, false, &location))));
return true;
}else if environment_name == String::from("detail") {
let mut detail = PContentDetail::new();
self.parse_content_sequence(detail.get_content_mut(), data, &String::from(""), &String::from("```\n"), true);
let id = data.get_current_id();
data.add_child(content, &PContentType::Detail(PLabeler::new(id, &detail)));
return true;
}else{
if self.highlight_plain_language(content, data, &environment_name) {
return true;
}else{
match self.p_environement_manager.get_map_environment().get(&environment_name) {
Some(env) => {
let location: PLocation = data.get_parser_location().clone();
let mut environment: PContentEnvironment = PContentEnvironment::new(&env.name, &env.balise, &env.image, &location);
let mut environment_content = environment.get_content_mut();
self.parse_content_sequence(&mut environment_content, data, &String::from(""), &String::from("```\n"), env.is_paragraph_allowed);
let id = data.get_current_id();
data.add_child(content, &PContentType::Environment(PLabeler::new(id, &environment)));
return true;
},
None => {
panic!("PLectureParser::parse_environment : Error at {}\n\tMissing defined environment after ```\n\tPossible values = {:?}", data.get_parser_location(), self.p_vec_highlighter);
}
}
}
}
}
fn parse_media_highlight(&self, content: &mut PVecContent, data: &mut PLectureData) -> bool{
if !data.get_file_iter().is_match(&String::from("![")) {
return false;
}
data.play_text(content);
let hook_content: String = data.get_file_iter().get_until(&String::from("]"));
if !data.get_file_iter().is_match(&String::from("(")) {
return false;
}
let is_figure: bool = hook_content == String::from("figure");
let media_width: usize = match hook_content.parse() {
Ok(value) => value,
Err(_) => 0
};
let media_url: String = data.get_file_iter().get_until(&String::from(")"));
let media_file = PathBuf::from(&media_url);
let media_extension: String = get_file_extension(&media_url);
if media_url.starts_with("https://") || media_url.starts_with("http://") {
let id = data.get_current_id();
match media_extension.as_str() {
"mp4" => data.add_child(content, &PContentType::from_media(id, &media_file, media_width, &PMediaType::Video, true)),
"png" => data.add_child(content, &PContentType::from_media(id, &media_file, media_width, &PMediaType::Image, is_figure)),
"jpg" => data.add_child(content, &PContentType::from_media(id, &media_file, media_width, &PMediaType::Image, is_figure)),
"svg" => data.add_child(content, &PContentType::from_media(id, &media_file, media_width, &PMediaType::Image, is_figure)),
_ => panic!("PLectureParser::parse_media_highlight : Error at {}\n\tUnknown extension '{}' with media_url = '{}'", data.get_parser_location(), media_extension, media_url)
};
return true;
}
match media_extension.as_str() {
"mp4" => self.copy_media_file(content, data, &media_file, media_width, &PMediaType::Video, true),
"png" => self.copy_media_file(content, data, &media_file, media_width, &PMediaType::Image, is_figure),
"jpg" => self.copy_media_file(content, data, &media_file, media_width, &PMediaType::Image, is_figure),
"svg" => self.copy_media_file(content, data, &media_file, media_width, &PMediaType::Image, is_figure),
_ => {
if !self.highlight_file_language(content, data, &media_file){
panic!("PLectureParser::parse_media_highlight : error at {}\n\tcannot highlight file {:?}", data.get_parser_location(), media_file);
}
}
};
return true;
}
fn parse_auto_url(&self, content: &mut PVecContent, data: &mut PLectureData, url_start: &String) -> bool{
if !data.get_file_iter().is_match(url_start) {
return false;
}
data.play_text(content);
let mut url = PContentUrl::new();
let text_url: String = String::from(format!("{}{}", url_start,
data.get_file_iter().get_str_of(&String::from(".-_/?%&@:#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"))));
url.set_url(&text_url);
url.get_text_mut().add_child(&&PContentType::from_text(data.get_current_id(), &text_url));
let id = data.get_current_id();
data.add_child(content, &PContentType::Url(PLabeler::new(id, &url)));
return true;
}
fn parse_url(&self, content: &mut PVecContent, data: &mut PLectureData) -> bool{
if !data.get_file_iter().is_match(&String::from("[")) {
return false;
}
data.play_text(content);
let mut url = PContentUrl::new();
let mut url_text_content = url.get_text_mut();
self.parse_content_sequence(&mut url_text_content, data, &String::from(""), &String::from("]"), false);
if !data.get_file_iter().is_match(&String::from("(")) {
panic!("PLectureParser::parse_url : Error at {}\n\tmissing link of url, should be [text](https://some/url)", data.get_parser_location());
}
let url_link: String = data.get_file_iter().get_until(&String::from(")"));
url.set_url(&url_link);
let id = data.get_current_id();
data.add_child(content, &PContentType::Url(PLabeler::new(id, &url)));
return true;
}
fn parse_bold(&self, content: &mut PVecContent, data: &mut PLectureData) -> bool{
if !data.get_file_iter().is_match(&String::from("**")) {
return false;
}
data.play_text(content);
let mut bold_content = PVecContent::new();
self.parse_content_sequence(&mut bold_content, data, &String::from(""), &String::from("**"), false);
let id = data.get_current_id();
data.add_child(content, &PContentType::TextBf(PLabeler::new(id, &bold_content)));
return true;
}
fn parse_italic(&self, content: &mut PVecContent, data: &mut PLectureData) -> bool{
if !data.get_file_iter().is_match(&String::from("*")) {
return false;
}
data.play_text(content);
let mut italic_content = PVecContent::new();
self.parse_content_sequence(&mut italic_content, data, &String::from(""), &String::from("*"), false);
let id = data.get_current_id();
data.add_child(content, &PContentType::TextIt(PLabeler::new(id, &italic_content)));
return true;
}
fn parse_code_snipet(&self, content: &mut PVecContent, data: &mut PLectureData) -> bool{
if !data.get_file_iter().is_match(&String::from("`")) {
return false;
}
data.play_text(content);
let mut code_snipet_content = PVecContent::new();
self.parse_content_sequence(&mut code_snipet_content, data, &String::from(""), &String::from("`"), false);
let id = data.get_current_id();
data.add_child(content, &PContentType::TextCode(PLabeler::new(id, &code_snipet_content)));
return true;
}
fn parse_comment(&self, content: &mut PVecContent, data: &mut PLectureData) -> bool{
if !data.get_file_iter().is_match(&String::from("<!--")) {
return false;
}
data.play_text(content);
data.get_file_iter().get_until(&String::from("-->"));
return true;
}
fn parse_content_sequence(&self, content: &mut PVecContent, data: &mut PLectureData, begin_pattern: &String, end_pattern: &String, is_paragraph_allowed: bool) -> bool{
if !is_paragraph_allowed{
data.disable_paragraph();
}
data.disable_numbered_title();
if !begin_pattern.is_empty() {
if !data.get_file_iter().is_match(begin_pattern) {
return false;
}
}
while !data.get_file_iter().is_end_of_file() && !data.get_file_iter().is_match(end_pattern) {
if self.parse_content(content, data) {}
else { data.increment_current_char();
}
}
data.play_text(content);
if !is_paragraph_allowed{
data.enable_paragraph();
}
data.enable_numbered_title();
return true;
}
fn copy_media_file(&self, content: &mut PVecContent, data: &mut PLectureData, media_file: &PathBuf, media_width: usize, media_type: &PMediaType, is_figure: bool){
let ressource_file = PRessourceFile::new(media_file, data.get_file_iter().get_location(), &self.p_output_path);
ressource_file.copy_to_output_dir();
let id = data.get_current_id();
data.add_child(content, &&PContentType::from_media(id, &ressource_file.get_relative_output_file(), media_width, media_type, is_figure));
}
fn highlight_file_language(&self, content: &mut PVecContent, data: &mut PLectureData, source_file: &PathBuf) -> bool{
match self.p_highligher_manager.get_highlighter_by_file(source_file) {
Some(highlighter) => {
self.highlight_file_language_source(content, data, source_file, &highlighter)
},
None => { false
}
}
}
fn highlight_file_language_source(&self, content: &mut PVecContent, data: &mut PLectureData, source_file: &PathBuf, highlighter: &PHighlighter) -> bool {
let ressource_file = PRessourceFile::new(source_file, data.get_file_iter().get_location(), &self.p_output_path);
ressource_file.create_dir_all();
let output_file = ressource_file.get_absolute_output_file();
let parser: PFileParser = PFileParser::from_file(ressource_file.get_absolute_input_file());
let mut source_data = PLectureData::new(data.get_current_id(), &parser);
let lecture_single_line_comment: String = make_lecture_comment(highlighter.get_language().get_single_line_comment());
let lecture_multi_line_comment_begin: String = make_lecture_comment(highlighter.get_language().get_multi_line_comment_begin());
let lecture_multi_line_comment_end = highlighter.get_language().get_multi_line_comment_end();
let mut full_source = String::from("");
while !source_data.get_file_iter().is_end_of_file() {
if source_data.get_file_iter().is_match(&lecture_single_line_comment) {
let lecture_comment = source_data.get_file_iter().get_until(&String::from("\n"));
self.highlight_lecture_and_source(content, &mut source_data, &mut full_source, &lecture_comment, highlighter);
}else if source_data.get_file_iter().is_match(&lecture_multi_line_comment_begin) {
let lecture_comment = source_data.get_file_iter().get_until(&lecture_multi_line_comment_end);
self.highlight_lecture_and_source(content, &mut source_data, &mut full_source, &lecture_comment, highlighter);
}else{
source_data.increment_current_char();
}
}
if !full_source.is_empty() { self.highlight_lecture_and_source(content, &mut source_data, &mut full_source, &String::from(""), highlighter);
}
data.set_current_id(source_data.get_current_id());
let mut source_url = PContentUrl::new();
source_url.get_text_mut().add_child(&PContentType::from_text(data.get_current_id(), &String::from(ressource_file.get_relative_output_file().file_name().unwrap().to_str().unwrap())));
source_url.set_url(&ressource_file.get_relative_output_file().to_str().unwrap().to_string());
if full_source.is_empty() {
full_source = source_data.flush_text();
}
let id = data.get_current_id();
content.add_child(&PContentType::Paragraph(PLabeler::new(id, &PContentParagraph::new())));
data.add_text(content, &String::from("Full file "));
let id = data.get_current_id();
data.add_child(content, &PContentType::Url(PLabeler::new(id, &source_url)));
let id = data.get_current_id();
data.add_child(content, &PContentType::from_text(id, &String::from(" : ")));
let highlighted_code = highlighter.highlight(&String::from(full_source.trim_start()));
let id = data.get_current_id();
data.add_child(content, &PContentType::Parser(PLabeler::new(id, &PContentParser::new(&highlighted_code, highlighter.get_language().get_is_line_number(), true))));
match fs::write(&output_file, full_source) {
Ok(_) => {},
Err(err) => panic!("PLectureParser::highlight_file_language_source : cannot write source file {:?}\n\tError {}", output_file, err)
};
let id = data.get_current_id();
data.add_child(content, &PContentType::from_text(id, &String::from("File url : ")));
let id = data.get_current_id();
data.add_child(content, &PContentType::Url(PLabeler::new(id, &source_url)));
return true;
}
fn highlight_lecture_and_source(&self, content: &mut PVecContent, source_data: &mut PLectureData, full_source: &mut String, lecture_comment: &String, highlighter: &PHighlighter){
let source_code: String = source_data.flush_text();
*full_source += &source_code;
let trim_code: String = String::from(source_code.trim_start_matches("\n"));
if !trim_code.is_empty() {
let highlighted_code = highlighter.highlight(&trim_code);
let id = source_data.get_current_id();
source_data.add_child(content, &PContentType::Parser(PLabeler::new(id, &PContentParser::new(&highlighted_code, highlighter.get_language().get_is_line_number(), true))));
}
let trim_lecture_comment: String = String::from(lecture_comment.trim());
if trim_lecture_comment.is_empty() {
return;
}
let parser: PFileParser = PFileParser::from_content(&trim_lecture_comment);
let mut lecture_data = PLectureData::new(source_data.get_current_id(), &parser);
self.parse(content, &mut lecture_data);
source_data.set_current_id(lecture_data.get_current_id());
}
fn highlight_plain_language(&self, content: &mut PVecContent, data: &mut PLectureData, language_name: &String) -> bool{
match self.p_highligher_manager.get_highlighter_by_name(language_name) {
Some(highlighter) => {
let source_code: String = data.get_file_iter().get_until(&String::from("```"));
let highlighted_code = highlighter.highlight(&String::from(source_code.trim_start()));
let id = data.get_current_id();
data.add_child(content, &PContentType::Parser(PLabeler::new(id, &PContentParser::new(&highlighted_code, highlighter.get_language().get_is_line_number(), false))));
return true;
},
None => false
}
}
}
#[cfg(test)]
mod tests{
use super::*;
use crate::pcontent::{
PAbstractContent, PLabelId, PStrBackend
};
#[test]
fn test_make_begin_comment(){
assert_eq!(make_lecture_comment(&String::from("")), String::from(""));
assert_eq!(make_lecture_comment(&String::from("//")), String::from("//{"));
assert_eq!(make_lecture_comment(&String::from("#")), String::from("#{"));
}
fn test_lecture_parser_content(lecture_parser: &PLectureParser, input_content: &String, expected_output: &String){
let mut parser: PFileParser = PFileParser::from_content(input_content);
parser.set_filename(&PathBuf::from("tests/index.md").canonicalize().unwrap());
let mut data = PLectureData::new(0, &parser);
let mut vec_content = PVecContent::new();
assert!(lecture_parser.parse(&mut vec_content, &mut data));
let mut backend = PStrBackend::new();
vec_content.to_html(&mut backend, &PLabelId::new(0));
assert_eq!(backend.get_body(), expected_output);
}
#[test]
fn test_lecture_parser(){
let base_output_path = PathBuf::from("target/test_lecture_parser");
fs::create_dir_all(&base_output_path).unwrap();
let output_path = base_output_path.canonicalize().unwrap();
fs::create_dir_all(&output_path.join("book")).unwrap();
let vec_parser_dir: Vec<PathBuf> = vec![PathBuf::from("tests/parser")];
let vec_environment_dir: Vec<PathBuf> = vec![PathBuf::from("tests/environment")];
let lecture_parser = PLectureParser::new(&output_path, &vec_parser_dir, &vec_environment_dir);
test_lecture_parser_content(&lecture_parser, &String::from("<!-- Some comment -->"), &String::from(""));
test_lecture_parser_content(&lecture_parser, &String::from("# Some title\n"), &String::from("<h1 id=\"1\">Some title</h1>\n"));
test_lecture_parser_content(&lecture_parser, &String::from("## Some title\n"), &String::from("<h2 id=\"1\">Some title</h2>\n"));
test_lecture_parser_content(&lecture_parser, &String::from("### Some title\n"), &String::from("<h3 id=\"1\">Some title</h3>\n"));
test_lecture_parser_content(&lecture_parser, &String::from("#### Some title\n"), &String::from("<h4 id=\"1\">Some title</h4>\n"));
test_lecture_parser_content(&lecture_parser, &String::from("##### Some title\n"), &String::from("<h5 id=\"1\">Some title</h5>\n"));
test_lecture_parser_content(&lecture_parser, &String::from("Some text\n"), &String::from("<p id=\"1\">Some text</p>\n"));
test_lecture_parser_content(&lecture_parser, &String::from("**Some bold**"), &String::from("<p id=\"2\"><b>Some bold</b></p>\n"));
test_lecture_parser_content(&lecture_parser, &String::from("*Some italic*"), &String::from("<p id=\"2\"><em>Some italic</em></p>\n"));
test_lecture_parser_content(&lecture_parser, &String::from("https://maqao.org"), &String::from("<p id=\"2\"><a id=\"1\" href=\"https://maqao.org\">https://maqao.org</a></p>\n"));
test_lecture_parser_content(&lecture_parser, &String::from("http://maqao.org"), &String::from("<p id=\"2\"><a id=\"1\" href=\"http://maqao.org\">http://maqao.org</a></p>\n"));
test_lecture_parser_content(&lecture_parser, &String::from("[some url](https://some.url.com/)"), &String::from("<p id=\"2\"><a id=\"1\" href=\"https://some.url.com/\">some url</a></p>\n"));
test_lecture_parser_content(&lecture_parser, &String::from("Some maqao usage\n"), &String::from("<p id=\"1\">Some <a class=\"program\" href=\"https://maqao.org/\">maqao</a> usage</p>\n"));
test_lecture_parser_content(&lecture_parser, &String::from("`Some code`\n"), &String::from("<p id=\"2\"><span class=\"code_inline\">Some code</span></p>\n"));
test_lecture_parser_content(&lecture_parser, &String::from("`${PREFIX_SHARE}`\n"), &String::from("<p id=\"2\"><span class=\"code_inline\">${PREFIX_SHARE}</span></p>\n"));
test_lecture_parser_content(&lecture_parser, &String::from("`$$`\n"), &String::from("<p id=\"2\"><span class=\"code_inline\">$</span></p>\n"));
test_lecture_parser_content(&lecture_parser, &String::from(""), &String::from("<p id=\"1\"><img id=\"0\" src=\"ressource/tests/Images/tb_tweety.png\" alt=\"nothing\" /></p>\n"));
test_lecture_parser_content(&lecture_parser, &String::from(""), &String::from("<div id=\"0\" class=\"figureStyle\"><img id=\"0\" src=\"ressource/tests/Images/tb_tweety.png\" alt=\"nothing\" /><p><b>Figure 0</b></p></div>\n"));
test_lecture_parser_content(&lecture_parser, &String::from("```advise some advise ```\n"), &String::from("<div id=\"2\" class=\"advise\"><p id=\"1\"> some advise </p>\n</div>\n"));
test_lecture_parser_content(&lecture_parser, &String::from("```advise\n some advise\n```warning\nRelated warning ```\n```\n"), &String::from("<div id=\"5\" class=\"advise\"><p id=\"1\"> some advise</p>\n\n<div id=\"4\" class=\"workinprogress\">\n\t<div class=\"workinprogressimage\"><img src=\"book/images/panel_warning.png\" alt=\"wip\" /></div>\n<div id=\"4\" class=\"warning\"><p id=\"3\"> Related warning </p>\n</div>\n</div>\n</div>\n"));
test_lecture_parser_content(&lecture_parser, &String::from("- some item\n- an other item\n\n"), &String::from("<lu><li><p id=\"3\">some item</p>\n</li>\n<li><p id=\"6\"> an other item</p>\n</li>\n</lu>\n"));
}
}