use gen_utils::{
common::tokenizer::{FUNCTION_SIGN, IMPORT},
error::{Error, ParseError},
parser::{parse_value, trim},
};
use nom::{
branch::alt,
bytes::complete::{tag, take_until, take_until1},
error::ErrorKind,
multi::many0,
sequence::{pair, tuple},
IResult,
};
use gen_utils::common::tokenizer::{
HOLDER_END, HOLDER_START, STYLE_CLASS, STYLE_END, STYLE_ID, STYLE_PESUDO, STYLE_START,
};
use crate::{nom_err, Comment, PropKey, Style, StyleVal};
use super::value::Value;
#[allow(dead_code)]
pub fn parse_style_tag(input: &str) -> IResult<&str, &str> {
let (input, _) = trim(tag(STYLE_START))(input)?;
let (_, input) = take_until(STYLE_END)(input)?;
Ok((input, "style"))
}
fn parse_ident(input: &str) -> IResult<&str, String> {
let (input, (style_type, name)) = pair(
alt((
trim(tag(STYLE_CLASS)),
trim(tag(STYLE_ID)),
trim(tag(STYLE_PESUDO)),
trim(tag(IMPORT)),
trim(tag(FUNCTION_SIGN)),
)),
parse_value,
)(input)?;
Ok((input, format!("{}{}", style_type, name)))
}
fn parse_property_key(input: &str) -> IResult<&str, &str> {
parse_value(input)
}
fn parse_property(input: &str) -> IResult<&str, (PropKey, Value)> {
let (input, _) = parse_comment(input)?;
let (input, (key, _, value)) =
tuple((parse_property_key, trim(tag(":")), take_until1(";")))(input)?;
let (input, _) = trim(tag(";"))(input)?;
match Value::parse_style(value) {
Ok(value) => {
let k = PropKey::from_value_with(&value, key, true);
let (input, _) = parse_comment(input)?;
Ok((input, (k, value)))
}
Err(_) => Err(nom_err!(input, ErrorKind::Fail)),
}
}
fn parse_properties(input: &str) -> IResult<&str, Vec<(PropKey, Value)>> {
many0(trim(parse_property))(input)
}
#[allow(dead_code)]
fn parse_comment(input: &str) -> IResult<&str, Vec<Comment>> {
many0(Comment::parse)(input)
}
fn parse_single(input: &str) -> IResult<&str, Style> {
let (input, _) = parse_comment(input)?;
let (input, key) = trim(parse_ident)(input)?;
let mut style = Style::new();
let (input, _) = trim(tag(HOLDER_START))(input)?;
let (input, children, properties) = if input.trim().starts_with(HOLDER_END) {
(input, None, None)
} else {
let (input, properties) = parse_properties(input)?;
let properties = if properties.is_empty() {
None
} else {
Some(StyleVal::from_iter(properties.into_iter()))
};
let (input, children) = many0(parse_single)(input)?;
let (input, _) = trim(tag(HOLDER_END))(input)?;
if children.is_empty() {
(input, None, properties)
} else {
(input, Some(children), properties)
}
};
style.insert(key.to_string(), properties.unwrap_or_default());
match children {
Some(children) => {
for child in children {
style.extend(child.into_iter().map(|(k, v)| (format!("{}-{}", key, k), v)));
}
}
None => {}
}
Ok((input, style))
}
#[allow(dead_code)]
pub fn parse(input: &str) -> Result<Style, Error> {
match many0(parse_single)(input) {
Ok((remain, styles)) => {
if remain.is_empty() {
let mut style = Style::new();
for s in styles {
style.extend(s.into_iter());
}
return Ok(style);
}
Err(ParseError::template(remain).into())
}
Result::Err(e) => Err(ParseError::template(&e.to_string()).into()),
}
}