use crate::extend::string::StringExtend;
use crate::new_less::fileinfo::FileInfo;
use crate::new_less::filenode::FileNode;
use crate::new_less::node::StyleNode;
use crate::new_less::option::ParseOption;
use serde_json::{Map, Value};
use std::collections::HashMap;
use std::fmt::{Debug, Formatter};
use std::path::Path;
use std::rc::Rc;
use std::sync::{Arc, Mutex, Weak};
use crate::new_less::hash::StyleHash;
pub type ParseCacheMap = Mutex<HashMap<String, String>>;
pub type ParseContext = Arc<Mutex<Context>>;
pub type RenderCacheMap = Mutex<HashMap<String, String>>;
pub struct Context {
pub option: ParseOption,
pub filecache: ParseCacheMap,
pub render_cache: RenderCacheMap,
pub application_fold: String,
pub code_gen_file_path: Vec<String>,
pub weak_ref: Option<Weak<Mutex<Context>>>,
}
impl Debug for Context {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Context")
.field("option", &self.option)
.field("entry", &self.application_fold)
.field("filepaths", &self.code_gen_file_path)
.finish()
}
}
impl Context {
pub fn new(
option: ParseOption,
application_fold: Option<String>,
) -> Result<Arc<Mutex<Self>>, String> {
let mut fold = application_fold.unwrap_or_else(|| {
std::env::current_dir()
.unwrap()
.into_os_string()
.into_string()
.unwrap()
});
let filepath = Path::new(&fold);
if filepath.exists() {
if filepath.is_absolute() {
if filepath.is_file() {
let current_dir = FileInfo::get_dir(&fold)?;
fold = current_dir
} else if !filepath.is_dir() {
return Err(format!("application_fold is not file or dir,{}", fold));
}
} else {
return Err(format!(
"application_fold is must absolutely path ,{}",
fold
));
}
} else {
return Err(format!("application_fold is not exists,{}", fold));
}
let mut obj = Context {
option,
filecache: Mutex::new(HashMap::new()),
render_cache: Mutex::new(HashMap::new()),
application_fold: fold.clone(),
code_gen_file_path: vec![],
weak_ref: None,
};
obj.set_include_paths(vec![fold]);
let heap_obj = Arc::new(Mutex::new(obj));
{
let mut self_value = heap_obj.try_lock().unwrap();
self_value.weak_ref = Some(Arc::downgrade(&heap_obj));
}
Ok(heap_obj)
}
pub fn get_parse_cache(&self, file_path: &str) -> Result<Option<FileNode>, String> {
self.recovery_parse_object(file_path)
}
pub fn set_parse_cache(&mut self, file_path: &str, file_info_json: String) {
let mut filecache = self.filecache.lock().unwrap();
let res = filecache.get(file_path);
if res.is_none() {
filecache.insert(file_path.to_string(), file_info_json);
}
}
pub fn clear_parse_cache(&mut self) {
self.filecache.lock().unwrap().clear();
}
pub fn get_options(&self) -> ParseOption {
self.option.clone()
}
pub fn set_include_paths(&mut self, paths: Vec<String>) {
paths.iter().for_each(|x| {
if !self.option.include_path.contains(x) {
self.option.include_path.push(x.clone());
}
});
}
pub fn add_codegen_record(&mut self, path: &str) {
self.code_gen_file_path.push(path.to_string());
}
pub fn clear_codegen_record(&mut self) {
self.code_gen_file_path.clear();
}
pub fn has_codegen_record(&self, path: &str) -> bool {
self.code_gen_file_path.contains(&path.to_string())
}
pub fn add_render_cache(&mut self, filepath: &str, source: &str) {
self
.render_cache
.lock()
.unwrap()
.insert(filepath.to_string(), source.to_string());
}
pub fn clear_render_cache(&mut self) {
self.render_cache.lock().unwrap().clear();
}
pub fn get_render_cache(&self, filepath: &str) -> Option<String> {
self.render_cache.lock().unwrap().get(filepath).cloned()
}
pub fn default() -> ParseContext {
Self::new(Default::default(), None).unwrap()
}
pub fn recovery_parse_object(&self, key: &str) -> Result<Option<FileNode>, String> {
let filecache = self.filecache.lock().unwrap();
let json_res = filecache.get(key);
if let Some(json) = json_res {
let root: HashMap<String, Value> = serde_json::from_str(&json).unwrap();
return if let Some(Value::Object(map)) = root.get("info") {
let node = self.deserializer(map)?;
Ok(Some(node))
} else {
Err(format!("info value is empty!"))
};
}
Ok(None)
}
fn deserializer(&self, map: &Map<String, Value>) -> Result<FileNode, String> {
let json_disk_location = map.get("disk_location");
let json_origin_txt_content = map.get("origin_txt_content");
let mut obj = FileInfo {
disk_location: "".to_string(),
block_node: vec![],
origin_txt_content: "".to_string(),
origin_charlist: vec![],
locmap: None,
context: self.weak_ref.as_ref().unwrap().upgrade().unwrap().clone(),
self_weak: None,
import_files: vec![],
modules: false,
class_selector_collect: Default::default(),
hash_perfix: "".to_string(),
};
if let Some(Value::String(disk_location)) = json_disk_location {
obj.disk_location = disk_location.to_string();
} else {
return Err(format!("deserializer FileNode -> disk_location is empty!"));
}
let need_modules =
FileNode::is_need_css_modules(obj.disk_location.as_str(), self.option.modules);
obj.modules = need_modules;
if let Some(Value::String(origin_txt_content)) = json_origin_txt_content {
obj.origin_txt_content = origin_txt_content.to_string();
obj.hash_perfix = StyleHash::generate_css_module_hash(&obj.disk_location, &origin_txt_content);
obj.origin_charlist = obj.origin_txt_content.tocharlist();
} else {
return Err(format!(
"deserializer FileNode -> origin_txt_content is empty!"
));
}
if self.option.sourcemap {
obj.locmap = Some(FileInfo::get_loc_by_content(&obj.origin_charlist));
}
let json_import_files = map.get("import_file");
if let Some(Value::Array(disk_location)) = json_import_files {
for json_item in disk_location {
if let Value::Object(json_import_file_node) = json_item {
let import_info = json_import_file_node
.get("info")
.unwrap()
.as_object()
.unwrap();
obj.import_files.push(self.deserializer(import_info)?);
}
}
}
let info = obj.toheap();
let json_block_node = map.get("block_node");
let mut block_node_recovery_list = vec![];
if let Some(Value::Array(block_nodes)) = json_block_node {
for json_node in block_nodes {
if let Value::Object(json_stylenode) = json_node {
block_node_recovery_list.push(StyleNode::deserializer(
json_stylenode,
self.weak_ref.as_ref().unwrap().upgrade().unwrap().clone(),
None,
Some(Rc::downgrade(&info)),
)?);
}
}
}
info.borrow_mut().block_node = block_node_recovery_list;
let node = FileNode { info };
Ok(node)
}
}