use crate::model::Template;
use crate::value::{Bind, Ident, Value};
use crate::{nom_err, Comment, Polls, PropKey, PropKeyType, SugarIter};
use gen_utils::error::{Error, ParseError};
use gen_utils::parser::parse_value;
use gen_utils::{
common::tokenizer::{END_SIGN, END_START_SIGN, EQUAL_SIGN, SELF_END_SIGN},
parser::{parse_string, trim},
};
use nom::combinator::opt;
use nom::error::ErrorKind;
use nom::{
branch::alt,
bytes::complete::{tag, take_until},
character::complete::char,
multi::many0,
sequence::{delimited, preceded, tuple},
IResult,
};
use std::collections::HashMap;
use std::sync::{Arc, RwLock};
#[allow(dead_code)]
fn parse_tag_name(input: &str) -> IResult<&str, &str> {
parse_value(input)
}
pub fn parse_tag_start(input: &str) -> IResult<&str, (Template, CloseType)> {
let (remain, (name, props)) = trim(preceded(
char('<'),
tuple((parse_tag_name, parse_properties)),
))(input)?;
let props = if let Some(props) = props {
Some(HashMap::from_iter(props.into_iter()))
} else {
None
};
let mut template = Template::new(name);
let mut remain = remain.trim();
let close_type = if remain.starts_with(SELF_END_SIGN) {
remain = remain.strip_prefix(SELF_END_SIGN).unwrap_or(remain);
CloseType::SelfClosed
} else {
remain = remain.trim_start_matches(END_SIGN);
CloseType::Usual
};
template.props = props;
Ok((remain, (template, close_type)))
}
#[allow(dead_code)]
fn parse_property_key(input: &str) -> IResult<&str, (PropKeyType, &str)> {
fn parse_sign_key<'a>(
input: &'a str,
sign: &'a str,
) -> IResult<&'a str, (PropKeyType, &'a str)> {
let (input, sign) = tag(sign)(input)?;
let (input, value) = parse_value(input)?;
let sign = sign
.parse::<PropKeyType>()
.map_err(|_| nom_err!(sign, ErrorKind::Tag))?;
Ok((input, (sign, value)))
}
fn parse_normal_key(input: &str) -> IResult<&str, (PropKeyType, &str)> {
let (input, value) = parse_value(input)?;
Ok((input, (PropKeyType::Normal, value)))
}
fn parse_bind_key(input: &str) -> IResult<&str, (PropKeyType, &str)> {
parse_sign_key(input, ":")
}
fn parse_function_key(input: &str) -> IResult<&str, (PropKeyType, &str)> {
parse_sign_key(input, "@")
}
trim(alt((parse_bind_key, parse_function_key, parse_normal_key)))(input)
}
#[allow(dead_code)]
fn parse_property(input: &str) -> IResult<&str, (PropKey, Value)> {
let (input, (key_type, key)) = parse_property_key(input)?;
let input = input.trim();
if !input.starts_with('=') {
let kv = if key == "else" {
(
PropKey::new_bind(key, false),
Value::Bind(Bind::Normal(vec![Ident::new("else")])),
)
} else {
(PropKey::new_tag_normal(key), Value::Bool(true))
};
return Ok((input, kv));
}
let (input, value) = preceded(tag(EQUAL_SIGN), parse_string)(input)?;
let value = key_type
.to_value(value)
.map_err(|_| nom_err!(value, ErrorKind::Tag))?;
Ok((input, (PropKey::new(key, false, key_type), value)))
}
fn parse_properties(input: &str) -> IResult<&str, Option<Vec<(PropKey, Value)>>> {
opt(many0(trim(parse_property)))(input)
}
#[allow(dead_code)]
fn parse_end_tag_common(input: &str) -> IResult<&str, (&str, &str)> {
let (input, value) = trim(delimited(
trim(tag(END_START_SIGN)),
parse_tag_name,
trim(tag(END_SIGN)),
))(input)?;
Ok((input, (END_START_SIGN, value)))
}
#[allow(dead_code)]
fn parse_tag_end(input: &str) -> IResult<&str, &str> {
alt((tag(SELF_END_SIGN), tag(END_SIGN)))(input)
}
#[allow(dead_code)]
fn parse_comment(input: &str) -> IResult<&str, Vec<Comment>> {
many0(Comment::parse)(input)
}
#[deprecated = "use parse_end_tag_common instead"]
#[allow(dead_code)]
fn to_end_tag(input: &str, tag_name: String) -> IResult<&str, &str> {
let mut rest = input;
let mut remain = "";
let mut nested_count = 0;
loop {
match take_until(END_START_SIGN)(rest) {
Ok((new_rest, taken)) => {
if taken.trim().starts_with(&(String::from("<") + &tag_name)) {
nested_count += 1;
}
match delimited(
trim(tag(END_START_SIGN)),
tag(tag_name.as_str()),
trim(tag(END_SIGN)),
)(new_rest)
{
Ok((final_rest, _)) => {
if nested_count == 0 {
remain = &input[..(remain.len() + taken.len())];
return Ok((final_rest, remain));
} else {
nested_count -= 1; remain = &input[..(remain.len() + taken.len() + tag_name.len() + 3)]; rest = final_rest;
}
}
Err(_) => {
remain = &input[..input.len() - new_rest.len() + 2]; rest = &new_rest[2..]; }
}
}
Err(e) => return Err(e),
}
}
}
#[allow(dead_code)]
fn parse_end_tag(input: &str, name: String) -> IResult<&str, (&str, &str)> {
let (input, value) = trim(delimited(
trim(tag(END_START_SIGN)),
tag(&*name),
trim(tag(END_SIGN)),
))(input)?;
Ok((input, (END_START_SIGN, value)))
}
#[allow(dead_code)]
fn parse_tag<'a>(
poll: Arc<RwLock<Polls>>,
mut root: bool,
mut iter: Option<SugarIter>,
) -> impl FnMut(&'a str) -> IResult<&'a str, Template> {
move |input: &str| {
let (input, comments) = parse_comment(input)?;
let (input, (mut template, close_type)) = parse_tag_start(input)?;
template.root = root;
root = false;
let sugar_iter = template
.after_prop_parse(Arc::clone(&poll), iter.as_ref())
.map_err(|e| {
eprintln!("parse_tag error: {:?}", e);
nom_err!(input, ErrorKind::Fail)
})?;
iter = sugar_iter;
if !comments.is_empty() {
template.comments.replace(comments);
}
let input = match close_type {
CloseType::SelfClosed => {
input
}
CloseType::Usual => {
let tag_name = template.name.to_string();
match parse_end_tag(input, tag_name.to_string()) {
Ok((input, _)) => input,
Err(_) => {
let (input, mut children) =
many0(parse_tag(Arc::clone(&poll), root, iter.clone()))(input)?;
let input = match parse_end_tag_common(input) {
Ok((remain, _)) => remain,
Err(_) => input,
};
if !children.is_empty() {
let (special, name) = template.as_parent();
for child in children.iter_mut() {
child.set_parent(
special.to_string(),
name.to_string(),
template.root,
);
child.after_all(Arc::clone(&poll)).map_err(|e| {
eprintln!("parse_tag error: {:?}", e);
nom_err!(input, ErrorKind::Fail)
})?;
}
template.children.replace(children);
}
let input = input.trim();
if preceded(char('<'), parse_tag_name)(input).is_ok()
&& parse_end_tag_common(input).is_err()
{
input
} else {
input
}
}
}
}
};
return Ok((input, template));
}
}
#[allow(dead_code)]
pub fn parse(input: &str, poll: Arc<RwLock<Polls>>, root: bool) -> Result<Template, Error> {
match parse_tag(poll, root, None)(input) {
Ok((remain, template)) => {
if remain.is_empty() {
return Ok(template);
}
Err(ParseError::template(remain).into())
}
Err(e) => Err(ParseError::template(&e.to_string()).into()),
}
}
#[derive(Debug, Clone, Copy)]
pub enum CloseType {
SelfClosed,
Usual,
}