diff-struct icon indicating copy to clipboard operation
diff-struct copied to clipboard

Q: Is it possible to diff a struct where one of the fields is serde_json::Value

Open AdamJSoftware opened this issue 2 years ago • 1 comments

I want to ensure that the data is proper json when ser/de, so I made the struct_value a serde_json::Value. However, if I try to diff it I get the following JsonValue: Diff is not satisfied


#[derive(Deserialize, Debug, Clone, Serialize, PartialEq, Diff)]
pub struct Field {
    pub r#type: PostgresTypes,
    #[serde(default = "default_false")]
    pub unique: bool,
    #[serde(default = "default_false")]
    pub required: bool,
    #[serde(default = "default_false")]
    pub primary: bool,
    #[serde(default = "default_empty_json")]
    pub metadata: Option<serde_json::Value>,
}

AdamJSoftware avatar Jul 10 '23 01:07 AdamJSoftware

So I've had a chance now to think about this, there's basically two options. I would probably create a wrapper struct for arbitrary json values, and implement Diff on this. The diff implementation would basically look like:

#[derive(Debug, Default, PartialEq, Clone, Serialize, Deserialize)]
#[serde(transparent)]
struct ArbitraryJson(Value);

impl Diff for ArbitraryJson {
    type Repr = Option<Value>;

    fn diff(&self, other: &Self) -> Self::Repr {
        if self != other {
            Some(other.clone())
        } else {
            None
        }
    }

    fn apply(&mut self, diff: &Self::Repr) {
        if let Some(diff) = diff {
            *self = diff.clone()
        }
    }

    fn identity() -> Self {
        Default::default()
    }
}

Alternatively, you can wrap it in a Cow, which works pretty much the same way (and likewise doesn't require T to implement Diff), but I think you have to be bothered to specify a lifetime for that.

What I'd like to do going forward, but will take some more time to refine, is highly generic diffing. That means a user should be able to specify which method of diffing they want to use for individual struct fields, instead of assuming that one type has only one diff method. This way, if a type satisfies some trait bounds, we can use a certain diff method on it without ever manually implementing Diff on that type. The first example would be this kind of "equality + clone" check

benhall-7 avatar Jul 23 '23 18:07 benhall-7