场景 如下示例代码,将 PEAR_BEGIN 和 PEAR_END 解析出来并缓存,下次再插入文件原位置 function add() { let a = 1; let b = 2; let c = 3; let d = 5; let e = 0; // PEAR_BEGIN:测试代码1 // 这是测试代码 // 可以随便放东西 let q = 100; let p = 200; c = q + p; console.log('result---'); // PEAR_END for (let i = 0; i < 100; i++) { e += i; // PEAR_BEGIN:测试代码2 let name = 'alex'; // PEAR_END } return e; } 解析后的数据: [ { "id": "16", "name": "测试代码1", "line": 9, "file_path": "target/code1.js", "total_dev_line": 8, "content": " // PEAR_BEGIN:测试代码1\n // 这是测试代码\n // 可以随便放东西\n let q = 100;\n let p = 200;\n c = q + p;\n console.log(\"result---\");\n // PEAR_END" }, { "id": "21", "name": "测试代码2", "line": 19, "file_path": "target/code1.js", "total_dev_line": 3, "content": " // PEAR_BEGIN:测试代码2\n let name = \"alex\";\n // PEAR_END" } ] 代码实现 //! 按行插入指定内容 use anyhow::{anyhow, Result}; use serde::{Deserialize, Serialize}; const DEV_BEGIN_SIGN: &'static str = "// PEAR_BEGIN:"; const SOURCE: &'static str = "target/code1.js"; const DB_FILE: &'static str = "target/code.db"; fn main() { // stop_dev(SOURCE).unwrap(); start_dev().unwrap(); } /// 开始开发模式 插入开发代码块到源文件中 pub fn start_dev() -> Result<()> { let db_content = std::fs::read_to_string(DB_FILE)?; let db_data: Vec<PearBlock> = serde_json::from_str(db_content.as_str())?; let all_files = get_all_file_from_db(&db_data); let mut errs: Vec<String> = Vec::new(); all_files.iter().for_each(|file_path| { let dev_blocks = db_data.iter().filter(|item| { item.file_path.as_str().eq(file_path.as_str()) }).collect::<Vec<&PearBlock>>(); if let Err(err) = insert_dev_data_to_file(file_path.as_str(), &dev_blocks) { errs.push(format!("文件还原失败: {:?}", err)); } }); if errs.len() > 0 { return Err(anyhow!("{}", errs.join("\n"))); } Ok(()) } /// 结束开发模式 保存开发代码块到数据库 pub fn stop_dev(source_file_path: &str) -> Result<()> { let source_file_data = parse_source_file_data(source_file_path)?; revert_source_file(&source_file_data)?; write_to_db(&source_file_data)?; Ok(()) } pub fn insert_dev_data_to_file(file_path: &str, dev_blocks: &Vec<&PearBlock>) -> Result<()> { let mut source_content: String = String::from(""); if let Ok(item) = std::fs::read_to_string(file_path) { source_content = item; } let mut source_content_arr: Vec<String> = Vec::new(); let mut line_index = 0u32; source_content.split("\n").for_each(|line| { if let Some(block) = dev_blocks.iter().find(|item| { item.line == line_index }) { block.content.split("\n").for_each(|i| { source_content_arr.push(String::from(i)); }); line_index += block.total_dev_line; } source_content_arr.push(String::from(line)); line_index += 1; }); let contents = source_content_arr.join("\n"); std::fs::write(file_path, contents.as_str())?; Ok(()) } pub fn get_all_file_from_db(db_data: &[PearBlock]) -> Vec<String> { let mut all_files: Vec<String> = Vec::new(); db_data.iter().for_each(|item| { let target = all_files.iter().find(|n| { n.as_str().eq(item.file_path.as_str()) }); if target.is_none() { all_files.push(String::from(item.file_path.as_str())); } }); all_files } pub fn write_to_db(source_file_data: &SourceFileData) -> Result<String> { let contents = serde_json::to_string_pretty(&source_file_data.dev_blocks)?; std::fs::write(DB_FILE, contents.as_str())?; Ok(String::from("写入数据库成功")) } pub fn revert_source_file(source_file_data: &SourceFileData) -> Result<String> { let file_path = source_file_data.file_path.as_str(); std::fs::write(file_path, source_file_data.contents.as_str())?; Ok(String::from("源文件已还原")) } /// 查找指定文件所有的开发代码块 pub fn parse_source_file_data(source_file_path: &str) -> Result<SourceFileData> { let source_content = std::fs::read_to_string(source_file_path)?; // 当前文件所有代码块数据 let mut blocks: Vec<PearBlock> = Vec::new(); // 源文件中有效内容 let mut normal_code_source: Vec<String> = Vec::new(); // 临时存储一个代码块内容 let mut block_contents: Vec<String> = Vec::new(); // 开发代码块起始行 let mut block_begin_line: i32 = -1; // 开发代码块名称 let mut block_name = String::new(); // 当前行索引 let mut line_number = 0u32; source_content.split("\n").for_each(|line| { println!("{:?}", line); match get_line_type(line, block_begin_line) { LineType::DevBegin => { block_begin_line = line_number as i32; block_name = get_dev_name(line); block_contents.push(String::from(line)); }, LineType::DevEnd => { block_contents.push(String::from(line)); blocks.push(PearBlock { id: format!("{}", line_number), name: String::from(block_name.as_str()), line: block_begin_line as u32, file_path: String::from(source_file_path), total_dev_line: block_contents.len() as u32, content: block_contents.join("\n"), }); block_begin_line = -1; block_name = String::from(""); block_contents.clear(); }, LineType::DevCode => { block_contents.push(String::from(line)); }, LineType::Normal => { normal_code_source.push(String::from(line)); } } line_number += 1; }); let file_data = SourceFileData { file_path: String::from(source_file_path), contents: normal_code_source.join("\n"), dev_blocks: blocks, }; Ok(file_data) } /// 获取当前行类型 pub fn get_line_type(line: &str, block_begin_line: i32) -> LineType { if is_dev_start(line) { return LineType::DevBegin; } if is_dev_end(line) { return LineType::DevEnd; } if block_begin_line >= 0 { return LineType::DevCode; } LineType::Normal } pub fn get_dev_name(line: &str) -> String { if !is_dev_start(line) { return String::from(""); } let name = line.trim().replace(DEV_BEGIN_SIGN, ""); name } pub fn is_dev_start(line: &str) -> bool { if line.contains(DEV_BEGIN_SIGN) { return true; } false } pub fn is_dev_end(line: &str) -> bool { if line.contains("// PEAR_END") { return true; } false } /// 代码行类型 pub enum LineType { // 开发开始 DevBegin, // 开发代码结束 DevEnd, // 开发代码块 DevCode, // 正常代码 Normal, } /// 开发代码分离后的文档内容 #[derive(Debug)] pub struct SourceFileData { /// 源文件路径 pub file_path: String, /// 源文件内容 pub contents: String, /// 开发代码块数据 pub dev_blocks: Vec<PearBlock>, } /// 代码块数据 #[derive(Debug, Deserialize, Serialize)] pub struct PearBlock { /// 代码块ID pub id: String, /// 代码块自定义名称 pub name: String, /// 对应的行 pub line: u32, /// 对应的文件路径 pub file_path: String, /// 开发代码块总行数 pub total_dev_line: u32, /// 代码内容 pub content: String, }