1#![warn(
38 missing_debug_implementations,
39 missing_docs,
40 unsafe_code,
41 bare_trait_objects
42)]
43#![warn(clippy::pedantic)]
44#![allow(
45 clippy::cast_possible_wrap, clippy::cast_possible_truncation, clippy::cast_sign_loss,
47 clippy::module_name_repetitions, clippy::similar_names, clippy::must_use_candidate,
49 clippy::pub_enum_variant_names,
50 clippy::indexing_slicing,
52 clippy::missing_errors_doc
54)]
55
56#[macro_use]
57extern crate serde_derive; pub use protobuf_convert::*;
60
61pub mod proto;
62
63use anyhow::{ensure, format_err, Error};
64use chrono::{DateTime, TimeZone, Utc};
65use protobuf::well_known_types;
66use serde::{de::Visitor, Deserializer, Serializer};
67
68use std::{collections::HashMap, convert::TryFrom, fmt};
69
70use crate::proto::bit_vec::BitVec;
71
72#[cfg(test)]
73mod tests;
74
75pub trait ProtobufConvert: Sized {
77 type ProtoStruct;
79
80 fn to_pb(&self) -> Self::ProtoStruct;
82 fn from_pb(pb: Self::ProtoStruct) -> Result<Self, Error>;
84}
85
86impl ProtobufConvert for DateTime<Utc> {
87 type ProtoStruct = well_known_types::Timestamp;
88
89 fn to_pb(&self) -> Self::ProtoStruct {
90 let mut ts = Self::ProtoStruct::new();
91 ts.set_seconds(self.timestamp());
92 ts.set_nanos(self.timestamp_subsec_nanos() as i32);
93 ts
94 }
95
96 fn from_pb(pb: Self::ProtoStruct) -> Result<Self, Error> {
97 Utc.timestamp_opt(pb.get_seconds(), pb.get_nanos() as u32)
98 .single()
99 .ok_or_else(|| format_err!("Failed to convert timestamp from bytes"))
100 }
101}
102
103impl ProtobufConvert for String {
104 type ProtoStruct = Self;
105
106 fn to_pb(&self) -> Self::ProtoStruct {
107 self.clone()
108 }
109
110 fn from_pb(pb: Self::ProtoStruct) -> Result<Self, Error> {
111 Ok(pb)
112 }
113}
114
115impl ProtobufConvert for u16 {
116 type ProtoStruct = u32;
117
118 fn to_pb(&self) -> Self::ProtoStruct {
119 u32::from(*self)
120 }
121
122 fn from_pb(pb: Self::ProtoStruct) -> Result<Self, Error> {
123 u16::try_from(pb).map_err(|_| format_err!("Value is out of range"))
124 }
125}
126
127impl ProtobufConvert for i16 {
128 type ProtoStruct = i32;
129
130 fn to_pb(&self) -> Self::ProtoStruct {
131 i32::from(*self)
132 }
133
134 fn from_pb(pb: Self::ProtoStruct) -> Result<Self, Error> {
135 i16::try_from(pb).map_err(|_| format_err!("Value is out of range"))
136 }
137}
138
139impl<T> ProtobufConvert for Vec<T>
140where
141 T: ProtobufConvert,
142{
143 type ProtoStruct = Vec<T::ProtoStruct>;
144
145 fn to_pb(&self) -> Self::ProtoStruct {
146 self.iter().map(ProtobufConvert::to_pb).collect()
147 }
148 fn from_pb(pb: Self::ProtoStruct) -> Result<Self, Error> {
149 pb.into_iter()
150 .map(ProtobufConvert::from_pb)
151 .collect::<Result<Vec<_>, _>>()
152 }
153}
154
155impl ProtobufConvert for () {
156 type ProtoStruct = protobuf::well_known_types::Empty;
157
158 fn to_pb(&self) -> Self::ProtoStruct {
159 Self::ProtoStruct::default()
160 }
161
162 fn from_pb(_pb: Self::ProtoStruct) -> Result<Self, Error> {
163 Ok(())
164 }
165}
166
167impl ProtobufConvert for Vec<u8> {
169 type ProtoStruct = Vec<u8>;
170
171 fn to_pb(&self) -> Self::ProtoStruct {
172 self.clone()
173 }
174 fn from_pb(pb: Self::ProtoStruct) -> Result<Self, Error> {
175 Ok(pb)
176 }
177}
178
179impl<K, T, S> ProtobufConvert for HashMap<K, T, S>
182where
183 K: Eq + std::hash::Hash + std::fmt::Debug + Clone,
184 T: ProtobufConvert,
185 S: Default + std::hash::BuildHasher,
186{
187 type ProtoStruct = HashMap<K, T::ProtoStruct, S>;
188 fn to_pb(&self) -> Self::ProtoStruct {
189 self.iter().map(|(k, v)| (k.clone(), v.to_pb())).collect()
190 }
191 fn from_pb(mut pb: Self::ProtoStruct) -> Result<Self, Error> {
192 pb.drain()
193 .map(|(k, v)| ProtobufConvert::from_pb(v).map(|v| (k, v)))
194 .collect::<Result<HashMap<_, _, _>, _>>()
195 }
196}
197
198impl ProtobufConvert for bit_vec::BitVec {
199 type ProtoStruct = BitVec;
200
201 fn to_pb(&self) -> Self::ProtoStruct {
202 let mut bit_vec = BitVec::new();
203 bit_vec.set_data(self.to_bytes());
204 bit_vec.set_len(self.len() as u64);
205 bit_vec
206 }
207
208 fn from_pb(pb: Self::ProtoStruct) -> Result<Self, Error> {
209 let data = pb.get_data();
210 let mut bit_vec = bit_vec::BitVec::from_bytes(data);
211 bit_vec.truncate(pb.get_len() as usize);
212 Ok(bit_vec)
213 }
214}
215
216macro_rules! impl_protobuf_convert_scalar {
217 ( $( $name:tt ),* )=> {
218 $(
219 impl ProtobufConvert for $name {
220 type ProtoStruct = $name;
221 fn to_pb(&self) -> Self::ProtoStruct {
222 *self
223 }
224 fn from_pb(pb: Self::ProtoStruct) -> Result<Self, Error> {
225 Ok(pb)
226 }
227 }
228 )*
229 };
230}
231
232impl_protobuf_convert_scalar! { bool, u32, u64, i32, i64, f32, f64 }
233
234macro_rules! impl_protobuf_convert_fixed_byte_array {
235 ( $( $arr_len:expr ),* ) => {
236 $(
237 impl ProtobufConvert for [u8; $arr_len] {
239 type ProtoStruct = Vec<u8>;
240
241 fn to_pb(&self) -> Self::ProtoStruct {
242 self.to_vec()
243 }
244
245 fn from_pb(pb: Self::ProtoStruct) -> Result<Self, Error> {
246 ensure!(
247 pb.len() == $arr_len,
248 "wrong array size: actual {}, expected {}",
249 pb.len(),
250 $arr_len
251 );
252
253 Ok({
254 let mut array = [0; $arr_len];
255 array.copy_from_slice(&pb);
256 array
257 })
258 }
259 }
260 )*
261 };
262}
263
264impl_protobuf_convert_fixed_byte_array! {
267 8, 16, 24, 32, 40, 48, 56, 64,
268 72, 80, 88, 96, 104, 112, 120, 128,
269 160, 256, 512, 1024, 2048
270}
271
272#[derive(Debug)]
308pub struct ProtobufBase64(());
309
310impl ProtobufBase64 {
311 pub fn serialize<S, T>(bytes: &T, serializer: S) -> Result<S::Ok, S::Error>
313 where
314 S: Serializer,
315 T: AsRef<[u8]> + ?Sized,
316 {
317 if serializer.is_human_readable() {
318 serializer.serialize_str(&base64::encode_config(bytes, base64::STANDARD_NO_PAD))
319 } else {
320 serializer.serialize_bytes(bytes.as_ref())
321 }
322 }
323
324 pub fn decode(value: &str) -> Result<Vec<u8>, base64::DecodeError> {
327 let value_without_padding = if value.ends_with("==") {
329 &value[..value.len() - 2]
330 } else if value.ends_with('=') {
331 &value[..value.len() - 1]
332 } else {
333 value
334 };
335
336 let is_url_safe = value_without_padding.contains(|ch: char| ch == '-' || ch == '_');
337 let config = if is_url_safe {
338 base64::URL_SAFE_NO_PAD
339 } else {
340 base64::STANDARD_NO_PAD
341 };
342 base64::decode_config(value_without_padding, config)
343 }
344
345 pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
347 where
348 D: Deserializer<'de>,
349 {
350 use serde::de::Error as DeError;
351
352 struct Base64Visitor;
353
354 impl<'de> Visitor<'de> for Base64Visitor {
355 type Value = Vec<u8>;
356
357 fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
358 f.write_str("base64-encoded byte array")
359 }
360
361 fn visit_str<E: DeError>(self, value: &str) -> Result<Self::Value, E> {
362 ProtobufBase64::decode(value).map_err(E::custom)
363 }
364
365 fn visit_bytes<E: DeError>(self, value: &[u8]) -> Result<Self::Value, E> {
367 Ok(value.to_vec())
368 }
369 }
370
371 struct BytesVisitor;
372
373 impl<'de> Visitor<'de> for BytesVisitor {
374 type Value = Vec<u8>;
375
376 fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
377 f.write_str("byte array")
378 }
379
380 fn visit_bytes<E: DeError>(self, value: &[u8]) -> Result<Self::Value, E> {
381 Ok(value.to_vec())
382 }
383 }
384
385 if deserializer.is_human_readable() {
386 deserializer.deserialize_str(Base64Visitor)
387 } else {
388 deserializer.deserialize_bytes(BytesVisitor)
389 }
390 }
391}