sheets_diff/core/
unified_format.rs

1use std::fmt;
2
3#[cfg(feature = "serde")]
4use serde::{Deserialize, Serialize};
5
6use super::diff::Diff;
7
8/// unified diff
9#[derive(Clone, Debug)]
10#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
11pub struct UnifiedDiff {
12    pub content: Vec<UnifiedDiffContent>,
13}
14
15/// formatted unified diff
16#[derive(Clone, Debug)]
17#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
18pub struct FormattedUnifiedDiff {
19    pub content: Vec<UnifiedDiffContent>,
20}
21
22/// unified diff content
23#[derive(Clone, Debug)]
24#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
25pub struct UnifiedDiffContent {
26    pub old_title: String,
27    pub new_title: String,
28    pub lines: Vec<UnifiedDiffLine>,
29}
30
31/// unified diff content lines
32#[derive(Clone, Debug)]
33#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
34pub struct UnifiedDiffLine {
35    pub pos: Option<String>,
36    pub old: Option<String>,
37    pub new: Option<String>,
38}
39
40/// unified diff split into old / new parts
41#[derive(Clone, Debug)]
42#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
43pub struct SplitUnifiedDiff {
44    pub old: Vec<SplitUnifiedDiffContent>,
45    pub new: Vec<SplitUnifiedDiffContent>,
46}
47
48/// unified diff content split into old / new parts
49#[derive(Clone, Debug)]
50#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
51pub struct SplitUnifiedDiffContent {
52    pub title: String,
53    pub lines: Vec<SplitUnifiedDiffLine>,
54}
55
56/// unified diff content lines split into old / new parts
57#[derive(Clone, Debug)]
58#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
59pub struct SplitUnifiedDiffLine {
60    pub pos: Option<String>,
61    pub text: Option<String>,
62}
63
64impl UnifiedDiff {
65    /// convert each string to one in unified format
66    pub fn format(&self) -> FormattedUnifiedDiff {
67        let content: Vec<UnifiedDiffContent> = self
68            .content
69            .iter()
70            .map(|x| {
71                let old_title = format!("--- {}", &x.old_title);
72                let new_title = format!("+++ {}", &x.new_title);
73
74                let lines: Vec<UnifiedDiffLine> = x
75                    .lines
76                    .iter()
77                    .map(|x| {
78                        let pos = if let Some(pos) = &x.pos {
79                            Some(format!("@@ {} @@", pos))
80                        } else {
81                            None
82                        };
83                        let old = if let Some(old) = &x.old {
84                            Some(format!("- {}", old))
85                        } else {
86                            None
87                        };
88                        let new = if let Some(new) = &x.new {
89                            Some(format!("+ {}", new))
90                        } else {
91                            None
92                        };
93                        UnifiedDiffLine { pos, old, new }
94                    })
95                    .collect();
96
97                UnifiedDiffContent {
98                    old_title,
99                    new_title,
100                    lines,
101                }
102            })
103            .collect();
104        FormattedUnifiedDiff { content }
105    }
106
107    /// split into old / new parts
108    pub fn split(&self) -> SplitUnifiedDiff {
109        let old: Vec<SplitUnifiedDiffContent> = self
110            .content
111            .iter()
112            .map(|x| {
113                let title = x.old_title.clone();
114                let lines: Vec<SplitUnifiedDiffLine> = x
115                    .lines
116                    .iter()
117                    .map(|x| {
118                        let pos = if let Some(pos) = &x.pos {
119                            Some(pos.to_owned())
120                        } else {
121                            None
122                        };
123                        let text = if let Some(text) = &x.old {
124                            Some(text.to_owned())
125                        } else {
126                            None
127                        };
128                        SplitUnifiedDiffLine { pos, text }
129                    })
130                    .collect();
131                SplitUnifiedDiffContent { title, lines }
132            })
133            .collect();
134        let new: Vec<SplitUnifiedDiffContent> = self
135            .content
136            .iter()
137            .map(|x| {
138                let title = x.new_title.clone();
139                let lines: Vec<SplitUnifiedDiffLine> = x
140                    .lines
141                    .iter()
142                    .map(|x| {
143                        let pos = if let Some(pos) = &x.pos {
144                            Some(pos.to_owned())
145                        } else {
146                            None
147                        };
148                        let text = if let Some(text) = &x.new {
149                            Some(text.to_owned())
150                        } else {
151                            None
152                        };
153                        SplitUnifiedDiffLine { pos, text }
154                    })
155                    .collect();
156                SplitUnifiedDiffContent { title, lines }
157            })
158            .collect();
159
160        SplitUnifiedDiff { old, new }
161    }
162}
163
164impl fmt::Display for FormattedUnifiedDiff {
165    /// generate string in unified format
166    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
167        self.content.iter().for_each(|x| {
168            let _ = writeln!(f, "{}", &x.old_title);
169            let _ = writeln!(f, "{}", &x.new_title);
170            x.lines.iter().for_each(|x| {
171                if let Some(pos) = &x.pos {
172                    let _ = writeln!(f, "{}", pos);
173                }
174                if let Some(old) = &x.old {
175                    let _ = writeln!(f, "{}", old);
176                }
177                if let Some(new) = &x.new {
178                    let _ = writeln!(f, "{}", new);
179                }
180            });
181        });
182        Ok(())
183    }
184}
185
186/// get unified diff str split into old / new parts
187pub fn unified_diff(diff: &Diff) -> UnifiedDiff {
188    let mut ret: Vec<UnifiedDiffContent> = vec![];
189
190    if !diff.sheet_diff.is_empty() {
191        let old_title = format!("{} (sheet names)", diff.old_filepath);
192        let new_title = format!("{} (sheet names)", diff.new_filepath);
193
194        let lines: Vec<UnifiedDiffLine> = diff
195            .sheet_diff
196            .iter()
197            .map(|x| {
198                let old_sheet = if let Some(sheet) = x.old.as_ref() {
199                    Some(sheet.to_owned())
200                } else {
201                    None
202                };
203                let new_sheet = if let Some(sheet) = x.new.as_ref() {
204                    Some(sheet.to_owned())
205                } else {
206                    None
207                };
208                UnifiedDiffLine {
209                    pos: None,
210                    old: old_sheet,
211                    new: new_sheet,
212                }
213            })
214            .collect();
215
216        ret.push(UnifiedDiffContent {
217            old_title,
218            new_title,
219            lines,
220        });
221    }
222
223    let cell_diffs_content: Vec<UnifiedDiffContent> = diff
224        .cell_diffs
225        .iter()
226        .map(|x| {
227            let cell_diffs_lines: Vec<UnifiedDiffLine> = x
228                .cells
229                .iter()
230                .map(|x| {
231                    let pos = Some(format!("{}({},{}) {}", x.addr, x.row, x.col, x.kind));
232
233                    let old = if let Some(sheet) = x.old.as_ref() {
234                        Some(sheet.to_owned())
235                    } else {
236                        None
237                    };
238                    let new = if let Some(sheet) = x.new.as_ref() {
239                        Some(sheet.to_owned())
240                    } else {
241                        None
242                    };
243
244                    UnifiedDiffLine { pos, old, new }
245                })
246                .collect();
247
248            UnifiedDiffContent {
249                old_title: format!("{} [{}]", diff.old_filepath, x.sheet),
250                new_title: format!("{} [{}]", diff.new_filepath, x.sheet),
251                lines: cell_diffs_lines,
252            }
253        })
254        .collect();
255
256    ret.extend(cell_diffs_content);
257
258    UnifiedDiff { content: ret }
259}