patch_rs/context/
flip.rs

1//!
2//! The context flipping algorithm.
3//!
4
5use std::{
6    cmp,
7    collections::VecDeque,
8};
9
10use log::*;
11
12use crate::{
13    context::{Context, ContextHeader},
14    line::Line,
15};
16
17enum FlipState {
18    StartContext,
19    Context,
20    Buffering,
21}
22
23impl Context {
24    pub fn flip(&self, mut context_radius: usize) -> VecDeque<Self> {
25        context_radius = cmp::max(1, context_radius);
26        context_radius = cmp::min(context_radius, self.opening_context_size());
27
28        let mut results = VecDeque::new();
29
30        let mut output = Self::default();
31        output.header.file1_l = self.header.file2_l;
32        output.header.file2_l = self.header.file1_l;
33
34        let mut state = FlipState::StartContext;
35        let mut deletes = Vec::new();
36        let mut inserts = Vec::new();
37
38        trace!("START");
39        for line in self.data.iter() {
40            match line {
41                Line::Context(_) => {
42                    match state {
43                        FlipState::StartContext => {
44                            trace!("Context StartContext");
45                            while output.opening_context_size() >= context_radius {
46                                output.data.pop();
47                                output.header.file1_l += 1;
48                                output.header.file2_l += 1;
49                                trace!("POP START");
50                            }
51                            output.data.push(line.clone());
52                        },
53                        FlipState::Context => {
54                            trace!("Context Context");
55                            output.data.push(line.clone());
56                            let lines = output.closing_context_size();
57                            if lines > 2 * context_radius {
58                                let mut data = Vec::new();
59                                data.push(output.data.pop().unwrap());
60                                for _ in context_radius*2..lines {
61                                    output.data.pop();
62                                    trace!("POP END");
63                                }
64                                output.update();
65
66                                trace!("PUSH OUTPUT");
67                                let output_next = Context {
68                                    header: ContextHeader {
69                                        file1_l: output.header.file2_l + output.header.file2_s + lines - 2,
70                                        file1_s: Default::default(),
71                                        file2_l: output.header.file1_l + output.header.file1_s + lines - 2,
72                                        file2_s: Default::default(),
73                                    },
74                                    data,
75                                };
76
77                                if output.has_changes() {
78                                    results.push_back(output);
79                                }
80                                output = output_next;
81
82                                state = FlipState::Context;
83                            }
84                        },
85                        FlipState::Buffering => {
86                            trace!("Context Buffering");
87                            output.data.append(&mut deletes);
88                            output.data.append(&mut inserts);
89                            state = FlipState::Context;
90                            output.data.push(line.clone());
91                        },
92                    }
93                },
94                Line::Delete(_) => {
95                    if let FlipState::StartContext = state {
96                        trace!("Delete StartContext");
97                        state = FlipState::Buffering;
98                    }
99                    if let FlipState::Context = state {
100                        trace!("Delete Context");
101                        state = FlipState::Buffering;
102                    }
103                    if let FlipState::Buffering = state {
104                        trace!("Delete Buffering");
105                        inserts.push(line.flip());
106                    }
107                },
108                Line::Insert(_) => {
109                    trace!("INSERT");
110                    if let FlipState::StartContext = state {
111                        trace!("Insert StartContext");
112                        state = FlipState::Buffering;
113                    }
114                    if let FlipState::Context = state {
115                        trace!("Insert Context");
116                        state = FlipState::Buffering;
117                    }
118                    if let FlipState::Buffering = state {
119                        trace!("Insert Buffering");
120                        deletes.push(line.flip());
121                    }
122                },
123            }
124        }
125
126        let lines = output.closing_context_size();
127        if lines > context_radius {
128            for _ in context_radius..lines {
129                output.data.pop();
130                trace!("POP END");
131            }
132        }
133
134        output.update();
135        if output.has_changes() {
136            results.push_back(output);
137        }
138
139        trace!("END");
140
141        results
142    }
143}