identity_diff/
option.rs

1// Copyright 2020-2021 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4use serde::Deserialize;
5use serde::Serialize;
6
7use std::fmt::Debug;
8use std::fmt::Formatter;
9
10use crate::Diff;
11
12/// A `DiffOption<T>` type which represents a Diffed `Option<T>`.  By default this value is untagged for `serde`. It
13/// also converts `to` and `from` Option<T> when serialized/deserialized
14#[derive(Clone, PartialEq, Deserialize, Serialize)]
15#[serde(untagged, into = "Option<T>", from = "Option<T>")]
16pub enum DiffOption<T: Diff> {
17  Some(<T as Diff>::Type),
18  None,
19}
20
21/// `Diff` Implementation for `Option<T>`
22impl<T> Diff for Option<T>
23where
24  T: Diff + Clone + Debug + PartialEq + Default + for<'de> Deserialize<'de> + Serialize,
25{
26  /// The Corresponding Diff type for `Option<T>`
27  type Type = DiffOption<T>;
28
29  /// Compares two `Option<T>` types; `self` and `other` and finds the Difference between them, returning a
30  /// `DiffOption<T>` type.
31  fn diff(&self, other: &Self) -> crate::Result<Self::Type> {
32    match (self, other) {
33      (Some(x), Some(y)) => Ok(Self::Type::Some(x.diff(y)?)),
34      (None, Some(y)) => Ok(Self::Type::Some(y.clone().into_diff()?)),
35      _ => Ok(Self::Type::None),
36    }
37  }
38
39  /// Merges a `DiffOption<T>`; `diff` type with an `Option<T>` type; `self`.
40  fn merge(&self, diff: Self::Type) -> crate::Result<Self> {
41    match (self, diff) {
42      (None, DiffOption::None) => Ok(None),
43      (Some(_), DiffOption::None) => Ok(None),
44      (None, DiffOption::Some(ref d)) => Ok(Some(<T>::from_diff(d.clone())?)),
45      (Some(t), DiffOption::Some(ref d)) => Ok(Some(t.merge(d.clone())?)),
46    }
47  }
48
49  /// converts a `DiffOption<T>`; `diff` to an `Option<T>` type.
50  fn from_diff(diff: Self::Type) -> crate::Result<Self> {
51    match diff {
52      Self::Type::None => Ok(None),
53      Self::Type::Some(diff) => Ok(Some(<T>::from_diff(diff)?)),
54    }
55  }
56
57  /// converts a `Option<T>`; `self` to an `DiffOption<T>` type.
58  fn into_diff(self) -> crate::Result<Self::Type> {
59    match self {
60      Self::None => Ok(DiffOption::None),
61      Self::Some(t) => Ok(DiffOption::Some(t.into_diff()?)),
62    }
63  }
64}
65
66/// Debug implementation for `DiffOption<T>`.
67impl<T: Diff> std::fmt::Debug for DiffOption<T> {
68  fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
69    match &self {
70      Self::Some(d) => write!(f, "DiffOption::Some({:#?})", d),
71      Self::None => write!(f, "DiffOption::None"),
72    }
73  }
74}
75
76/// Default implementation for `DiffOption<T>`.
77impl<T: Diff> Default for DiffOption<T> {
78  fn default() -> Self {
79    Self::None
80  }
81}
82
83/// From `DiffOption<T>` implementation for `Option<T>`.
84impl<T> From<DiffOption<T>> for Option<T>
85where
86  T: Diff,
87{
88  fn from(other: DiffOption<T>) -> Self {
89    match other {
90      DiffOption::Some(s) => Some(Diff::from_diff(s).expect("Unable to convert from diff")),
91      DiffOption::None => None,
92    }
93  }
94}
95
96/// From `Option<T>` implementation for `DiffOption<T>`.
97impl<T> From<Option<T>> for DiffOption<T>
98where
99  T: Diff,
100{
101  fn from(opt: Option<T>) -> Self {
102    match opt {
103      Some(s) => DiffOption::Some(s.into_diff().expect("Unable to convert to diff")),
104      None => DiffOption::None,
105    }
106  }
107}
108
109#[cfg(test)]
110mod tests {
111  use super::*;
112  use crate::string::DiffString;
113
114  #[test]
115  fn test_option_diff() {
116    let a = Some("A".to_owned());
117    let b = Some("B".to_owned());
118
119    let diff = a.diff(&b).unwrap();
120
121    assert_eq!(diff, DiffOption::Some(DiffString(Some("B".to_owned()))));
122
123    let c = a.merge(diff).unwrap();
124
125    assert_eq!(b, c);
126  }
127}