use std::{collections::HashMap, fs, path::PathBuf};
use serde::Deserialize;
use toml::{self, de::Error};
use crate::phighlighter::{
phighlighter::PHighlighter,
planguage::PLanguage,
};
#[derive(Debug, Clone, Deserialize)]
pub struct PHighlighterMainConfig{
is_line_number: bool,
token_charset: String,
vec_extention: Vec<String>,
vec_filename: Vec<String>,
single_line_comment: String,
multi_line_comment_begin: String,
multi_line_comment_end: String,
is_escape_char: bool,
example: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct PHighlighterKeyword{
style: String,
vec_token: Vec<String>,
vec_match: Vec<String>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct PHighlighterGetUntil{
pub style: String,
pub start_token: String,
pub end_token: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct PHighlighterGetUntilReplace{
pub start_token: String,
pub end_token: String,
pub replace_start: String,
pub replace_end: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct PHighlighterStep{
pub token: Option<String>,
pub oneof: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct PHighlighterSequence{
pub style: String,
pub vec_step: Vec<PHighlighterStep>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct PHighlighterConfig{
highlighting: PHighlighterMainConfig,
keyword: Option<Vec<PHighlighterKeyword>>,
replace: Option<HashMap<String, String>>,
getuntil: Option<Vec<PHighlighterGetUntil>>,
getuntilreplace: Option<Vec<PHighlighterGetUntilReplace>>,
sequence: Option<Vec<PHighlighterSequence>>,
}
pub fn load_highlighter(filename: &PathBuf) -> Result<PHighlighterConfig, Error> {
let config: PHighlighterConfig = match fs::read_to_string(filename) {
Ok(str) => match toml::from_str(&str){
Ok(value) => value,
Err(error) => panic!("load_highlighter : cannot parse file {:?}, parsing error {}", filename, error)
},
Err(err) => panic!("load_highlighter : cannot read file {:?}, error: {}", filename, err)
};
Ok(config)
}
pub fn load_node_highlight(filename: &PathBuf) -> PHighlighter{
let config: PHighlighterConfig = match load_highlighter(filename) {
Ok(value) => value,
Err(err) => panic!("load_node_highlight : cannot load highlighter configuration {:?}. Error {}", filename, err)
};
let mut language = PLanguage::new(config.highlighting.is_line_number, config.highlighting.token_charset);
language.set_vec_extension(&config.highlighting.vec_extention);
language.set_vec_filename(&config.highlighting.vec_filename);
language.set_single_line_comment(&config.highlighting.single_line_comment);
language.set_multi_line_comment_begin(&config.highlighting.multi_line_comment_begin);
language.set_multi_line_comment_end(&config.highlighting.multi_line_comment_end);
language.set_is_escape_char(config.highlighting.is_escape_char);
language.set_example(&config.highlighting.example);
let mut highlighter: PHighlighter = PHighlighter::new();
highlighter.set_language(&language);
match &config.keyword {
Some(vec_keyword) => {
for keyword in vec_keyword.iter(){ let css_style = keyword.style.clone();
highlighter.add_vec_token(&keyword.vec_token, &css_style);
highlighter.add_vec_match(&keyword.vec_match, &css_style);
}
},
None => {}
};
match &config.replace {
Some(map_replace) => {
for (pattern, replace) in map_replace.iter() {
highlighter.add_replace(&pattern, &replace);
}
},
None => {}
};
match &config.getuntil {
Some(getuntil) => highlighter.add_vec_getuntil(getuntil),
None => {}
};
match &config.getuntilreplace {
Some(getuntilreplace) => highlighter.add_vec_getuntilreplace(getuntilreplace),
None => {}
};
match &config.sequence {
Some(sequence) => highlighter.add_vec_sequence(sequence),
None => {}
};
return highlighter;
}
#[cfg(test)]
mod tests{
use super::*;
#[test]
fn test_load_highlighter_config(){
let parser_cpp: PHighlighterConfig = load_highlighter(&PathBuf::from("tests/parser/cpp.toml")).unwrap();
assert_eq!(parser_cpp.highlighting.is_line_number, true);
assert_eq!(parser_cpp.highlighting.token_charset, String::from("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"));
assert_eq!(parser_cpp.highlighting.single_line_comment, String::from("//"));
assert_eq!(parser_cpp.highlighting.multi_line_comment_begin, String::from("/*"));
assert_eq!(parser_cpp.highlighting.multi_line_comment_end, String::from("*/"));
assert_eq!(parser_cpp.keyword.unwrap().len(), 20);
}
#[test]
fn test_load_highlighter(){
let parser_cpp: PHighlighter = load_node_highlight(&PathBuf::from("tests/parser/cpp.toml"));
assert_eq!(parser_cpp.highlight(&String::from("int function(float base, size_t index);")), String::from("<span class=\"dsType\">int</span> function(<span class=\"dsType\">float</span> base, <span class=\"dsType\">size_t</span> index);"));
assert_eq!(parser_cpp.highlight(&String::from("std::string name(\"shadoko\");")), String::from("<span class=\"cppstdF\">std</span>::<span class=\"cppstdF\">string</span> name(<span class=\"dsString\">"shadoko"</span>);"));
assert_eq!(parser_cpp.highlight(&String::from("int value = 42;")), String::from("<span class=\"dsType\">int</span> value = <span class=\"dsNumber\">42</span>;"));
assert_eq!(parser_cpp.highlight(&String::from("int value = 4;")), String::from("<span class=\"dsType\">int</span> value = <span class=\"dsNumber\">4</span>;"));
assert_eq!(parser_cpp.highlight(&String::from("return 1;")), String::from("<span class=\"dsKeyword\">return</span> <span class=\"dsNumber\">1</span>;"));
assert_eq!(parser_cpp.highlight(&String::from("return 10;")), String::from("<span class=\"dsKeyword\">return</span> <span class=\"dsNumber\">10</span>;"));
assert_eq!(parser_cpp.highlight(&String::from("int value = 0xff;")), String::from("<span class=\"dsType\">int</span> value = <span class=\"dsNumber\">0xff</span>;"));
assert_eq!(parser_cpp.highlight(&String::from("int value = 0b10;")), String::from("<span class=\"dsType\">int</span> value = <span class=\"dsNumber\">0b10</span>;"));
assert_eq!(parser_cpp.highlight(&String::from("return 0;")), String::from("<span class=\"dsKeyword\">return</span> <span class=\"dsNumber\">0</span>;"));
}
#[test]
fn test_limit_cpp_highlighter(){
let parser_cpp: PHighlighter = load_node_highlight(&PathBuf::from("tests/parser/cpp.toml"));
assert_eq!(parser_cpp.highlight(&String::from("int_function_call();")), String::from("int_function_call();"));
}
#[test]
fn test_limit_toml_highlighter(){
let parser_toml: PHighlighter = load_node_highlight(&PathBuf::from("tests/parser/toml.toml"));
assert_eq!(parser_toml.highlight(&String::from("value_something = false\n")), String::from("<span class=\"cppstandardF\">value_something</span> <span class=\"dsKeyword\">=</span> <span class=\"cppqtMacro\">false</span>\n"));
assert_eq!(parser_toml.highlight(&String::from("in_something = false\n")), String::from("<span class=\"cppstandardF\">in_something</span> <span class=\"dsKeyword\">=</span> <span class=\"cppqtMacro\">false</span>\n"));
}
}