use std::borrow::Cow;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ChangeTag {
Equal,
Delete,
Insert,
}
#[derive(Debug)]
pub struct Change<'a> {
tag: ChangeTag,
values: Vec<(bool, Cow<'a, str>)>,
missing_newline: bool,
}
impl<'a> Change<'a> {
#[must_use]
pub const fn new(tag: ChangeTag) -> Self {
Self {
tag,
values: Vec::new(),
missing_newline: false,
}
}
pub fn add_value(&mut self, highlight: bool, value: Cow<'a, str>) {
self.values.push((highlight, value));
}
pub const fn set_missing_newline(&mut self, missing_newline: bool) {
self.missing_newline = missing_newline;
}
#[must_use]
pub const fn tag(&self) -> ChangeTag {
self.tag
}
#[must_use]
pub fn values(&self) -> &[(bool, Cow<'a, str>)] {
&self.values
}
#[must_use]
pub const fn missing_newline(&self) -> bool {
self.missing_newline
}
}
#[derive(Debug)]
pub struct DiffOp {
tag: ChangeTag,
old_start: usize,
old_len: usize,
new_start: usize,
new_len: usize,
}
impl DiffOp {
#[must_use]
pub const fn new(
tag: ChangeTag,
old_start: usize,
old_len: usize,
new_start: usize,
new_len: usize,
) -> Self {
Self {
tag,
old_start,
old_len,
new_start,
new_len,
}
}
#[must_use]
pub const fn tag(&self) -> ChangeTag {
self.tag
}
#[must_use]
pub const fn old_start(&self) -> usize {
self.old_start
}
#[must_use]
pub const fn old_len(&self) -> usize {
self.old_len
}
#[must_use]
pub const fn new_start(&self) -> usize {
self.new_start
}
#[must_use]
pub const fn new_len(&self) -> usize {
self.new_len
}
}
pub trait DiffAlgorithm {
fn ops<'a>(&self, old: &'a str, new: &'a str) -> Vec<DiffOp>;
fn iter_inline_changes<'a>(&self, old: &'a str, new: &'a str, op: &DiffOp) -> Vec<Change<'a>>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Algorithm {
Similar,
Myers,
}
impl Algorithm {
#[must_use]
pub fn available_algorithms() -> Vec<Self> {
let algorithms = vec![
#[cfg(feature = "similar")]
Self::Similar,
#[cfg(feature = "myers")]
Self::Myers,
];
algorithms
}
#[must_use]
pub fn has_available_algorithms() -> bool {
let algorithms = Self::available_algorithms();
!algorithms.is_empty()
}
#[must_use]
pub fn first_available() -> Option<Self> {
let algorithms = Self::available_algorithms();
if algorithms.is_empty() {
None
} else {
Some(algorithms[0])
}
}
}
impl Default for Algorithm {
fn default() -> Self {
Self::first_available().unwrap_or(Self::Similar)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_has_available_algorithms() {
let has_algorithms = Algorithm::has_available_algorithms();
#[cfg(any(feature = "similar", feature = "myers"))]
assert!(
has_algorithms,
"Should have available algorithms when features are enabled"
);
#[cfg(not(any(feature = "similar", feature = "myers")))]
assert!(
!has_algorithms,
"Should not have available algorithms when no features are enabled"
);
}
#[test]
fn test_has_available_algorithms_implementation() {
let algorithms = Algorithm::available_algorithms();
let expected_result = !algorithms.is_empty();
let actual_result = Algorithm::has_available_algorithms();
assert_eq!(
actual_result, expected_result,
"has_available_algorithms() should return !algorithms.is_empty()"
);
}
#[test]
fn test_has_available_algorithms_behavior_with_empty_vec() {
let mock_empty_vec = || Vec::<Algorithm>::new();
let result = !mock_empty_vec().is_empty();
assert!(
!result,
"Should return false when algorithms vector is empty"
);
let mock_non_empty_vec = || vec![Algorithm::Myers];
let result = !mock_non_empty_vec().is_empty();
assert!(
result,
"Should return true when algorithms vector is not empty"
);
#[cfg(not(any(feature = "myers", feature = "similar")))]
{
assert!(
!Algorithm::has_available_algorithms(),
"Should return false when no algorithms are available"
);
}
#[cfg(any(feature = "myers", feature = "similar"))]
{
assert!(
Algorithm::has_available_algorithms(),
"Should return true when algorithms are available"
);
}
}
#[test]
#[cfg(not(any(feature = "myers", feature = "similar")))]
fn test_has_available_algorithms_with_no_features() {
assert!(
!Algorithm::has_available_algorithms(),
"has_available_algorithms() should return false when no features are enabled"
);
let algorithms = Algorithm::available_algorithms();
assert!(
algorithms.is_empty(),
"available_algorithms() should return an empty vector when no features are enabled"
);
}
#[test]
fn test_first_available() {
let first = Algorithm::first_available();
#[cfg(any(feature = "similar", feature = "myers"))]
assert!(
first.is_some(),
"Should have a first algorithm when features are enabled"
);
#[cfg(not(any(feature = "similar", feature = "myers")))]
assert!(
first.is_none(),
"Should not have a first algorithm when no features are enabled"
);
}
}