diff.rs
1 use radicle_surf::{self as surf}; 2 use serde_json::{json, Value}; 3 4 use radicle::cob; 5 6 pub(crate) struct Diff<'a>(&'a surf::diff::Diff); 7 8 impl<'a> Diff<'a> { 9 pub fn new(diff: &'a surf::diff::Diff) -> Self { 10 Self(diff) 11 } 12 13 pub fn as_json(&self) -> Value { 14 let s = self.0.stats(); 15 json!({ 16 "files": self.0.files().map(|f| { 17 match f { 18 surf::diff::FileDiff::Added(added) => json!({ 19 "status": "added", 20 "path": added.path, 21 "diff": DiffContent::new(&added.diff).as_json(), 22 "new": DiffFile::new(&added.new).as_json(), 23 }), 24 surf::diff::FileDiff::Deleted(deleted) => json!({ 25 "status": "deleted", 26 "path": deleted.path, 27 "diff": DiffContent::new(&deleted.diff).as_json(), 28 "old": DiffFile::new(&deleted.old).as_json(), 29 }), 30 surf::diff::FileDiff::Modified(modified) => json!({ 31 "status": "modified", 32 "path": modified.path, 33 "diff": DiffContent::new(&modified.diff).as_json(), 34 "old": DiffFile::new(&modified.old).as_json(), 35 "new": DiffFile::new(&modified.new).as_json(), 36 }), 37 surf::diff::FileDiff::Moved(moved) => { 38 if moved.old == moved.new { 39 json!({ 40 "status": "moved", 41 "oldPath": moved.old_path, 42 "newPath": moved.new_path, 43 "current": DiffFile::new(&moved.new).as_json(), 44 }) 45 } else { 46 json!({ 47 "status": "moved", 48 "oldPath": moved.old_path, 49 "newPath": moved.new_path, 50 "old": DiffFile::new(&moved.old).as_json(), 51 "new": DiffFile::new(&moved.new).as_json(), 52 "diff": DiffContent::new(&moved.diff).as_json() 53 }) 54 } 55 }, 56 surf::diff::FileDiff::Copied(copied) => { 57 if copied.old == copied.new { 58 json!({ 59 "status": "copied", 60 "oldPath": copied.old_path, 61 "newPath": copied.new_path, 62 "current": DiffFile::new(&copied.new).as_json() 63 }) 64 } else { 65 json!({ 66 "status": "copied", 67 "oldPath": copied.old_path, 68 "newPath": copied.new_path, 69 "old": DiffFile::new(&copied.old).as_json(), 70 "new": DiffFile::new(&copied.new).as_json(), 71 "diff": DiffContent::new(&copied.diff).as_json() 72 }) 73 } 74 }, 75 } 76 }).collect::<Vec<_>>(), 77 "stats": json!({ 78 "filesChanged": s.files_changed, 79 "insertions": s.insertions, 80 "deletions": s.deletions, 81 }), 82 }) 83 } 84 } 85 86 pub(crate) struct CodeLocation<'a>(&'a cob::CodeLocation); 87 88 impl<'a> CodeLocation<'a> { 89 pub fn new(location: &'a cob::CodeLocation) -> Self { 90 Self(location) 91 } 92 93 pub fn as_json(&self) -> Value { 94 match (&self.0.old, &self.0.new) { 95 (Some(old), Some(new)) => json!({ 96 "commit": self.0.commit, 97 "path": self.0.path, 98 "old": code_range(old), 99 "new": code_range(new) 100 }), 101 (None, Some(new)) => json!({ 102 "commit": self.0.commit, 103 "path": self.0.path, 104 "new": code_range(new) 105 }), 106 (Some(old), None) => json!({ 107 "commit": self.0.commit, 108 "path": self.0.path, 109 "old": code_range(old) 110 }), 111 (None, None) => json!({ 112 "commit": self.0.commit, 113 "path": self.0.path 114 }), 115 } 116 } 117 } 118 119 fn code_range(range: &cob::CodeRange) -> Value { 120 match range { 121 cob::CodeRange::Lines { range } => json!({ 122 "type": "lines", 123 "range": range 124 }), 125 cob::CodeRange::Chars { line, range } => { 126 json!({ "type": "chars", "line": line, "range": range }) 127 } 128 } 129 } 130 131 pub(crate) struct DiffContent<'a>(&'a surf::diff::DiffContent); 132 133 impl<'a> DiffContent<'a> { 134 pub fn new(value: &'a surf::diff::DiffContent) -> Self { 135 Self(value) 136 } 137 138 pub fn as_json(&self) -> Value { 139 match self.0 { 140 surf::diff::DiffContent::Binary => json!({ "type": "binary" }), 141 surf::diff::DiffContent::Empty => json!({ "type": "empty" }), 142 surf::diff::DiffContent::Plain { hunks, stats, eof } => { 143 let hunks = hunks 144 .iter() 145 .map(|h| Hunk::new(h).as_json()) 146 .collect::<Vec<_>>(); 147 148 json!({ 149 "type": "plain", 150 "hunks": hunks, 151 "stats": json!({ 152 "additions": stats.additions, 153 "deletions": stats.deletions 154 }), 155 "eof": match eof { 156 surf::diff::EofNewLine::OldMissing => "oldMissing", 157 surf::diff::EofNewLine::NewMissing => "newMissing", 158 surf::diff::EofNewLine::BothMissing => "bothMissing", 159 surf::diff::EofNewLine::NoneMissing => "noneMissing", 160 } 161 }) 162 } 163 } 164 } 165 } 166 167 pub(crate) struct DiffFile<'a>(&'a surf::diff::DiffFile); 168 169 impl<'a> DiffFile<'a> { 170 pub fn new(value: &'a surf::diff::DiffFile) -> Self { 171 Self(value) 172 } 173 174 pub fn as_json(&self) -> Value { 175 json!({ "oid": self.0.oid, "mode": match self.0.mode { 176 surf::diff::FileMode::Blob => "blob", 177 surf::diff::FileMode::BlobExecutable => "blobExecutable", 178 surf::diff::FileMode::Tree => "tree", 179 surf::diff::FileMode::Link => "link", 180 surf::diff::FileMode::Commit => "commit", 181 } }) 182 } 183 } 184 185 pub(crate) struct Modification<'a>(&'a surf::diff::Modification); 186 187 impl<'a> Modification<'a> { 188 pub fn new(value: &'a surf::diff::Modification) -> Self { 189 Self(value) 190 } 191 192 pub fn as_json(&self) -> Value { 193 match self.0 { 194 surf::diff::Modification::Addition(addition) => { 195 json!({ 196 "type": "addition", 197 "line": addition.line.clone().from_utf8().ok(), 198 "lineNo": addition.line_no 199 }) 200 } 201 surf::diff::Modification::Deletion(deletion) => { 202 json!({ 203 "type": "deletion", 204 "line": deletion.line.clone().from_utf8().ok(), 205 "lineNo": deletion.line_no 206 }) 207 } 208 surf::diff::Modification::Context { 209 line, 210 line_no_old, 211 line_no_new, 212 } => { 213 json!({ 214 "type": "context", 215 "line": line.clone().from_utf8().ok(), 216 "lineNoOld": line_no_old, 217 "lineNoNew": line_no_new 218 }) 219 } 220 } 221 } 222 } 223 224 pub(crate) struct Hunk<'a>(&'a surf::diff::Hunk<surf::diff::Modification>); 225 226 impl<'a> Hunk<'a> { 227 pub fn new(value: &'a surf::diff::Hunk<surf::diff::Modification>) -> Self { 228 Self(value) 229 } 230 231 pub fn as_json(&self) -> Value { 232 let lines = self 233 .0 234 .lines 235 .iter() 236 .map(|line| Modification::new(line).as_json()) 237 .collect::<Vec<_>>(); 238 239 json!({ 240 "header": self.0.header, 241 "lines": lines, 242 "old": self.0.old, 243 "new": self.0.new, 244 }) 245 } 246 }