noir_protobuf/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
use color_eyre::eyre::{self, Context, bail, eyre};

/// A protobuf codec to convert between a domain type `T`
/// and its protobuf representation `R`.
///
/// It is to be implemented on a `Self` independent of `T` and `R`,
/// so that `T` can be in a third party crate, and `Self` can be
/// generic in the `F` _field_ type as well, which would be cumbersome
/// if we had to implement traits on `R` because `T` is in another
/// crate from the schema, or to scatter the `.proto` schema around
/// so that the traits can be co-defined with `T` which is what can
/// actually be generic in `F`.
pub trait ProtoCodec<T, R> {
    /// Convert domain type `T` to protobuf representation `R`.
    fn encode(value: &T) -> R;
    /// Encode a field as `Some`.
    fn encode_some(value: &T) -> Option<R> {
        Some(Self::encode(value))
    }
    /// Encode an `enum` to the `i32` value that `prost` represents it with.
    fn encode_enum(value: &T) -> i32
    where
        R: Into<i32>,
    {
        Self::encode(value).into()
    }
    /// Encode multiple values as a vector.
    fn encode_vec<'a, I>(values: I) -> Vec<R>
    where
        I: IntoIterator<Item = &'a T>,
        T: 'a,
    {
        values.into_iter().map(Self::encode).collect()
    }

    /// Try to convert protobuf representation `R` to domain type `T`.
    fn decode(value: &R) -> eyre::Result<T>;
    /// Decode a field and attach the name of the field if it fails.
    fn decode_wrap(value: &R, msg: &'static str) -> eyre::Result<T> {
        Self::decode(value).wrap_err(msg)
    }
    /// Decode multiple values into a vector.
    fn decode_vec(values: &[R]) -> eyre::Result<Vec<T>> {
        values.iter().map(Self::decode).collect()
    }
    /// Decode multiple values into a vector, attaching a field name to any errors.
    fn decode_vec_wrap(values: &[R], msg: &'static str) -> eyre::Result<Vec<T>> {
        Self::decode_vec(values).wrap_err(msg)
    }
    /// Decode a fixed size array.
    fn decode_arr<const N: usize>(values: &[R]) -> eyre::Result<[T; N]> {
        match Self::decode_vec(values)?.try_into() {
            Ok(arr) => Ok(arr),
            Err(vec) => {
                bail!("expected {N} items, got {}", vec.len());
            }
        }
    }
    /// Decode a fixed size array, attaching a field name to any errors
    fn decode_arr_wrap<const N: usize>(values: &[R], msg: &'static str) -> eyre::Result<[T; N]> {
        Self::decode_arr(values).wrap_err(msg)
    }
    /// Decode a boxed fixed size array.
    fn decode_box_arr<const N: usize>(values: &[R]) -> eyre::Result<Box<[T; N]>> {
        Self::decode_arr(values).map(Box::new)
    }
    /// Decode a boxed fixed size array, attaching a field name to any errors
    fn decode_box_arr_wrap<const N: usize>(
        values: &[R],
        msg: &'static str,
    ) -> eyre::Result<Box<[T; N]>> {
        Self::decode_box_arr(values).wrap_err(msg)
    }
    /// Decode an optional field as a required one; fails if it's `None`.
    fn decode_some(value: &Option<R>) -> eyre::Result<T> {
        match value {
            Some(value) => Self::decode(value),
            None => Err(eyre!("missing field")),
        }
    }
    /// Decode an optional field as a required one, attaching a field name to any errors.
    /// Returns error if the field is missing.
    fn decode_some_wrap(value: &Option<R>, msg: &'static str) -> eyre::Result<T> {
        Self::decode_some(value).wrap_err(msg)
    }
    /// Decode an optional field, attaching a field name to any errors.
    /// Return `None` if the field is missing.
    fn decode_opt_wrap(value: &Option<R>, msg: &'static str) -> eyre::Result<Option<T>> {
        value.as_ref().map(|value| Self::decode_wrap(value, msg)).transpose()
    }
    /// Decode the numeric representation of an enum into the domain type.
    /// Return an error if the value cannot be recognized.
    fn decode_enum(value: i32) -> eyre::Result<T>
    where
        R: TryFrom<i32, Error = prost::UnknownEnumValue>,
    {
        let r = R::try_from(value)?;
        Self::decode(&r)
    }
    /// Decode the numeric representation of an enum, attaching the field name to any errors.
    fn decode_enum_wrap(value: i32, msg: &'static str) -> eyre::Result<T>
    where
        R: TryFrom<i32, Error = prost::UnknownEnumValue>,
    {
        Self::decode_enum(value).wrap_err(msg)
    }

    /// Encode a domain type to protobuf and serialize it to bytes.
    fn serialize_to_vec(value: &T) -> Vec<u8>
    where
        R: prost::Message,
    {
        Self::encode(value).encode_to_vec()
    }
    /// Deserialize a buffer into protobuf and then decode into the domain type.
    fn deserialize_from_slice(buf: &[u8]) -> eyre::Result<T>
    where
        R: prost::Message + Default,
    {
        let repr = R::decode(buf).wrap_err("failed to decode into protobuf")?;
        Self::decode(&repr).wrap_err("failed to decode protobuf into domain")
    }
}

/// Decode repeated items by mapping a function over them, attaching an error message if it fails.
/// Useful when a lambda needs to be applied before we can use one of the type class methods.
pub fn decode_vec_map_wrap<R, T, F>(rs: &[R], msg: &'static str, f: F) -> eyre::Result<Vec<T>>
where
    F: Fn(&R) -> eyre::Result<T>,
{
    rs.iter().map(f).collect::<eyre::Result<Vec<_>>>().wrap_err(msg)
}

/// Decode an optional item, returning an error if it's `None`.
/// Useful when a lambda needs to be applied before we can use one of the type class methods.
pub fn decode_some_map<R, T, F>(r: &Option<R>, f: F) -> eyre::Result<T>
where
    F: Fn(&R) -> eyre::Result<T>,
{
    match r {
        Some(r) => f(r),
        None => Err(eyre!("missing field")),
    }
}

/// Decode an optional item, attaching a field name to any errors.
/// Useful when a lambda needs to be applied before we can use one of the type class methods.
pub fn decode_some_map_wrap<R, T, F>(r: &Option<R>, msg: &'static str, f: F) -> eyre::Result<T>
where
    F: Fn(&R) -> eyre::Result<T>,
{
    decode_some_map(r, f).wrap_err(msg)
}

/// Decode a `oneof` field, returning an error if it's missing.
/// Useful when a lambda needs to be applied before we can use one of the type class methods.
pub fn decode_oneof_map<R, T, F>(r: &Option<R>, f: F) -> eyre::Result<T>
where
    F: Fn(&R) -> eyre::Result<T>,
{
    decode_some_map_wrap(r, "oneof value", f)
}