diff_man/
diff.rs

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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
use std::path::{Path, PathBuf};

pub const DIFF_SIGN_LINE_ADDED: &str = "+";
pub const DIFF_SIGN_LINE_DELETED: &str = "-";
pub const DIFF_SIGN_LINE_DEFAULT: &str = " ";
pub const DIFF_SIGN_HEADER_ORIGIN: &str = "---";
pub const DIFF_SIGN_HEADER_NEW: &str = "+++";
pub const DIFF_SIGN_HUNK: &str = "@@";

#[derive(Debug)]
pub enum DiffFormat {
    GitUdiff,
}

#[derive(Debug)]
pub struct DiffComposition {
    pub format: DiffFormat,
    pub diff: Vec<Diff>,
}
#[derive(Debug)]
pub struct Diff {
    pub command: Option<String>,
    pub index: Option<String>, // TODO: type this
    pub path: PathBuf,
    pub hunk: Vec<DiffHunk>,
}
#[derive(Debug)]
pub struct DiffHunk {
    pub old_line: usize,
    pub old_len: usize,
    pub new_line: usize,
    pub new_len: usize,
    pub change: Vec<LineChange>,
}
#[derive(Debug)]
pub struct LineChange {
    pub kind: Change,
    pub content: String,
}

#[derive(Debug, Copy, Clone)]
pub enum Change {
    Default,
    Added,
    Deleted,
}

#[derive(Debug)]
pub enum Line {
    Command,
    Index,
    OrignPath,
    NewPath,
    Hunk,
    LineChange(Change),
    Unknown,
}

impl DiffComposition {
    pub fn apply(&self, root: &Path) {}
}

impl Diff {
    pub fn apply(&self, original: &str) -> String {
        let mut buffer = String::new();

        // index of original line
        let mut oidx: usize = 0;
        let lines: Vec<&str> = original.lines().collect();

        for hunk in &self.hunk {
            while oidx < (hunk.old_line - 1) {
                buffer.push_str(
                    lines.get(oidx).expect("there is no line in lines"),
                );
                buffer.push('\n');
                oidx += 1;
            }
            for change in &hunk.change {
                match change.kind {
                    Change::Default => {
                        buffer.push_str(
                            lines.get(oidx).expect("there is no line in lines"),
                        );
                        buffer.push('\n');
                        oidx += 1;
                    }
                    Change::Deleted => {
                        oidx += 1;
                        continue;
                    }
                    Change::Added => {
                        buffer.push_str(&change.content);
                        buffer.push('\n');
                    }
                }
            }
        }

        buffer
    }
}

#[cfg(test)]
mod test {
    use {core::panic, std::fs};

    use crate::{diff::*, parser::Parser};

    #[test]
    fn test_diff_apply() {
        let original = fs::read_to_string("test_data/simple.before").unwrap();
        println!("<<original start>>");
        print!("{}", original);
        println!("<<original end>>");

        let diff_file = fs::read_to_string("test_data/simple.diffs").unwrap();

        let com = Parser::parse_git_udiff(&diff_file);
        println!("{:#?}", com);
        let diff = com.diff.first().unwrap();
        let applied = diff.apply(&original);

        println!("<<applied start>>");
        print!("{}", applied);
        println!("<<applied end>>");

        let expected = fs::read_to_string("test_data/simple.after").unwrap();
        println!("<<expected start>>");
        print!("{}", expected);
        println!("<<expected end>>");

        assert_eq!(applied.as_str(), expected.as_str())
    }
}