#difference #algorithm #diff #find #data

diffus

在任意数据结构的两个实例之间查找差异。支持:集合、字符串、映射等。在适用的情况下使用LCS。还支持通过diffus-derive进行派生。

14个版本 (8个破坏性更新)

0.10.0 2021年5月17日
0.9.1 2020年1月1日
0.9.0 2019年12月31日
0.4.0 2019年11月26日

#202数据结构

Download history 11466/week @ 2024-03-14 10608/week @ 2024-03-21 10504/week @ 2024-03-28 7707/week @ 2024-04-04 8853/week @ 2024-04-11 9953/week @ 2024-04-18 8853/week @ 2024-04-25 8908/week @ 2024-05-02 12585/week @ 2024-05-09 10767/week @ 2024-05-16 9727/week @ 2024-05-23 9795/week @ 2024-05-30 11540/week @ 2024-06-06 12188/week @ 2024-06-13 10667/week @ 2024-06-20 8264/week @ 2024-06-27

44,625 每月下载量
14 个crate中使用 (直接使用3个)

Apache-2.0

35KB
1K SLoC

Diffus

在任意数据结构的两个实例之间查找差异。

Diffus 动作展示

use diffus_derive::Diffus;
use diffus::{edit, Diffable};

#[derive(Diffus)]
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let left_point = Point { x: 1, y: 2 };
    let right_point = Point { x: 1, y: 3 };

    let diff = left_point.diff(&right_point);

    match diff {
        edit::Edit::Copy => println!("point: no difference"),
        edit::Edit::Change(EditedPoint { x, y }) => {
            match x {
                edit::Edit::Copy => println!("x: no difference"),
                edit::Edit::Change((left_x, right_x)) => println!("x: {} => {}", left_x, right_x),
            }
            match y {
                edit::Edit::Copy => println!("y: no difference"),
                edit::Edit::Change((left_y, right_y)) => println!("y: {} => {}", left_y, right_y),
            }
        }
    }
}

Diffus 在映射中的应用

映射之间的差异是通过其键进行的。元素的标识来自其关联的键,在其他方面它以与使用Same的集合相同的方式进行工作。

映射的可能编辑操作有 CopyInsertRemoveChange

use diffus::{edit, Diffable};

fn main() {
    let unity: std::collections::HashMap<_, _> =
        [(1, 1), (2, 2), (3, 3)].iter().cloned().collect();
    let not_unity: std::collections::HashMap<_, _> =
        [(1, 1), (2, 3), (4, 4)].iter().cloned().collect();

    if let edit::Edit::Change(diff) = unity.diff(&not_unity) {
        assert!(diff[&1].is_copy());
        assert_eq!(diff[&2].change().unwrap(), &(&2, &3));
        assert!(diff[&3].is_remove());
        assert_eq!(diff[&4].insert().unwrap(), &4);
    } else {
        unreachable!()
    }
}

Diffus 在集合中的应用

通过最长公共子序列 (LCS) 算法以及支持对象值改变但保持其Same“标识”的额外支持来进行集合之间的差异。

字符串被视为集合。

集合的可能编辑操作有 CopyInsertRemoveChange

use diffus_derive::Diffus;
use diffus::{edit::{self, collection}, Same, Diffable};

#[derive(Diffus, Debug)]
struct Identified {
    id: u32,
    value: u32,
}

impl Same for Identified {
    fn same(&self, other: &Self) -> bool {
        self.id == other.id
    }
}

fn main() {
    let left = vec![
        Identified { id: 1, value: 0 },
        Identified { id: 2, value: 0 },
        Identified { id: 3, value: 0 },
        Identified { id: 4, value: 0 },
        Identified { id: 5, value: 0 },
        Identified { id: 6, value: 0 },
        Identified { id: 7, value: 0 },
    ];
    let right = vec![
        Identified { id: 1, value: 0 },
        Identified { id: 2, value: 1 },
        Identified { id: 4, value: 0 },
        Identified { id: 3, value: 0 },
        Identified { id: 5, value: 0 },
        Identified { id: 6, value: 0 },
    ];

    let diff = left.diff(&right);

    match diff {
        edit::Edit::Copy => println!("no difference"),
        edit::Edit::Change(diff) => {
            diff.into_iter().map(|edit| {
                match edit {
                    collection::Edit::Copy(elem) => println!("copy: {:?}", elem),
                    collection::Edit::Insert(elem) => println!("insert: {:?}", elem),
                    collection::Edit::Remove(elem) => println!("remove: {:?}", elem),
                    collection::Edit::Change(EditedIdentified { id, value}) => {
                        println!("changed:");
                        match id {
                            edit::Edit::Copy => println!("    copy: id"),
                            edit::Edit::Change((left_id, right_id)) => println!("    id: {} => {}", left_id, right_id),
                        }
                        match value {
                            edit::Edit::Copy => println!("    copy: value"),
                            edit::Edit::Change((left_value, right_value)) => println!("    value: {} => {}", left_value, right_value),
                        }
                    }
                };
            }).collect::<Vec<_>>();
        },
    };
}

Diffus 在枚举中的应用

两个枚举之间的差异按预期工作,它将变体更改与关联变体字段更改分开。

枚举的可能编辑操作有 CopyVariantChangedAssociatedChanged

use diffus_derive::Diffus;
use diffus::{edit, Diffable};

#[derive(Diffus, Debug, PartialEq)]
enum Test {
    A,
    B(String),
    Bd(String, u32),
    C { x: u32 },
    Cd { x: u32, y: String },
}

fn main() {
    let left = Test::Cd {
        x: 42,
        y: "Bilbo Baggins".to_owned(),
    };
    let right = Test::Cd {
        x: 42,
        y: "Frodo Baggins".to_owned(),
    };
    if let edit::Edit::Change(edit::enm::Edit::AssociatedChanged(
        EditedTest::Cd { x, y },
    )) = left.diff(&right)
    {
        assert!(x.is_copy());
        assert!(y.is_change());
    } else {
        unreachable!()
    }

    let left = Test::Cd {
        x: 42,
        y: "Bilbo Baggins".to_owned(),
    };
    let right = Test::B("Frodo Baggins".to_owned());
    if let edit::Edit::Change(edit::enm::Edit::VariantChanged(l, r)) =
        left.diff(&right)
    {
        assert_eq!(&left, l);
        assert_eq!(&right, r);
    } else {
        unreachable!()
    }
}

使用diffus进行自定义差异

差异可以轻松地专门化以满足您的需求。

use diffus::{edit, Diffable};

struct Secret(String);

impl<'a> Diffable<'a> for Secret {
    type Diff = ();

    fn diff(&'a self, other: &'a Self) -> edit::Edit<Self::Diff> {
        if self.0 == other.0 {
            edit::Edit::Copy
        } else {
            edit::Edit::Change(())
        }
    }
}

fn main() {
    assert_eq!(
        Secret("Something".to_owned()).diff(&Secret("Else".to_owned()))
            .change().unwrap(),
        &()
    );
}

依赖项

~0.4–0.8MB
~16K SLoC