use crate::less::fileinfo::{FileRef, FileWeakRef};
use crate::less::media::MediaQuery;
use crate::less::node::{NodeRef, NodeWeakRef};
use crate::less::select::{NewSelector, SelectParadigm};
use crate::less::var::HandleResult;
use crate::sourcemap::loc::{Loc, LocMap};
use crate::style_core::option::OptionExtend;
use crate::token::select::TokenCombinaChar;
use crate::util::str_enum::EnumToChar;
use serde::Serialize;
use serde_json::{Map, Value};
use std::collections::HashSet;
use std::ops::Deref;
#[derive(Debug, Clone, Serialize)]
#[serde(tag = "type", content = "value")]
pub enum SelectorNode {
Select(NewSelector),
Media(MediaQuery),
}
impl SelectorNode {
pub fn new(
charlist: Vec<char>,
loc: &mut Option<Loc>,
parent: NodeWeakRef,
fileinfo: FileWeakRef,
) -> Result<Self, String> {
let mut map: Option<LocMap> = None;
let start_loc = loc.as_ref().cloned();
match parent.as_ref().unwrap().upgrade() {
None => {}
Some(p) => {
if p.deref().borrow().get_options().sourcemap {
let (calcmap, end) = LocMap::merge(loc.as_ref().unwrap(), &charlist);
*loc = Some(end);
map = Some(calcmap);
}
}
}
match MediaQuery::new(charlist.clone(), start_loc, map.clone(), parent.clone()) {
HandleResult::Success(obj) => {
return Ok(SelectorNode::Media(obj));
}
HandleResult::Fail(msg) => {
return Err(msg);
}
HandleResult::Swtich => {}
};
let obj = NewSelector::new(charlist, start_loc, map, parent, fileinfo);
Ok(SelectorNode::Select(obj))
}
pub fn deserializer(
map: &Map<String, Value>,
parent: NodeWeakRef,
fileinfo: FileWeakRef,
) -> Result<Self, String> {
if parent.is_none() {
return Err("SelectorNode -> parent must not be None!".to_string());
}
let value_type = map.get("type").unwrap().to_string();
if value_type == r#""Select""# {
let value_map = map.get("value").unwrap().as_object().unwrap();
return Ok(SelectorNode::Select(NewSelector::deserializer(
value_map, parent, fileinfo,
)?));
} else if value_type == r#""Media""# {
let value_map = map.get("value").unwrap().as_object().unwrap();
return Ok(SelectorNode::Media(MediaQuery::deserializer(
value_map, parent,
)?));
}
Err("SelectorNode -> noting type is matched".to_string())
}
pub fn value(&self) -> String {
match self {
SelectorNode::Select(obj) => obj.value(),
SelectorNode::Media(obj) => obj.value(),
}
}
pub fn need_css_modules(&self) -> (bool, Option<String>) {
if let Some(file) = self.get_file() {
if file.borrow().modules {
return (true, Some(file.borrow().hash_perfix.clone()));
}
}
(false, None)
}
pub fn get_file(&self) -> Option<FileRef> {
let rule = self.get_rule();
if let Some(rule_heap) = rule {
rule_heap
.borrow()
.file_info
.as_ref()
.map(|file| file.upgrade().unwrap())
} else {
None
}
}
pub fn get_rule(&self) -> Option<NodeRef> {
if let SelectorNode::Select(ss) = &self {
return if ss.parent.is_some() {
Some(ss.parent.as_ref().unwrap().upgrade().unwrap())
} else {
None
};
} else if let SelectorNode::Media(mm) = &self {
return if mm.parent.is_some() {
Some(mm.parent.as_ref().unwrap().upgrade().unwrap())
} else {
None
};
}
None
}
pub fn convert_class_paradigm(
&self,
word: String,
hash_value: &str,
map: &mut HashSet<String>,
) -> String {
let class_word = word[1..word.len()].to_string();
map.insert(class_word);
format!("{}_{}", word, hash_value)
}
pub fn convert_paradigm_to_word(
&self,
list: &Vec<SelectParadigm>,
map: &mut HashSet<String>,
) -> Result<Vec<SelectParadigm>, String> {
let mut new_list = vec![];
let mut index = 0;
let mut has_global = false;
while index < list.len() {
let par = list.get(index).unwrap();
let nextpar = if index + 1 < list.len() {
list.get(index + 1)
} else {
None
};
let prevpar = if index > 0 { list.get(index - 1) } else { None };
match par {
SelectParadigm::SelectWrap(ss) => {
if ss == ":global" {
if nextpar == Some(&SelectParadigm::CominaWrap(TokenCombinaChar::Space)) && !has_global
{
has_global = true;
index += 1;
}
} else if &ss[0..1] == "." && !has_global {
let new_value =
self.convert_class_paradigm(ss.to_string(), "@@@hash_str_replace_value@@@", map);
new_list.push(SelectParadigm::SelectWrap(new_value));
} else if &ss[0..1] == "." && has_global {
new_list.push(par.clone());
} else if &ss[0..1] == "("
&& &ss[ss.len() - 1..ss.len()] == ")"
&& prevpar == Some(&SelectParadigm::SelectWrap(":global".to_string()))
{
let new_value = ss[1..ss.len() - 1].to_string();
new_list.push(SelectParadigm::SelectWrap(new_value));
} else {
new_list.push(par.clone())
}
}
SelectParadigm::CominaWrap(..) | SelectParadigm::KeyWrap(..) => new_list.push(par.clone()),
_ => {
return Err(format!(
"{:#?} \n -> list_paradigm must not include SelectParadigm::VarWrap",
list
));
}
}
index += 1;
}
Ok(new_list)
}
pub fn calc_paradigm(
&self,
list: Vec<Vec<SelectParadigm>>,
css_module_info: (bool, Option<String>),
map: &mut HashSet<String>,
) -> Result<String, String> {
let mut select_res = "".to_string();
let (css_module, _) = css_module_info;
for (index, index_list) in list.iter().enumerate() {
let mut txt = "".to_string();
let calc_index_list = if css_module {
self.convert_paradigm_to_word(index_list, map)?
} else {
index_list.to_owned()
};
for par in calc_index_list.iter() {
match par {
SelectParadigm::SelectWrap(ss) => {
txt += ss;
}
SelectParadigm::CominaWrap(cc) => txt += &cc.to_str().to_string(),
SelectParadigm::KeyWrap(key) => {
txt += key;
}
_ => {
return Err(format!(
"{:#?} \n -> list_paradigm must not include SelectParadigm::VarWrap",
list
));
}
}
}
select_res += &txt;
if index != list.len() - 1 {
select_res += ",";
}
}
Ok(select_res)
}
pub fn code_gen(&self, map: &mut HashSet<String>) -> Result<(String, String), String> {
let mut node = None;
let css_module_info = self.need_css_modules();
if let Self::Select(ss) = &self {
node = ss.parent.clone();
} else if let Self::Media(ss) = &self {
node = ss.parent.clone();
}
let nearly_select_node = NewSelector::find_up_select_node(node.clone());
let nearly_media_node = MediaQuery::find_up_media_node(node);
if nearly_select_node.is_none() && nearly_media_node.is_none() {
return Err(
"codegen select_node -> nearly_select_node || nearly_media_node one of them is not empty!"
.to_string(),
);
}
let mut select_res = "".to_string();
if let Some(snode) = nearly_select_node {
if let SelectorNode::Select(s) = snode.upgrade().unwrap().borrow().selector.as_ref().unwrap()
{
select_res = self.calc_paradigm(s.code_gen()?, css_module_info, map)?;
}
}
let mut media_res = "".to_string();
if let Some(mnode) = nearly_media_node {
if let SelectorNode::Media(m) = mnode.upgrade().unwrap().borrow().selector.as_ref().unwrap() {
media_res = m.code_gen().join(" and ");
}
}
Ok((select_res, media_res))
}
}