use crate::extend::string::StringExtend;
use crate::extend::vec_str::VecCharExtend;
use crate::less::fileinfo::FileWeakRef;
use crate::less::node::{NodeWeakRef, StyleNode};
use crate::less::select_node::SelectorNode;
use crate::less::value::ValueNode;
use crate::less::var::VarRuleNode;
use crate::sourcemap::loc::{Loc, LocMap};
use crate::style_core::scan::traversal;
use crate::token::lib::Token;
use crate::token::select::{TokenAllowChar, TokenCombinaChar, TokenKeyWordChar, TokenSelectChar};
use crate::util::str_enum::{CharToEnum, EnumToChar};
use serde::ser::SerializeStruct;
use serde::{Deserialize, Serialize, Serializer};
use serde_json::{Map, Value};
use std::cmp::Ordering;
use std::ops::Deref;
#[derive(Debug, Clone, Serialize, Eq, PartialEq)]
#[serde(tag = "type", content = "value")]
enum SelectVarText {
Txt(String),
Var(String),
}
#[derive(Debug, Clone, Serialize, Eq, PartialEq, Deserialize)]
#[serde(tag = "type", content = "value")]
pub enum SelectParadigm {
SelectWrap(String),
CominaWrap(TokenCombinaChar),
VarWrap(char),
KeyWrap(String),
}
impl SelectParadigm {
pub fn deserializer(val: &Value) -> Self {
serde_json::from_str(&serde_json::to_string(val).unwrap()).unwrap()
}
}
pub trait Paradigm {
fn tostr(&self) -> String;
}
impl Paradigm for Vec<SelectParadigm> {
fn tostr(&self) -> String {
let mut txt = "".to_string();
self.iter().for_each(|par| match par {
SelectParadigm::SelectWrap(cc) => txt += cc,
SelectParadigm::CominaWrap(cc) => {
txt.push(cc.to_str());
}
SelectParadigm::VarWrap(cc) => txt.push(*cc),
SelectParadigm::KeyWrap(cc) => {
txt = cc.clone();
}
});
txt
}
}
#[derive(Debug, Clone)]
pub struct NewSelector {
pub paradigm_vec: Vec<Vec<SelectParadigm>>,
pub loc: Option<Loc>,
map: LocMap,
pub charlist: Vec<char>,
pub parent: NodeWeakRef,
pub fileinfo: FileWeakRef,
}
impl Serialize for NewSelector {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct("FileInfo", 3)?;
state.serialize_field("loc", &self.loc)?;
state.serialize_field("content", &self.charlist.poly())?;
state.serialize_field("paradigm_vec", &self.paradigm_vec)?;
state.end()
}
}
impl NewSelector {
pub fn new(
charlist: Vec<char>,
loc: Option<Loc>,
map: Option<LocMap>,
parent: NodeWeakRef,
fileinfo: FileWeakRef,
) -> Self {
NewSelector {
paradigm_vec: vec![],
loc,
map: map.unwrap_or_else(|| LocMap::new(&charlist)),
charlist,
parent,
fileinfo,
}
}
pub fn find_up_select_node(node: NodeWeakRef) -> NodeWeakRef {
if let Some(ref heap_node) = node {
let rule = heap_node.upgrade().unwrap();
if matches!(
*rule.deref().borrow().selector.as_ref().unwrap(),
SelectorNode::Select(..)
) {
node.clone()
} else {
let parent = rule.deref().borrow().parent.clone();
Self::find_up_select_node(parent)
}
} else {
None
}
}
pub fn deserializer(
map: &Map<String, Value>,
parent: NodeWeakRef,
fileinfo: FileWeakRef,
) -> Result<Self, String> {
let mut select = Self {
paradigm_vec: vec![],
loc: None,
map: LocMap::new(&[]),
charlist: vec![],
parent,
fileinfo,
};
if let Some(Value::String(content)) = map.get("content") {
select.charlist = content.to_char_vec();
} else {
return Err("deserializer NewSelector has error -> charlist is empty!".to_string());
}
if let Some(Value::Object(loc)) = map.get("loc") {
select.loc = Some(Loc::deserializer(loc));
select.map = LocMap::merge(select.loc.as_ref().unwrap(), &select.charlist).0;
} else {
select.map = LocMap::new(&select.charlist);
}
if let Some(Value::Array(charlist)) = map.get("paradigm_vec") {
for paradigm_vec in charlist {
let mut list = vec![];
if let Value::Array(paradigm) = paradigm_vec {
list = paradigm.iter().map(SelectParadigm::deserializer).collect();
}
if !list.is_empty() {
select.paradigm_vec.push(list);
}
}
} else {
return Err("deserializer NewSelector has error -> paradigm_vec is empty!".to_string());
}
Ok(select)
}
pub fn value(&self) -> String {
self.charlist.poly()
}
pub fn code_gen(&self) -> Result<Vec<Vec<SelectParadigm>>, String> {
let mut split_select_txt: Vec<Vec<SelectParadigm>> = vec![];
for list in self.paradigm_vec.iter() {
let self_rule = self.parent.as_ref().unwrap().upgrade().unwrap();
let node = self_rule.deref().borrow().parent.clone();
let select_rule_node = Self::find_up_select_node(node);
let mut parent_select_txt = vec![];
if let Some(any_parent_rule) = select_rule_node {
let heap_any_parent_rule = any_parent_rule.upgrade().unwrap();
if let Some(SelectorNode::Select(ps)) =
heap_any_parent_rule.deref().borrow().selector.as_ref()
{
parent_select_txt = ps.code_gen()?;
};
}
let mut self_list = vec![];
let mut has_var = false;
let mut var_index = -1;
for (np, p) in list.iter().enumerate() {
if matches!(p, SelectParadigm::VarWrap(..)) {
var_index = np as i32;
} else if matches!(p, SelectParadigm::KeyWrap(..)) {
has_var = true;
}
}
if var_index > -1 {
has_var = true;
for expr in parent_select_txt.iter() {
let mut cplist = list.clone();
cplist.remove(var_index as usize);
for item in expr.iter().rev() {
cplist.insert(var_index as usize, item.clone())
}
self_list.push(cplist);
}
} else {
self_list.push(list.clone());
}
if has_var || parent_select_txt.is_empty() {
if !self_list.is_empty() {
split_select_txt.append(&mut self_list);
}
} else {
for expr in parent_select_txt.iter() {
for m in self_list.iter() {
let mut cp_expr = expr.clone();
if cp_expr.last() != Some(&SelectParadigm::CominaWrap(TokenCombinaChar::Space)) {
cp_expr.push(SelectParadigm::CominaWrap(TokenCombinaChar::Space));
}
cp_expr.append(&mut m.clone());
split_select_txt.push(cp_expr);
}
}
}
}
Ok(split_select_txt)
}
pub fn errormsg(&self, index: &usize) -> Result<(), String> {
let mut safe_index = *index;
if *index > self.charlist.len() - 1 {
safe_index = self.charlist.len() - 1;
}
let char = *self.charlist.get(safe_index).unwrap();
let error_loc = self.map.get(safe_index).unwrap();
Err(format!(
"select text {}, char {} is not allow, line is {} col is {}",
self.charlist.poly(),
char,
error_loc.line,
error_loc.col
))
}
pub fn add_paradigm(&mut self, obj: SelectParadigm) {
if self.paradigm_vec.is_empty() {
if !matches!(obj, SelectParadigm::CominaWrap(..)) {
self.paradigm_vec.push(vec![obj]);
} else {
self.paradigm_vec.push(vec![
obj,
SelectParadigm::CominaWrap(TokenCombinaChar::Space),
]);
}
} else {
let list = self.paradigm_vec.last_mut().unwrap();
if !matches!(obj, SelectParadigm::CominaWrap(..))
|| matches!(obj, SelectParadigm::CominaWrap(TokenCombinaChar::Space))
{
if matches!(obj, SelectParadigm::VarWrap(..))
&& matches!(list.get(0), Some(&SelectParadigm::CominaWrap(..)))
{
list.remove(0);
}
if list.is_empty() && !matches!(obj, SelectParadigm::CominaWrap(TokenCombinaChar::Space))
|| !list.is_empty()
{
list.push(obj);
}
} else {
let last_paradigm = list.last();
if last_paradigm.is_some()
&& !matches!(
last_paradigm,
Some(SelectParadigm::CominaWrap(TokenCombinaChar::Space))
)
{
list.push(SelectParadigm::CominaWrap(TokenCombinaChar::Space));
}
list.push(obj);
list.push(SelectParadigm::CominaWrap(TokenCombinaChar::Space));
}
}
}
pub fn clear_paraigm(&mut self, index: &usize) -> Result<(), String> {
if let Some(list) = self.paradigm_vec.last_mut() {
let mut rm_index_list: Vec<usize> = vec![];
let mut num = 0;
for (index, word) in list.iter().rev().enumerate() {
match word {
SelectParadigm::CominaWrap(_) => {
rm_index_list.push(list.len() - 1 - index);
}
_ => {
break;
}
}
}
for (_, val) in rm_index_list.into_iter().enumerate() {
list.remove(val - num);
num += 1;
}
if list.is_empty() {
return Err(self.errormsg(index).err().unwrap());
}
Ok(())
} else {
Err(self.errormsg(index).err().unwrap())
}
}
pub fn add_paradigm_vec(&mut self) {
self.paradigm_vec.push(vec![]);
}
pub fn last_paradigm(&self) -> Option<&SelectParadigm> {
if self.paradigm_vec.last().is_some() {
return self.paradigm_vec.last().unwrap().last();
}
None
}
pub fn last_paradigm_without_space(&self) -> Option<&SelectParadigm> {
if let Some(list) = self.paradigm_vec.last() {
for p in list.iter().rev() {
if *p != SelectParadigm::CominaWrap(TokenCombinaChar::Space) {
return Some(p);
} else {
continue;
}
}
None
} else {
None
}
}
pub fn is_end(char: Option<&char>, extend_char: Option<Vec<char>>) -> bool {
if let Some(cc) = char {
let mut charlist: Vec<char> = vec![];
if let Some(mut extend_list) = extend_char {
charlist.append(&mut extend_list);
}
charlist.append(
&mut TokenSelectChar::iterator()
.map(|x| x.to_str())
.collect::<Vec<char>>(),
);
charlist.append(
&mut TokenCombinaChar::iterator()
.map(|x| x.to_str())
.collect::<Vec<char>>(),
);
charlist.append(
&mut TokenKeyWordChar::iterator()
.map(|x| x.to_str())
.collect::<Vec<char>>(),
);
charlist.contains(cc)
} else {
false
}
}
pub fn parse(&mut self, parent_node: NodeWeakRef) -> Result<(), String> {
let index: usize = 0;
if self.charlist.contains(&'@') {
self.pure_select_txt(parent_node)?;
}
let charlist = &self.charlist.clone();
let (_, end) = traversal(
Some(index),
charlist,
&mut (|arg, charword| {
let (index, _, _) = arg;
let (_, char, _) = charword;
if Token::is_token(Some(char)) {
if TokenSelectChar::is(char) {
let (select_word, end) = self.parse_selector_word(index)?;
self.add_paradigm(SelectParadigm::SelectWrap(select_word));
*index = end;
} else if TokenCombinaChar::is(char) {
let (_, end) = self.parse_combina_word(index)?;
*index = end;
} else if TokenKeyWordChar::is(char) {
if *char == '&' {
self.add_paradigm(SelectParadigm::VarWrap(*char));
} else {
let (key_word, end) = self.parse_at_keyword(index)?;
self.add_paradigm(SelectParadigm::KeyWrap(key_word));
*index = end;
}
} else if TokenAllowChar::is(char) {
let (select_word, end) = self.parse_selector_word(index)?;
self.add_paradigm(SelectParadigm::SelectWrap(select_word));
*index = end;
} else {
return Err(self.errormsg(index).err().unwrap());
}
} else {
let (select_word, end) = self.parse_selector_word(index)?;
self.add_paradigm(SelectParadigm::SelectWrap(select_word));
*index = end;
}
Ok(())
}),
)?;
self.clear_paraigm(&end)?;
Ok(())
}
fn parse_at_keyword(&mut self, index: &usize) -> Result<(String, usize), String> {
traversal(
Some(*index),
&self.charlist,
&mut (|arg, charword| {
let (index, temp, end) = arg;
let (_, char, next) = charword;
if Token::is_token(Some(char)) {
if *char == '@' {
if temp.is_empty() {
temp.push(*char);
} else {
return Err(self.errormsg(index).err().unwrap());
}
} else if TokenAllowChar::is(char) || Token::is_space_token(Some(char)) {
temp.push(*char);
} else {
return Err(self.errormsg(index).err().unwrap());
}
} else {
temp.push(*char);
}
if next.is_none() {
*end = true;
}
Ok(())
}),
)
}
fn parse_combina_word(&mut self, index: &usize) -> Result<(String, usize), String> {
let char = *self.charlist.get(*index).unwrap();
if Token::is_space_token(Some(&char)) {
let last_pardigm = self.last_paradigm();
if let Some(SelectParadigm::CominaWrap(token)) = last_pardigm {
if *token != TokenCombinaChar::Space {
self.add_paradigm(SelectParadigm::CominaWrap(TokenCombinaChar::Space));
}
} else {
self.add_paradigm(SelectParadigm::CominaWrap(TokenCombinaChar::Space));
}
} else if char == TokenCombinaChar::AddChar.to_str()
|| char == TokenCombinaChar::ExtendChar.to_str()
|| char == TokenCombinaChar::BrotherMatchChar.to_str()
|| char == TokenCombinaChar::ColumnChar.to_str()
{
let last_pardigm = self.last_paradigm_without_space();
if !matches!(last_pardigm, Some(SelectParadigm::CominaWrap(..))) {
let combin_token = char.to_enum::<TokenCombinaChar>().unwrap();
self.add_paradigm(SelectParadigm::CominaWrap(combin_token));
} else {
return Err(self.errormsg(index).err().unwrap());
}
} else if char == TokenCombinaChar::Comma.to_str() {
self.clear_paraigm(index)?;
self.add_paradigm_vec();
}
Ok((char.to_string(), *index))
}
fn parse_selector_word(&mut self, start: &usize) -> Result<(String, usize), String> {
let res: (String, usize);
let charlist = &self.charlist;
let char = charlist.get(*start).unwrap();
if *char == '.' || *char == '#' {
res = self.parse_selector_class_or_id_word(start)?;
} else if *char == ':' {
res = self.parse_selector_pseudo_class_word(start)?;
} else if *char == '*' {
res = ('*'.to_string(), *start);
} else if *char == '[' {
res = self.parse_selector_attr(start)?;
} else if *char == '(' {
res = self.parse_selector_brackets(start)?;
} else if !Token::is_token(Some(char)) || TokenAllowChar::is(char) {
res = self.parse_selector_ele(start)?;
} else {
return Err(self.errormsg(start).err().unwrap());
}
Ok(res)
}
fn parse_selector_ele(&mut self, start: &usize) -> Result<(String, usize), String> {
traversal(
Some(*start),
&self.charlist,
&mut (|arg, charword| {
let (index, temp, end) = arg;
let (_, char, next) = charword;
if Token::is_token(Some(char)) {
if TokenAllowChar::is(char) {
temp.push(*char);
} else {
return Err(self.errormsg(index).err().unwrap());
}
} else {
temp.push(*char);
}
if next.is_none() || Self::is_end(next, None) {
*end = true;
}
Ok(())
}),
)
}
fn parse_selector_class_or_id_word(&mut self, start: &usize) -> Result<(String, usize), String> {
let charlist = &self.charlist;
let mut record = false;
traversal(
Some(*start),
charlist,
&mut (|arg, charword| {
let (index, temp, end) = arg;
let (_, char, next) = charword;
if Token::is_token(Some(char)) {
if TokenAllowChar::is(char) {
temp.push(*char);
} else if TokenSelectChar::is(char) && !record {
record = true;
temp.push(*char);
} else {
return Err(self.errormsg(index).err().unwrap());
}
} else {
temp.push(*char);
}
if (next.is_none() || Self::is_end(next, None)) && record {
if temp.len() < 2 {
return Err(self.errormsg(index).err().unwrap());
}
*end = true;
}
Ok(())
}),
)
}
fn parse_selector_pseudo_class_word(&mut self, start: &usize) -> Result<(String, usize), String> {
let charlist = &self.charlist;
let mut record = false;
traversal(
Some(*start),
charlist,
&mut (|arg, charword| {
let (index, temp, end) = arg;
let (_, char, next) = charword;
let mut next_fix = next;
if Token::is_token(Some(char)) {
if !record && *char == ':' {
if next == Some(&':') {
temp.push(*char);
*index += 1;
next_fix = {
if *index + 1 < charlist.len() {
charlist.get(*index + 1)
} else {
None
}
};
}
temp.push(*char);
record = true;
} else if TokenAllowChar::is(char) {
temp.push(*char);
} else {
return Err(self.errormsg(index).err().unwrap());
}
} else {
temp.push(*char);
}
if (next.is_none() || Self::is_end(next_fix, None)) && record {
if (temp.len() < 2 && *temp == vec![':']) || (temp.len() < 3 && *temp == vec![':', ':']) {
return Err(self.errormsg(index).err().unwrap());
}
*end = true;
}
Ok(())
}),
)
}
fn parse_selector_brackets(&mut self, start: &usize) -> Result<(String, usize), String> {
let mut level = 0;
let res = traversal(
Some(*start),
&self.charlist,
&mut (|arg, charword| {
let (index, temp, end) = arg;
let (_, char, next) = charword;
if Token::is_token(Some(char)) {
if TokenAllowChar::is(char) {
temp.push(*char);
return Ok(());
}
if *char == ')' {
level -= 1;
match level.cmp(&0) {
Ordering::Less => {
return Err(self.errormsg(index).err().unwrap());
}
Ordering::Equal => {
*end = true;
temp.push(*char);
return Ok(());
}
Ordering::Greater => {
temp.push(*char);
}
}
} else if *char == '@' && next != Some(&'{') {
return Err(self.errormsg(index).err().unwrap());
} else if *char == '(' {
level += 1;
temp.push(*char);
} else {
temp.push(*char);
}
} else {
temp.push(*char);
}
Ok(())
}),
)?;
if level > 0 {
return Err(self.errormsg(&res.1).err().unwrap());
}
Ok(res)
}
fn parse_selector_attr(&mut self, start: &usize) -> Result<(String, usize), String> {
let equal_chars = vec!['~', '|', '^', '$', '*'];
let mut hasequal = false;
let mut has_brackest = false;
let mut queto: Option<char> = None;
let res = traversal(
Some(*start),
&self.charlist,
&mut (|arg, charword| {
let (index, temp, end) = arg;
let (prev, char, next) = charword;
let get_prev_char_without_space = || -> Option<char> {
let mut res: Option<char> = None;
for tc in temp.iter().rev() {
if *tc == ' ' {
continue;
} else {
res = Some(*tc);
break;
}
}
res
};
if let Some(token_queto) = queto {
if has_brackest {
if *char == token_queto && prev != Some(&'\\') {
queto = None;
}
temp.push(*char);
} else {
return Err(self.errormsg(index).err().unwrap());
}
} else if Token::is_token(Some(char)) {
if has_brackest {
if *char == '[' {
return Err(self.errormsg(index).err().unwrap());
} else if *char == ']' {
let prev_char_without_space = get_prev_char_without_space();
if prev_char_without_space == Some('=') {
return Err(self.errormsg(index).err().unwrap());
}
temp.push(*char);
has_brackest = false;
*end = true;
} else if TokenAllowChar::is(char) {
temp.push(*char);
} else if equal_chars.contains(char) {
if next == Some(&'=') && !hasequal {
temp.push(*char);
temp.push('=');
hasequal = true;
*index += 1;
} else {
return Err(self.errormsg(index).err().unwrap());
}
} else if *char == '=' {
let prev_char_without_space = get_prev_char_without_space();
if prev_char_without_space == Some('[') {
return Err(self.errormsg(index).err().unwrap());
}
if !hasequal {
temp.push('=');
hasequal = true;
} else {
return Err(self.errormsg(index).err().unwrap());
}
} else if *char == '"' || *char == '\'' {
queto = Some(*char);
temp.push(*char);
} else {
return Err(self.errormsg(index).err().unwrap());
}
} else {
if *char == '[' {
temp.push(*char);
has_brackest = true;
} else {
return Err(self.errormsg(index).err().unwrap());
}
}
} else if has_brackest {
temp.push(*char);
} else {
return Err(self.errormsg(index).err().unwrap());
}
Ok(())
}),
)?;
if has_brackest {
return Err(self.errormsg(&res.1).err().unwrap());
}
Ok(res)
}
fn pure_select_txt(&mut self, parent_node: NodeWeakRef) -> Result<(), String> {
let mut record = false;
let mut list: Vec<SelectVarText> = vec![];
traversal(
None,
&self.charlist,
&mut (|arg, charword| {
let (index, temp, _end) = arg;
let (_prev, char, next) = charword;
if *char == '@' && next == Some(&'{') {
if !temp.is_empty() {
list.push(SelectVarText::Txt(temp.poly()));
}
temp.clear();
temp.push('@');
temp.push('{');
*index += 1;
record = true
} else if *char == '}' && record {
temp.push(*char);
if !temp.is_empty() {
list.push(SelectVarText::Var(temp.poly()));
} else {
return Err(format!(
"select txt {} index is {} -> @ var is not closure",
self.charlist.poly(),
*index
));
}
temp.clear();
record = false;
} else {
temp.push(*char);
}
Ok(())
}),
)?;
let mut new_content = "".to_string();
if !list.is_empty() {
for tt in list {
if let SelectVarText::Txt(t) = tt {
new_content += &t;
} else if let SelectVarText::Var(v) = tt {
let val = v.to_char_vec()[2..v.len() - 1].to_vec().poly();
let var_ident = format!("@{}", val);
let var_node_value = self.get_var_by_key(
var_ident.as_str(),
parent_node.clone(),
self.fileinfo.clone(),
)?;
new_content += &var_node_value.code_gen()?;
}
}
self.charlist = new_content.to_char_vec();
}
Ok(())
}
pub fn get_var_by_key(
&self,
key: &str,
rule_info: NodeWeakRef,
file_info: FileWeakRef,
) -> Result<ValueNode, String> {
if let Some(rule_ref) = rule_info {
let rule = rule_ref.upgrade().unwrap();
let nodelist = &rule.deref().borrow().block_node;
for item in nodelist {
if let StyleNode::Var(VarRuleNode::Var(var)) = item.deref() {
if var.key.as_ref().unwrap() == key {
return Ok(var.value.as_ref().unwrap().clone());
}
}
}
return if rule.deref().borrow().parent.is_some() {
self.get_var_by_key(key, rule.deref().borrow().parent.clone(), None)
} else {
self.get_var_by_key(key, None, self.fileinfo.clone())
};
}
else if let Some(file_ref) = file_info {
let fileinfo_ref = file_ref.upgrade().unwrap();
let nodelist = &fileinfo_ref.deref().borrow().block_node;
for item in nodelist {
if let StyleNode::Var(VarRuleNode::Var(var)) = item.deref() {
if var.key.as_ref().unwrap() == key {
return Ok(var.value.as_ref().unwrap().clone());
}
}
}
let top_level_other_vars = fileinfo_ref.deref().borrow().collect_vars();
for var in top_level_other_vars {
if var.key.as_ref().unwrap() == key {
return Ok(var.value.as_ref().unwrap().clone());
}
}
};
Err(format!("no var key {} has found", key))
}
}