use super::{ADD, DELETE, END, EOF_MARK, Error, FileOp, Hunk, MOVE, UPDATE};
pub(super) fn is_section(line: &str) -> bool {
[ADD, DELETE, UPDATE, MOVE]
.iter()
.any(|m| line.starts_with(m))
}
pub(super) fn parse_add(
path: &str,
lines: &[&str],
from: usize,
until: usize,
) -> Result<(FileOp, usize), Error> {
let mut content = Vec::new();
let mut i = from;
while i < until {
if let Some(rest) = lines[i].strip_prefix('+') {
content.push(rest.to_string());
} else {
break;
}
i += 1;
}
if content.is_empty() {
return Err(Error::EmptyAdd {
path: path.to_string(),
});
}
let op = FileOp::Add {
path: path.to_string(),
lines: content,
};
Ok((op, i))
}
pub(super) fn parse_update(
path: &str,
lines: &[&str],
from: usize,
until: usize,
) -> Result<(FileOp, usize), Error> {
let mut i = from;
let mut move_to = None;
if i < until
&& let Some(to) = lines[i].strip_prefix(MOVE)
{
move_to = Some(to.to_string());
i += 1;
}
let mut hunks: Vec<Hunk> = Vec::new();
let mut cur = Hunk::default();
let mut flush = |cur: &mut Hunk| -> Result<(), Error> {
let hunk = std::mem::take(cur);
if hunk.is_blank() {
return Ok(());
}
if hunk.old == hunk.new {
return Err(Error::NoChange {
path: path.to_string(),
hunk: hunks.len() + 1,
});
}
hunks.push(hunk);
Ok(())
};
let mut after_eof = false;
while i < until && !is_section(lines[i]) && lines[i] != END {
let line = lines[i];
if line == EOF_MARK {
cur.eof = true;
flush(&mut cur)?;
after_eof = true;
} else if line.is_empty() {
if !after_eof {
cur.old.push(String::new());
cur.new.push(String::new());
}
} else if line == "@@" {
flush(&mut cur)?;
after_eof = false;
} else if let Some(anchor) = line.strip_prefix("@@ ") {
if !cur.old.is_empty() || !cur.new.is_empty() {
flush(&mut cur)?;
}
cur.anchors.push(anchor.to_string());
after_eof = false;
} else if let Some(rest) = line.strip_prefix('+') {
cur.new.push(rest.to_string());
after_eof = false;
} else if let Some(rest) = line.strip_prefix('-') {
cur.old.push(rest.to_string());
after_eof = false;
} else if let Some(rest) = line.strip_prefix(' ') {
cur.old.push(rest.to_string());
cur.new.push(rest.to_string());
after_eof = false;
} else {
return Err(Error::BadLine {
line: i + 1,
content: line.to_string(),
});
}
i += 1;
}
flush(&mut cur)?;
if hunks.is_empty() {
return Err(Error::EmptyUpdate {
path: path.to_string(),
});
}
let op = FileOp::Update {
path: path.to_string(),
move_to,
hunks,
};
Ok((op, i))
}