(existing_code: &str, diff: &str)
| 201 | } |
| 202 | |
| 203 | fn apply_unified_diff(existing_code: &str, diff: &str) -> Result<String> { |
| 204 | let original_lines = split_lines_preserving_empty(existing_code); |
| 205 | let mut out = Vec::new(); |
| 206 | let mut current_original_index = 0usize; |
| 207 | let mut in_hunk = false; |
| 208 | |
| 209 | for raw_line in diff.lines() { |
| 210 | if raw_line.starts_with("--- ") || raw_line.starts_with("+++ ") { |
| 211 | continue; |
| 212 | } |
| 213 | if let Some((old_start, _old_len, _new_start, _new_len)) = parse_hunk_header(raw_line)? { |
| 214 | let target_index = old_start.saturating_sub(1); |
| 215 | if target_index > original_lines.len() { |
| 216 | bail!("hunk starts past end of file"); |
| 217 | } |
| 218 | out.extend_from_slice(&original_lines[current_original_index..target_index]); |
| 219 | current_original_index = target_index; |
| 220 | in_hunk = true; |
| 221 | continue; |
| 222 | } |
| 223 | if !in_hunk { |
| 224 | continue; |
| 225 | } |
| 226 | let (prefix, content) = raw_line |
| 227 | .chars() |
| 228 | .next() |
| 229 | .map(|ch| (ch, &raw_line[ch.len_utf8()..])) |
| 230 | .ok_or_else(|| anyhow!("malformed diff line"))?; |
| 231 | match prefix { |
| 232 | ' ' => { |
| 233 | let original = original_lines |
| 234 | .get(current_original_index) |
| 235 | .ok_or_else(|| anyhow!("context line exceeds original file"))?; |
| 236 | if original != &content { |
| 237 | bail!("context mismatch while applying patch"); |
| 238 | } |
| 239 | out.push(content.to_string()); |
| 240 | current_original_index += 1; |
| 241 | } |
| 242 | '-' => { |
| 243 | let original = original_lines |
| 244 | .get(current_original_index) |
| 245 | .ok_or_else(|| anyhow!("deletion exceeds original file"))?; |
| 246 | if original != &content { |
| 247 | bail!("deletion mismatch while applying patch"); |
| 248 | } |
| 249 | current_original_index += 1; |
| 250 | } |
| 251 | '+' => out.push(content.to_string()), |
| 252 | _ => bail!("unsupported diff line prefix '{prefix}'"), |
| 253 | } |
| 254 | } |
| 255 | |
| 256 | out.extend_from_slice(&original_lines[current_original_index..]); |
| 257 | Ok(join_lines_like_source(existing_code, &out)) |
| 258 | } |
| 259 | |
| 260 | fn parse_hunk_header(line: &str) -> Result<Option<(usize, usize, usize, usize)>> { |
no test coverage detected
searching dependent graphs…