disma 0.16.4

Discord server management has never been easier!
Documentation
#[derive(Clone, Debug, PartialEq)]
pub enum Diff {
    Add(String),
    Remove(String),
    Update(String, Vec<Diff>),
}

pub trait Differ<T> {
    fn diffs_with(&self, target: &T) -> Vec<Diff>;
}

impl Differ<bool> for bool {
    fn diffs_with(&self, target: &Self) -> Vec<Diff> {
        diffs_between(self, target)
    }
}

impl Differ<String> for String {
    fn diffs_with(&self, target: &Self) -> Vec<Diff> {
        diffs_between(self, target)
    }
}

impl<'a, 'b> Differ<&'b str> for &'a str {
    fn diffs_with(&self, target: &&'b str) -> Vec<Diff> {
        diffs_between(self, target)
    }
}

impl<T, U> Differ<Option<U>> for Option<T>
where
    T: PartialEq<T> + ToString + Differ<U>,
    U: PartialEq<U> + ToString,
{
    fn diffs_with(&self, target: &Option<U>) -> Vec<Diff> {
        match (self, target) {
            (None, None) => vec![],
            (Some(origin), None) => vec![Diff::Remove(origin.to_string())],
            (None, Some(target)) => vec![Diff::Add(target.to_string())],
            (Some(origin), Some(target)) => origin.diffs_with(target),
        }
    }
}

impl<T> Differ<Vec<T>> for Vec<T>
where
    T: PartialEq<T> + ToString,
{
    fn diffs_with(&self, target: &Self) -> Vec<Diff> {
        let mut diffs = vec![];

        for item in self.iter() {
            if !target.contains(item) {
                diffs.push(Diff::Remove(item.to_string()))
            }
        }

        for item in target.iter() {
            if !self.contains(item) {
                diffs.push(Diff::Add(item.to_string()))
            }
        }

        diffs
    }
}

fn diffs_between<T>(origin: T, target: T) -> Vec<Diff>
where
    T: PartialEq<T> + ToString,
{
    let mut diffs = vec![];

    if origin != target {
        diffs.push(Diff::Remove(origin.to_string()));
        diffs.push(Diff::Add(target.to_string()));
    }

    diffs
}

#[cfg(test)]
mod tests {
    mod vec_diffs {
        use crate::core::diffs::{Diff, Differ};

        #[test]
        fn it_calculates_additions() {
            let origin = vec!["hello"];
            let target = vec!["hello", "world!"];

            let diffs = origin.diffs_with(&target);

            let expected_diffs = vec![Diff::Add("world!".into())];
            assert_eq!(diffs, expected_diffs);
        }

        #[test]
        fn it_calculates_removals() {
            let origin = vec!["hello", "world!"];
            let target = vec!["hello"];

            let diffs = origin.diffs_with(&target);

            let expected_diffs = vec![Diff::Remove("world!".into())];
            assert_eq!(diffs, expected_diffs);
        }

        #[test]
        fn it_calculates_both_additions_and_removals() {
            let origin = vec!["super", "mario"];
            let target = vec!["hello", "world!"];

            let diffs = origin.diffs_with(&target);

            let expected_diffs = vec![
                Diff::Remove("super".into()),
                Diff::Remove("mario".into()),
                Diff::Add("hello".into()),
                Diff::Add("world!".into()),
            ];
            assert_eq!(diffs, expected_diffs);
        }

        #[test]
        fn given_same_arrays_returns_no_diff() {
            let origin = vec!["hello", "world!"];
            let target = vec!["hello", "world!"];

            let diffs = origin.diffs_with(&target);

            assert_eq!(diffs.len(), 0);
        }
    }

    mod str_diffs {
        use crate::core::diffs::{Diff, Differ};

        #[test]
        fn given_same_str_returns_no_diff() {
            let origin = "hello";
            let target = "hello";

            let diffs = origin.diffs_with(&target);

            assert_eq!(diffs.len(), 0);
        }

        #[test]
        fn can_differ_strings() {
            let origin = "hello";
            let target = "world!";

            let diffs = origin.diffs_with(&target);

            let expected_diffs = vec![Diff::Remove("hello".into()), Diff::Add("world!".into())];
            assert_eq!(diffs, expected_diffs);
        }
    }

    mod bool_diffs {
        use crate::core::diffs::{Diff, Differ};

        #[test]
        fn can_differ_bools() {
            let origin = true;
            let target = false;

            let diffs = origin.diffs_with(&target);

            let expected_diffs = vec![Diff::Remove("true".into()), Diff::Add("false".into())];
            assert_eq!(diffs, expected_diffs);
        }
    }
}