use crate::extend::string::StringExtend;
use crate::extend::vec_str::VecCharExtend;
use crate::less::fileinfo::FileWeakRef;
use crate::less::node::NodeWeakRef;
use crate::sourcemap::loc::{Loc, LocMap};
use crate::style_core::scan::traversal;
use crate::token::ident::IdentType;
use crate::token::lib::Token;
use serde::ser::SerializeStruct;
use serde::{Serialize, Serializer};
use serde_json::{Map, Value};
use smol_str::SmolStr;
use std::fmt::{Debug, Formatter};
#[derive(Clone)]
pub struct ValueNode {
pub charlist: Vec<char>,
pub parent: NodeWeakRef,
pub fileinfo: FileWeakRef,
map: LocMap,
pub loc: Option<Loc>,
pub word_ident_list: Vec<IdentType>,
}
impl Serialize for ValueNode {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct("ValueNode", 2)?;
state.serialize_field("content", &self.charlist.poly())?;
state.serialize_field("loc", &self.loc)?;
state.serialize_field("ident", &self.word_ident_list)?;
state.end()
}
}
impl Debug for ValueNode {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ValueNode")
.field("origin_txt", &self.charlist.poly())
.field("ident", &self.word_ident_list)
.finish()
}
}
impl ValueNode {
pub fn new(
charlist: Vec<char>,
loc: Option<Loc>,
parent: NodeWeakRef,
fileinfo: FileWeakRef,
) -> Result<Self, String> {
let map = if loc.is_none() {
LocMap::new(&charlist)
} else {
LocMap::merge(loc.as_ref().unwrap(), &charlist).0
};
let mut obj = Self {
charlist,
parent,
fileinfo,
map,
loc,
word_ident_list: vec![],
};
obj.parse()?;
Ok(obj)
}
pub fn deserializer(
map: &Map<String, Value>,
parent: NodeWeakRef,
fileinfo: FileWeakRef,
) -> Result<Self, String> {
let mut obj = Self {
charlist: vec![],
parent,
fileinfo,
map: LocMap::new(&[]),
loc: None,
word_ident_list: vec![],
};
if let Some(Value::String(content)) = map.get("content") {
obj.charlist = content.to_char_vec();
} else {
return Err("deserializer ValueNode has error -> content is empty!".to_string());
}
if let Some(Value::Object(loc)) = map.get("loc") {
obj.loc = Some(Loc::deserializer(loc));
obj.map = LocMap::merge(obj.loc.as_ref().unwrap(), &obj.charlist).0;
} else {
obj.map = LocMap::new(&obj.charlist);
}
if let Some(Value::Array(array_value)) = map.get("ident") {
obj.word_ident_list = array_value
.iter()
.map(IdentType::deserializer)
.collect::<Vec<IdentType>>()
} else {
return Err("deserializer ValueNode has error -> ident is empty!".to_string());
}
Ok(obj)
}
pub fn error_msg(&self, index: &usize) -> String {
let error_loc = self.map.get(*index).unwrap();
let char = self.charlist.get(*index).unwrap().to_string();
format!(
"text {}, char {} is not allow, line is {} col is {}",
&self.charlist.poly(),
char,
error_loc.line,
error_loc.col
)
}
pub fn is_number(char: Option<&char>) -> bool {
if let Some(cc) = char {
cc.is_ascii_digit()
} else {
false
}
}
pub fn is_brackets(char: Option<&char>) -> bool {
if let Some(cc) = char {
matches!(*cc, '(' | ')' | '[' | ']' | '{' | '}')
} else {
false
}
}
pub fn is_end(char: Option<&char>, extend_char: Option<Vec<char>>) -> bool {
if let Some(cc) = char {
let is_static_end = matches!(
*cc,
';' | '@' | '~' | '#' | '$' | '(' | ')' | '[' | ']' | '+' | '*' | '/' | ','
);
is_static_end
|| Token::is_space_token(Some(cc))
|| extend_char.map(|lst| lst.contains(cc)).unwrap_or(false)
} else {
false
}
}
pub fn parse_value_word(&self, start: &usize) -> Result<(String, usize), String> {
let charlist = &self.charlist;
let res = traversal(
Some(*start),
charlist,
&mut (|arg, charword| {
let (index, temp, has_end) = arg;
let (_, char, next_char) = charword;
temp.push(*char);
if *char == ':' {
return Err(self.error_msg(index));
}
if Self::is_end(next_char, None) {
*has_end = true;
}
Ok(())
}),
)?;
Ok(res)
}
pub fn parse_value_string_const(&self, start: &usize) -> Result<(String, usize), String> {
let charlist = &self.charlist;
let mut keyword = '\0';
let (value, end) = traversal(
Some(*start),
charlist,
&mut (|arg, charword| {
let (index, temp, hasend) = arg;
let (_, char, nextchar) = charword;
if temp.is_empty() {
if *char == '\'' || *char == '"' {
keyword = *char;
temp.push(*char);
} else {
return Err(self.error_msg(index));
}
} else {
temp.push(*char);
}
if nextchar.is_some() && *nextchar.unwrap() == keyword && *char != '\\' {
*hasend = true;
temp.push(keyword);
*index += 1;
}
Ok(())
}),
)?;
if value.is_empty() {
return Err(self.error_msg(start));
}
if (value.len() > 1
&& *value.to_char_vec().get(0).unwrap() == keyword
&& *value.to_char_vec().get(value.len() - 1).unwrap() != keyword)
|| value.len() == 1
{
return Err(format!("{} is not closure", self.charlist.poly()));
}
Ok((value, end))
}
pub fn parse_value_var(&self, start: &usize) -> Result<(String, usize), String> {
let charlist = &self.charlist;
let res = traversal(
Some(*start),
charlist,
&mut (|arg, charword| {
let (index, temp, hasend) = arg;
let (_, char, nextchar) = charword;
if temp.is_empty() && *char == '@' {
temp.push('@');
Ok(())
} else if temp.is_empty() {
Err(self.error_msg(index))
} else {
if Token::is_token(Some(char)) {
if *char == '-' {
if Token::is_token(nextchar) {
*hasend = true;
*index -= 1;
} else if nextchar.is_some() {
temp.push(*char);
}
if temp.len() < 2 {
return Err(self.error_msg(index));
}
} else if Self::is_end(Some(char), None) {
if temp.len() < 2 {
return Err(self.error_msg(index));
}
*hasend = true;
*index -= 1;
} else if *char == '\\' {
temp.push(*char);
} else {
return Err(self.error_msg(index));
}
} else {
temp.push(*char);
}
Ok(())
}
}),
)?;
Ok(res)
}
fn find_prev_no_space_ident(&self) -> Option<IdentType> {
for item in self.word_ident_list.iter().rev() {
if !item.is_space() {
return Some(item.clone());
}
}
None
}
fn find_next_no_space_char(&self, mut index: usize) -> Option<char> {
index += 1;
while index < self.charlist.len() {
let cur = self.charlist.get(index).unwrap();
if !Token::is_space_token(Some(cur)) {
return Some(*cur);
}
index += 1;
}
None
}
pub fn parse_value_number(
&self,
start: &usize,
) -> Result<((String, Option<String>), usize), String> {
let charlist = &self.charlist;
let mut value: String = "".to_string();
let mut unit: String = "".to_string();
let mut has_record_value = false;
let mut has_single = false;
let (_, end) = traversal(
Some(*start),
charlist,
&mut (|arg, charword| {
let (index, _, hasend) = arg;
let (prev_char, char, next_char) = charword;
if Token::is_token(Some(char)) {
if *char == '.' && !has_single && Self::is_number(prev_char) && Self::is_number(next_char)
{
value.push(*char);
has_single = true;
} else if *char == '%' {
unit.push(*char);
} else if *char == '-' && value.is_empty() {
value.push(*char);
} else {
return Err(self.error_msg(index));
}
} else if Self::is_number(Some(char)) {
if !has_record_value {
value.push(*char);
} else {
*index -= 1;
*hasend = true;
}
} else {
if value.is_empty() {
return Err(self.error_msg(index));
}
if !has_record_value {
has_record_value = true;
}
unit.push(*char);
}
if Self::is_end(next_char, Some(vec!['-']))
|| (has_single && next_char.is_some() && *next_char.unwrap() == '.')
|| *char == '%'
{
*hasend = true;
}
Ok(())
}),
)?;
if unit.is_empty() {
Ok(((value, None), end))
} else {
Ok(((value, Some(unit)), end))
}
}
fn is_operator(char: &char) -> bool {
vec!['+', '-', '*', '/'].contains(char)
}
fn validate_brackets() -> Box<dyn FnMut(&char) -> Result<Vec<char>, String>> {
let mut brackets_vaildate: Vec<char> = vec![];
Box::new(move |char: &char| {
if vec!['(', ')', '[', ']', '\\'].contains(char) {
if *char == ']' || *char == ')' {
let last = brackets_vaildate.last();
if let Some(last_char) = last {
if (*last_char == '(' && *char == ')') || (*last_char == '[' && *char == ']') {
brackets_vaildate.remove(brackets_vaildate.len() - 1);
} else {
return Err(format!(r#"{} is error "#, char));
}
} else {
return Err(format!(r#"{} is error "#, char));
}
} else {
brackets_vaildate.push(*char)
}
} else {
return Err(format!(r#"{} is not '(' ')' '[' ']' "#, char));
}
Ok(brackets_vaildate.clone())
})
}
fn parse(&mut self) -> Result<(), String> {
let charlist = &self.charlist.clone();
if charlist.is_empty() {
return Err("var declare text is empty".to_string());
}
let index: usize = 0;
let mut validate_fn = Self::validate_brackets();
let mut vaildate_res: Vec<char> = vec![];
traversal(
Some(index),
charlist,
&mut (|arg, charword| {
let (index, _, _) = arg;
let (prev, char, next) = charword;
if Token::is_space_token(Some(char)) {
match self.word_ident_list.last() {
None => {}
Some(val) => match val {
IdentType::Space => {
return Ok(());
}
_ => {
self.word_ident_list.push(IdentType::Space);
}
},
}
} else if *char == '@' {
let (var, end) = self.parse_value_var(index)?;
self.word_ident_list.push(IdentType::Var(var.into()));
*index = end;
}
else if *char == ';' {
return if *index == self.charlist.len() - 1 {
Ok(())
} else {
Err(self.error_msg(index))
};
}
else if *char == '$' {
return Err(format!(
"$ style_rule or ~ reference has not support \n {}",
self.error_msg(index)
));
} else if *char == '~' {
self.word_ident_list.push(IdentType::Word("~".into()));
}
else if *char == '#' {
let (color, end) = self.parse_value_word(index)?;
self.word_ident_list.push(IdentType::Color(color.into()));
*index = end;
}
else if *char == '!' {
let end = *index + 10;
if self.charlist.len() >= end
&& &self.charlist[*index..end].to_vec().poly() == "!important"
{
self
.word_ident_list
.push(IdentType::KeyWord("!important".into()));
*index += 10;
} else {
let (word, end) = self.parse_value_word(index)?;
self.word_ident_list.push(IdentType::Word(word.into()));
*index = end;
}
}
else if *char == '"' || *char == '\'' {
let (string_const, end) = self.parse_value_string_const(index)?;
self
.word_ident_list
.push(IdentType::StringConst(string_const.into()));
*index = end;
}
else if vec!['(', ')', '[', ']', '\\'].contains(char) {
if *char != '\\' {
match validate_fn(char) {
Ok(res) => {
vaildate_res = res;
self
.word_ident_list
.push(IdentType::Brackets(char.to_string().into()));
}
Err(..) => {
return Err(self.error_msg(index));
}
};
} else {
return Ok(());
}
}
else if Self::is_operator(char) {
if *char == '-' && prev == Some(&' ') && Self::is_number(Some(next.unwrap_or(&' '))) {
let ((val, unit), end) = self.parse_value_number(index)?;
self
.word_ident_list
.push(IdentType::Number(val.into(), unit.map(SmolStr::new)));
*index = end;
} else {
let last_item = self.find_prev_no_space_ident();
let next_char_no_space = self.find_next_no_space_char(*index).unwrap();
if last_item.is_some()
&& last_item.unwrap().is_number()
&& (Self::is_number(Some(&next_char_no_space))
|| Self::is_brackets(Some(&next_char_no_space)))
{
self
.word_ident_list
.push(IdentType::Operator(char.to_string().into()));
} else {
let (word, end) = self.parse_value_word(index)?;
self.word_ident_list.push(IdentType::Word(word.into()));
*index = end;
}
}
}
else if Self::is_number(Some(char)) {
let ((val, unit), end) = self.parse_value_number(index)?;
self
.word_ident_list
.push(IdentType::Number(val.into(), unit.map(SmolStr::new)));
*index = end;
}
else {
let (word, end) = self.parse_value_word(index)?;
self.word_ident_list.push(IdentType::Word(word.into()));
*index = end;
}
Ok(())
}),
)?;
if !vaildate_res.is_empty() {
return Err(format!(
"{} contains unclosed parentheses -> {:#?}",
&self.charlist.poly(),
&vaildate_res
));
}
Ok(())
}
}