mysql_common/binlog/
jsondiff.rs

1// Copyright (c) 2021 Anatoly Ikorsky
2//
3// Licensed under the Apache License, Version 2.0
4// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
5// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. All files in the project carrying such notice may not be copied,
7// modified, or distributed except according to those terms.
8
9use std::{borrow::Cow, convert::TryFrom, io};
10
11use crate::{
12    io::ParseBuf,
13    misc::raw::{bytes::LenEnc, Const, RawBytes, RawInt},
14    proto::MyDeserialize,
15};
16
17use super::jsonb;
18
19/// An operation kind of a JsonDiff object.
20#[allow(non_camel_case_types)]
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
22#[repr(u8)]
23pub enum JsonDiffOperation {
24    /// The JSON value in the given path is replaced with a new value.
25    REPLACE,
26    /// Add a new element at the given path.
27    INSERT,
28    /// The JSON value at the given path is removed from an array or object.
29    REMOVE,
30}
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)]
33#[error("Unknown JsonDiff operation {}", _0)]
34#[repr(transparent)]
35pub struct UnknownJsonDiffOperation(pub u8);
36
37impl From<UnknownJsonDiffOperation> for u8 {
38    fn from(x: UnknownJsonDiffOperation) -> Self {
39        x.0
40    }
41}
42
43impl TryFrom<u8> for JsonDiffOperation {
44    type Error = UnknownJsonDiffOperation;
45
46    fn try_from(value: u8) -> Result<Self, Self::Error> {
47        match value {
48            0 => Ok(Self::REPLACE),
49            1 => Ok(Self::INSERT),
50            2 => Ok(Self::REMOVE),
51            x => Err(UnknownJsonDiffOperation(x)),
52        }
53    }
54}
55
56/// A class that represents a logical change to a JSON document.
57///
58/// It is used by row-based replication to send information about changes in JSON documents
59/// without sending the whole updated document.
60#[derive(Debug, Clone, PartialEq)]
61pub struct JsonDiff<'a> {
62    path: RawBytes<'a, LenEnc>,
63    operation: Const<JsonDiffOperation, u8>,
64    value: Option<jsonb::Value<'a>>,
65}
66
67impl<'a> JsonDiff<'a> {
68    /// Returns the raw JsonDiff path.
69    pub fn path(&'a self) -> &'a [u8] {
70        self.path.as_bytes()
71    }
72
73    /// Returns the JsonDiff path as a string (lossy converted).
74    pub fn path_str(&'a self) -> Cow<'a, str> {
75        self.path.as_str()
76    }
77
78    /// Returns JsonDiff operation.
79    pub fn operation(&self) -> JsonDiffOperation {
80        *self.operation
81    }
82
83    /// Returns JsonDiff value (if any).
84    pub fn value(&'a self) -> Option<&'a jsonb::Value<'a>> {
85        self.value.as_ref()
86    }
87
88    /// Returns a `'static` version of `self`.
89    pub fn into_owned(self) -> JsonDiff<'static> {
90        JsonDiff {
91            path: self.path.into_owned(),
92            operation: self.operation,
93            value: self.value.map(|x| x.into_owned()),
94        }
95    }
96}
97
98impl<'de> MyDeserialize<'de> for JsonDiff<'de> {
99    const SIZE: Option<usize> = None;
100    type Ctx = ();
101
102    fn deserialize((): Self::Ctx, buf: &mut ParseBuf<'de>) -> io::Result<Self> {
103        let operation: Const<JsonDiffOperation, u8> = buf.parse(())?;
104        let path = buf.parse(())?;
105        let value = if *operation != JsonDiffOperation::REMOVE {
106            let len: RawInt<LenEnc> = buf.parse(())?;
107            let mut value: ParseBuf = buf.parse(*len as usize)?;
108            Some(value.parse(())?)
109        } else {
110            None
111        };
112
113        Ok(Self {
114            path,
115            operation,
116            value,
117        })
118    }
119}