protobuf_mapper/
convert.rs

1use bigdecimal::BigDecimal;
2use chrono::{DateTime, Utc};
3use prost_types;
4use prost_types::{Timestamp, Value};
5use serde::{Deserialize, Serialize};
6use serde_json::Value as JsonValue;
7use std::cmp::Eq;
8use std::collections::HashMap;
9use std::hash::Hash;
10
11use crate::result::{self, Result};
12use crate::{ProtoPack, ProtoUnpack};
13
14macro_rules! impl_option {
15  ($rust:ty => $proto:ty) => {
16    impl ProtoPack<Option<$proto>> for $rust {
17      fn pack(self) -> Result<Option<$proto>> {
18        Ok(Some(self.pack()?))
19      }
20    }
21
22    impl ProtoUnpack<Option<$proto>> for $rust {
23      fn unpack(value: Option<$proto>) -> Result<$rust> {
24        if let Some(value) = value {
25          Ok(<$rust>::unpack(value)?)
26        } else {
27          Err(result::Error::ValueNotPresent)
28        }
29      }
30    }
31  };
32}
33
34// JSON value
35
36impl ProtoPack<Value> for JsonValue {
37  fn pack(self) -> Result<Value> {
38    json_value_to_value(self)
39  }
40}
41
42impl ProtoUnpack<Value> for JsonValue {
43  fn unpack(value: Value) -> Result<JsonValue> {
44    value_to_json_value(value)
45  }
46}
47
48impl_option!(JsonValue => Value);
49
50const MAX_JSON_NEST: usize = 100;
51
52fn value_to_json_value(value: Value) -> Result<JsonValue> {
53  fn convert(nest: usize, value: Value) -> Result<JsonValue> {
54    use prost_types::{value::Kind, ListValue, Struct};
55    use serde_json::{Map as JsonMap, Number as JsonNumber};
56
57    if nest >= MAX_JSON_NEST {
58      return Err(result::Error::JsonValueNestedTooDeeply);
59    }
60
61    if let Some(kind) = value.kind {
62      let converted = match kind {
63        Kind::NullValue(_) => JsonValue::Null,
64        Kind::NumberValue(v) => {
65          if let Some(number) = JsonNumber::from_f64(v) {
66            JsonValue::Number(number)
67          } else {
68            JsonValue::Null
69          }
70        }
71        Kind::StringValue(v) => JsonValue::String(v),
72        Kind::BoolValue(v) => JsonValue::Bool(v),
73        Kind::StructValue(Struct { fields }) => JsonValue::Object({
74          let mut json_map = JsonMap::with_capacity(fields.len());
75          for (k, v) in fields {
76            json_map.insert(k, convert(nest + 1, v)?);
77          }
78          json_map
79        }),
80        Kind::ListValue(ListValue { values }) => {
81          let mut json_values = Vec::with_capacity(values.len());
82          for v in values {
83            json_values.push(convert(nest + 1, v)?);
84          }
85          JsonValue::Array(json_values)
86        }
87      };
88      Ok(converted)
89    } else {
90      Ok(JsonValue::Null)
91    }
92  }
93
94  convert(0, value)
95}
96
97fn json_value_to_value(value: JsonValue) -> Result<Value> {
98  fn convert(nest: usize, value: JsonValue) -> Result<Value> {
99    use prost_types::{value::Kind, ListValue, Struct};
100    use std::collections::BTreeMap;
101
102    if nest >= MAX_JSON_NEST {
103      return Err(result::Error::JsonValueNestedTooDeeply);
104    }
105
106    let kind = match value {
107      JsonValue::Null => Kind::NullValue(0),
108      JsonValue::Bool(v) => Kind::BoolValue(v),
109      JsonValue::Number(v) => {
110        if let Some(v) = v.as_f64() {
111          Kind::NumberValue(v)
112        } else {
113          Kind::NullValue(0)
114        }
115      }
116      JsonValue::String(v) => Kind::StringValue(v),
117      JsonValue::Array(values) => {
118        let mut value_values = Vec::with_capacity(values.len());
119        for v in values {
120          value_values.push(convert(nest + 1, v)?);
121        }
122        Kind::ListValue(ListValue {
123          values: value_values,
124        })
125      }
126      JsonValue::Object(map) => {
127        let mut value_map = BTreeMap::new();
128        for (k, v) in map {
129          value_map.insert(k, convert(nest + 1, v)?);
130        }
131        Kind::StructValue(Struct { fields: value_map })
132      }
133    };
134    Ok(Value { kind: Some(kind) })
135  }
136
137  convert(0, value)
138}
139
140/// Helper type to convert any serializable type from/to `google.protobuf.Value`
141pub struct Json<T>(pub T);
142
143impl<T> ProtoPack<Value> for Json<T>
144where
145  T: Serialize + for<'de> Deserialize<'de>,
146{
147  fn pack(self) -> Result<Value> {
148    pack_value(self.0)
149  }
150}
151
152impl<T> ProtoUnpack<Value> for Json<T>
153where
154  T: Serialize + for<'de> Deserialize<'de>,
155{
156  fn unpack(value: Value) -> Result<Json<T>> {
157    unpack_value(value).map(Json)
158  }
159}
160
161pub fn pack_value<T>(value: T) -> Result<Value>
162where
163  T: Serialize,
164{
165  serde_json::to_value(&value)?.pack()
166}
167
168pub fn unpack_value<T>(value: Value) -> Result<T>
169where
170  T: for<'de> Deserialize<'de>,
171{
172  let value = JsonValue::unpack(value)?;
173  Ok(serde_json::from_value(value)?)
174}
175
176// Timestamp
177
178impl ProtoPack<Timestamp> for DateTime<Utc> {
179  fn pack(self) -> Result<Timestamp> {
180    Ok(Timestamp {
181      seconds: self.timestamp(),
182      nanos: self.timestamp_subsec_nanos() as i32,
183    })
184  }
185}
186
187impl ProtoUnpack<Timestamp> for DateTime<Utc> {
188  fn unpack(Timestamp { seconds, nanos }: Timestamp) -> Result<DateTime<Utc>> {
189    let dt = chrono::NaiveDateTime::from_timestamp(seconds, nanos as u32);
190    Ok(DateTime::from_utc(dt, Utc))
191  }
192}
193
194// Duration
195
196impl ProtoPack<prost_types::Duration> for chrono::Duration {
197  fn pack(self) -> Result<prost_types::Duration> {
198    let duration = <prost_types::Duration as TryFrom<std::time::Duration>>::try_from(
199      self.to_std().map_err(|e| result::Error::ParseDuration {
200        message: e.to_string(),
201      })?,
202    )
203    .map_err(|e| result::Error::ParseDuration {
204      message: e.to_string(),
205    })?;
206
207    Ok(duration)
208  }
209}
210
211impl ProtoUnpack<prost_types::Duration> for chrono::Duration {
212  fn unpack(value: prost_types::Duration) -> Result<chrono::Duration> {
213    let std_duration = <prost_types::Duration as TryInto<std::time::Duration>>::try_into(value)
214      .map_err(|e| result::Error::ParseDuration {
215        message: format!(
216          "Source duration value is out of range for the target type: {}",
217          e.to_string()
218        ),
219      })?;
220
221    chrono::Duration::from_std(std_duration).map_err(|e| result::Error::ParseDuration {
222      message: e.to_string(),
223    })
224  }
225}
226
227impl_option!(DateTime<Utc> => Timestamp);
228impl_option!(chrono::Duration => prost_types::Duration);
229
230// BigDecimal
231
232impl ProtoPack<String> for BigDecimal {
233  fn pack(self) -> Result<String> {
234    Ok(self.to_string())
235  }
236}
237
238impl<T> ProtoUnpack<T> for BigDecimal
239where
240  T: AsRef<str>,
241{
242  fn unpack(v: T) -> Result<BigDecimal> {
243    v.as_ref().parse().map_err(Into::into)
244  }
245}
246
247// Wrappers
248
249macro_rules! impl_self {
250  (
251    $($ty:ty),*
252  ) => {
253    $(
254      impl ProtoPack<$ty> for $ty {
255        fn pack(self) -> Result<$ty> {
256          Ok(self)
257        }
258      }
259
260      impl ProtoUnpack<$ty> for $ty {
261        fn unpack(value: $ty) -> Result<$ty> {
262          Ok(value)
263        }
264      }
265    )*
266  }
267}
268
269impl_self! {
270  f32,
271  f64,
272  i64,
273  u64,
274  i32,
275  u32,
276  bool,
277  String,
278  Vec<u8>
279}
280
281// repeated value
282
283impl<T, T2> ProtoPack<Vec<T>> for Vec<T2>
284where
285  T2: ProtoPack<T>,
286{
287  fn pack(self) -> Result<Vec<T>> {
288    let mut r = vec![];
289    for (i, elem) in self.into_iter().enumerate() {
290      let item = elem.pack().map_err(|e| result::Error::ListElement {
291        source: Box::new(e),
292        index: i,
293      })?;
294      r.push(item);
295    }
296    Ok(r)
297  }
298}
299
300impl<T, T2> ProtoUnpack<Vec<T>> for Vec<T2>
301where
302  T2: ProtoUnpack<T>,
303{
304  fn unpack(value: Vec<T>) -> Result<Vec<T2>> {
305    let mut r = vec![];
306    for (i, elem) in value.into_iter().enumerate() {
307      let item = T2::unpack(elem).map_err(|e| result::Error::ListElement {
308        source: Box::new(e),
309        index: i,
310      })?;
311      r.push(item);
312    }
313    Ok(r)
314  }
315}
316
317// map
318
319impl<K, V, K2, V2> ProtoPack<HashMap<K, V>> for HashMap<K2, V2>
320where
321  K: Eq + Hash,
322  K2: ProtoPack<K> + Eq + Hash,
323  V2: ProtoPack<V>,
324{
325  fn pack(self) -> Result<HashMap<K, V>> {
326    let mut r = vec![];
327    for (k, v) in self.into_iter() {
328      let k2 = k.pack().map_err(|e| result::Error::MapEntry {
329        source: Box::new(e),
330      })?;
331      let v2 = v.pack().map_err(|e| result::Error::MapEntry {
332        source: Box::new(e),
333      })?;
334      r.push((k2, v2));
335    }
336    Ok(r.into_iter().collect())
337  }
338}
339
340impl<K, V, K2, V2> ProtoUnpack<HashMap<K, V>> for HashMap<K2, V2>
341where
342  K: Eq + Hash,
343  K2: ProtoUnpack<K> + Eq + Hash,
344  V2: ProtoUnpack<V>,
345{
346  fn unpack(value: HashMap<K, V>) -> Result<HashMap<K2, V2>> {
347    let mut r = vec![];
348    for (k, v) in value.into_iter() {
349      let k2 = K2::unpack(k).map_err(|e| result::Error::MapEntry {
350        source: Box::new(e),
351      })?;
352      let v2 = V2::unpack(v).map_err(|e| result::Error::MapEntry {
353        source: Box::new(e),
354      })?;
355      r.push((k2, v2));
356    }
357    Ok(r.into_iter().collect())
358  }
359}