summaryrefslogtreecommitdiffstats
path: root/src/parse.rs
blob: fba0c0997dd9f40dac61a12a4c6ddc33cc2d03d1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
use std::path::Path;

/// Given input like
/// "diff --git a/src/main.rs b/src/main.rs"
/// Return "rs", i.e. a single file extension consistent with both files.
pub fn get_file_extension_from_diff_line(line: &str) -> Option<&str> {
    match get_file_extensions_from_diff_line(line) {
        (Some(_ext1), Some(ext2)) => {
            // If they differ then it's a rename; use the new extension.
            Some(ext2)
        }
        (Some(ext1), None) => Some(ext1),
        (None, Some(ext2)) => Some(ext2),
        (None, None) => None,
    }
}

pub fn get_file_path_from_file_meta_line(line: &str) -> String {
    if line.starts_with("rename") {
        match line.split(' ').nth(2) {
            Some(path) => path,
            _ => "",
        }
        .to_string()
    } else {
        match line.split(' ').nth(1) {
            Some("/dev/null") => "/dev/null",
            Some(path) => &path[2..],
            _ => "",
        }
        .to_string()
    }
}

pub fn get_file_change_description_from_file_paths(minus_file: &str, plus_file: &str) -> String {
    match (minus_file, plus_file) {
        (minus_file, plus_file) if minus_file == plus_file => minus_file.to_string(),
        (minus_file, "/dev/null") => format!("deleted: {}", minus_file),
        ("/dev/null", plus_file) => format!("added: {}", plus_file),
        (minus_file, plus_file) => format!("renamed: {} ⟶   {}", minus_file, plus_file),
    }
}

/// Given input like
/// "@@ -74,15 +74,14 @@ pub fn delta("
/// Return " pub fn delta("
pub fn parse_hunk_metadata(line: &str) -> (&str, &str) {
    let mut iter = line.split("@@").skip(1);
    let line_number = iter
        .next()
        .and_then(|s| s.split('+').nth(1).and_then(|s| s.split(',').next()))
        .unwrap_or("");
    let code_fragment = iter.next().unwrap_or("");
    (code_fragment, line_number)
}

/// Given input like "diff --git a/src/main.rs b/src/main.rs"
/// return ("rs", "rs").
fn get_file_extensions_from_diff_line(line: &str) -> (Option<&str>, Option<&str>) {
    let mut iter = line.split(' ').skip(2);
    (
        iter.next().and_then(|s| get_extension(&s[2..])),
        iter.next().and_then(|s| get_extension(&s[2..])),
    )
}

/// Attempt to parse input as a file path and return extension as a &str.
fn get_extension(s: &str) -> Option<&str> {
    let path = Path::new(s);
    path.extension()
        .and_then(|e| e.to_str())
        // E.g. 'Makefile' is the file name and also the extension
        .or_else(|| path.file_name().and_then(|s| s.to_str()))
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_get_file_extension_from_diff_line() {
        assert_eq!(
            get_file_extension_from_diff_line("diff --git a/src/main.rs b/src/main.rs"),
            Some("rs")
        );
    }

    #[test]
    fn test_get_file_path_from_file_meta_line() {
        assert_eq!(
            get_file_path_from_file_meta_line("--- a/src/delta.rs"),
            "src/delta.rs"
        );
        assert_eq!(
            get_file_path_from_file_meta_line("+++ b/src/delta.rs"),
            "src/delta.rs"
        );
    }

    #[test]
    fn test_parse_hunk_metadata() {
        assert_eq!(
            parse_hunk_metadata("@@ -74,15 +75,14 @@ pub fn delta(\n"),
            (" pub fn delta(\n", "75")
        );
    }
}