use serde::{Deserialize, Serialize};
use std::fmt;
use super::error::PatchError;
use super::verify_chunk::VerifyChunk;
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
#[serde(bound(serialize = "T: Serialize", deserialize = "T: Deserialize<'de>"))]
pub struct Chunk<T> {
position: usize,
lines: Vec<T>,
change_position: Option<Vec<usize>>,
}
impl<T> Chunk<T> {
pub fn new(position: usize, lines: Vec<T>, change_position: Option<Vec<usize>>) -> Self {
Self {
position,
lines,
change_position,
}
}
pub fn with_lines(position: usize, lines: Vec<T>) -> Self {
Self::new(position, lines, None)
}
pub fn verify_chunk(&self, target: &[T]) -> Result<VerifyChunk, PatchError>
where
T: PartialEq,
{
self.verify_chunk_at(target, 0, self.position)
}
pub fn verify_chunk_at(
&self,
target: &[T],
fuzz: usize,
position: usize,
) -> Result<VerifyChunk, PatchError>
where
T: PartialEq,
{
let start_index = fuzz;
let last_index = self.len().saturating_sub(fuzz);
let last = position + self.len().saturating_sub(1);
if position.saturating_add(fuzz) > target.len() || last.saturating_sub(fuzz) > target.len()
{
return Ok(VerifyChunk::PositionOutOfTarget);
}
for i in start_index..last_index {
let target_idx = position + i;
if target_idx >= target.len() || target[target_idx] != self.lines[i] {
return Ok(VerifyChunk::ContentDoesNotMatchTarget);
}
}
Ok(VerifyChunk::Ok)
}
#[inline]
pub fn position(&self) -> usize {
self.position
}
#[inline]
pub fn lines(&self) -> &[T] {
&self.lines
}
#[inline]
pub fn lines_mut(&mut self) -> &mut Vec<T> {
&mut self.lines
}
pub fn set_lines(&mut self, lines: Vec<T>) {
self.lines = lines;
}
#[inline]
pub fn change_position(&self) -> Option<&[usize]> {
self.change_position.as_deref()
}
#[inline]
pub fn len(&self) -> usize {
self.lines.len()
}
#[inline]
pub fn size(&self) -> usize {
self.lines.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.lines.is_empty()
}
#[inline]
pub fn last(&self) -> usize {
if self.lines.is_empty() {
self.position
} else {
self.position + self.lines.len() - 1
}
}
}
impl<T: fmt::Debug> fmt::Display for Chunk<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"[position: {}, size: {}, lines: {:?}]",
self.position,
self.len(),
self.lines
)
}
}