use crate::error::AppError;
use log::warn;
use regex::Regex;
use serde_json::map::Entry;
use serde_json::Value;
use serde_json_path::JsonPath;
use std::collections::HashSet;
use std::path::{Component, Path, PathBuf};
pub fn get_matched_file_paths(
from: &Path,
ignore_dirs: &Option<Vec<String>>,
) -> Result<Vec<String>, AppError> {
let real_ignore = match ignore_dirs {
Some(value) => value,
None => &vec![
".DS_Store".to_string(),
".git".to_string(),
".idea".to_string(),
".vscode".to_string(),
],
};
let mut matched_paths = HashSet::new();
matched_paths.insert(PathBuf::new());
for cpt in from.components() {
let mut tmp_paths = HashSet::new();
if matched_paths.is_empty() {
panic!(
"`{}` 路径错误; 可能是路径中有不存在的文件夹导致!",
from.display()
);
}
for path in matched_paths {
let new_path = path.join(cpt);
if new_path.exists() {
tmp_paths.insert(new_path);
continue;
}
let cpt_to_str = cpt.as_os_str().to_str().unwrap();
let rex = Regex::new(cpt_to_str).unwrap();
if path.is_file() {
panic!(
"在 `{}` 出遇到问题,可能是路径中间有文件导致, 完整匹配为 `{}`",
path.display(),
from.display()
);
}
path.read_dir()
.map_err(|e| AppError::DirectoryError(format!("读取目录失败: {}", e)))?
.filter_map(|item| {
item.ok().and_then(|dir_entry| {
dir_entry
.file_name()
.into_string()
.ok()
.and_then(|file_name| {
if real_ignore.contains(&file_name) {
None
} else if rex.find(&file_name)?.as_str() == file_name {
Some(file_name)
} else {
None
}
})
})
})
.for_each(|file_name| {
tmp_paths.insert(path.join(file_name));
})
}
matched_paths = tmp_paths;
}
Ok(matched_paths
.iter()
.map(|item| item.to_str().unwrap().to_string())
.collect::<Vec<String>>())
}
pub fn format_to_path_by_regexp(from_rex: &str, to_rex: &str, path: &str) -> String {
let rex = Regex::new(from_rex).unwrap();
let mut result = String::new();
let caps = rex.captures(path).unwrap();
caps.expand(to_rex, &mut result);
result
}
pub fn replace_with<F: FnMut(&str, Value) -> Option<Value>>(
replace_path: &str,
mut value: Value,
func: &mut F,
) -> Result<Value, AppError> {
let json_path = JsonPath::parse(replace_path)
.map_err(|_| AppError::JsonPathParse(replace_path.to_string()))?;
let pointers = json_path
.query_located(&value)
.locations()
.map(|l| l.to_json_pointer())
.collect::<Vec<String>>();
for pointer in pointers {
let path_vec = pointer
.split("/")
.filter(|v| !v.is_empty())
.collect::<Vec<&str>>();
let last_index = path_vec.len().saturating_sub(1);
let mut target = &mut value;
for index in 0..path_vec.len() {
let key = path_vec[index];
let is_last = index == last_index;
let target_once = target;
let target_next = match *target_once {
Value::Object(ref mut obj) => {
if is_last {
if let Entry::Occupied(mut e) = obj.entry(key) {
let v = e.insert(Value::Null);
if let Some(res) = func(&pointer, v) {
e.insert(res);
} else {
e.remove();
}
} else {
warn!("这个路径 ({}) 将会获取到空 entry, 跳过该路径!", &pointer);
}
None
} else if let Some(tmp) = obj.get_mut(key) {
Some(tmp)
} else {
return Err(AppError::InvalidObjectPath(pointer.to_string()));
}
}
Value::Array(ref mut arr) => {
if let Ok(i) = key.parse::<usize>() {
if is_last {
let v = std::mem::replace(&mut arr[i], Value::Null);
if let Some(res) = func(&pointer, v) {
arr[i] = res;
} else {
arr.remove(i);
}
None
} else if let Some(tmp) = arr.get_mut(i) {
Some(tmp)
} else {
return Err(AppError::InvalidArrayPath(pointer.to_string()));
}
} else {
return Err(AppError::InvalidIndex(key.to_string()));
}
}
_ => None,
};
if let Some(ret) = target_next {
target = ret;
} else {
break;
}
}
}
Ok(value)
}
pub fn normalize_path(path: &Path) -> PathBuf {
let mut tmp = PathBuf::new();
for component in path.components() {
match component {
Component::ParentDir => {
tmp.pop();
}
Component::CurDir => {}
_ => {
tmp.push(component);
}
}
}
tmp
}
pub fn path_to_normalized_str(path: &Path) -> Option<String> {
normalize_path(path).to_str().map(|s| s.to_string())
}
pub trait Normalize {
fn normalize(&self) -> Self;
}
impl Normalize for PathBuf {
fn normalize(&self) -> Self {
normalize_path(&self)
}
}