1use std::borrow::Cow;
13use std::char::CharTryFromError;
14use std::collections::{BTreeMap, BTreeSet};
15use std::fmt;
16use std::num::{NonZeroU64, TryFromIntError};
17use std::sync::Arc;
18
19use mz_ore::Overflowing;
20use mz_ore::cast::CastFrom;
21use mz_ore::num::{NonNeg, NonNegError};
22use num::Signed;
23use proptest::prelude::Strategy;
24use prost::UnknownEnumValue;
25
26#[cfg(feature = "chrono")]
27pub mod chrono;
28
29include!(concat!(env!("OUT_DIR"), "/mz_proto.rs"));
30
31#[derive(Debug)]
34pub enum TryFromProtoError {
35 TryFromIntError(TryFromIntError),
37 NonNegError(NonNegError),
39 CharTryFromError(CharTryFromError),
41 DateConversionError(String),
43 RegexError(regex::Error),
45 RowConversionError(String),
47 DeserializationError(serde_json::Error),
50 MissingField(String),
53 UnknownEnumVariant(String),
55 InvalidShardId(String),
58 CodecMismatch(String),
61 InvalidPersistState(String),
63 InvalidSemverVersion(String),
65 InvalidUri(http::uri::InvalidUri),
67 GlobError(globset::Error),
69 InvalidUrl(url::ParseError),
71 InvalidBitFlags(String),
73 LikePatternDeserializationError(String),
75 InvalidFieldError(String),
77}
78
79impl TryFromProtoError {
80 pub fn missing_field<T: ToString>(s: T) -> TryFromProtoError {
82 TryFromProtoError::MissingField(s.to_string())
83 }
84
85 pub fn unknown_enum_variant<T: ToString>(s: T) -> TryFromProtoError {
87 TryFromProtoError::UnknownEnumVariant(s.to_string())
88 }
89}
90
91impl From<TryFromIntError> for TryFromProtoError {
92 fn from(error: TryFromIntError) -> Self {
93 TryFromProtoError::TryFromIntError(error)
94 }
95}
96
97impl From<NonNegError> for TryFromProtoError {
98 fn from(error: NonNegError) -> Self {
99 TryFromProtoError::NonNegError(error)
100 }
101}
102
103impl From<CharTryFromError> for TryFromProtoError {
104 fn from(error: CharTryFromError) -> Self {
105 TryFromProtoError::CharTryFromError(error)
106 }
107}
108
109impl From<UnknownEnumValue> for TryFromProtoError {
110 fn from(UnknownEnumValue(n): UnknownEnumValue) -> Self {
111 TryFromProtoError::UnknownEnumVariant(format!("value {n}"))
112 }
113}
114
115impl From<regex::Error> for TryFromProtoError {
119 fn from(error: regex::Error) -> Self {
120 TryFromProtoError::RegexError(error)
121 }
122}
123
124impl From<serde_json::Error> for TryFromProtoError {
125 fn from(error: serde_json::Error) -> Self {
126 TryFromProtoError::DeserializationError(error)
127 }
128}
129
130impl From<http::uri::InvalidUri> for TryFromProtoError {
131 fn from(error: http::uri::InvalidUri) -> Self {
132 TryFromProtoError::InvalidUri(error)
133 }
134}
135
136impl From<globset::Error> for TryFromProtoError {
137 fn from(error: globset::Error) -> Self {
138 TryFromProtoError::GlobError(error)
139 }
140}
141
142impl From<url::ParseError> for TryFromProtoError {
143 fn from(error: url::ParseError) -> Self {
144 TryFromProtoError::InvalidUrl(error)
145 }
146}
147
148impl std::fmt::Display for TryFromProtoError {
149 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
150 use TryFromProtoError::*;
151 match self {
152 TryFromIntError(error) => error.fmt(f),
153 NonNegError(error) => error.fmt(f),
154 CharTryFromError(error) => error.fmt(f),
155 DateConversionError(msg) => write!(f, "Date conversion failed: `{}`", msg),
156 RegexError(error) => error.fmt(f),
157 DeserializationError(error) => error.fmt(f),
158 RowConversionError(msg) => write!(f, "Row packing failed: `{}`", msg),
159 MissingField(field) => write!(f, "Missing value for `{}`", field),
160 UnknownEnumVariant(field) => write!(f, "Unknown enum value for `{}`", field),
161 InvalidShardId(value) => write!(f, "Invalid value of ShardId found: `{}`", value),
162 CodecMismatch(error) => error.fmt(f),
163 InvalidPersistState(error) => error.fmt(f),
164 InvalidSemverVersion(error) => error.fmt(f),
165 InvalidUri(error) => error.fmt(f),
166 GlobError(error) => error.fmt(f),
167 InvalidUrl(error) => error.fmt(f),
168 InvalidBitFlags(error) => error.fmt(f),
169 LikePatternDeserializationError(inner_error) => write!(
170 f,
171 "Protobuf deserialization failed for a LIKE/ILIKE pattern: `{}`",
172 inner_error
173 ),
174 InvalidFieldError(error) => error.fmt(f),
175 }
176 }
177}
178
179impl From<TryFromProtoError> for String {
182 fn from(error: TryFromProtoError) -> Self {
183 error.to_string()
184 }
185}
186
187impl std::error::Error for TryFromProtoError {
188 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
189 use TryFromProtoError::*;
190 match self {
191 TryFromIntError(error) => Some(error),
192 NonNegError(error) => Some(error),
193 CharTryFromError(error) => Some(error),
194 RegexError(error) => Some(error),
195 DeserializationError(error) => Some(error),
196 DateConversionError(_) => None,
197 RowConversionError(_) => None,
198 MissingField(_) => None,
199 UnknownEnumVariant(_) => None,
200 InvalidShardId(_) => None,
201 CodecMismatch(_) => None,
202 InvalidPersistState(_) => None,
203 InvalidSemverVersion(_) => None,
204 InvalidUri(error) => Some(error),
205 GlobError(error) => Some(error),
206 InvalidUrl(error) => Some(error),
207 InvalidBitFlags(_) => None,
208 LikePatternDeserializationError(_) => None,
209 InvalidFieldError(_) => None,
210 }
211 }
212}
213
214pub trait ProtoRepr: Sized + RustType<Self::Proto> {
217 type Proto: ::prost::Message;
218}
219
220pub trait RustType<Proto>: Sized {
235 fn into_proto(&self) -> Proto;
237
238 fn into_proto_owned(self) -> Proto {
242 self.into_proto()
243 }
244
245 fn from_proto(proto: Proto) -> Result<Self, TryFromProtoError>;
251}
252
253pub trait ProtoMapEntry<K, V> {
256 fn from_rust<'a>(entry: (&'a K, &'a V)) -> Self;
257 fn into_rust(self) -> Result<(K, V), TryFromProtoError>;
258}
259
260macro_rules! rust_type_id(
261 ($($t:ty),*) => (
262 $(
263 impl RustType<$t> for $t {
265 #[inline]
266 fn into_proto(&self) -> $t {
267 self.clone()
268 }
269
270 #[inline]
271 fn from_proto(proto: $t) -> Result<Self, TryFromProtoError> {
272 Ok(proto)
273 }
274 }
275 )+
276 );
277);
278
279rust_type_id![bool, f32, f64, i32, i64, String, u32, u64, Vec<u8>];
280
281impl RustType<u64> for Option<NonZeroU64> {
282 fn into_proto(&self) -> u64 {
283 match self {
284 Some(d) => d.get(),
285 None => 0,
286 }
287 }
288
289 fn from_proto(proto: u64) -> Result<Self, TryFromProtoError> {
290 Ok(NonZeroU64::new(proto)) }
292}
293
294impl RustType<i64> for Overflowing<i64> {
295 #[inline(always)]
296 fn into_proto(&self) -> i64 {
297 self.into_inner()
298 }
299
300 #[inline(always)]
301 fn from_proto(proto: i64) -> Result<Self, TryFromProtoError> {
302 Ok(proto.into())
303 }
304}
305
306impl<K, V, T> RustType<Vec<T>> for BTreeMap<K, V>
309where
310 K: std::cmp::Eq + std::cmp::Ord,
311 T: ProtoMapEntry<K, V>,
312{
313 fn into_proto(&self) -> Vec<T> {
314 self.iter().map(T::from_rust).collect()
315 }
316
317 fn from_proto(proto: Vec<T>) -> Result<Self, TryFromProtoError> {
318 proto
319 .into_iter()
320 .map(T::into_rust)
321 .collect::<Result<BTreeMap<_, _>, _>>()
322 }
323}
324
325impl<R, P> RustType<Vec<P>> for BTreeSet<R>
327where
328 R: RustType<P> + std::cmp::Ord,
329{
330 fn into_proto(&self) -> Vec<P> {
331 self.iter().map(R::into_proto).collect()
332 }
333
334 fn from_proto(proto: Vec<P>) -> Result<Self, TryFromProtoError> {
335 proto
336 .into_iter()
337 .map(R::from_proto)
338 .collect::<Result<BTreeSet<_>, _>>()
339 }
340}
341
342impl<R, P> RustType<Vec<P>> for Vec<R>
344where
345 R: RustType<P>,
346{
347 fn into_proto(&self) -> Vec<P> {
348 self.iter().map(R::into_proto).collect()
349 }
350
351 fn from_proto(proto: Vec<P>) -> Result<Self, TryFromProtoError> {
352 proto.into_iter().map(R::from_proto).collect()
353 }
354}
355
356impl<R, P> RustType<Vec<P>> for Box<[R]>
358where
359 R: RustType<P>,
360{
361 fn into_proto(&self) -> Vec<P> {
362 self.iter().map(R::into_proto).collect()
363 }
364
365 fn from_proto(proto: Vec<P>) -> Result<Self, TryFromProtoError> {
366 proto.into_iter().map(R::from_proto).collect()
367 }
368}
369
370impl<R, P> RustType<Option<P>> for Option<R>
372where
373 R: RustType<P>,
374{
375 fn into_proto(&self) -> Option<P> {
376 self.as_ref().map(R::into_proto)
377 }
378
379 fn from_proto(proto: Option<P>) -> Result<Self, TryFromProtoError> {
380 proto.map(R::from_proto).transpose()
381 }
382}
383
384impl<R, P> RustType<Box<P>> for Box<R>
386where
387 R: RustType<P>,
388{
389 fn into_proto(&self) -> Box<P> {
390 Box::new((**self).into_proto())
391 }
392
393 fn from_proto(proto: Box<P>) -> Result<Self, TryFromProtoError> {
394 (*proto).into_rust().map(Box::new)
395 }
396}
397
398impl<R, P> RustType<P> for Arc<R>
399where
400 R: RustType<P>,
401{
402 fn into_proto(&self) -> P {
403 (**self).into_proto()
404 }
405
406 fn from_proto(proto: P) -> Result<Self, TryFromProtoError> {
407 proto.into_rust().map(Arc::new)
408 }
409}
410
411impl<R1, R2, P1, P2> RustType<(P1, P2)> for (R1, R2)
412where
413 R1: RustType<P1>,
414 R2: RustType<P2>,
415{
416 fn into_proto(&self) -> (P1, P2) {
417 (self.0.into_proto(), self.1.into_proto())
418 }
419
420 fn from_proto(proto: (P1, P2)) -> Result<Self, TryFromProtoError> {
421 let first = proto.0.into_rust()?;
422 let second = proto.1.into_rust()?;
423
424 Ok((first, second))
425 }
426}
427
428impl RustType<()> for () {
429 fn into_proto(&self) -> () {
430 *self
431 }
432
433 fn from_proto(proto: ()) -> Result<Self, TryFromProtoError> {
434 Ok(proto)
435 }
436}
437
438impl RustType<u64> for usize {
439 fn into_proto(&self) -> u64 {
440 u64::cast_from(*self)
441 }
442
443 fn from_proto(proto: u64) -> Result<Self, TryFromProtoError> {
444 usize::try_from(proto).map_err(TryFromProtoError::from)
445 }
446}
447
448impl RustType<u32> for char {
449 fn into_proto(&self) -> u32 {
450 (*self).into()
451 }
452
453 fn from_proto(proto: u32) -> Result<Self, TryFromProtoError> {
454 char::try_from(proto).map_err(TryFromProtoError::from)
455 }
456}
457
458impl RustType<u32> for u8 {
459 fn into_proto(&self) -> u32 {
460 u32::from(*self)
461 }
462
463 fn from_proto(proto: u32) -> Result<Self, TryFromProtoError> {
464 u8::try_from(proto).map_err(TryFromProtoError::from)
465 }
466}
467
468impl RustType<u32> for u16 {
469 fn into_proto(&self) -> u32 {
470 u32::from(*self)
471 }
472
473 fn from_proto(repr: u32) -> Result<Self, TryFromProtoError> {
474 u16::try_from(repr).map_err(TryFromProtoError::from)
475 }
476}
477
478impl RustType<i32> for i8 {
479 fn into_proto(&self) -> i32 {
480 i32::from(*self)
481 }
482
483 fn from_proto(proto: i32) -> Result<Self, TryFromProtoError> {
484 i8::try_from(proto).map_err(TryFromProtoError::from)
485 }
486}
487
488impl RustType<i32> for i16 {
489 fn into_proto(&self) -> i32 {
490 i32::from(*self)
491 }
492
493 fn from_proto(repr: i32) -> Result<Self, TryFromProtoError> {
494 i16::try_from(repr).map_err(TryFromProtoError::from)
495 }
496}
497
498impl RustType<u64> for std::num::NonZeroUsize {
499 fn into_proto(&self) -> u64 {
500 usize::from(*self).into_proto()
501 }
502
503 fn from_proto(proto: u64) -> Result<Self, TryFromProtoError> {
504 Ok(usize::from_proto(proto)?.try_into()?)
505 }
506}
507
508impl<T> RustType<T> for NonNeg<T>
509where
510 T: Clone + Signed + fmt::Display,
511{
512 fn into_proto(&self) -> T {
513 (**self).clone()
514 }
515
516 fn from_proto(proto: T) -> Result<Self, TryFromProtoError> {
517 Ok(NonNeg::<T>::try_from(proto)?)
518 }
519}
520
521impl RustType<ProtoDuration> for std::time::Duration {
522 fn into_proto(&self) -> ProtoDuration {
523 ProtoDuration {
524 secs: self.as_secs(),
525 nanos: self.subsec_nanos(),
526 }
527 }
528
529 fn from_proto(proto: ProtoDuration) -> Result<Self, TryFromProtoError> {
530 Ok(std::time::Duration::new(proto.secs, proto.nanos))
531 }
532}
533
534impl<'a> RustType<String> for Cow<'a, str> {
535 fn into_proto(&self) -> String {
536 self.to_string()
537 }
538 fn from_proto(proto: String) -> Result<Self, TryFromProtoError> {
539 Ok(Cow::Owned(proto))
540 }
541}
542
543impl RustType<String> for Box<str> {
544 fn into_proto(&self) -> String {
545 self.to_string()
546 }
547 fn from_proto(proto: String) -> Result<Self, TryFromProtoError> {
548 Ok(proto.into())
549 }
550}
551
552pub trait ProtoType<Rust>: Sized {
561 fn into_rust(self) -> Result<Rust, TryFromProtoError>;
563
564 fn from_rust(rust: &Rust) -> Self;
566}
567
568impl<P, R> ProtoType<R> for P
571where
572 R: RustType<P>,
573{
574 #[inline]
575 fn into_rust(self) -> Result<R, TryFromProtoError> {
576 R::from_proto(self)
577 }
578
579 #[inline]
580 fn from_rust(rust: &R) -> Self {
581 R::into_proto(rust)
582 }
583}
584
585pub fn any_duration() -> impl Strategy<Value = std::time::Duration> {
586 (0..u64::MAX, 0..1_000_000_000u32)
587 .prop_map(|(secs, nanos)| std::time::Duration::new(secs, nanos))
588}
589
590pub trait IntoRustIfSome<T> {
594 fn into_rust_if_some<S: ToString>(self, field: S) -> Result<T, TryFromProtoError>;
595}
596
597impl<R, P> IntoRustIfSome<R> for Option<P>
600where
601 R: RustType<P>,
602{
603 fn into_rust_if_some<S: ToString>(self, field: S) -> Result<R, TryFromProtoError> {
604 R::from_proto(self.ok_or_else(|| TryFromProtoError::missing_field(field))?)
605 }
606}
607
608pub trait TryIntoIfSome<T> {
612 fn try_into_if_some<S: ToString>(self, field: S) -> Result<T, TryFromProtoError>;
613}
614
615impl<T, U> TryIntoIfSome<T> for Option<U>
618where
619 T: TryFrom<U, Error = TryFromProtoError>,
620{
621 fn try_into_if_some<S: ToString>(self, field: S) -> Result<T, TryFromProtoError> {
622 self.ok_or_else(|| TryFromProtoError::missing_field(field))?
623 .try_into()
624 }
625}
626
627pub fn protobuf_roundtrip<R, P>(val: &R) -> anyhow::Result<R>
630where
631 P: ProtoType<R> + ::prost::Message + Default,
632{
633 let vec = P::from_rust(val).encode_to_vec();
634 let val = P::decode(&*vec)?.into_rust()?;
635 Ok(val)
636}
637
638#[cfg(test)]
639mod tests {
640 use mz_ore::assert_ok;
641 use proptest::prelude::*;
642
643 use super::*;
644
645 proptest! {
646 #![proptest_config(ProptestConfig::with_cases(4096))]
647
648 #[mz_ore::test]
649 #[cfg_attr(miri, ignore)] fn duration_protobuf_roundtrip(expect in any_duration() ) {
651 let actual = protobuf_roundtrip::<_, ProtoDuration>(&expect);
652 assert_ok!(actual);
653 assert_eq!(actual.unwrap(), expect);
654 }
655 }
656}