patch_rs/context/
reduce.rs

1//!
2//! The context reducing 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}
21
22impl Context {
23    pub fn reduce(&self, mut context_radius: usize) -> VecDeque<Self> {
24        context_radius = cmp::max(1, context_radius);
25        context_radius = cmp::min(context_radius, self.opening_context_size());
26
27        let mut results = VecDeque::new();
28
29        let mut output = Self::default();
30        output.header.file1_l = self.header.file1_l;
31        output.header.file2_l = self.header.file2_l;
32
33        let mut state = FlipState::StartContext;
34
35        trace!("START");
36        for line in self.data.iter() {
37            match line {
38                Line::Context(_) => {
39                    match state {
40                        FlipState::StartContext => {
41                            trace!("Context StartContext");
42                            while output.opening_context_size() >= context_radius {
43                                output.data.pop();
44                                output.header.file1_l += 1;
45                                output.header.file2_l += 1;
46                                trace!("POP START");
47                            }
48                            output.data.push(line.clone());
49                        },
50                        FlipState::Context => {
51                            trace!("Context Context");
52                            output.data.push(line.clone());
53                            let lines = output.closing_context_size();
54                            if lines > 2 * context_radius {
55                                let mut data = Vec::new();
56                                data.push(output.data.pop().unwrap());
57                                for _ in context_radius*2..lines {
58                                    output.data.pop();
59                                    trace!("POP END");
60                                }
61                                output.update();
62
63                                trace!("PUSH OUTPUT");
64                                let output_next = Context {
65                                    header: ContextHeader {
66                                        file1_l: output.header.file1_l + output.header.file1_s + lines - 2,
67                                        file1_s: Default::default(),
68                                        file2_l: output.header.file2_l + output.header.file2_s + lines - 2,
69                                        file2_s: Default::default(),
70                                    },
71                                    data,
72                                };
73
74                                if output.has_changes() {
75                                    results.push_back(output);
76                                }
77                                output = output_next;
78                            }
79                        },
80                    }
81                },
82                Line::Delete(_) | Line::Insert(_) => {
83                    if let FlipState::StartContext = state {
84                        state = FlipState::Context;
85                    }
86                    output.data.push(line.clone());
87                },
88            }
89        }
90
91        let lines = output.closing_context_size();
92        if lines > context_radius {
93            for _ in context_radius..lines {
94                output.data.pop();
95                trace!("POP END");
96            }
97        }
98
99        output.update();
100        if output.has_changes() {
101            results.push_back(output);
102        }
103
104        trace!("END");
105
106        results
107    }
108}