use serde::{Deserialize, Serialize};
use std::fmt;
use super::chunk::Chunk;
use super::delta_type::DeltaType;
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 Delta<T> {
delta_type: DeltaType,
source: Chunk<T>,
target: Chunk<T>,
}
impl<T> Delta<T> {
pub fn new(delta_type: DeltaType, source: Chunk<T>, target: Chunk<T>) -> Self {
Self {
delta_type,
source,
target,
}
}
#[inline]
pub fn source(&self) -> &Chunk<T> {
&self.source
}
#[inline]
pub fn source_mut(&mut self) -> &mut Chunk<T> {
&mut self.source
}
#[inline]
pub fn target(&self) -> &Chunk<T> {
&self.target
}
#[inline]
pub fn target_mut(&mut self) -> &mut Chunk<T> {
&mut self.target
}
#[inline]
pub fn delta_type(&self) -> DeltaType {
self.delta_type
}
pub fn verify_chunk_to_fit_target(&self, target: &[T]) -> Result<VerifyChunk, PatchError>
where
T: PartialEq,
{
self.source.verify_chunk(target)
}
pub fn verify_and_apply_to(&self, target: &mut Vec<T>) -> Result<VerifyChunk, PatchError>
where
T: Clone + PartialEq,
{
let verify = self.verify_chunk_to_fit_target(target)?;
if verify == VerifyChunk::Ok {
self.apply_to(target)?;
}
Ok(verify)
}
pub fn apply_to(&self, target: &mut Vec<T>) -> Result<(), PatchError>
where
T: Clone + PartialEq,
{
self.apply_at(target, self.source.position())
}
pub fn apply_at(&self, target: &mut Vec<T>, pos: usize) -> Result<(), PatchError>
where
T: Clone + PartialEq,
{
if pos > target.len() {
return Err(PatchError::PatchFailed(format!(
"Patch position {} out of bounds for target length {}",
pos,
target.len()
)));
}
match self.delta_type {
DeltaType::Delete => {
let len = self.source.len();
if pos + len > target.len() {
return Err(PatchError::PatchFailed(format!(
"Delete delta range [{}..{}] exceeds target length {}",
pos,
pos + len,
target.len()
)));
}
target.drain(pos..pos + len);
}
DeltaType::Insert => {
let lines = self.target.lines();
target.splice(pos..pos, lines.iter().cloned());
}
DeltaType::Change => {
let len = self.source.len();
if pos + len > target.len() {
return Err(PatchError::PatchFailed(format!(
"Change delta source range [{}..{}] exceeds target length {}",
pos,
pos + len,
target.len()
)));
}
target.splice(pos..pos + len, self.target.lines().iter().cloned());
}
DeltaType::Equal => {}
}
Ok(())
}
pub fn restore(&self, target: &mut Vec<T>) -> Result<(), PatchError>
where
T: Clone + PartialEq,
{
let pos = self.target.position();
match self.delta_type {
DeltaType::Delete => {
let lines = self.source.lines();
target.splice(pos..pos, lines.iter().cloned());
}
DeltaType::Insert => {
let len = self.target.len();
if pos + len > target.len() {
return Err(PatchError::PatchFailed(format!(
"Restore insert delta range [{}..{}] exceeds target length {}",
pos,
pos + len,
target.len()
)));
}
target.drain(pos..pos + len);
}
DeltaType::Change => {
let len = self.target.len();
if pos + len > target.len() {
return Err(PatchError::PatchFailed(format!(
"Restore change delta target range [{}..{}] exceeds target length {}",
pos,
pos + len,
target.len()
)));
}
target.splice(pos..pos + len, self.source.lines().iter().cloned());
}
DeltaType::Equal => {}
}
Ok(())
}
pub fn apply_fuzzy_to_at(
&self,
target: &mut Vec<T>,
_fuzz: usize,
position: usize,
) -> Result<(), PatchError>
where
T: Clone + PartialEq,
{
if position > target.len() {
return Err(PatchError::PatchFailed(format!(
"Fuzzy patch position {} out of bounds for target length {}",
position,
target.len()
)));
}
match self.delta_type {
DeltaType::Delete => {
let src_len = self.source.len();
let end = (position + src_len).min(target.len());
if position < end {
target.drain(position..end);
}
Ok(())
}
DeltaType::Insert => {
if _fuzz > 0 {
return Err(PatchError::UnsupportedOperation(
"Fuzzy patching is not supported for InsertDelta".to_string(),
));
}
let insert_pos = position.min(target.len());
let lines = self.target.lines();
target.splice(insert_pos..insert_pos, lines.iter().cloned());
Ok(())
}
DeltaType::Change => {
let src_len = self.source.len();
let target_lines = self.target.lines();
let end = (position + src_len).min(target.len());
target.splice(position..end, target_lines.iter().cloned());
Ok(())
}
DeltaType::Equal => Ok(()),
}
}
#[must_use]
pub fn with_chunks(&self, source: Chunk<T>, target: Chunk<T>) -> Self {
Self {
delta_type: self.delta_type,
source,
target,
}
}
}
impl<T> AsRef<Delta<T>> for Delta<T> {
fn as_ref(&self) -> &Delta<T> {
self
}
}
impl<T> From<Box<Delta<T>>> for Delta<T> {
fn from(boxed: Box<Delta<T>>) -> Self {
*boxed
}
}
fn format_lines<T: fmt::Display>(lines: &[T]) -> String {
let formatted_items = lines
.iter()
.map(|item| item.to_string())
.collect::<Vec<_>>()
.join(", ");
format!("[{}]", formatted_items)
}
impl<T: fmt::Display> fmt::Display for Delta<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.delta_type {
DeltaType::Insert => write!(
f,
"[InsertDelta, position: {}, lines: {}]",
self.source().position(),
format_lines(self.target().lines())
),
DeltaType::Delete => write!(
f,
"[DeleteDelta, position: {}, lines: {}]",
self.source().position(),
format_lines(self.source().lines())
),
DeltaType::Change => write!(
f,
"[ChangeDelta, position: {}, lines: {} to {}]",
self.source().position(),
format_lines(self.source().lines()),
format_lines(self.target().lines())
),
DeltaType::Equal => write!(
f,
"[EqualDelta, position: {}, lines: {}]",
self.source().position(),
format_lines(self.source().lines())
),
}
}
}