diff --git a/rp/src/main.rs b/rp/src/main.rs index cc6397382432f2941d5feb80ac909aec8448c376..5c9ae1f9764cd8ce908887da5ac828208fbcacb3 100644 --- a/rp/src/main.rs +++ b/rp/src/main.rs @@ -1,9 +1,34 @@ -use std::{ - env, - fs::File, - io::{BufRead}, -}; +/*! +本项目是用rust解析patch文件,开发文档请参考:https://openeuler.feishu.cn/docs/doccn1seRdc9f8V7nqATtAy3ulf + +USAGE: + rp -- + +Arguments: + The file to apply patch + +Options: + -c Interpret the patch file as a ordinary context diff + -u Interpret the patch file as a unified context diff + -e Interpret the patch file as an ed script + -n Interpret the patch file as a normal diff + -o Output patched files to FILE + -i Read patch from PATCH FILE instead of stdin + -h, --help Print help information + -V, --version Print version information + +例如: + +```bash + +$ rp -c -i context.patch -- file_to_apply +$ rp -u -i uni.patch -- file_to_apply + +``` +*/ + use std::io::Write; +use std::{env, fs::File, io::BufRead}; use clap::Parser; use regex::Regex; @@ -75,12 +100,12 @@ fn main() { } to_patch_array } - _ => { Vec::new() } + _ => Vec::new(), }; let patch_str_array = match args.input { Some(val) => { - let mut path = current_dir.clone(); + let mut path = current_dir; path.push(val); let patch_file = File::open(path).unwrap(); @@ -97,7 +122,8 @@ fn main() { }; let res_file = if args.context { - todo!("parse and apply context patch") + parse_context(patch_str_array, &mut file_to_patch); + file_to_patch } else if args.unified { let pat: Patch = parse_unified(patch_str_array); apply_unified_patch(pat, file_to_patch) @@ -116,7 +142,9 @@ fn main() { let mut output = File::create(path).expect("Unable to create file"); for line in res_file { let tmp = line + "\n"; - output.write_all((*tmp).as_ref()).expect("Unable to write file"); + output + .write_all((*tmp).as_ref()) + .expect("Unable to write file"); } } _ => { @@ -135,12 +163,22 @@ fn parse_unified(patch_arr: Vec) -> Patch { }; for str in &patch_arr { if str.starts_with("--- ") { - patch.file_old = str.split(" ").collect::>().get(1). - unwrap().parse().unwrap(); + patch.file_old = str + .split(' ') + .collect::>() + .get(1) + .unwrap() + .parse() + .unwrap(); } else if str.starts_with("+++ ") { - patch.file_new = str.split(" ").collect::>().get(1). - unwrap().parse().unwrap(); - } else if patch.file_new != "" && patch.file_old != "" { + patch.file_new = str + .split(' ') + .collect::>() + .get(1) + .unwrap() + .parse() + .unwrap(); + } else if !patch.file_new.is_empty() && !patch.file_old.is_empty() { break; } } @@ -180,7 +218,7 @@ fn parse_unified(patch_arr: Vec) -> Patch { if index == 0 { patch.huck.push(huck.clone()); } - }; + } patch } @@ -193,24 +231,28 @@ fn parse_normal(patch_arr: Vec, file_to_patch: &mut Vec) { let mut opr_1: i32 = 0; let mut opr_2: i32 = -1; for (index, line) in patch_arr.iter().enumerate() { - if re.is_match(&line) { - for cap in re.captures_iter(&line) { + if re.is_match(line) { + for cap in re.captures_iter(line) { op = cap[3].parse().unwrap(); opl_1 = cap[1].parse().unwrap(); - if cap.get(2) != None { + if Option::is_some(&cap.get(2)) { opl_2 = cap[2].parse().unwrap(); - } else { opl_2 = -1; } + } else { + opl_2 = -1; + } opr_1 = cap[4].parse().unwrap(); - if cap.get(5) != None { + if Option::is_some(&cap.get(5)) { opr_2 = cap[5].parse().unwrap(); - } else { opr_2 = -1; } + } else { + opr_2 = -1; + } } match op { 'd' => { if opl_2 != -1 { - file_to_patch.drain((opl_1 + off_set - 1) - as usize..(opl_2 + off_set) as usize); - off_set = off_set - (opl_2 - opl_1 + 1); + file_to_patch + .drain((opl_1 + off_set - 1) as usize..(opl_2 + off_set) as usize); + off_set -= opl_2 - opl_1 + 1; } else { file_to_patch.remove((opl_1 + off_set) as usize); off_set -= 1; @@ -223,13 +265,17 @@ fn parse_normal(patch_arr: Vec, file_to_patch: &mut Vec) { } let change; if opl_2 != -1 { - change = Vec::from(&patch_arr[(index as i32 + 3 + opl_2 - opl_1) as usize.. - (cap + index as i32 + 3 + opl_2 - opl_1) as usize]); - file_to_patch.drain((opl_1 + off_set - 1) - as usize..(opl_2 + off_set) as usize); + change = Vec::from( + &patch_arr[(index as i32 + 3 + opl_2 - opl_1) as usize + ..(cap + index as i32 + 3 + opl_2 - opl_1) as usize], + ); + file_to_patch + .drain((opl_1 + off_set - 1) as usize..(opl_2 + off_set) as usize); } else { - change = Vec::from(&patch_arr[(index as i32 + 3) as usize.. - (cap + index as i32 + 3) as usize]); + change = Vec::from( + &patch_arr + [(index as i32 + 3) as usize..(cap + index as i32 + 3) as usize], + ); file_to_patch.remove((opl_1 + off_set - 1) as usize); } let mut index_change = opl_1 + off_set - 1; @@ -241,14 +287,19 @@ fn parse_normal(patch_arr: Vec, file_to_patch: &mut Vec) { } index_change += 1; } - off_set += if opl_2 == -1 { -2 + cap } else { opl_2 - opl_1 + 1 - cap }; + off_set += if opl_2 == -1 { + -2 + cap + } else { + opl_2 - opl_1 + 1 - cap + }; } 'a' => { let mut cap = 1; if opr_2 != -1 { cap = opr_2 - opr_1 + 1; } - let change = Vec::from(&patch_arr[index + 1..(cap + index as i32 + 1) as usize]); + let change = + Vec::from(&patch_arr[index + 1..(cap + index as i32 + 1) as usize]); let mut index_change = opl_1 + off_set + 1; for line in change { if index_change > file_to_patch.len() as i32 { @@ -278,9 +329,11 @@ fn parse_ed(patch_arr: Vec, file_to_patch: &mut Vec) { for cap in re.captures_iter(&line) { start_line = cap[1].parse().unwrap(); op = cap[3].parse().unwrap(); - if cap.get(2) != None { + if Option::is_some(&cap.get(2)) { end_line = cap[2].parse().unwrap(); - } else { end_line = -1; } + } else { + end_line = -1; + } } } else if !line.eq(".") { match op { @@ -363,3 +416,377 @@ fn apply_unified_patch(patch: Patch, tp: Vec) -> Vec { } file_to_patch } + +fn parse_context(patch_arr: Vec, file_to_patch: &mut Vec) { + let mut off_set: i32 = 0; + let re_from = Regex::new(r"^\*\*\* ([0-9]+),([0-9]+) \*\*\*\*$").unwrap(); + let re_to = Regex::new(r"^--- ([0-9]+),([0-9]+) ----$").unwrap(); + for (index, line) in patch_arr.iter().enumerate() { + let mut op_1 = 0; + let mut op_2 = 0; + let mut has_change_seg = true; + if re_from.is_match(line) { + for cap in re_from.captures_iter(line) { + op_1 = cap[1].parse().unwrap(); + op_2 = cap[2].parse().unwrap(); + } + if re_to.is_match(patch_arr.get(index + 1).unwrap()) { + has_change_seg = false; + } else if re_to.is_match(patch_arr.get(index + op_2 as usize).unwrap()) { + has_change_seg = true; + } + off_set = apply_context_huck( + index as u32, + off_set, + &patch_arr, + file_to_patch, + op_1, + op_2, + has_change_seg, + ); + } + } +} + +fn apply_context_huck( + index: u32, + off_set: i32, + patch_arr: &Vec, + file_to_patch: &mut Vec, + op_1: u32, + op_2: u32, + has_change_seg: bool, +) -> i32 { + let mut change_count = 0; + let mut off_set = off_set; + let mut index = index; + if !has_change_seg { + index += 2; + let mut times = 0; + let mut line = patch_arr.get(index as usize).unwrap().to_string(); + while line != "***************" && ((times + index) as usize) < patch_arr.len() { + if line.starts_with("- ") { + file_to_patch.remove(((op_1 + times) as i32 + off_set - 1) as usize); + off_set -= 1; + } + if let Some(line) = line.strip_prefix("+ ") { + if ((op_1 + times) as i32 + off_set) as usize >= file_to_patch.len() { + file_to_patch.push(line.to_string()) + } else { + file_to_patch + .insert(((op_1 + times) as i32 + off_set) as usize, line.to_string()); + } + off_set += 1; + } + times += 1; + if ((times + index) as usize) < patch_arr.len() { + line = patch_arr.get((index + times) as usize).unwrap().to_string(); + } + } + } else { + let mut index_change = index + op_2 - op_1 + 3; + let mut in_change = false; + let mut line = patch_arr.get(index_change as usize).unwrap().to_string(); + let mut changes_vec: Vec = Vec::new(); + while line != "***************" && (index_change as usize) < patch_arr.len() { + if line.starts_with("! ") { + if !in_change { + changes_vec.push((index_change as i32) as u32); + } + in_change = true; + } else { + in_change = false; + } + index_change += 1; + if ((index_change) as usize) < patch_arr.len() { + line = patch_arr.get((index_change) as usize).unwrap().to_string(); + } + } + index += 1; + let mut times = 0; + let mut line = patch_arr.get(index as usize).unwrap().to_string(); + let re_to = Regex::new(r"^--- ([0-9]+),([0-9]+) ----$").unwrap(); + while line != "***************" + && !re_to.is_match(&line) + && ((times + index) as usize) < patch_arr.len() + { + if line.starts_with("- ") { + file_to_patch.remove(((op_1 + times) as i32 + off_set - 1) as usize); + off_set -= 1; + } + if let Some(line) = line.strip_prefix("+ ") { + if ((op_1 + times) as i32 + off_set) as usize >= file_to_patch.len() { + file_to_patch.push(line.to_string()) + } else { + file_to_patch + .insert(((op_1 + times) as i32 + off_set) as usize, line.to_string()); + } + off_set += 1; + } + if line.starts_with("! ") { + let mut change_start = *changes_vec.get(change_count as usize).unwrap(); + let mut change_vec: Vec = Vec::new(); + let mut to_change_end_line = + patch_arr.get(change_start as usize).unwrap().to_string(); + while to_change_end_line.starts_with("! ") + && (change_start as usize) < patch_arr.len() + { + change_vec.push(to_change_end_line.clone()[2..].to_string()); + change_start += 1; + if ((change_start) as usize) < patch_arr.len() { + to_change_end_line = + patch_arr.get(change_start as usize).unwrap().to_string(); + } + } + let mut end = index + times; + let to_ins = index + times; + while line.starts_with("! ") && (end as usize) < patch_arr.len() { + end += 1; + times += 1; + if ((times + index) as usize) < patch_arr.len() { + line = patch_arr.get((index + times) as usize).unwrap().to_string(); + } + } + times -= 1; + let mut start = (op_1 + times) as i32 + off_set - 1; + off_set += change_vec.len() as i32 - (end - to_ins) as i32; + file_to_patch.drain(start as usize..(start + (end - to_ins) as i32) as usize); + if start as usize >= file_to_patch.len() { + file_to_patch.append(&mut change_vec); + } else { + for ins in change_vec { + file_to_patch.insert(start as usize, ins); + start += 1; + } + } + change_count += 1; + } + times += 1; + if ((times + index) as usize) < patch_arr.len() { + line = patch_arr.get((index + times) as usize).unwrap().to_string(); + } + } + } + off_set +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_ed_script() { + let mut input_fle = vec![ + "The Way that can be told of is not the eternal Way;".to_string(), + "The name that can be named is not the eternal name.".to_string(), + "The Nameless is the origin of Heaven and Earth;".to_string(), + "The Named is the mother of all things.".to_string(), + "Therefore let there always be non-being,".to_string(), + " so we may see their subtlety,".to_string(), + "And let there always be being,".to_string(), + " so we may see their outcome.".to_string(), + "The two are the same,".to_string(), + "But after they are produced,".to_string(), + " they have different names.".to_string(), + ]; + let patch = vec![ + "11a".to_string(), + "They both may be called deep and profound.".to_string(), + "Deeper and more profound,".to_string(), + "The door of all subtleties!".to_string(), + ".".to_string(), + "4c".to_string(), + "The named is the mother of all things.".to_string(), + "".to_string(), + ".".to_string(), + "1,2d".to_string(), + ]; + let out_put = vec![ + "The Nameless is the origin of Heaven and Earth;", + "The named is the mother of all things.", + "", + "Therefore let there always be non-being,", + " so we may see their subtlety,", + "And let there always be being,", + " so we may see their outcome.", + "The two are the same,", + "But after they are produced,", + " they have different names.", + "They both may be called deep and profound.", + "Deeper and more profound,", + "The door of all subtleties!", + ]; + parse_ed(patch, &mut input_fle); + assert_eq!(input_fle, out_put); + } + + #[test] + fn test_parse_normal() { + let mut input_fle = vec![ + "The Way that can be told of is not the eternal Way;".to_string(), + "The name that can be named is not the eternal name.".to_string(), + "The Nameless is the origin of Heaven and Earth;".to_string(), + "The Named is the mother of all things.".to_string(), + "Therefore let there always be non-being,".to_string(), + " so we may see their subtlety,".to_string(), + "And let there always be being,".to_string(), + " so we may see their outcome.".to_string(), + "The two are the same,".to_string(), + "But after they are produced,".to_string(), + " they have different names.".to_string(), + ]; + let out_put = vec![ + "The Nameless is the origin of Heaven and Earth;", + "The named is the mother of all things.", + "", + "Therefore let there always be non-being,", + " so we may see their subtlety,", + "And let there always be being,", + " so we may see their outcome.", + "The two are the same,", + "But after they are produced,", + " they have different names.", + "They both may be called deep and profound.", + "Deeper and more profound,", + "The door of all subtleties!", + ]; + let normal_patch = vec![ + "1,2d0".to_string(), + "< The Way that can be told of is not the eternal Way;".to_string(), + "< The name that can be named is not the eternal name.".to_string(), + "4c2,3".to_string(), + "< The Named is the mother of all things.".to_string(), + "---".to_string(), + "> The named is the mother of all things.".to_string(), + "> ".to_string(), + "11a11,13".to_string(), + "> They both may be called deep and profound.".to_string(), + "> Deeper and more profound,".to_string(), + "> The door of all subtleties!".to_string(), + ]; + parse_normal(normal_patch, &mut input_fle); + assert_eq!(input_fle, out_put); + } + + #[test] + fn test_parse_unified() { + let input_fle = vec![ + "The Way that can be told of is not the eternal Way;".to_string(), + "The name that can be named is not the eternal name.".to_string(), + "The Nameless is the origin of Heaven and Earth;".to_string(), + "The Named is the mother of all things.".to_string(), + "Therefore let there always be non-being,".to_string(), + " so we may see their subtlety,".to_string(), + "And let there always be being,".to_string(), + " so we may see their outcome.".to_string(), + "The two are the same,".to_string(), + "But after they are produced,".to_string(), + " they have different names.".to_string(), + ]; + let out_put = vec![ + "The Nameless is the origin of Heaven and Earth;", + "The named is the mother of all things.", + "", + "Therefore let there always be non-being,", + " so we may see their subtlety,", + "And let there always be being,", + " so we may see their outcome.", + "The two are the same,", + "But after they are produced,", + " they have different names.", + "They both may be called deep and profound.", + "Deeper and more profound,", + "The door of all subtleties!", + ]; + let unified_patch = vec![ + "--- lao 2022-11-04 14:41:40.495706560 +0800".to_string(), + "+++ tzu 2022-11-16 16:58:28.009384953 +0800".to_string(), + "@@ -1,7 +1,6 @@".to_string(), + "-The Way that can be told of is not the eternal Way;".to_string(), + "-The name that can be named is not the eternal name.".to_string(), + " The Nameless is the origin of Heaven and Earth;".to_string(), + "-The Named is the mother of all things.".to_string(), + "+The named is the mother of all things.".to_string(), + "+".to_string(), + " Therefore let there always be non-being,".to_string(), + " so we may see their subtlety,".to_string(), + " And let there always be being,".to_string(), + "@@ -9,3 +8,6 @@".to_string(), + " The two are the same,".to_string(), + " But after they are produced,".to_string(), + " they have different names.".to_string(), + "+They both may be called deep and profound.".to_string(), + "+Deeper and more profound,".to_string(), + "+The door of all subtleties!".to_string(), + ]; + let pat: Patch = parse_unified(unified_patch); + let old = pat.file_old.clone(); + let new = pat.file_new.clone(); + assert_eq!(apply_unified_patch(pat, input_fle), out_put); + assert_eq!(new, "tzu"); + assert_eq!(old, "lao"); + } + + #[test] + fn test_parce_contest() { + let mut input_fle = vec![ + "The Way that can be told of is not the eternal Way;".to_string(), + "The name that can be named is not the eternal name.".to_string(), + "The Nameless is the origin of Heaven and Earth;".to_string(), + "The Named is the mother of all things.".to_string(), + "Therefore let there always be non-being,".to_string(), + " so we may see their subtlety,".to_string(), + "And let there always be being,".to_string(), + " so we may see their outcome.".to_string(), + "The two are the same,".to_string(), + "But after they are produced,".to_string(), + " they have different names.".to_string(), + ]; + let out_put = vec![ + "The Nameless is the origin of Heaven and Earth;", + "The named is the mother of all things.", + "", + "Therefore let there always be non-being,", + " so we may see their subtlety,", + "And let there always be being,", + " so we may see their outcome.", + "The two are the same,", + "But after they are produced,", + " they have different names.", + "They both may be called deep and profound.", + "Deeper and more profound,", + "The door of all subtleties!", + ]; + let context = vec![ + "*** lao 2022-11-04 14:41:40.495706560 +0800".to_string(), + "--- tzu 2022-11-04 14:42:09.495709324 +0800".to_string(), + "***************".to_string(), + "*** 1,7 ****".to_string(), + "- The Way that can be told of is not the eternal Way;".to_string(), + "- The name that can be named is not the eternal name.".to_string(), + " The Nameless is the origin of Heaven and Earth;".to_string(), + "! The Named is the mother of all things.".to_string(), + " Therefore let there always be non-being,".to_string(), + " so we may see their subtlety,".to_string(), + " And let there always be being,".to_string(), + "--- 1,6 ----".to_string(), + " The Nameless is the origin of Heaven and Earth;".to_string(), + "! The named is the mother of all things.".to_string(), + "! ".to_string(), + " Therefore let there always be non-being,".to_string(), + " so we may see their subtlety,".to_string(), + " And let there always be being,".to_string(), + "***************".to_string(), + "*** 9,11 ****".to_string(), + "--- 8,13 ----".to_string(), + " The two are the same,".to_string(), + " But after they are produced,".to_string(), + " they have different names.".to_string(), + "+ They both may be called deep and profound.".to_string(), + "+ Deeper and more profound,".to_string(), + "+ The door of all subtleties!".to_string(), + ]; + parse_context(context, &mut input_fle); + assert_eq!(input_fle, out_put); + } +}