reddb_wire/replication/
util.rs1use serde_json::Value as JsonValue;
2
3#[derive(Debug, Clone, PartialEq, Eq)]
4pub enum ReplicationPayloadError {
5 NotJson,
6 NotObject,
7 MissingField(&'static str),
8 InvalidField(&'static str),
9 InvalidHex(&'static str),
10}
11
12impl std::fmt::Display for ReplicationPayloadError {
13 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
14 match self {
15 Self::NotJson => write!(f, "replication payload must be JSON"),
16 Self::NotObject => write!(f, "replication payload must be a JSON object"),
17 Self::MissingField(field) => write!(f, "missing replication field {field}"),
18 Self::InvalidField(field) => write!(f, "invalid replication field {field}"),
19 Self::InvalidHex(field) => write!(f, "invalid hex field {field}"),
20 }
21 }
22}
23
24impl std::error::Error for ReplicationPayloadError {}
25
26pub(crate) type Result<T> = std::result::Result<T, ReplicationPayloadError>;
27
28pub(crate) fn object_from_slice(bytes: &[u8]) -> Result<serde_json::Map<String, JsonValue>> {
29 match serde_json::from_slice(bytes).map_err(|_| ReplicationPayloadError::NotJson)? {
30 JsonValue::Object(obj) => Ok(obj),
31 _ => Err(ReplicationPayloadError::NotObject),
32 }
33}
34
35pub(crate) fn get_u64(
36 obj: &serde_json::Map<String, JsonValue>,
37 field: &'static str,
38) -> Result<u64> {
39 obj.get(field)
40 .and_then(JsonValue::as_u64)
41 .ok_or(ReplicationPayloadError::MissingField(field))
42}
43
44pub(crate) fn get_opt_u64(obj: &serde_json::Map<String, JsonValue>, field: &str) -> Option<u64> {
45 obj.get(field).and_then(JsonValue::as_u64)
46}
47
48pub(crate) fn get_bool_default(
49 obj: &serde_json::Map<String, JsonValue>,
50 field: &str,
51 default: bool,
52) -> bool {
53 obj.get(field)
54 .and_then(JsonValue::as_bool)
55 .unwrap_or(default)
56}
57
58pub(crate) fn get_string(
59 obj: &serde_json::Map<String, JsonValue>,
60 field: &'static str,
61) -> Result<String> {
62 obj.get(field)
63 .and_then(JsonValue::as_str)
64 .filter(|s| !s.is_empty())
65 .map(ToString::to_string)
66 .ok_or(ReplicationPayloadError::MissingField(field))
67}
68
69pub(crate) fn get_opt_string(
70 obj: &serde_json::Map<String, JsonValue>,
71 field: &str,
72) -> Option<String> {
73 obj.get(field)
74 .and_then(JsonValue::as_str)
75 .filter(|s| !s.is_empty())
76 .map(ToString::to_string)
77}
78
79pub(crate) fn hex_encode(bytes: &[u8]) -> String {
80 const ALPHA: &[u8; 16] = b"0123456789abcdef";
81 let mut out = String::with_capacity(bytes.len() * 2);
82 for byte in bytes {
83 out.push(ALPHA[(byte >> 4) as usize] as char);
84 out.push(ALPHA[(byte & 0x0f) as usize] as char);
85 }
86 out
87}
88
89pub(crate) fn hex_decode(field: &'static str, value: &str) -> Result<Vec<u8>> {
90 let bytes = value.as_bytes();
91 if !bytes.len().is_multiple_of(2) {
92 return Err(ReplicationPayloadError::InvalidHex(field));
93 }
94 let mut out = Vec::with_capacity(bytes.len() / 2);
95 let mut i = 0;
96 while i < bytes.len() {
97 let hi = hex_value(bytes[i]).ok_or(ReplicationPayloadError::InvalidHex(field))?;
98 let lo = hex_value(bytes[i + 1]).ok_or(ReplicationPayloadError::InvalidHex(field))?;
99 out.push((hi << 4) | lo);
100 i += 2;
101 }
102 Ok(out)
103}
104
105fn hex_value(byte: u8) -> Option<u8> {
106 match byte {
107 b'0'..=b'9' => Some(byte - b'0'),
108 b'a'..=b'f' => Some(byte - b'a' + 10),
109 b'A'..=b'F' => Some(byte - b'A' + 10),
110 _ => None,
111 }
112}