use super::element_text::ElementText;
use super::helpers::*;
use std::str::Lines;
#[derive(Debug, Default)]
pub struct TagInfo {
pub name: String,
pub indent: usize,
pub is_self_closing: bool,
pub in_props: bool,
}
#[derive(Debug, Default)]
pub struct AutoWrapper {
pub wrap_children_with: &'static str,
pub tags: Vec<&'static str>,
pub enable_manual_wrap: bool, }
#[derive(Debug)]
pub struct Parser {
pub self_closing_tags: Vec<&'static str>,
pub tags_before_non_indents: Vec<&'static str>,
pub no_x_padding_tags: Vec<&'static str>,
pub tags_with_non_indent_first_child: Vec<&'static str>,
pub tags_with_no_indents: Vec<&'static str>,
pub auto_wrappers: Vec<AutoWrapper>,
pub track_line_delta: isize,
}
impl Parser {
pub fn new(
self_closing_tags: Vec<&'static str>,
auto_wrappers: Vec<AutoWrapper>,
no_x_padding_tags: Vec<&'static str>,
tags_before_non_indents: Vec<&'static str>,
tags_with_non_indent_first_child: Vec<&'static str>,
tags_with_no_indents: Vec<&'static str>,
) -> Parser {
Parser {
self_closing_tags,
auto_wrappers,
tags_with_non_indent_first_child,
tags_before_non_indents,
no_x_padding_tags,
tags_with_no_indents,
track_line_delta: 0,
}
}
pub fn transform(&mut self, elm: String, start_index: isize) -> String {
let mut output = String::new();
let mut tag_stack: Vec<TagInfo> = Vec::new();
let lines = elm.lines();
let mut lines_to_skip: u32 = 0;
let mut track_line_index;
for (index, line) in lines.clone().enumerate() {
if lines_to_skip > 0 {
lines_to_skip = lines_to_skip - 1;
continue;
}
track_line_index = (index as isize) + start_index - self.track_line_delta;
let trimmed_line = line.trim_start();
let indent = get_line_indent(line);
check_indent_size(indent as isize, track_line_index);
if let Some(last) = tag_stack.last() {
check_extra_spaces(indent, last.indent, track_line_index);
}
if trimmed_line.starts_with("|> ") {
let tag_name = trimmed_line[3..].trim().to_string();
tag_loop(&mut tag_stack, &mut output, &indent);
output.push_str(&format!("<{}\n", tag_name));
tag_stack.push(TagInfo {
name: tag_name.clone(),
indent,
is_self_closing: self.self_closing_tags.contains(&tag_name.as_str()),
in_props: true,
});
if tag_name == "Exercises" {
self.track_line_delta += 1
}
if tag_name == "Example" {
self.track_line_delta += 1
}
if tag_name == "Exercise" {
self.track_line_delta += 3
}
if tag_name == "Solution" {
self.track_line_delta += 1
}
continue;
}
if trimmed_line.is_empty() && tag_stack
.last()
.map_or(false, |tag| tag.in_props && !tag.is_self_closing)
{
output.push_str(">\n\"\" ");
if let Some(last) = tag_stack.last_mut() {
last.in_props = false;
}
continue;
}
if !trimmed_line.is_empty() {
tag_loop(&mut tag_stack, &mut output, &indent);
let last = tag_stack.last().expect(&format!(
"There is no parent tag . line {}",
track_line_index
));
if last.in_props {
output.push_str(&format!("{}\n", Self::handle_prop_line(line)));
continue;
}
let mut nodes: Vec<Vec<(String, bool)>> = Vec::new(); nodes.push(Vec::new());
let mut text_node = String::new();
let mut inner_lines_to_skip: u32 = 0;
for (j, text_line) in lines.clone().skip(index).enumerate() {
if inner_lines_to_skip > 0 {
inner_lines_to_skip = inner_lines_to_skip - 1;
continue;
}
let inner_trimmed_line = text_line.trim_start();
let indent = get_line_indent(text_line);
check_indent_size(indent as isize, track_line_index + j as isize);
check_extra_spaces(
indent,
tag_stack.last().unwrap().indent,
track_line_index + j as isize,
);
if let Some(next_line) = lines.clone().nth(index + j + 1) {
let next_line_trimmed = next_line.trim_start();
let next_line_indent = get_line_indent(next_line);
if next_line_indent <= tag_stack.last().unwrap().indent
&& next_line_trimmed.starts_with("|>")
{
if text_node != "" {
nodes.last_mut().unwrap().push((text_node.clone(), false));
}
break;
}
}
if inner_trimmed_line.is_empty() {
if text_node != "" {
nodes.last_mut().unwrap().push((text_node.clone(), false));
text_node = "".to_string();
}
lines_to_skip += 1;
nodes.push(Vec::new());
continue;
}
if indent < tag_stack.last().unwrap().indent {
if text_node != "" {
nodes.last_mut().unwrap().push((text_node.clone(), false));
}
break;
}
if !inner_trimmed_line.starts_with("|>") {
text_node += &format!(" {}", inner_trimmed_line);
lines_to_skip += 1;
continue;
}
if text_node != "" {
nodes.last_mut().unwrap().push((text_node, false));
text_node = "".to_string();
}
let (element, skips) =
self.handle_inline_element(lines.clone(), j + index, indent);
inner_lines_to_skip += skips;
lines_to_skip += skips + 1;
nodes
.last_mut()
.unwrap()
.push((format!("\"#{}r#\"", element), true));
}
let mut processed_text = String::new();
for (node_idx, sub_nodes) in nodes.iter().enumerate() {
if sub_nodes.len() == 0 {
continue;
}
let mut sub_node_text = "".to_string();
for (sub_node_idx, (text, is_element)) in sub_nodes.iter().enumerate() {
if *is_element {
sub_node_text += &text;
continue;
}
let no_paragraph_indent = self.handle_paragraph_indent(
&tag_stack,
&nodes,
&sub_nodes,
node_idx,
sub_node_idx,
&text,
);
if no_paragraph_indent {
sub_node_text += &ElementText::new(&text).handle_delimeters();
} else {
let handled_text = &format!(
"\"#<Indent>r#\"{}\"#</Indent>r#\"",
&ElementText::new(&text).handle_delimeters()
);
sub_node_text += handled_text;
}
}
let auto_wrapper = self.auto_wrappers.iter().find(|wrapper| {
wrapper
.tags
.contains(&tag_stack.last().unwrap().name.as_str())
});
if let Some(a_w) = auto_wrapper {
if a_w.enable_manual_wrap
&& get_tag_from_string(&sub_node_text) == a_w.wrap_children_with
{
processed_text += sub_node_text.as_str();
continue;
}
let no_padding = self
.no_x_padding_tags
.contains(&tag_stack.last().unwrap().name.as_str());
processed_text += &format!(
"\"#<{} {}>r#\"{}\"#</{}>r#\"",
a_w.wrap_children_with,
if no_padding { "no_padding = true" } else { "" },
sub_node_text,
a_w.wrap_children_with
);
continue;
}
processed_text += sub_node_text.as_str();
}
nodes = vec![];
output.push_str(&concat_ignore_spaces("r#\"", &processed_text, "\"#\n"));
}
}
while let Some(last_tag_info) = tag_stack.pop() {
if last_tag_info.is_self_closing {
output.push_str("/>\n");
continue;
}
output.push_str(&format!("</{}>\n", last_tag_info.name));
}
output
.replace("\n", " ")
.replace("r#\"\"#", "")
.replace("r#\" \"#", " ")
}
fn handle_inline_element(
&mut self,
lines: Lines<'_>,
start_from: usize,
initial_indent: usize,
) -> (String, u32) {
let mut element = "".to_string();
let mut end_line = start_from;
let mut inner_indent = initial_indent + 4;
let mut skips: u32 = 0;
let mut prev_line = "";
while inner_indent > initial_indent
|| lines.clone().nth(end_line).unwrap().is_empty()
|| lines
.clone()
.nth(end_line)
.unwrap()
.chars()
.all(char::is_whitespace)
{
if lines.clone().nth(end_line + 1).is_none() {
break;
}
let inner_line = lines.clone().nth(end_line + 1).unwrap();
inner_indent = get_line_indent(inner_line);
if inner_indent > initial_indent
|| inner_line.is_empty()
|| inner_line.chars().all(char::is_whitespace)
{
end_line += 1;
skips += 1;
continue;
}
break;
}
let prev_line = lines.clone().nth(end_line).unwrap();
if prev_line.is_empty() || prev_line.chars().all(char::is_whitespace) {
skips -= 1
}
let mut i = start_from;
for i in start_from..=end_line {
element += &(lines.clone().nth(i).unwrap().to_string() + "\n");
}
element += &("".to_string() + "\n");
element = self.transform(element, start_from as isize);
(element, skips)
}
fn handle_paragraph_indent(
&self,
tag_stack: &Vec<TagInfo>,
nodes: &Vec<Vec<(String, bool)>>,
sub_nodes: &Vec<(String, bool)>,
node_idx: usize,
sub_node_idx: usize,
text: &str,
) -> bool {
if self
.tags_with_no_indents
.contains(&tag_stack.last().unwrap().name.as_str())
{
return true;
}
if node_idx == 0
&& sub_node_idx == 0
&& self
.tags_with_non_indent_first_child
.contains(&tag_stack.last().unwrap().name.as_str())
{
return true;
}
let centering_delimiters = vec!["$$", "__", "|_", "_|"];
if text.len() > 4 && centering_delimiters.contains(&&get_slice(text, 1, 3).unwrap()) {
return true;
}
if node_idx > 0 {
if let Some(first_prev_sub_node) = nodes[node_idx - 1].first() {
let prev_text = &first_prev_sub_node.0;
if prev_text.len() > 1
&& centering_delimiters
.contains(&prev_text.chars().rev().take(2).collect::<String>().as_str())
{
println!("asdasd");
return true;
}
println!("pass");
}
}
let mut prev_tag = "".to_string();
if sub_node_idx > 0 {
prev_tag = get_tag_from_string(sub_nodes[sub_node_idx - 1].0.as_str());
}
if node_idx > 0 {
prev_tag = get_tag_from_string(nodes[node_idx - 1].last().unwrap().0.as_str());
}
if self.tags_before_non_indents.contains(&prev_tag.as_str()) {
return true;
}
false
}
fn handle_prop_line(line: &str) -> String {
let mut prop_line = line.trim().to_string();
let prop_value = prop_line.split_once(" ");
if let Some((prop_key, prop_value)) = prop_value {
let is_number = prop_value.trim().parse::<f32>();
let is_bool = prop_value.trim() == "false" || prop_value.trim() == "true";
let is_vec = prop_value.trim().starts_with("vec![");
if is_number.is_err() && !is_bool && !is_vec {
prop_line = match prop_key {
"src" => format!("{}=\"/{}\"", prop_key.trim(), prop_value.trim()),
_ => format!("{}=\"{}\"", prop_key.trim(), prop_value.trim()),
}
} else {
prop_line = format!("{}={}", prop_key.trim(), prop_value.trim())
}
}
prop_line
}
}