use std::path::PathBuf;
use std::fs;
use crate::phighlighter::plocation::PLocation;
#[derive(Debug,Clone,Default)]
pub struct PFileParserIter<'a>{
p_location: PLocation,
p_iter_content: std::slice::Iter<'a, u8>,
p_current_char: Option<u8>,
p_current_indentation_level: Option<usize>,
p_is_first_indented_char: bool,
p_escape_char: Option<u8>,
p_is_current_chat_escaped: bool,
}
impl<'a> PFileParserIter<'a>{
pub fn get_location(&self) -> &PLocation{
&self.p_location
}
pub fn is_end_of_file(&self) -> bool{
return self.p_current_char == None;
}
pub fn get_str_of(&mut self, charset: &String) -> String{
let mut out: String = String::from("");
let mut is_found = true;
while is_found {
if self.is_current_char_in_charset(charset) {
match self.p_current_char {
Some(ch) => {
out += &String::from(ch as char);
self.next();
},
None => is_found = false
};
}else{
is_found = false;
}
}
return out;
}
pub fn is_current_char_in_charset(&self, charset: &String) -> bool{
match self.p_current_char {
Some(ch) => charset.contains(ch as char),
None => false,
}
}
pub fn get_current_char(&self) -> Option<u8>{
self.p_current_char
}
pub fn next(&mut self){
match self.p_escape_char {
Some(escape_char) => {
match self.p_current_char {
Some(ch) => {
self.p_is_current_chat_escaped = escape_char == ch && !self.p_is_current_chat_escaped;
},
None => self.p_is_current_chat_escaped = false
}
},
None => self.p_is_current_chat_escaped = false
}
self.p_current_char = match self.p_iter_content.next(){
Some(ch) => {
if self.p_is_first_indented_char {
self.p_is_first_indented_char = false;
self.p_current_indentation_level = None;
}else{
match &mut self.p_current_indentation_level {
Some(indentation_level) => {
if *ch == ' ' as u8 || *ch == '\t' as u8 {
*indentation_level += 1;
}else{ self.p_is_first_indented_char = true;
}
}, None => {}
};
}
Some(*ch)
},
None => {
self.p_current_indentation_level = None;
None
}
};
self.p_location.set_current_col(self.p_location.get_current_col() + 1);
if self.is_new_line() { self.p_location.set_current_line(self.p_location.get_current_line() + 1); self.p_location.set_current_col(0); self.p_current_indentation_level = Some(0); self.p_is_first_indented_char = false; }
}
pub fn is_new_line(&self) -> bool {
match self.p_current_char {
Some(ch) => ch == '\n' as u8,
None => false,
}
}
pub fn is_match(&mut self, pattern: &String) -> bool{
if pattern.is_empty() {
return false;
}
let vec_u8_pattern: Vec<u8> = Vec::from(pattern.clone().into_bytes());
return self.is_match_u8(&vec_u8_pattern);
}
fn is_match_current_char(&self, current_char: u8) -> bool{
match self.p_current_char {
Some(ch) => ch == current_char,
None => false,
}
}
fn is_match_u8(&mut self, vec_u8_pattern: &Vec<u8>) -> bool{
if self.p_is_current_chat_escaped { return false;
}
let save_iter = self.p_iter_content.clone();
let seve_current_char = self.p_current_char.clone();
let save_location = self.p_location.clone();
let save_indentation = self.p_current_indentation_level.clone();
let save_is_first_indent_char = self.p_is_first_indented_char;
for ch in vec_u8_pattern.iter() {
if !self.is_match_current_char(*ch) {
self.p_iter_content = save_iter.clone();
self.p_current_char = seve_current_char.clone();
self.p_location = save_location.clone();
self.p_current_indentation_level = save_indentation.clone();
self.p_is_first_indented_char = save_is_first_indent_char;
return false;
}
self.next();
}
return true;
}
pub fn get_until(&mut self, end_pattern: &String) -> String{
if end_pattern.is_empty() {
return String::from("");
}
let vec_u8_pattern: Vec<u8> = Vec::from(end_pattern.clone().into_bytes());
let mut out = String::from("");
while !self.is_match_u8(&vec_u8_pattern) && !self.is_end_of_file() {
match self.p_current_char {
Some(ch) => {
let current_char = String::from(ch as char);
out += ¤t_char;
},
None => {}
};
self.next(); }
return out;
}
pub fn get_indentation_level(&self) -> &Option<usize> {
&self.p_current_indentation_level
}
}
pub struct PFileParser{
p_filename: PathBuf,
p_content: Vec<u8>,
}
impl PFileParser{
pub fn from_file(filename: &PathBuf) -> Self{
let file_content: String = match fs::read_to_string(filename) {
Ok(content) => content,
Err(err) => panic!("PFileParser::from_file : cannot open file {:?}\n\tError {}",filename, err)
};
PFileParser{
p_filename: filename.clone(),
p_content: Vec::from(file_content.into_bytes()),
}
}
pub fn from_content(content: &String) -> Self{
PFileParser{
p_filename: Default::default(),
p_content: Vec::from(content.clone().into_bytes()),
}
}
pub fn iter(&self, is_escape_char: bool) -> PFileParserIter<'_>{
let mut other = PFileParserIter{
p_location: PLocation::new(&self.p_filename, 1, 0),
p_iter_content: self.p_content.iter(),
p_current_char: None,
p_current_indentation_level: Some(0),
p_is_first_indented_char: false,
p_escape_char: if is_escape_char {Some('\\' as u8)}else{None},
p_is_current_chat_escaped: false,
};
other.next(); return other;
}
pub fn get_filename(&self) -> &PathBuf{
&self.p_filename
}
}
#[cfg(test)]
mod tests{
use super::*;
use std::fs;
#[test]
fn test_contains_charset(){
let charset = String::from("shadok");
assert!(charset.contains("s"));
assert!(charset.contains("h"));
assert!(charset.contains("d"));
}
#[test]
fn test_pfileparser_from_content(){
let parser: PFileParser = PFileParser::from_content(&String::from("shadok"));
let mut it = parser.iter(false);
assert_eq!(it.get_location().get_current_line(), 1);
assert_eq!(it.get_location().get_current_col(), 1);
assert_eq!(it.get_str_of(&String::from("ahsk")), String::from("sha"));
}
#[test]
fn test_pfileparser_from_file(){
let file_content = String::from("Some\nText\nOn\nfew\nlines\n\tIndented\n");
let filename = PathBuf::from("target/parse_test.txt");
fs::write(&filename, &file_content).unwrap();
let parser: PFileParser = PFileParser::from_file(&filename);
assert_eq!(parser.get_filename(), &filename);
let mut it = parser.iter(false);
assert_eq!(it.get_indentation_level(), &Some(0));
assert_eq!(it.get_location().get_filename(), &filename);
assert_eq!(it.get_location().get_current_line(), 1);
assert_eq!(it.get_location().get_current_col(), 1);
assert_eq!(it.get_str_of(&String::from("oemS")), String::from("Some"));
assert_eq!(it.get_location().get_current_line(), 2);
assert_eq!(it.get_location().get_current_col(), 0);
assert_eq!(it.get_current_char(), Some('\n' as u8));
assert!(it.is_new_line());
assert_eq!(String::from(format!("{}", it.get_location())), String::from("\"target/parse_test.txt\":2:0"));
it.next(); assert!(it.is_match(&String::from("Text")));
assert!(!it.is_match(&String::from("")));
it.next(); assert_eq!(it.get_location().get_current_line(), 3);
assert_eq!(it.get_location().get_current_col(), 1);
assert!(!it.is_match(&String::from("Off")));
assert_eq!(it.get_location().get_current_line(), 3);
assert_eq!(it.get_location().get_current_col(), 1);
assert_eq!(it.get_current_char(), Some('O' as u8));
assert_eq!(it.get_until(&String::from("lines")), String::from("On\nfew\n"));
assert_eq!(it.get_until(&String::from("")), String::from(""));
assert_eq!(it.get_indentation_level(), &Some(0));
it.next();
assert_eq!(it.get_indentation_level(), &Some(1));
it.next();
assert_eq!(it.get_indentation_level(), &Some(1));
it.next();
assert_eq!(it.get_indentation_level(), &None);
}
#[test]
fn test_limit_pfileparser_getuntil(){
let file_content = String::from("Some text\\\"but not the end yet\"");
let parser: PFileParser = PFileParser::from_content(&file_content);
let mut it = parser.iter(true);
assert_eq!(it.get_until(&String::from("\"")), String::from("Some text\\\"but not the end yet"));
}
#[test]
fn test_limit_pfileparser_getuntil_multiple_escape(){
let file_content = String::from("\\\\\" other stuff");
let parser: PFileParser = PFileParser::from_content(&file_content);
let mut it = parser.iter(true);
assert_eq!(it.get_until(&String::from("\"")), String::from("\\\\"));
}
#[test]
fn test_pfileparser_from_content_indentation(){
let parser: PFileParser = PFileParser::from_content(&String::from("A\n# Introduction\n"));
let mut it = parser.iter(false);
assert_eq!(it.get_location().get_current_line(), 1);
assert_eq!(it.get_location().get_current_col(), 1);
it.next();
assert_eq!(it.get_location().get_current_line(), 2);
assert_eq!(it.get_location().get_current_col(), 0);
assert_eq!(it.get_indentation_level(), &Some(0));
it.next();
assert_eq!(it.get_indentation_level(), &Some(0));
assert_eq!(it.get_current_char(), Some('#' as u8));
}
}