1const HEADERS: &[&str] = &[
22 "*** Add File:",
23 "*** Update File:",
24 "*** Delete File:",
25 "*** Move to:",
26];
27
28#[must_use]
30pub fn writes(text: &str) -> Vec<String> {
31 let mut paths: Vec<String> = Vec::new();
32 for line in text.lines() {
33 let line = line.trim();
34 let Some(path) = HEADERS
35 .iter()
36 .find_map(|h| strip_header(line, h))
37 .map(str::trim)
38 .filter(|p| !p.is_empty())
39 else {
40 continue;
41 };
42 if !paths.iter().any(|p| p == path) {
43 paths.push(path.to_owned());
44 }
45 }
46 paths
47}
48
49fn strip_header<'a>(line: &'a str, header: &str) -> Option<&'a str> {
51 let head = line.get(..header.len())?;
52 head.eq_ignore_ascii_case(header)
53 .then(|| &line[header.len()..])
54}
55
56#[cfg(test)]
57mod tests {
58 use super::*;
59
60 #[test]
61 fn every_header_names_a_written_file() {
62 let patch = "*** Begin Patch\n*** Add File: src/new.rs\n+fn x() {}\n\
63 *** Update File: src/lib.rs\n*** Move to: src/core.rs\n@@\n-a\n+b\n\
64 *** Delete File: old.txt\r\n*** Update File: src/lib.rs\n*** End Patch\n";
65 assert_eq!(
66 writes(patch),
67 ["src/new.rs", "src/lib.rs", "src/core.rs", "old.txt"]
68 );
69 }
70
71 #[test]
72 fn added_lines_that_look_like_headers_are_content() {
73 assert_eq!(writes("*** Add File: a\n+*** Add File: ~/.zshrc\n"), ["a"]);
75 }
76
77 #[test]
78 fn indented_and_differently_cased_headers_are_writes() {
79 let patch = "*** Begin Patch\n*** Add File: a\n+x\n *** Delete File: ~/.ssh/id_rsa\n\
82 \t*** update file: ~/.zshrc\n*** End Patch\n";
83 assert_eq!(writes(patch), ["a", "~/.ssh/id_rsa", "~/.zshrc"]);
84 }
85
86 #[test]
87 fn headers_outside_the_envelope_still_count() {
88 let patch = "*** Begin Patch\n*** Add File: a\n+x\n*** End Patch\n*** Delete File: b\n";
89 assert_eq!(writes(patch), ["a", "b"]);
90 }
91
92 #[test]
93 fn a_root_path_is_passed_on_for_the_engine_to_decide() {
94 assert_eq!(writes("*** Delete File: /\n"), ["/"]);
95 }
96
97 #[test]
98 fn no_headers_means_no_files() {
99 assert!(writes("just text").is_empty());
100 assert!(writes("*** Add File: \n").is_empty());
101 }
102}