#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Changed<'a, T: 'a> {
Added(&'a T),
Removed(&'a T),
AddedAt(usize, &'a T, usize),
RemovedAt(usize, &'a T, usize),
Moved(&'a T, usize, usize),
ModifiedAt(usize, &'a T),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MapChanged<'a, K: 'a, V: 'a> {
AddedEntry(&'a K, &'a V),
RemovedEntry(&'a K, &'a V),
ChangedEntry(&'a K),
}
pub trait HasChanges {
type Change<'a>
where
Self: 'a;
fn collect_changes<'a>(old: &'a Self, new: &'a Self, out: &mut Vec<Self::Change<'a>>)
where
Self: 'a;
}
#[inline]
pub fn diff_changes<'a, T: HasChanges>(old: &'a T, new: &'a T) -> Vec<T::Change<'a>> {
let mut v = Vec::new();
T::collect_changes(old, new, &mut v);
v
}
#[macro_export]
macro_rules! changed {
(
$change:expr;
// variant WITH `@` shorthand
$ty:ident $( . $path:ident )* @ ( $pat:pat ) => $body:block
$( ; $($rest:tt)* )?
) => {{
$crate::__changed_arm_at! { $change, $ty $( . $path )*, $pat, $body }
$( $crate::changed!($change; $($rest)* ); )?
}};
(
$change:expr;
$ty:ident $( . $path:ident )* ( $pat:pat ) => $body:block
$( ; $($rest:tt)* )?
) => {{
$crate::__changed_arm! { $change, $ty $( . $path )*, $pat, $body }
$( $crate::changed!($change; $($rest)* ); )?
}};
( $change:expr; ) => {};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __changed_arm {
( $change:expr, $ty:ident . self, $pat:pat, $body:block ) => {
if let $ty Change::self_($pat) = $change { $body }
};
( $change:expr, $ty:ident . $field:ident, $pat:pat, $body:block ) => {
paste::paste! {
if let [<$ty Change>]::$field($pat) = $change { $body }
}
};
( $change:expr, $ty:ident . $first:ident . $($tail:ident).+, $pat:pat, $body:block ) => {
paste::paste! {
if let [<$ty Change>]::[<$first>](inner) = $change {
$crate::changed!(inner; [<$first:camel>] . $($tail).+($pat) => $body );
}
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __changed_arm_at {
( $change:expr, $ty:ident . $field:ident, $pat:pat, $body:block ) => {
paste::paste! {
if let [<$ty Change>]::$field(inner) = $change {
if let [<$field:camel Change>]::self_($pat) = inner { $body }
}
}
};
( $change:expr, $ty:ident . $first:ident . $($tail:ident).+, $pat:pat, $body:block ) => {
paste::paste! {
if let [<$ty Change>]::[<$first>](inner) = $change {
$crate::__changed_arm_at!( inner, [<$first:camel>] . $($tail).+, $pat, $body );
}
}
};
}